summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml21
-rw-r--r--.github/workflows/static_checks.yml14
-rw-r--r--.github/workflows/windows_builds.yml2
-rw-r--r--.gitignore4
-rw-r--r--CHANGELOG.md4
-rw-r--r--CONTRIBUTING.md81
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct28
-rw-r--r--core/config/project_settings.cpp12
-rw-r--r--core/core_bind.cpp38
-rw-r--r--core/core_bind.h4
-rw-r--r--core/core_constants.cpp2
-rw-r--r--core/crypto/SCsub19
-rw-r--r--core/crypto/crypto.cpp2
-rw-r--r--core/crypto/crypto.h2
-rw-r--r--core/doc_data.cpp1
-rw-r--r--core/doc_data.h61
-rw-r--r--core/extension/gdextension.cpp39
-rw-r--r--core/extension/gdextension_interface.cpp88
-rw-r--r--core/extension/gdextension_interface.h1
-rw-r--r--core/input/input.cpp26
-rw-r--r--core/input/input_event.cpp5
-rw-r--r--core/io/dir_access.h2
-rw-r--r--core/io/file_access.h2
-rw-r--r--core/io/file_access_compressed.cpp8
-rw-r--r--core/io/file_access_network.cpp498
-rw-r--r--core/io/file_access_network.h167
-rw-r--r--core/io/http_client.cpp5
-rw-r--r--core/io/http_client_tcp.cpp1
-rw-r--r--core/io/json.cpp8
-rw-r--r--core/io/packed_data_container.cpp11
-rw-r--r--core/io/packed_data_container.h1
-rw-r--r--core/io/remote_filesystem_client.cpp329
-rw-r--r--core/io/remote_filesystem_client.h65
-rw-r--r--core/io/resource_format_binary.cpp47
-rw-r--r--core/io/resource_format_binary.h2
-rw-r--r--core/io/resource_loader.cpp604
-rw-r--r--core/io/resource_loader.h72
-rw-r--r--core/math/a_star_grid_2d.cpp74
-rw-r--r--core/math/a_star_grid_2d.h15
-rw-r--r--core/math/basis.cpp4
-rw-r--r--core/math/bvh_debug.inc6
-rw-r--r--core/math/convex_hull.cpp20
-rw-r--r--core/math/math_funcs.h10
-rw-r--r--core/math/quaternion.cpp3
-rw-r--r--core/math/static_raycaster.h16
-rw-r--r--core/object/class_db.cpp8
-rw-r--r--core/object/message_queue.cpp176
-rw-r--r--core/object/message_queue.h29
-rw-r--r--core/object/method_bind.h49
-rw-r--r--core/object/object.cpp96
-rw-r--r--core/object/object.h11
-rw-r--r--core/object/ref_counted.h12
-rw-r--r--core/object/worker_thread_pool.cpp55
-rw-r--r--core/object/worker_thread_pool.h4
-rw-r--r--core/os/mutex.h19
-rw-r--r--core/os/os.cpp16
-rw-r--r--core/os/os.h7
-rw-r--r--core/os/semaphore.h55
-rw-r--r--core/os/spin_lock.h6
-rw-r--r--core/os/thread.cpp9
-rw-r--r--core/os/thread.h4
-rw-r--r--core/os/time.cpp8
-rw-r--r--core/register_core_types.cpp12
-rw-r--r--core/string/print_string.cpp6
-rw-r--r--core/string/print_string.h10
-rw-r--r--core/string/translation.cpp17
-rw-r--r--core/string/ustring.cpp205
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/templates/hash_map.h4
-rw-r--r--core/templates/hash_set.h4
-rw-r--r--core/templates/hashfuncs.h6
-rw-r--r--core/templates/local_vector.h19
-rw-r--r--core/templates/paged_allocator.h45
-rw-r--r--core/templates/vector.h5
-rw-r--r--core/variant/binder_common.h128
-rw-r--r--core/variant/typed_array.h14
-rw-r--r--core/variant/variant_call.cpp2
-rw-r--r--core/variant/variant_construct.cpp11
-rw-r--r--core/variant/variant_internal.h38
-rw-r--r--core/variant/variant_utility.cpp2
-rw-r--r--doc/classes/@GlobalScope.xml12
-rw-r--r--doc/classes/AStarGrid2D.xml13
-rw-r--r--doc/classes/Animation.xml4
-rw-r--r--doc/classes/AnimationNode.xml4
-rw-r--r--doc/classes/AnimationNodeAdd2.xml2
-rw-r--r--doc/classes/AnimationNodeAdd3.xml2
-rw-r--r--doc/classes/AnimationNodeAnimation.xml6
-rw-r--r--doc/classes/AnimationNodeBlend2.xml2
-rw-r--r--doc/classes/AnimationNodeBlend3.xml2
-rw-r--r--doc/classes/AnimationNodeBlendSpace1D.xml11
-rw-r--r--doc/classes/AnimationNodeBlendSpace2D.xml10
-rw-r--r--doc/classes/AnimationNodeBlendTree.xml6
-rw-r--r--doc/classes/AnimationNodeOneShot.xml35
-rw-r--r--doc/classes/AnimationNodeOutput.xml5
-rw-r--r--doc/classes/AnimationNodeStateMachine.xml6
-rw-r--r--doc/classes/AnimationNodeStateMachinePlayback.xml6
-rw-r--r--doc/classes/AnimationNodeStateMachineTransition.xml4
-rw-r--r--doc/classes/AnimationNodeSync.xml4
-rw-r--r--doc/classes/AnimationNodeTimeScale.xml6
-rw-r--r--doc/classes/AnimationNodeTimeSeek.xml4
-rw-r--r--doc/classes/AnimationNodeTransition.xml4
-rw-r--r--doc/classes/AnimationPlayer.xml11
-rw-r--r--doc/classes/AnimationRootNode.xml4
-rw-r--r--doc/classes/AnimationTree.xml4
-rw-r--r--doc/classes/AudioStreamGenerator.xml26
-rw-r--r--doc/classes/BaseMaterial3D.xml4
-rw-r--r--doc/classes/Bone2D.xml22
-rw-r--r--doc/classes/BoneAttachment3D.xml5
-rw-r--r--doc/classes/BoneMap.xml4
-rw-r--r--doc/classes/Button.xml5
-rw-r--r--doc/classes/CanvasItem.xml16
-rw-r--r--doc/classes/CodeEdit.xml11
-rw-r--r--doc/classes/ColorPicker.xml3
-rw-r--r--doc/classes/Control.xml1
-rw-r--r--doc/classes/Curve.xml2
-rw-r--r--doc/classes/Curve2D.xml2
-rw-r--r--doc/classes/Curve3D.xml2
-rw-r--r--doc/classes/CurveTexture.xml2
-rw-r--r--doc/classes/Decal.xml2
-rw-r--r--doc/classes/DirAccess.xml1
-rw-r--r--doc/classes/DisplayServer.xml12
-rw-r--r--doc/classes/EditorResourcePreviewGenerator.xml4
-rw-r--r--doc/classes/EditorResourceTooltipPlugin.xml46
-rw-r--r--doc/classes/EditorScript.xml4
-rw-r--r--doc/classes/EditorSettings.xml25
-rw-r--r--doc/classes/EditorVCSInterface.xml38
-rw-r--r--doc/classes/Engine.xml2
-rw-r--r--doc/classes/EngineDebugger.xml2
-rw-r--r--doc/classes/Expression.xml1
-rw-r--r--doc/classes/FileAccess.xml32
-rw-r--r--doc/classes/FileSystemDock.xml14
-rw-r--r--doc/classes/FontFile.xml2
-rw-r--r--doc/classes/GPUParticlesCollisionHeightField3D.xml2
-rw-r--r--doc/classes/Geometry3D.xml2
-rw-r--r--doc/classes/GeometryInstance3D.xml7
-rw-r--r--doc/classes/HSlider.xml3
-rw-r--r--doc/classes/Image.xml10
-rw-r--r--doc/classes/ImageTexture3D.xml2
-rw-r--r--doc/classes/ImageTextureLayered.xml2
-rw-r--r--doc/classes/ImmediateMesh.xml13
-rw-r--r--doc/classes/Input.xml1
-rw-r--r--doc/classes/ItemList.xml10
-rw-r--r--doc/classes/Light3D.xml2
-rw-r--r--doc/classes/LineEdit.xml6
-rw-r--r--doc/classes/Marker2D.xml2
-rw-r--r--doc/classes/MeshDataTool.xml4
-rw-r--r--doc/classes/Mutex.xml6
-rw-r--r--doc/classes/NavigationAgent2D.xml65
-rw-r--r--doc/classes/NavigationAgent3D.xml79
-rw-r--r--doc/classes/NavigationLink2D.xml8
-rw-r--r--doc/classes/NavigationLink3D.xml8
-rw-r--r--doc/classes/NavigationObstacle2D.xml53
-rw-r--r--doc/classes/NavigationObstacle3D.xml60
-rw-r--r--doc/classes/NavigationRegion2D.xml25
-rw-r--r--doc/classes/NavigationRegion3D.xml3
-rw-r--r--doc/classes/NavigationServer2D.xml171
-rw-r--r--doc/classes/NavigationServer3D.xml210
-rw-r--r--doc/classes/Node.xml81
-rw-r--r--doc/classes/Node3D.xml5
-rw-r--r--doc/classes/OS.xml10
-rw-r--r--doc/classes/Object.xml8
-rw-r--r--doc/classes/PackedByteArray.xml50
-rw-r--r--doc/classes/PackedDataContainer.xml29
-rw-r--r--doc/classes/PackedDataContainerRef.xml25
-rw-r--r--doc/classes/ParticleProcessMaterial.xml11
-rw-r--r--doc/classes/PathFollow3D.xml2
-rw-r--r--doc/classes/PhysicalBone2D.xml24
-rw-r--r--doc/classes/PhysicalBone3D.xml3
-rw-r--r--doc/classes/PhysicsDirectBodyState2D.xml4
-rw-r--r--doc/classes/PhysicsDirectBodyState3D.xml4
-rw-r--r--doc/classes/PhysicsServer2D.xml2
-rw-r--r--doc/classes/PopupMenu.xml15
-rw-r--r--doc/classes/ProjectSettings.xml473
-rw-r--r--doc/classes/RDAttachmentFormat.xml5
-rw-r--r--doc/classes/RDFramebufferPass.xml6
-rw-r--r--doc/classes/RDPipelineColorBlendState.xml6
-rw-r--r--doc/classes/RDPipelineColorBlendStateAttachment.xml70
-rw-r--r--doc/classes/RDPipelineDepthStencilState.xml3
-rw-r--r--doc/classes/RDPipelineMultisampleState.xml8
-rw-r--r--doc/classes/RDPipelineRasterizationState.xml8
-rw-r--r--doc/classes/RDPipelineSpecializationConstant.xml5
-rw-r--r--doc/classes/RDSamplerState.xml16
-rw-r--r--doc/classes/RDShaderFile.xml9
-rw-r--r--doc/classes/RDShaderSPIRV.xml17
-rw-r--r--doc/classes/RDShaderSource.xml11
-rw-r--r--doc/classes/RDTextureFormat.xml11
-rw-r--r--doc/classes/RDTextureView.xml7
-rw-r--r--doc/classes/RDUniform.xml4
-rw-r--r--doc/classes/RDVertexAttribute.xml2
-rw-r--r--doc/classes/Rect2.xml2
-rw-r--r--doc/classes/Rect2i.xml4
-rw-r--r--doc/classes/ReflectionProbe.xml14
-rw-r--r--doc/classes/RenderingDevice.xml615
-rw-r--r--doc/classes/RenderingServer.xml677
-rw-r--r--doc/classes/ResourceLoader.xml1
-rw-r--r--doc/classes/RibbonTrailMesh.xml13
-rw-r--r--doc/classes/RichTextLabel.xml2
-rw-r--r--doc/classes/RigidBody2D.xml2
-rw-r--r--doc/classes/RigidBody3D.xml2
-rw-r--r--doc/classes/SceneTree.xml6
-rw-r--r--doc/classes/ScrollContainer.xml18
-rw-r--r--doc/classes/Semaphore.xml4
-rw-r--r--doc/classes/ShaderGlobalsOverride.xml4
-rw-r--r--doc/classes/Skeleton2D.xml6
-rw-r--r--doc/classes/Skeleton3D.xml4
-rw-r--r--doc/classes/SkeletonIK3D.xml6
-rw-r--r--doc/classes/SkeletonModification2D.xml4
-rw-r--r--doc/classes/SkeletonModification2DTwoBoneIK.xml4
-rw-r--r--doc/classes/SkeletonProfile.xml2
-rw-r--r--doc/classes/Sky.xml2
-rw-r--r--doc/classes/SoftBody3D.xml4
-rw-r--r--doc/classes/StreamPeer.xml52
-rw-r--r--doc/classes/StreamPeerTLS.xml2
-rw-r--r--doc/classes/String.xml36
-rw-r--r--doc/classes/StringName.xml36
-rw-r--r--doc/classes/SurfaceTool.xml2
-rw-r--r--doc/classes/SyntaxHighlighter.xml2
-rw-r--r--doc/classes/TextEdit.xml4
-rw-r--r--doc/classes/TextServerDummy.xml14
-rw-r--r--doc/classes/Thread.xml5
-rw-r--r--doc/classes/TileMap.xml16
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--doc/classes/TileSetScenesCollectionSource.xml2
-rw-r--r--doc/classes/Timer.xml3
-rw-r--r--doc/classes/Tree.xml5
-rw-r--r--doc/classes/TreeItem.xml46
-rw-r--r--doc/classes/TubeTrailMesh.xml11
-rw-r--r--doc/classes/VSlider.xml3
-rw-r--r--doc/classes/VideoStreamPlayback.xml10
-rw-r--r--doc/classes/Viewport.xml10
-rw-r--r--doc/classes/ViewportTexture.xml3
-rw-r--r--doc/classes/VisualInstance3D.xml2
-rw-r--r--doc/classes/VoxelGI.xml2
-rw-r--r--doc/classes/Window.xml15
-rw-r--r--doc/classes/WorkerThreadPool.xml51
-rw-r--r--doc/classes/X509Certificate.xml15
-rwxr-xr-xdoc/tools/make_rst.py20
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp7
-rw-r--r--drivers/alsamidi/midi_driver_alsamidi.cpp4
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp4
-rw-r--r--drivers/gl_context/SCsub6
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp38
-rw-r--r--drivers/gles3/shader_gles3.cpp206
-rw-r--r--drivers/gles3/shader_gles3.h3
-rw-r--r--drivers/gles3/shaders/scene.glsl2
-rw-r--r--drivers/gles3/storage/light_storage.cpp1
-rw-r--r--drivers/gles3/storage/material_storage.cpp3
-rw-r--r--drivers/gles3/storage/texture_storage.cpp7
-rw-r--r--drivers/gles3/storage/texture_storage.h1
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp10
-rw-r--r--drivers/unix/file_access_unix.cpp12
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp77
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h2
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp29
-rw-r--r--drivers/windows/dir_access_windows.cpp15
-rw-r--r--drivers/windows/dir_access_windows.h3
-rw-r--r--drivers/windows/file_access_windows.cpp8
-rw-r--r--drivers/windows/file_access_windows.h1
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp7
-rw-r--r--editor/code_editor.cpp112
-rw-r--r--editor/code_editor.h3
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_parser.cpp17
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_protocol.cpp6
-rw-r--r--editor/debugger/editor_debugger_node.cpp16
-rw-r--r--editor/debugger/editor_file_server.cpp424
-rw-r--r--editor/debugger/editor_file_server.h30
-rw-r--r--editor/debugger/script_editor_debugger.cpp10
-rw-r--r--editor/debugger/script_editor_debugger.h2
-rw-r--r--editor/doc_tools.cpp53
-rw-r--r--editor/doc_tools.h2
-rw-r--r--editor/editor_audio_buses.cpp12
-rw-r--r--editor/editor_command_palette.cpp14
-rw-r--r--editor/editor_data.cpp20
-rw-r--r--editor/editor_data.h7
-rw-r--r--editor/editor_file_system.cpp4
-rw-r--r--editor/editor_help.cpp153
-rw-r--r--editor/editor_help.h4
-rw-r--r--editor/editor_help_search.cpp5
-rw-r--r--editor/editor_inspector.cpp26
-rw-r--r--editor/editor_inspector.h4
-rw-r--r--editor/editor_interface.cpp17
-rw-r--r--editor/editor_node.cpp1011
-rw-r--r--editor/editor_node.h98
-rw-r--r--editor/editor_plugin.cpp6
-rw-r--r--editor/editor_plugin_settings.cpp1
-rw-r--r--editor/editor_properties.cpp13
-rw-r--r--editor/editor_properties.h1
-rw-r--r--editor/editor_properties_array_dict.cpp15
-rw-r--r--editor/editor_property_name_processor.cpp9
-rw-r--r--editor/editor_resource_preview.cpp115
-rw-r--r--editor/editor_resource_preview.h17
-rw-r--r--editor/editor_run.cpp7
-rw-r--r--editor/editor_run.h1
-rw-r--r--editor/editor_run_native.cpp7
-rw-r--r--editor/editor_run_native.h6
-rw-r--r--editor/editor_script.cpp (renamed from editor/editor_run_script.cpp)33
-rw-r--r--editor/editor_script.h (renamed from editor/editor_run_script.h)21
-rw-r--r--editor/editor_sectioned_inspector.cpp11
-rw-r--r--editor/editor_sectioned_inspector.h3
-rw-r--r--editor/editor_settings.cpp13
-rw-r--r--editor/editor_settings.h3
-rw-r--r--editor/editor_themes.cpp46
-rw-r--r--editor/export/editor_export.cpp34
-rw-r--r--editor/export/editor_export_platform.cpp121
-rw-r--r--editor/export/editor_export_platform.h5
-rw-r--r--editor/export/editor_export_preset.cpp11
-rw-r--r--editor/export/editor_export_preset.h2
-rw-r--r--editor/export/project_export.cpp15
-rw-r--r--editor/filesystem_dock.cpp330
-rw-r--r--editor/filesystem_dock.h31
-rw-r--r--editor/gui/editor_run_bar.cpp444
-rw-r--r--editor/gui/editor_run_bar.h115
-rw-r--r--editor/gui/editor_spin_slider.cpp9
-rw-r--r--editor/gui/editor_spin_slider.h1
-rw-r--r--editor/icons/BusVuActive.svg (renamed from editor/icons/BusVuFull.svg)0
-rw-r--r--editor/icons/BusVuEmpty.svg1
-rw-r--r--editor/icons/MakeFloating.svg1
-rw-r--r--editor/icons/SnapDisable.svg1
-rw-r--r--editor/icons/Vector4i.svg1
-rw-r--r--editor/import/collada.cpp1008
-rw-r--r--editor/import/collada.h72
-rw-r--r--editor/import/dynamic_font_import_settings.cpp12
-rw-r--r--editor/import/editor_import_collada.cpp7
-rw-r--r--editor/import/post_import_plugin_skeleton_rest_fixer.cpp2
-rw-r--r--editor/import/resource_importer_obj.cpp2
-rw-r--r--editor/import/resource_importer_shader_file.cpp2
-rw-r--r--editor/import/scene_import_settings.cpp5
-rw-r--r--editor/import_defaults_editor.cpp6
-rw-r--r--editor/import_dock.cpp2
-rw-r--r--editor/inspector_dock.cpp3
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp9
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp136
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h12
-rw-r--r--editor/plugins/curve_editor_plugin.cpp25
-rw-r--r--editor/plugins/curve_editor_plugin.h5
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp19
-rw-r--r--editor/plugins/debugger_editor_plugin.h1
-rw-r--r--editor/plugins/editor_preview_plugins.cpp29
-rw-r--r--editor/plugins/editor_preview_plugins.h26
-rw-r--r--editor/plugins/editor_resource_tooltip_plugins.cpp122
-rw-r--r--editor/plugins/editor_resource_tooltip_plugins.h70
-rw-r--r--editor/plugins/material_editor_plugin.cpp13
-rw-r--r--editor/plugins/material_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_obstacle_2d_editor_plugin.cpp66
-rw-r--r--editor/plugins/navigation_obstacle_2d_editor_plugin.h61
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.cpp599
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.h117
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp74
-rw-r--r--editor/plugins/node_3d_editor_plugin.h23
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.cpp9
-rw-r--r--editor/plugins/script_editor_plugin.cpp202
-rw-r--r--editor/plugins/script_editor_plugin.h28
-rw-r--r--editor/plugins/script_text_editor.cpp45
-rw-r--r--editor/plugins/script_text_editor.h3
-rw-r--r--editor/plugins/shader_editor_plugin.cpp150
-rw-r--r--editor/plugins/shader_editor_plugin.h13
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/text_editor.cpp14
-rw-r--r--editor/plugins/text_editor.h3
-rw-r--r--editor/plugins/text_shader_editor.cpp5
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp83
-rw-r--r--editor/plugins/tiles/tile_data_editors.h18
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp14
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp11
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp11
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp151
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h4
-rw-r--r--editor/project_settings_editor.cpp1
-rw-r--r--editor/property_selector.cpp2
-rw-r--r--editor/register_editor_types.cpp12
-rw-r--r--editor/scene_tree_dock.cpp122
-rw-r--r--editor/scene_tree_dock.h5
-rw-r--r--editor/script_create_dialog.cpp6
-rw-r--r--editor/window_wrapper.cpp474
-rw-r--r--editor/window_wrapper.h110
-rw-r--r--main/main.cpp162
-rwxr-xr-xmisc/scripts/codespell.sh2
-rw-r--r--modules/astcenc/SCsub1
-rw-r--r--modules/csg/csg.cpp135
-rw-r--r--modules/csg/csg.h17
-rw-r--r--modules/csg/csg_shape.cpp42
-rw-r--r--modules/csg/csg_shape.h6
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml10
-rw-r--r--modules/enet/doc_classes/ENetConnection.xml10
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml12
-rw-r--r--modules/enet/doc_classes/ENetPacketPeer.xml10
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp2
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml25
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp272
-rw-r--r--modules/gdscript/editor/gdscript_docgen.h42
-rw-r--r--modules/gdscript/gdscript.cpp304
-rw-r--r--modules/gdscript/gdscript.h24
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp106
-rw-r--r--modules/gdscript/gdscript_analyzer.h1
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h2
-rw-r--r--modules/gdscript/gdscript_cache.cpp10
-rw-r--r--modules/gdscript/gdscript_cache.h3
-rw-r--r--modules/gdscript/gdscript_codegen.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp300
-rw-r--r--modules/gdscript/gdscript_compiler.h3
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp23
-rw-r--r--modules/gdscript/gdscript_function.h3
-rw-r--r--modules/gdscript/gdscript_parser.cpp104
-rw-r--r--modules/gdscript/gdscript_parser.h23
-rw-r--r--modules/gdscript/gdscript_vm.cpp14
-rw-r--r--modules/gdscript/gdscript_warning.cpp215
-rw-r--r--modules/gdscript/gdscript_warning.h56
-rw-r--r--modules/gdscript/language_server/godot_lsp.h4
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp26
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/overriding_native_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/constants.out16
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/static_typing.out10
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_argument.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.gd15
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_constructor.gd13
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_constructor.out4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables.gd56
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables.out16
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables_load.gd10
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables_load.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables_other.gd11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/static_variables_other.out2
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml2
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp1
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp4
-rw-r--r--modules/gltf/gltf_document.cpp6
-rw-r--r--modules/gltf/structures/gltf_accessor.h2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml10
-rw-r--r--modules/gridmap/grid_map.cpp4
-rw-r--r--modules/mbedtls/SCsub4
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp59
-rw-r--r--modules/mbedtls/crypto_mbedtls.h2
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.cpp1
-rw-r--r--modules/mono/csharp_script.cpp7
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs3
-rw-r--r--modules/mono/editor/bindings_generator.cpp10
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs35
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml2
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml10
-rw-r--r--modules/multiplayer/doc_classes/SceneMultiplayer.xml4
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml16
-rw-r--r--modules/navigation/SCsub29
-rw-r--r--modules/navigation/godot_navigation_server.cpp218
-rw-r--r--modules/navigation/godot_navigation_server.h33
-rw-r--r--modules/navigation/nav_agent.cpp290
-rw-r--r--modules/navigation/nav_agent.h113
-rw-r--r--modules/navigation/nav_base.h5
-rw-r--r--modules/navigation/nav_link.cpp12
-rw-r--r--modules/navigation/nav_map.cpp266
-rw-r--r--modules/navigation/nav_map.h52
-rw-r--r--modules/navigation/nav_obstacle.cpp86
-rw-r--r--modules/navigation/nav_obstacle.h78
-rw-r--r--modules/navigation/nav_region.cpp13
-rw-r--r--modules/navigation/nav_region.h7
-rw-r--r--modules/noise/config.py1
-rw-r--r--modules/noise/doc_classes/Noise.xml33
-rw-r--r--modules/noise/doc_classes/NoiseTexture3D.xml49
-rw-r--r--modules/noise/fastnoise_lite.cpp2
-rw-r--r--modules/noise/noise.cpp148
-rw-r--r--modules/noise/noise.h196
-rw-r--r--modules/noise/noise_texture_2d.cpp4
-rw-r--r--modules/noise/noise_texture_3d.cpp352
-rw-r--r--modules/noise/noise_texture_3d.h113
-rw-r--r--modules/noise/register_types.cpp2
-rw-r--r--modules/noise/tests/test_noise_texture_3d.h235
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp4
-rw-r--r--modules/openxr/extensions/openxr_htc_controller_extension.cpp3
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp4
-rw-r--r--modules/text_server_adv/SCsub6
-rw-r--r--modules/text_server_fb/SCsub6
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--modules/upnp/doc_classes/UPNP.xml18
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml18
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml16
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml2
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml2
-rw-r--r--modules/websocket/wsl_peer.cpp4
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml12
-rw-r--r--platform/android/display_server_android.h2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml8
-rw-r--r--platform/android/export/export_plugin.cpp54
-rw-r--r--platform/android/export/export_plugin.h9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java38
-rw-r--r--platform/android/java_godot_lib_jni.cpp38
-rw-r--r--platform/android/java_godot_wrapper.cpp12
-rw-r--r--platform/android/java_godot_wrapper.h2
-rw-r--r--platform/android/os_android.cpp20
-rw-r--r--platform/android/os_android.h4
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp3
-rw-r--r--platform/ios/display_server_ios.h2
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml24
-rw-r--r--platform/ios/export/export_plugin.cpp19
-rw-r--r--platform/ios/export/export_plugin.h5
-rw-r--r--platform/ios/ios.mm18
-rw-r--r--platform/ios/os_ios.mm10
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp1
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp5
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp5
-rw-r--r--platform/linuxbsd/joypad_linux.cpp48
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp44
-rw-r--r--platform/linuxbsd/os_linuxbsd.h3
-rw-r--r--platform/linuxbsd/x11/detect_prime_x11.cpp3
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp27
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h5
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp3
-rw-r--r--platform/macos/detect.py2
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm10
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml10
-rw-r--r--platform/macos/export/export_plugin.cpp100
-rw-r--r--platform/macos/export/export_plugin.h11
-rw-r--r--platform/macos/os_macos.h2
-rw-r--r--platform/macos/os_macos.mm29
-rw-r--r--platform/uwp/export/export.cpp1
-rw-r--r--platform/uwp/export/export_plugin.cpp8
-rw-r--r--platform/uwp/export/export_plugin.h5
-rw-r--r--platform/web/audio_driver_web.cpp2
-rw-r--r--platform/web/display_server_web.h2
-rw-r--r--platform/web/export/export.cpp1
-rw-r--r--platform/web/export/export_plugin.cpp5
-rw-r--r--platform/windows/detect.py2
-rw-r--r--platform/windows/display_server_windows.h3
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml3
-rw-r--r--platform/windows/export/export_plugin.cpp25
-rw-r--r--platform/windows/export/export_plugin.h6
-rw-r--r--platform/windows/os_windows.cpp43
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--scene/2d/collision_object_2d.cpp9
-rw-r--r--scene/2d/navigation_agent_2d.cpp222
-rw-r--r--scene/2d/navigation_agent_2d.h114
-rw-r--r--scene/2d/navigation_link_2d.cpp34
-rw-r--r--scene/2d/navigation_link_2d.h2
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp337
-rw-r--r--scene/2d/navigation_obstacle_2d.h66
-rw-r--r--scene/2d/navigation_region_2d.cpp182
-rw-r--r--scene/2d/navigation_region_2d.h24
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/3d/collision_object_3d.cpp6
-rw-r--r--scene/3d/collision_polygon_3d.cpp6
-rw-r--r--scene/3d/collision_shape_3d.cpp6
-rw-r--r--scene/3d/light_3d.cpp3
-rw-r--r--scene/3d/lightmapper.h16
-rw-r--r--scene/3d/navigation_agent_3d.cpp282
-rw-r--r--scene/3d/navigation_agent_3d.h136
-rw-r--r--scene/3d/navigation_link_3d.cpp42
-rw-r--r--scene/3d/navigation_link_3d.h2
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp564
-rw-r--r--scene/3d/navigation_obstacle_3d.h78
-rw-r--r--scene/3d/navigation_region_3d.cpp64
-rw-r--r--scene/3d/navigation_region_3d.h7
-rw-r--r--scene/3d/path_3d.cpp3
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp9
-rw-r--r--scene/3d/sprite_3d.cpp4
-rw-r--r--scene/animation/animation_blend_tree.cpp162
-rw-r--r--scene/animation/animation_blend_tree.h37
-rw-r--r--scene/animation/animation_node_state_machine.cpp6
-rw-r--r--scene/animation/animation_node_state_machine.h2
-rw-r--r--scene/gui/button.cpp69
-rw-r--r--scene/gui/button.h5
-rw-r--r--scene/gui/code_edit.cpp112
-rw-r--r--scene/gui/code_edit.h2
-rw-r--r--scene/gui/color_picker.cpp37
-rw-r--r--scene/gui/color_picker.h2
-rw-r--r--scene/gui/graph_edit.cpp17
-rw-r--r--scene/gui/graph_edit.h4
-rw-r--r--scene/gui/graph_node.cpp13
-rw-r--r--scene/gui/graph_node.h2
-rw-r--r--scene/gui/item_list.cpp22
-rw-r--r--scene/gui/item_list.h2
-rw-r--r--scene/gui/popup_menu.cpp23
-rw-r--r--scene/gui/popup_menu.h3
-rw-r--r--scene/gui/scroll_container.cpp24
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/slider.cpp31
-rw-r--r--scene/gui/slider.h3
-rw-r--r--scene/gui/subviewport_container.cpp28
-rw-r--r--scene/gui/subviewport_container.h1
-rw-r--r--scene/gui/tab_container.cpp8
-rw-r--r--scene/gui/text_edit.cpp31
-rw-r--r--scene/gui/tree.cpp228
-rw-r--r--scene/gui/tree.h36
-rw-r--r--scene/main/canvas_item.cpp14
-rw-r--r--scene/main/http_request.cpp4
-rw-r--r--scene/main/node.cpp499
-rw-r--r--scene/main/node.h114
-rw-r--r--scene/main/scene_tree.cpp539
-rw-r--r--scene/main/scene_tree.h54
-rw-r--r--scene/main/viewport.cpp149
-rw-r--r--scene/main/viewport.h7
-rw-r--r--scene/main/window.cpp12
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/audio_stream_wav.cpp56
-rw-r--r--scene/resources/audio_stream_wav.h2
-rw-r--r--scene/resources/bit_map.cpp2
-rw-r--r--scene/resources/canvas_item_material.cpp6
-rw-r--r--scene/resources/canvas_item_material.h1
-rw-r--r--scene/resources/capsule_shape_2d.cpp3
-rw-r--r--scene/resources/default_theme/default_theme.cpp3
-rw-r--r--scene/resources/material.cpp51
-rw-r--r--scene/resources/material.h10
-rw-r--r--scene/resources/mesh.cpp6
-rw-r--r--scene/resources/mesh.h2
-rw-r--r--scene/resources/particle_process_material.cpp5
-rw-r--r--scene/resources/particle_process_material.h1
-rw-r--r--scene/resources/resource_format_text.cpp83
-rw-r--r--scene/resources/resource_format_text.h2
-rw-r--r--scene/resources/surface_tool.cpp24
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/texture.cpp3
-rw-r--r--scene/resources/visual_shader.cpp30
-rw-r--r--scene/resources/visual_shader.h6
-rw-r--r--scene/resources/world_2d.cpp1
-rw-r--r--scene/resources/world_3d.cpp1
-rw-r--r--servers/audio/audio_driver_dummy.cpp6
-rw-r--r--servers/audio/effects/audio_effect_record.cpp4
-rw-r--r--servers/audio_server.cpp14
-rw-r--r--servers/audio_server.h5
-rw-r--r--servers/movie_writer/movie_writer.cpp19
-rw-r--r--servers/movie_writer/movie_writer.h5
-rw-r--r--servers/navigation_server_2d.cpp158
-rw-r--r--servers/navigation_server_2d.h72
-rw-r--r--servers/navigation_server_3d.cpp282
-rw-r--r--servers/navigation_server_3d.h126
-rw-r--r--servers/navigation_server_3d_dummy.h28
-rw-r--r--servers/physics_2d/godot_shape_2d.cpp11
-rw-r--r--servers/physics_2d/godot_shape_2d.h9
-rw-r--r--servers/physics_3d/godot_body_pair_3d.cpp8
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp28
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h1
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp24
-rw-r--r--servers/rendering/renderer_rd/cluster_builder_rd.cpp20
-rw-r--r--servers/rendering/renderer_rd/environment/fog.cpp63
-rw-r--r--servers/rendering/renderer_rd/environment/fog.h14
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp16
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp29
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp3
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp30
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h5
-rw-r--r--servers/rendering/renderer_rd/shaders/cluster_render.glsl18
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl204
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/gi.glsl8
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl23
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl23
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.h10
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp11
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h1
-rw-r--r--servers/rendering/renderer_viewport.cpp7
-rw-r--r--servers/rendering/renderer_viewport.h1
-rw-r--r--servers/rendering/rendering_device.cpp11
-rw-r--r--servers/rendering/rendering_device.h5
-rw-r--r--servers/rendering/rendering_device_binds.h11
-rw-r--r--servers/rendering/rendering_server_default.cpp4
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering/shader_compiler.cpp43
-rw-r--r--servers/rendering/shader_language.cpp120
-rw-r--r--servers/rendering/shader_language.h48
-rw-r--r--servers/rendering/shader_preprocessor.cpp16
-rw-r--r--servers/rendering/shader_preprocessor.h4
-rw-r--r--servers/rendering/shader_types.cpp2
-rw-r--r--servers/rendering/storage/texture_storage.h1
-rw-r--r--servers/rendering_server.cpp25
-rw-r--r--servers/rendering_server.h4
-rw-r--r--tests/core/math/test_math_funcs.h10
-rw-r--r--tests/core/object/test_class_db.h11
-rw-r--r--tests/core/object/test_object.h42
-rw-r--r--tests/core/string/test_string.h6
-rw-r--r--tests/scene/test_code_edit.h141
-rw-r--r--tests/scene/test_curve_3d.h261
-rw-r--r--tests/scene/test_viewport.h2
-rw-r--r--tests/test_macros.h2
-rw-r--r--tests/test_main.cpp5
-rw-r--r--thirdparty/README.md33
-rw-r--r--thirdparty/astcenc/astcenc.h18
-rw-r--r--thirdparty/astcenc/astcenc_averages_and_directions.cpp59
-rw-r--r--thirdparty/astcenc/astcenc_block_sizes.cpp20
-rw-r--r--thirdparty/astcenc/astcenc_color_quantize.cpp142
-rw-r--r--thirdparty/astcenc/astcenc_compress_symbolic.cpp14
-rw-r--r--thirdparty/astcenc/astcenc_diagnostic_trace.cpp19
-rw-r--r--thirdparty/astcenc/astcenc_entry.cpp88
-rw-r--r--thirdparty/astcenc/astcenc_find_best_partitioning.cpp28
-rw-r--r--thirdparty/astcenc/astcenc_internal.h79
-rw-r--r--thirdparty/astcenc/astcenc_platform_isa_detection.cpp166
-rw-r--r--thirdparty/astcenc/astcenc_quantization.cpp891
-rw-r--r--thirdparty/astcenc/astcenc_symbolic_physical.cpp79
-rw-r--r--thirdparty/astcenc/astcenc_weight_align.cpp6
-rw-r--r--thirdparty/basis_universal/patches/basisu-pr344.patch43
-rw-r--r--thirdparty/basis_universal/transcoder/basisu_transcoder_internal.h2
-rw-r--r--thirdparty/certs/ca-certificates.crt172
-rw-r--r--thirdparty/doctest/LICENSE.txt2
-rw-r--r--thirdparty/doctest/doctest.h149
-rw-r--r--thirdparty/enet/godot.cpp2
-rw-r--r--thirdparty/glad/gl.c12
-rw-r--r--thirdparty/glad/glad/gl.h25
-rw-r--r--thirdparty/mbedtls/include/godot_core_mbedtls_config.h37
-rw-r--r--thirdparty/mbedtls/include/godot_module_mbedtls_config.h58
-rw-r--r--thirdparty/rvo2/patches/rvo2-godot-changes.patch282
-rw-r--r--thirdparty/rvo2/rvo2_2d/Agent2d.cpp594
-rw-r--r--thirdparty/rvo2/rvo2_2d/Agent2d.h160
-rw-r--r--thirdparty/rvo2/rvo2_2d/Definitions.h110
-rw-r--r--thirdparty/rvo2/rvo2_2d/KdTree2d.cpp357
-rw-r--r--thirdparty/rvo2/rvo2_2d/KdTree2d.h203
-rw-r--r--thirdparty/rvo2/rvo2_2d/Obstacle2d.cpp38
-rw-r--r--thirdparty/rvo2/rvo2_2d/Obstacle2d.h72
-rw-r--r--thirdparty/rvo2/rvo2_2d/RVOSimulator2d.cpp363
-rw-r--r--thirdparty/rvo2/rvo2_2d/RVOSimulator2d.h592
-rw-r--r--thirdparty/rvo2/rvo2_2d/Vector2.h346
-rw-r--r--thirdparty/rvo2/rvo2_3d/Agent3d.cpp (renamed from thirdparty/rvo2/Agent.cpp)114
-rw-r--r--thirdparty/rvo2/rvo2_3d/Agent3d.h (renamed from thirdparty/rvo2/Agent.h)60
-rw-r--r--thirdparty/rvo2/rvo2_3d/Definitions.h (renamed from thirdparty/rvo2/Definitions.h)2
-rw-r--r--thirdparty/rvo2/rvo2_3d/KdTree3d.cpp (renamed from thirdparty/rvo2/KdTree.cpp)17
-rw-r--r--thirdparty/rvo2/rvo2_3d/KdTree3d.h (renamed from thirdparty/rvo2/KdTree.h)30
-rw-r--r--thirdparty/rvo2/rvo2_3d/RVOSimulator3d.cpp274
-rw-r--r--thirdparty/rvo2/rvo2_3d/RVOSimulator3d.h324
-rw-r--r--thirdparty/rvo2/rvo2_3d/Vector3.h (renamed from thirdparty/rvo2/Vector3.h)38
-rw-r--r--thirdparty/thorvg/AUTHORS7
-rw-r--r--thirdparty/thorvg/LICENSE2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h185
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h40
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp5
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp55
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp5
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp11
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp688
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h22
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h90
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h24
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h141
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h21
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp341
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h15
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp180
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp45
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp24
-rw-r--r--thirdparty/thorvg/src/lib/tvgAccessor.cpp19
-rw-r--r--thirdparty/thorvg/src/lib/tvgArray.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.cpp5
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgBinaryDesc.h6
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvas.cpp15
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvasImpl.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgCommon.h13
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgGlCanvas.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgInitializer.cpp16
-rw-r--r--thirdparty/thorvg/src/lib/tvgIteratorAccessor.h5
-rw-r--r--thirdparty/thorvg/src/lib/tvgLinearGradient.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoadModule.h9
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.cpp6
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgMath.h5
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp75
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h13
-rw-r--r--thirdparty/thorvg/src/lib/tvgPicture.cpp23
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h116
-rw-r--r--thirdparty/thorvg/src/lib/tvgRadialGradient.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h185
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaveModule.h5
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaver.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgScene.cpp3
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h34
-rw-r--r--thirdparty/thorvg/src/lib/tvgShape.cpp168
-rw-r--r--thirdparty/thorvg/src/lib/tvgShapeImpl.h364
-rw-r--r--thirdparty/thorvg/src/lib/tvgSwCanvas.cpp5
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp12
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.h11
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp62
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h5
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp38
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h5
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp34
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h2
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp37
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h5
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp75
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp1120
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h5
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h110
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp11
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp61
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp6
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp2
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h2
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp55
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h2
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp3
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h2
-rw-r--r--thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp25
-rw-r--r--thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h3
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh4
838 files changed, 27773 insertions, 10993 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 0b4abac1af..1050f259a0 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -5,9 +5,10 @@ body:
- type: markdown
attributes:
value: |
- - Read our [CONTRIBUTING.md guide](https://github.com/godotengine/godot/blob/master/CONTRIBUTING.md#reporting-bugs) on reporting bugs.
+ - When reporting bugs, you'll make our life simpler (and the fix will come sooner) if you follow the guidelines in this template.
- Write a descriptive issue title above.
- - Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
+ - The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them.
+ - Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. If you don't find a relevant match or if you're unsure, don't hesitate to **open a new issue**. The bugsquad will handle it from there if it's a duplicate.
- Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html).
- type: input
@@ -24,9 +25,11 @@ body:
attributes:
label: System information
description: |
- Specify the OS version, and when relevant hardware information.
- For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan).
- placeholder: Windows 10, GLES3, Intel HD Graphics 620 (27.20.100.9616)
+ - Specify the OS version, and when relevant hardware information.
+ - For issues that are likely OS-specific and/or graphics-related, please specify the CPU model and architecture.
+ - For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan).
+ - **Bug reports not including the required information may be closed at the maintainers' discretion.** If in doubt, always include all the requested information; it's better to include too much information than not enough information.
+ placeholder: Windows 10, Intel Core i5-7200U, GLES3, Intel HD Graphics 620 (27.20.100.9616)
validations:
required: true
@@ -52,8 +55,10 @@ body:
attributes:
label: Minimal reproduction project
description: |
- A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`).
- Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field.
- Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.**
+ - A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`).
+ - Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field.
+ - Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.**
+ - **Note for C# users:** If your issue is *not* Mono-specific, please upload a minimal reproduction project written in GDScript or VisualScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a Mono setup available.
+ - **If you've been asked by a maintainer to upload a minimal reproduction project, you *must* do so within 7 days.** Otherwise, your bug report will be closed as it'll be considered too difficult to diagnose.
validations:
required: true
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 275f6cc38c..f35206ab57 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -28,14 +28,18 @@ jobs:
- name: Get changed files
id: changed-files
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
- files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} 2> /dev/null || true)
+ files=$(gh pr diff ${{ github.event.pull_request.number }} --name-only)
elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then
files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true)
fi
echo "$files" >> changed.txt
cat changed.txt
+ files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "./{}"' | tr '\n' ' ')
+ echo "CHANGED_FILES=$files" >> $GITHUB_ENV
- name: File formatting checks (file_format.sh)
run: |
@@ -98,9 +102,9 @@ jobs:
fi
- name: Spell checks via codespell
+ if: github.event_name == 'pull_request' && env.CHANGED_FILES != ''
uses: codespell-project/actions-codespell@v1
with:
- skip: ./.*,./**/.*,./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/project_converter_3_to_4.cpp,./misc/scripts/codespell.sh,./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json
- check_hidden: false
- ignore_words_list: curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te
- only_warn: true
+ skip: "./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/project_converter_3_to_4.cpp,./misc/scripts/codespell.sh,./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json"
+ ignore_words_list: "curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te,vai"
+ path: ${{ env.CHANGED_FILES }}
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index da90e9641c..0f37db5f14 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -28,7 +28,7 @@ jobs:
target: editor
tests: true
# Skip debug symbols, they're way too big with MSVC.
- sconsflags: debug_symbols=no vsproj=yes
+ sconsflags: debug_symbols=no vsproj=yes windows_subsystem=console
bin: "./bin/godot.windows.editor.x86_64.exe"
- name: Template (target=template_release)
diff --git a/.gitignore b/.gitignore
index 17c9a6c95a..060f5696b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,6 +63,10 @@ tests/data/*.translation
# Binutils tmp linker output of the form "stXXXXXX" where "X" is alphanumeric
st[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]
+# Python development
+.venv
+venv
+
# Python generated
__pycache__/
*.pyc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 008aa0db3e..9f795394ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -912,7 +912,7 @@ See the [release announcement](https://godotengine.org/article/godot-3-4-is-rele
- Fix reloading `tool` scripts in the editor ([GH-52883](https://github.com/godotengine/godot/pull/52883)).
- Fix C# bindings generator for default value types ([GH-49702](https://github.com/godotengine/godot/pull/49702)).
- Ignore paths with invalid chars in `PathWhich` ([GH-50918](https://github.com/godotengine/godot/pull/50918)).
-- Fix `List<T>` marshalling ([GH-53628](https://github.com/godotengine/godot/pull/53628)).
+- Fix `List<T>` marshaling ([GH-53628](https://github.com/godotengine/godot/pull/53628)).
- Fix `hint_string` for enum arrays ([GH-53638](https://github.com/godotengine/godot/pull/53638)).
- Keep order for C# exported members ([GH-54199](https://github.com/godotengine/godot/pull/54199)).
@@ -1276,7 +1276,7 @@ See the [release announcement](https://godotengine.org/article/godot-3-3-has-arr
#### Mono (C#)
- [Fix targeting .NETFramework with .NET 5](https://github.com/godotengine/godot/pull/44135).
-- [Fix System.Collections.Generic.List marshalling](https://github.com/godotengine/godot/pull/45029).
+- [Fix System.Collections.Generic.List marshaling](https://github.com/godotengine/godot/pull/45029).
- [Fix support for Unicode identifiers](https://github.com/godotengine/godot/pull/45310).
- [Fixes to Mono on WebAssembly](https://github.com/godotengine/godot/pull/44374).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aea1c8c5ff..30934dcf9b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,87 +8,10 @@
- [Contributing to Godot's translation](#contributing-to-godots-translation)
- [Communicating with developers](#communicating-with-developers)
-**Please read the first section before reporting a bug!**
-
## Reporting bugs
-The golden rule is to **always open *one* issue for *one* bug**. If you notice
-several bugs and want to report them, make sure to create one new issue for
-each of them.
-
-If you're reporting a new bug, you'll make our life simpler (and the
-fix will come sooner) by following these guidelines:
-
-### Search first in the existing database
-
-Issues are often reported several times by various users. It's good practice to
-**search first in the [issue tracker](https://github.com/godotengine/godot/issues)
-before reporting your issue**. If you don't find a relevant match or if you're
-unsure, don't hesitate to **open a new issue**. The bugsquad will handle it
-from there if it's a duplicate.
-
-### Specify the platform
-
-Godot runs on a large variety of platforms and operating systems and devices.
-**In your bug reports, please always specify:**
-
-- Operating system and version (e.g. Windows 10, macOS 10.15, Ubuntu 19.10)
-- Godot version (e.g. 3.2, 3.1.2, or the Git commit hash if you're using a development branch)
-
-For bugs that are likely OS-specific and/or graphics-related, please also specify:
-
-- Device (CPU model including architecture, e.g. x86_64, arm64, etc.)
-- GPU model (and the driver version in use if you know it)
-
-**Bug reports not including the required information may be closed at the
-maintainers' discretion.** If in doubt, always include all the requested
-information; it's better to include too much information than not enough
-information.
-
-### Specify steps to reproduce
-
-Many bugs can't be reproduced unless specific steps are taken. Please **specify
-the exact steps** that must be taken to reproduce the condition, and try to
-keep them as minimal as possible. If you're describing a procedure to follow
-in the editor, don't hesitate to include screenshots.
-
-Making your bug report easy to reproduce will make it easier for contributors
-to fix the bug.
-
-### Provide a simple example project
-
-Sometimes, unexpected behavior can happen in your project. In such case,
-understand that:
-
-- What happens to you may not happen to other users.
-- We can't take the time to look at your project, understand how it is set up
- and then figure out why it's failing.
-- On the contributors' end, recreating a test project from scratch takes valuable
- time that can be saved by uploading a *minimal* project.
-
-To speed up our work, **please upload a minimal project** that isolates
-and reproduces the issue. This is always the **best way for us to fix it**.
-We recommend attaching a ZIP file with the minimal project directly to the bug report,
-by drag and dropping the file in the GitHub edition field. This ensures the file
-can remain available for a long period of time. Only use third-party file hosts
-if your ZIP file isn't accepted by GitHub because it's too large.
-
-We recommend always attaching a minimal reproduction project, even if the issue
-may seem simple to reproduce manually.
-
-**Note for C# users:** If your issue is *not* .NET-specific, please upload a
-minimal reproduction project written in GDScript.
-This will make it easier for contributors to reproduce the issue
-locally as not everyone has a .NET setup available.
-
-**If you've been asked by a maintainer to upload a minimal reproduction project,
-you *must* do so within 7 days.** Otherwise, your bug report will be closed as
-it'll be considered too difficult to diagnose.
-
-Now that you've read the guidelines, click the link below to create a
-bug report:
-
-- **[Report a bug](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml)**
+Report bugs [here](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml).
+Please follow the instructions in the template when you do.
## Proposing features or improvements
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 6c157c32f4..c8bfc1c8d2 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -169,7 +169,7 @@ License: Expat
Files: ./thirdparty/doctest/
Comment: doctest
-Copyright: 2016-2021, Viktor Kirilov
+Copyright: 2016-2023, Viktor Kirilov
License: Expat
Files: ./thirdparty/embree/
diff --git a/SConstruct b/SConstruct
index f5efc58585..4e67fb7117 100644
--- a/SConstruct
+++ b/SConstruct
@@ -217,7 +217,11 @@ opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and be
opts.Add("build_profile", "Path to a file containing a feature build profile", "")
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
-opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
+opts.Add(
+ "system_certs_path",
+ "Use this path as TLS certificates default for editor and Linux/BSD export templates (for package maintainers)",
+ "",
+)
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
# Thirdparty libraries
@@ -241,7 +245,8 @@ opts.Add(BoolVariable("builtin_miniupnpc", "Use the built-in miniupnpc library",
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))
-opts.Add(BoolVariable("builtin_rvo2", "Use the built-in RVO2 library", True))
+opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
+opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
@@ -697,7 +702,8 @@ if selected_platform in platform_list:
if env["warnings"] == "extra":
env.Append(CCFLAGS=["/W4"])
elif env["warnings"] == "all":
- env.Append(CCFLAGS=["/W3"])
+ # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too.
+ env.Append(CCFLAGS=["/W3", "/w34458"])
elif env["warnings"] == "moderate":
env.Append(CCFLAGS=["/W2"])
# Disable warnings which we don't plan to fix.
@@ -726,7 +732,7 @@ if selected_platform in platform_list:
common_warnings = []
if methods.using_gcc(env):
- common_warnings += ["-Wshadow-local", "-Wno-misleading-indentation"]
+ common_warnings += ["-Wshadow", "-Wno-misleading-indentation"]
if cc_version_major == 7: # Bogus warning fixed in 8+.
common_warnings += ["-Wno-strict-overflow"]
if cc_version_major < 11:
@@ -736,6 +742,7 @@ if selected_platform in platform_list:
if cc_version_major >= 12: # False positives in our error macros, see GH-58747.
common_warnings += ["-Wno-return-type"]
elif methods.using_clang(env) or methods.using_emcc(env):
+ common_warnings += ["-Wshadow-field-in-constructor", "-Wshadow-uncaptured-local"]
# We often implement `operator<` for structs of pointers as a requirement
# for putting them in `Set` or `Map`. We don't mind about unreliable ordering.
common_warnings += ["-Wno-ordered-compare-function-pointers"]
@@ -831,6 +838,15 @@ if selected_platform in platform_list:
env.module_list = modules_enabled
methods.sort_module_list(env)
+ if env.editor_build:
+ # Add editor-specific dependencies to the dependency graph.
+ env.module_add_dependencies("editor", ["freetype", "svg"])
+
+ # And check if they are met.
+ if not env.module_check_dependencies("editor"):
+ print("Not all modules required by editor builds are enabled.")
+ Exit(255)
+
methods.generate_version_header(env.module_version_string)
env["PROGSUFFIX_WRAP"] = suffix + env.module_version_string + ".console" + env["PROGSUFFIX"]
@@ -851,7 +867,7 @@ if selected_platform in platform_list:
if env["disable_3d"]:
if env.editor_build:
- print("Build option 'disable_3d=yes' cannot be used for editor builds, but only for export templates.")
+ print("Build option 'disable_3d=yes' cannot be used for editor builds, only for export template builds.")
Exit(255)
else:
env.Append(CPPDEFINES=["_3D_DISABLED"])
@@ -859,7 +875,7 @@ if selected_platform in platform_list:
if env.editor_build:
print(
"Build option 'disable_advanced_gui=yes' cannot be used for editor builds, "
- "but only for export templates."
+ "only for export template builds."
)
Exit(255)
else:
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 9fd7ef9988..264259eb2f 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -36,7 +36,6 @@
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
-#include "core/io/file_access_network.h"
#include "core/io/file_access_pack.h"
#include "core/io/marshalls.h"
#include "core/os/keyboard.h"
@@ -502,17 +501,6 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
}
}
- // If looking for files in a network client, use it directly
-
- if (FileAccessNetworkClient::get_singleton()) {
- Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
- if (err == OK && !p_ignore_override) {
- // Optional, we don't mind if it fails
- _load_settings_text("res://override.cfg");
- }
- return err;
- }
-
// Attempt with a user-defined main pack first
if (!p_main_pack.is_empty()) {
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 50587bb402..8afca5a2df 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -423,7 +423,14 @@ Error OS::set_thread_name(const String &p_name) {
};
bool OS::has_feature(const String &p_feature) const {
- return ::OS::get_singleton()->has_feature(p_feature);
+ const bool *value_ptr = feature_cache.getptr(p_feature);
+ if (value_ptr) {
+ return *value_ptr;
+ } else {
+ const bool has = ::OS::get_singleton()->has_feature(p_feature);
+ feature_cache[p_feature] = has;
+ return has;
+ }
}
uint64_t OS::get_static_memory_usage() const {
@@ -975,10 +982,11 @@ Vector<Vector3> Geometry3D::segment_intersects_cylinder(const Vector3 &p_from, c
return r;
}
-Vector<Vector3> Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes) {
+Vector<Vector3> Geometry3D::segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const TypedArray<Plane> &p_planes) {
Vector<Vector3> r;
Vector3 res, norm;
- if (!::Geometry3D::segment_intersects_convex(p_from, p_to, p_planes.ptr(), p_planes.size(), &res, &norm)) {
+ Vector<Plane> planes = Variant(p_planes);
+ if (!::Geometry3D::segment_intersects_convex(p_from, p_to, planes.ptr(), planes.size(), &res, &norm)) {
return r;
}
@@ -1170,14 +1178,30 @@ void Thread::_start_func(void *ud) {
String func_name = t->target_callable.is_custom() ? t->target_callable.get_custom()->get_as_text() : String(t->target_callable.get_method());
::Thread::set_name(func_name);
+ // To avoid a circular reference between the thread and the script which can possibly contain a reference
+ // to the thread, we will do the call (keeping a reference up to that point) and then break chains with it.
+ // When the call returns, we will reference the thread again if possible.
+ ObjectID th_instance_id = t->get_instance_id();
+ Callable target_callable = t->target_callable;
+ t = Ref<Thread>();
+
Callable::CallError ce;
- t->target_callable.callp(nullptr, 0, t->ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
+ Variant ret;
+ target_callable.callp(nullptr, 0, ret, ce);
+ // If script properly kept a reference to the thread, we should be able to re-reference it now
+ // (well, or if the call failed, since we had to break chains anyway because the outcome isn't known upfront).
+ t = Ref<Thread>(ObjectDB::get_instance(th_instance_id));
+ if (t.is_valid()) {
+ t->ret = ret;
t->running.clear();
- ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ } else {
+ // We could print a warning here, but the Thread object will be eventually destroyed
+ // noticing wait_to_finish() hasn't been called on it, and it will print a warning itself.
}
- t->running.clear();
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ }
}
Error Thread::start(const Callable &p_callable, Priority p_priority) {
diff --git a/core/core_bind.h b/core/core_bind.h
index 4138a85440..be43ae2c9d 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -119,6 +119,8 @@ public:
class OS : public Object {
GDCLASS(OS, Object);
+ mutable HashMap<String, bool> feature_cache;
+
protected:
static void _bind_methods();
static OS *singleton;
@@ -324,7 +326,7 @@ public:
Vector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius);
Vector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius);
- Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes);
+ Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const TypedArray<Plane> &p_planes);
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index d88dda6609..2332bc235b 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -645,6 +645,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_NAVIGATION);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_AVOIDANCE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FILE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DIR);
@@ -704,6 +705,7 @@ void register_global_constants() {
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_BASIC_SETTING);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_READ_ONLY);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SECRET);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR);
diff --git a/core/crypto/SCsub b/core/crypto/SCsub
index 9b7953fdc5..ac79e10d19 100644
--- a/core/crypto/SCsub
+++ b/core/crypto/SCsub
@@ -20,12 +20,13 @@ if is_builtin or not has_module:
# Only if the module is not enabled, we must compile here the required sources
# to make a "light" build with only the necessary mbedtls files.
if not has_module:
- env_thirdparty = env_crypto.Clone()
- env_thirdparty.disable_warnings()
- # Custom config file
- env_thirdparty.Append(
+ # Minimal mbedTLS config file
+ env_crypto.Append(
CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]
)
+ # Build minimal mbedTLS library (MD5/SHA/Base64/AES).
+ env_thirdparty = env_crypto.Clone()
+ env_thirdparty.disable_warnings()
thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/"
thirdparty_mbedtls_sources = [
"aes.c",
@@ -40,8 +41,16 @@ if not has_module:
]
thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources]
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources)
+ # Needed to force rebuilding the library when the configuration file is updated.
+ env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_mbedtls_config.h")
env.core_sources += thirdparty_obj
-
+elif is_builtin:
+ # Module mbedTLS config file
+ env_crypto.Append(
+ CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')]
+ )
+ # Needed to force rebuilding the core files when the configuration file is updated.
+ thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"]
# Godot source files
diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp
index 939c1c298f..6b1c2a9cb2 100644
--- a/core/crypto/crypto.cpp
+++ b/core/crypto/crypto.cpp
@@ -63,6 +63,8 @@ X509Certificate *X509Certificate::create() {
void X509Certificate::_bind_methods() {
ClassDB::bind_method(D_METHOD("save", "path"), &X509Certificate::save);
ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load);
+ ClassDB::bind_method(D_METHOD("save_to_string"), &X509Certificate::save_to_string);
+ ClassDB::bind_method(D_METHOD("load_from_string", "string"), &X509Certificate::load_from_string);
}
/// TLSOptions
diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h
index 999fe076d6..4b5bf8305f 100644
--- a/core/crypto/crypto.h
+++ b/core/crypto/crypto.h
@@ -65,6 +65,8 @@ public:
virtual Error load(String p_path) = 0;
virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0;
virtual Error save(String p_path) = 0;
+ virtual String save_to_string() = 0;
+ virtual Error load_from_string(const String &string) = 0;
};
class TLSOptions : public RefCounted {
diff --git a/core/doc_data.cpp b/core/doc_data.cpp
index 5e09e560d5..2d909f5335 100644
--- a/core/doc_data.cpp
+++ b/core/doc_data.cpp
@@ -165,6 +165,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) {
p_const.name = p_name;
p_const.value = p_value;
+ p_const.is_value_valid = (p_value.get_type() != Variant::OBJECT);
p_const.description = p_desc;
}
diff --git a/core/doc_data.h b/core/doc_data.h
index d064818cd5..fc184c411c 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -137,7 +137,7 @@ public:
return arguments[0] < p_method.arguments[0];
}
}
- return name < p_method.name;
+ return name.naturalcasecmp_to(p_method.name) < 0;
}
static MethodDoc from_dict(const Dictionary &p_dict) {
MethodDoc doc;
@@ -315,61 +315,6 @@ public:
}
};
- struct EnumDoc {
- String name = "@unnamed_enum";
- bool is_bitfield = false;
- String description;
- Vector<DocData::ConstantDoc> values;
- static EnumDoc from_dict(const Dictionary &p_dict) {
- EnumDoc doc;
-
- if (p_dict.has("name")) {
- doc.name = p_dict["name"];
- }
-
- if (p_dict.has("is_bitfield")) {
- doc.is_bitfield = p_dict["is_bitfield"];
- }
-
- if (p_dict.has("description")) {
- doc.description = p_dict["description"];
- }
-
- Array values;
- if (p_dict.has("values")) {
- values = p_dict["values"];
- }
- for (int i = 0; i < values.size(); i++) {
- doc.values.push_back(ConstantDoc::from_dict(values[i]));
- }
-
- return doc;
- }
- static Dictionary to_dict(const EnumDoc &p_doc) {
- Dictionary dict;
-
- if (!p_doc.name.is_empty()) {
- dict["name"] = p_doc.name;
- }
-
- dict["is_bitfield"] = p_doc.is_bitfield;
-
- if (!p_doc.description.is_empty()) {
- dict["description"] = p_doc.description;
- }
-
- if (!p_doc.values.is_empty()) {
- Array values;
- for (int i = 0; i < p_doc.values.size(); i++) {
- values.push_back(ConstantDoc::to_dict(p_doc.values[i]));
- }
- dict["values"] = values;
- }
-
- return dict;
- }
- };
-
struct PropertyDoc {
String name;
String type;
@@ -382,7 +327,7 @@ public:
bool is_deprecated = false;
bool is_experimental = false;
bool operator<(const PropertyDoc &p_prop) const {
- return name < p_prop.name;
+ return name.naturalcasecmp_to(p_prop.name) < 0;
}
static PropertyDoc from_dict(const Dictionary &p_dict) {
PropertyDoc doc;
@@ -487,7 +432,7 @@ public:
bool operator<(const ThemeItemDoc &p_theme_item) const {
// First sort by the data type, then by name.
if (data_type == p_theme_item.data_type) {
- return name < p_theme_item.name;
+ return name.naturalcasecmp_to(p_theme_item.name) < 0;
}
return data_type < p_theme_item.data_type;
}
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index f158755a85..91038b9bdf 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -146,9 +146,11 @@ String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile>
class GDExtensionMethodBind : public MethodBind {
GDExtensionClassMethodCall call_func;
+ GDExtensionClassMethodValidatedCall validated_call_func;
GDExtensionClassMethodPtrCall ptrcall_func;
void *method_userdata;
bool vararg;
+ uint32_t argument_count;
PropertyInfo return_value_info;
GodotTypeInfo::Metadata return_value_metadata;
List<PropertyInfo> arguments_info;
@@ -191,6 +193,40 @@ public:
r_error.expected = ce.expected;
return ret;
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+ ERR_FAIL_COND_MSG(vararg, "Validated methods don't have ptrcall support. This is most likely an engine bug.");
+ GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
+
+ if (validated_call_func) {
+ // This is added here, but it's unlikely to be provided by most extensions.
+ validated_call_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstVariantPtr *>(p_args), (GDExtensionVariantPtr)r_ret);
+ } else {
+#if 1
+ // Slow code-path, but works for the time being.
+ Callable::CallError ce;
+ call(p_object, p_args, argument_count, ce);
+#else
+ // This is broken, because it needs more information to do the calling properly
+
+ // If not provided, go via ptrcall, which is faster than resorting to regular call.
+ const void **argptrs = (const void **)alloca(argument_count * sizeof(void *));
+ for (uint32_t i = 0; i < argument_count; i++) {
+ argptrs[i] = VariantInternal::get_opaque_pointer(p_args[i]);
+ }
+
+ bool returns = true;
+ void *ret_opaque;
+ if (returns) {
+ ret_opaque = VariantInternal::get_opaque_pointer(r_ret);
+ } else {
+ ret_opaque = nullptr; // May be unnecessary as this is ignored, but just in case.
+ }
+
+ ptrcall(p_object, argptrs, ret_opaque);
+#endif
+ }
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
@@ -204,6 +240,7 @@ public:
explicit GDExtensionMethodBind(const GDExtensionClassMethodInfo *p_method_info) {
method_userdata = p_method_info->method_userdata;
call_func = p_method_info->call_func;
+ validated_call_func = nullptr;
ptrcall_func = p_method_info->ptrcall_func;
set_name(*reinterpret_cast<StringName *>(p_method_info->name));
@@ -218,7 +255,7 @@ public:
}
set_hint_flags(p_method_info->method_flags);
-
+ argument_count = p_method_info->argument_count;
vararg = p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_VARARG;
_set_returns(p_method_info->has_return_value);
_set_const(p_method_info->method_flags & GDEXTENSION_METHOD_FLAG_CONST);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index c892fa3a68..96a57115bf 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -680,13 +680,17 @@ static GDExtensionInt gdextension_string_to_wide_chars(GDExtensionConstStringPtr
static char32_t *gdextension_string_operator_index(GDExtensionStringPtr p_self, GDExtensionInt p_index) {
String *self = (String *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->length() + 1)) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const char32_t *gdextension_string_operator_index_const(GDExtensionConstStringPtr p_self, GDExtensionInt p_index) {
const String *self = (const String *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->length() + 1, nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->length() + 1)) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
@@ -747,121 +751,161 @@ static int64_t gdextension_worker_thread_pool_add_native_task(GDExtensionObjectP
static uint8_t *gdextension_packed_byte_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedByteArray *self = (PackedByteArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const uint8_t *gdextension_packed_byte_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedByteArray *self = (const PackedByteArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_color_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedColorArray *self = (PackedColorArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_color_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedColorArray *self = (const PackedColorArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static float *gdextension_packed_float32_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedFloat32Array *self = (PackedFloat32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const float *gdextension_packed_float32_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedFloat32Array *self = (const PackedFloat32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static double *gdextension_packed_float64_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedFloat64Array *self = (PackedFloat64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const double *gdextension_packed_float64_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedFloat64Array *self = (const PackedFloat64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static int32_t *gdextension_packed_int32_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedInt32Array *self = (PackedInt32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const int32_t *gdextension_packed_int32_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedInt32Array *self = (const PackedInt32Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static int64_t *gdextension_packed_int64_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedInt64Array *self = (PackedInt64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptrw()[p_index];
}
static const int64_t *gdextension_packed_int64_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedInt64Array *self = (const PackedInt64Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return &self->ptr()[p_index];
}
static GDExtensionStringPtr gdextension_packed_string_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedStringArray *self = (PackedStringArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionStringPtr)&self->ptrw()[p_index];
}
static GDExtensionStringPtr gdextension_packed_string_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedStringArray *self = (const PackedStringArray *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionStringPtr)&self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector2_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedVector2Array *self = (PackedVector2Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector2_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedVector2Array *self = (const PackedVector2Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector3_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
PackedVector3Array *self = (PackedVector3Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptrw()[p_index];
}
static GDExtensionTypePtr gdextension_packed_vector3_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const PackedVector3Array *self = (const PackedVector3Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionTypePtr)&self->ptr()[p_index];
}
static GDExtensionVariantPtr gdextension_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
Array *self = (Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
const Array *self = (const Array *)p_self;
- ERR_FAIL_INDEX_V(p_index, self->size(), nullptr);
+ if (unlikely(p_index < 0 || p_index >= self->size())) {
+ return nullptr;
+ }
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index f323b2aa53..f1412d667f 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -295,6 +295,7 @@ typedef enum {
} GDExtensionClassMethodArgumentMetadata;
typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+typedef void (*GDExtensionClassMethodValidatedCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionVariantPtr r_return);
typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef struct {
diff --git a/core/input/input.cpp b/core/input/input.cpp
index e74523e059..2b3e0b56e4 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -35,6 +35,10 @@
#include "core/input/input_map.h"
#include "core/os/os.h"
+#ifdef DEV_ENABLED
+#include "core/os/thread.h"
+#endif
+
static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = {
"a",
"b",
@@ -486,6 +490,10 @@ Vector3 Input::get_gyroscope() const {
}
void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
+ // This function does the final delivery of the input event to user land.
+ // Regardless where the event came from originally, this has to happen on the main thread.
+ DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id());
+
// Notes on mouse-touch emulation:
// - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects
// as true mouse events. The only difference is the situation is flagged as emulated so they are not
@@ -537,7 +545,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
touch_event->set_position(mb->get_position());
touch_event->set_double_tap(mb->is_double_click());
touch_event->set_device(InputEvent::DEVICE_ID_EMULATION);
+ _THREAD_SAFE_UNLOCK_
event_dispatch_function(touch_event);
+ _THREAD_SAFE_LOCK_
}
}
@@ -563,7 +573,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
drag_event->set_velocity(get_last_mouse_velocity());
drag_event->set_device(InputEvent::DEVICE_ID_EMULATION);
+ _THREAD_SAFE_UNLOCK_
event_dispatch_function(drag_event);
+ _THREAD_SAFE_LOCK_
}
}
@@ -664,7 +676,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (ge.is_valid()) {
if (event_dispatch_function) {
+ _THREAD_SAFE_UNLOCK_
event_dispatch_function(ge);
+ _THREAD_SAFE_LOCK_
}
}
@@ -687,7 +701,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}
if (event_dispatch_function) {
+ _THREAD_SAFE_UNLOCK_
event_dispatch_function(p_event);
+ _THREAD_SAFE_LOCK_
}
}
@@ -831,6 +847,7 @@ bool Input::is_emulating_touch_from_mouse() const {
// Calling this whenever the game window is focused helps unsticking the "touch mouse"
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
void Input::ensure_touch_mouse_raised() {
+ _THREAD_SAFE_METHOD_
if (mouse_from_touch_index != -1) {
mouse_from_touch_index = -1;
@@ -937,8 +954,15 @@ void Input::flush_buffered_events() {
_THREAD_SAFE_METHOD_
while (buffered_events.front()) {
- _parse_input_event_impl(buffered_events.front()->get(), false);
+ // The final delivery of the input event involves releasing the lock.
+ // While the lock is released, another thread may lock it and add new events to the back.
+ // Therefore, we get each event and pop it while we still have the lock,
+ // to ensure the list is in a consistent state.
+ List<Ref<InputEvent>>::Element *E = buffered_events.front();
+ Ref<InputEvent> e = E->get();
buffered_events.pop_front();
+
+ _parse_input_event_impl(e, false);
}
}
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 46f07fe041..19e4d6182a 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -484,7 +484,10 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode, bool p_physica
ie->set_keycode(p_keycode & KeyModifierMask::CODE_MASK);
}
- ie->set_unicode(char32_t(p_keycode & KeyModifierMask::CODE_MASK));
+ char32_t ch = char32_t(p_keycode & KeyModifierMask::CODE_MASK);
+ if (ch < 0xd800 || (ch > 0xdfff && ch <= 0x10ffff)) {
+ ie->set_unicode(ch);
+ }
if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) {
ie->set_shift_pressed(true);
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 51eb68eaea..52ed688deb 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -68,7 +68,7 @@ protected:
virtual String _get_root_string() const;
AccessType get_access_type() const;
- String fix_path(String p_path) const;
+ virtual String fix_path(String p_path) const;
template <class T>
static Ref<DirAccess> _create_builtin() {
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 34c80b3dd9..ad1ac665f3 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -81,7 +81,7 @@ protected:
static void _bind_methods();
AccessType get_access_type() const;
- String fix_path(const String &p_path) const;
+ virtual String fix_path(const String &p_path) const;
virtual Error open_internal(const String &p_path, int p_mode_flags) = 0; ///< open a file
virtual uint64_t _get_modified_time(const String &p_file) = 0;
virtual void _set_access_type(AccessType p_access);
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index da59ae8c59..3e5a1217dd 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -34,13 +34,7 @@
void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, uint32_t p_block_size) {
magic = p_magic.ascii().get_data();
- if (magic.length() > 4) {
- magic = magic.substr(0, 4);
- } else {
- while (magic.length() < 4) {
- magic += " ";
- }
- }
+ magic = (magic + " ").substr(0, 4);
cmode = p_mode;
block_size = p_block_size;
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
deleted file mode 100644
index 7fabff26ac..0000000000
--- a/core/io/file_access_network.cpp
+++ /dev/null
@@ -1,498 +0,0 @@
-/**************************************************************************/
-/* file_access_network.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 "file_access_network.h"
-
-#include "core/config/project_settings.h"
-#include "core/io/ip.h"
-#include "core/io/marshalls.h"
-#include "core/os/os.h"
-
-//#define DEBUG_PRINT(m_p) print_line(m_p)
-//#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec());
-#define DEBUG_PRINT(m_p)
-#define DEBUG_TIME(m_what)
-
-void FileAccessNetworkClient::lock_mutex() {
- mutex.lock();
- lockcount++;
-}
-
-void FileAccessNetworkClient::unlock_mutex() {
- lockcount--;
- mutex.unlock();
-}
-
-void FileAccessNetworkClient::put_32(int p_32) {
- uint8_t buf[4];
- encode_uint32(p_32, buf);
- client->put_data(buf, 4);
- DEBUG_PRINT("put32: " + itos(p_32));
-}
-
-void FileAccessNetworkClient::put_64(int64_t p_64) {
- uint8_t buf[8];
- encode_uint64(p_64, buf);
- client->put_data(buf, 8);
- DEBUG_PRINT("put64: " + itos(p_64));
-}
-
-int FileAccessNetworkClient::get_32() {
- uint8_t buf[4];
- client->get_data(buf, 4);
- return decode_uint32(buf);
-}
-
-int64_t FileAccessNetworkClient::get_64() {
- uint8_t buf[8];
- client->get_data(buf, 8);
- return decode_uint64(buf);
-}
-
-void FileAccessNetworkClient::_thread_func() {
- client->set_no_delay(true);
- while (!quit) {
- DEBUG_PRINT("SEM WAIT - " + itos(sem->get()));
- sem.wait();
- DEBUG_TIME("sem_unlock");
- //DEBUG_PRINT("semwait returned "+itos(werr));
- DEBUG_PRINT("MUTEX LOCK " + itos(lockcount));
- lock_mutex();
- DEBUG_PRINT("MUTEX PASS");
-
- {
- MutexLock lock(blockrequest_mutex);
- while (block_requests.size()) {
- put_32(block_requests.front()->get().id);
- put_32(FileAccessNetwork::COMMAND_READ_BLOCK);
- put_64(block_requests.front()->get().offset);
- put_32(block_requests.front()->get().size);
- block_requests.pop_front();
- }
- }
-
- DEBUG_PRINT("THREAD ITER");
-
- DEBUG_TIME("sem_read");
- int id = get_32();
-
- int response = get_32();
- DEBUG_PRINT("GET RESPONSE: " + itos(response));
-
- FileAccessNetwork *fa = nullptr;
-
- if (response != FileAccessNetwork::RESPONSE_DATA) {
- if (!accesses.has(id)) {
- unlock_mutex();
- ERR_FAIL_COND(!accesses.has(id));
- }
- }
-
- if (accesses.has(id)) {
- fa = accesses[id];
- }
-
- switch (response) {
- case FileAccessNetwork::RESPONSE_OPEN: {
- DEBUG_TIME("sem_open");
- int status = get_32();
- if (status != OK) {
- fa->_respond(0, Error(status));
- } else {
- int64_t len = get_64();
- fa->_respond(len, Error(status));
- }
-
- fa->sem.post();
-
- } break;
- case FileAccessNetwork::RESPONSE_DATA: {
- int64_t offset = get_64();
- int32_t len = get_32();
-
- Vector<uint8_t> resp_block;
- resp_block.resize(len);
- client->get_data(resp_block.ptrw(), len);
-
- if (fa) { //may have been queued
- fa->_set_block(offset, resp_block);
- }
-
- } break;
- case FileAccessNetwork::RESPONSE_FILE_EXISTS: {
- int status = get_32();
- fa->exists_modtime = status != 0;
- fa->sem.post();
-
- } break;
- case FileAccessNetwork::RESPONSE_GET_MODTIME: {
- uint64_t status = get_64();
- fa->exists_modtime = status;
- fa->sem.post();
-
- } break;
- }
-
- unlock_mutex();
- }
-}
-
-void FileAccessNetworkClient::_thread_func(void *s) {
- FileAccessNetworkClient *self = static_cast<FileAccessNetworkClient *>(s);
-
- self->_thread_func();
-}
-
-Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const String &p_password) {
- IPAddress ip;
-
- if (p_host.is_valid_ip_address()) {
- ip = p_host;
- } else {
- ip = IP::get_singleton()->resolve_hostname(p_host);
- }
-
- DEBUG_PRINT("IP: " + String(ip) + " port " + itos(p_port));
- Error err = client->connect_to_host(ip, p_port);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot connect to host with IP: " + String(ip) + " and port: " + itos(p_port));
- while (client->get_status() == StreamPeerTCP::STATUS_CONNECTING) {
- //DEBUG_PRINT("trying to connect....");
- OS::get_singleton()->delay_usec(1000);
- }
-
- if (client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
- return ERR_CANT_CONNECT;
- }
-
- CharString cs = p_password.utf8();
- put_32(cs.length());
- client->put_data((const uint8_t *)cs.ptr(), cs.length());
-
- int e = get_32();
-
- if (e != OK) {
- return ERR_INVALID_PARAMETER;
- }
-
- thread.start(_thread_func, this);
-
- return OK;
-}
-
-FileAccessNetworkClient *FileAccessNetworkClient::singleton = nullptr;
-
-FileAccessNetworkClient::FileAccessNetworkClient() {
- singleton = this;
- client.instantiate();
-}
-
-FileAccessNetworkClient::~FileAccessNetworkClient() {
- quit = true;
- sem.post();
- thread.wait_to_finish();
-}
-
-void FileAccessNetwork::_set_block(uint64_t p_offset, const Vector<uint8_t> &p_block) {
- int32_t page = p_offset / page_size;
- ERR_FAIL_INDEX(page, pages.size());
- if (page < pages.size() - 1) {
- ERR_FAIL_COND(p_block.size() != page_size);
- } else {
- ERR_FAIL_COND((uint64_t)p_block.size() != total_size % page_size);
- }
-
- {
- MutexLock lock(buffer_mutex);
- pages.write[page].buffer = p_block;
- pages.write[page].queued = false;
- }
-
- if (waiting_on_page == page) {
- waiting_on_page = -1;
- page_sem.post();
- }
-}
-
-void FileAccessNetwork::_respond(uint64_t p_len, Error p_status) {
- DEBUG_PRINT("GOT RESPONSE - len: " + itos(p_len) + " status: " + itos(p_status));
- response = p_status;
- if (response != OK) {
- return;
- }
- opened = true;
- total_size = p_len;
- int32_t pc = ((total_size - 1) / page_size) + 1;
- pages.resize(pc);
-}
-
-Error FileAccessNetwork::open_internal(const String &p_path, int p_mode_flags) {
- ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE);
- _close();
-
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- DEBUG_PRINT("open: " + p_path);
-
- DEBUG_TIME("open_begin");
-
- nc->lock_mutex();
- nc->put_32(id);
- nc->accesses[id] = this;
- nc->put_32(COMMAND_OPEN_FILE);
- CharString cs = p_path.utf8();
- nc->put_32(cs.length());
- nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
- pos = 0;
- eof_flag = false;
- last_page = -1;
- last_page_buff = nullptr;
-
- //buffers.clear();
- nc->unlock_mutex();
- DEBUG_PRINT("OPEN POST");
- DEBUG_TIME("open_post");
- nc->sem.post(); //awaiting answer
- DEBUG_PRINT("WAIT...");
- sem.wait();
- DEBUG_TIME("open_end");
- DEBUG_PRINT("WAIT ENDED...");
-
- return response;
-}
-
-void FileAccessNetwork::_close() {
- if (!opened) {
- return;
- }
-
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
-
- DEBUG_PRINT("CLOSE");
- nc->lock_mutex();
- nc->put_32(id);
- nc->put_32(COMMAND_CLOSE);
- pages.clear();
- opened = false;
- nc->unlock_mutex();
-}
-
-bool FileAccessNetwork::is_open() const {
- return opened;
-}
-
-void FileAccessNetwork::seek(uint64_t p_position) {
- ERR_FAIL_COND_MSG(!opened, "File must be opened before use.");
-
- eof_flag = p_position > total_size;
-
- if (p_position >= total_size) {
- p_position = total_size;
- }
-
- pos = p_position;
-}
-
-void FileAccessNetwork::seek_end(int64_t p_position) {
- seek(total_size + p_position);
-}
-
-uint64_t FileAccessNetwork::get_position() const {
- ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use.");
- return pos;
-}
-
-uint64_t FileAccessNetwork::get_length() const {
- ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use.");
- return total_size;
-}
-
-bool FileAccessNetwork::eof_reached() const {
- ERR_FAIL_COND_V_MSG(!opened, false, "File must be opened before use.");
- return eof_flag;
-}
-
-uint8_t FileAccessNetwork::get_8() const {
- uint8_t v;
- get_buffer(&v, 1);
- return v;
-}
-
-void FileAccessNetwork::_queue_page(int32_t p_page) const {
- if (p_page >= pages.size()) {
- return;
- }
- if (pages[p_page].buffer.is_empty() && !pages[p_page].queued) {
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- {
- MutexLock lock(nc->blockrequest_mutex);
-
- FileAccessNetworkClient::BlockRequest br;
- br.id = id;
- br.offset = (uint64_t)p_page * page_size;
- br.size = page_size;
- nc->block_requests.push_back(br);
- pages.write[p_page].queued = true;
- }
- DEBUG_PRINT("QUEUE PAGE POST");
- nc->sem.post();
- DEBUG_PRINT("queued " + itos(p_page));
- }
-}
-
-uint64_t FileAccessNetwork::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
- ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
-
- if (pos + p_length > total_size) {
- eof_flag = true;
- }
- if (pos + p_length >= total_size) {
- p_length = total_size - pos;
- }
-
- uint8_t *buff = last_page_buff;
-
- for (uint64_t i = 0; i < p_length; i++) {
- int32_t page = pos / page_size;
-
- if (page != last_page) {
- buffer_mutex.lock();
- if (pages[page].buffer.is_empty()) {
- waiting_on_page = page;
- for (int32_t j = 0; j < read_ahead; j++) {
- _queue_page(page + j);
- }
- buffer_mutex.unlock();
- DEBUG_PRINT("wait");
- page_sem.wait();
- DEBUG_PRINT("done");
- } else {
- for (int32_t j = 0; j < read_ahead; j++) {
- _queue_page(page + j);
- }
- buffer_mutex.unlock();
- }
-
- buff = pages.write[page].buffer.ptrw();
- last_page_buff = buff;
- last_page = page;
- }
-
- p_dst[i] = buff[pos - uint64_t(page) * page_size];
- pos++;
- }
-
- return p_length;
-}
-
-Error FileAccessNetwork::get_error() const {
- return pos == total_size ? ERR_FILE_EOF : OK;
-}
-
-void FileAccessNetwork::flush() {
- ERR_FAIL();
-}
-
-void FileAccessNetwork::store_8(uint8_t p_dest) {
- ERR_FAIL();
-}
-
-bool FileAccessNetwork::file_exists(const String &p_path) {
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- nc->lock_mutex();
- nc->put_32(id);
- nc->put_32(COMMAND_FILE_EXISTS);
- CharString cs = p_path.utf8();
- nc->put_32(cs.length());
- nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
- nc->unlock_mutex();
- DEBUG_PRINT("FILE EXISTS POST");
- nc->sem.post();
- sem.wait();
-
- return exists_modtime != 0;
-}
-
-uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) {
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- nc->lock_mutex();
- nc->put_32(id);
- nc->put_32(COMMAND_GET_MODTIME);
- CharString cs = p_file.utf8();
- nc->put_32(cs.length());
- nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
- nc->unlock_mutex();
- DEBUG_PRINT("MODTIME POST");
- nc->sem.post();
- sem.wait();
-
- return exists_modtime;
-}
-
-uint32_t FileAccessNetwork::_get_unix_permissions(const String &p_file) {
- ERR_PRINT("Getting UNIX permissions from network drives is not implemented yet");
- return 0;
-}
-
-Error FileAccessNetwork::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
- ERR_PRINT("Setting UNIX permissions on network drives is not implemented yet");
- return ERR_UNAVAILABLE;
-}
-
-void FileAccessNetwork::configure() {
- GLOBAL_DEF(PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater"), 65536); // Is used as denominator and can't be zero
- GLOBAL_DEF(PropertyInfo(Variant::INT, "network/remote_fs/page_read_ahead", PROPERTY_HINT_RANGE, "0,8,1,or_greater"), 4);
-}
-
-void FileAccessNetwork::close() {
- _close();
-
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- nc->lock_mutex();
- nc->accesses.erase(id);
- nc->unlock_mutex();
-}
-
-FileAccessNetwork::FileAccessNetwork() {
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- nc->lock_mutex();
- id = nc->last_id++;
- nc->accesses[id] = this;
- nc->unlock_mutex();
- page_size = GLOBAL_GET("network/remote_fs/page_size");
- read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead");
-}
-
-FileAccessNetwork::~FileAccessNetwork() {
- _close();
-
- FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
- nc->lock_mutex();
- nc->accesses.erase(id);
- nc->unlock_mutex();
-}
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
deleted file mode 100644
index 78c19347ce..0000000000
--- a/core/io/file_access_network.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/**************************************************************************/
-/* file_access_network.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 FILE_ACCESS_NETWORK_H
-#define FILE_ACCESS_NETWORK_H
-
-#include "core/io/file_access.h"
-#include "core/io/stream_peer_tcp.h"
-#include "core/os/semaphore.h"
-#include "core/os/thread.h"
-
-class FileAccessNetwork;
-
-class FileAccessNetworkClient {
- struct BlockRequest {
- int32_t id;
- uint64_t offset;
- int32_t size;
- };
-
- List<BlockRequest> block_requests;
-
- Semaphore sem;
- Thread thread;
- bool quit = false;
- Mutex mutex;
- Mutex blockrequest_mutex;
- HashMap<int, FileAccessNetwork *> accesses;
- Ref<StreamPeerTCP> client;
- int32_t last_id = 0;
- int32_t lockcount = 0;
-
- Vector<uint8_t> block;
-
- void _thread_func();
- static void _thread_func(void *s);
-
- void put_32(int32_t p_32);
- void put_64(int64_t p_64);
- int32_t get_32();
- int64_t get_64();
- void lock_mutex();
- void unlock_mutex();
-
- friend class FileAccessNetwork;
- static FileAccessNetworkClient *singleton;
-
-public:
- static FileAccessNetworkClient *get_singleton() { return singleton; }
-
- Error connect(const String &p_host, int p_port, const String &p_password = "");
-
- FileAccessNetworkClient();
- ~FileAccessNetworkClient();
-};
-
-class FileAccessNetwork : public FileAccess {
- Semaphore sem;
- Semaphore page_sem;
- Mutex buffer_mutex;
- bool opened = false;
- uint64_t total_size = 0;
- mutable uint64_t pos = 0;
- int32_t id = -1;
- mutable bool eof_flag = false;
- mutable int32_t last_page = -1;
- mutable uint8_t *last_page_buff = nullptr;
-
- int32_t page_size = 0;
- int32_t read_ahead = 0;
-
- mutable int waiting_on_page = -1;
-
- struct Page {
- int activity = 0;
- bool queued = false;
- Vector<uint8_t> buffer;
- };
-
- mutable Vector<Page> pages;
-
- mutable Error response;
-
- uint64_t exists_modtime = 0;
-
- friend class FileAccessNetworkClient;
- void _queue_page(int32_t p_page) const;
- void _respond(uint64_t p_len, Error p_status);
- void _set_block(uint64_t p_offset, const Vector<uint8_t> &p_block);
- void _close();
-
-public:
- enum Command {
- COMMAND_OPEN_FILE,
- COMMAND_READ_BLOCK,
- COMMAND_CLOSE,
- COMMAND_FILE_EXISTS,
- COMMAND_GET_MODTIME,
- };
-
- enum Response {
- RESPONSE_OPEN,
- RESPONSE_DATA,
- RESPONSE_FILE_EXISTS,
- RESPONSE_GET_MODTIME,
- };
-
- virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
- virtual bool is_open() const override; ///< true when file is open
-
- virtual void seek(uint64_t p_position) override; ///< seek to a given position
- virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file
- virtual uint64_t get_position() const override; ///< get position in the file
- virtual uint64_t get_length() const override; ///< get size of the file
-
- virtual bool eof_reached() const override; ///< reading passed EOF
-
- virtual uint8_t get_8() const override; ///< get a byte
- virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
-
- virtual Error get_error() const override; ///< get last error
-
- virtual void flush() override;
- virtual void store_8(uint8_t p_dest) override; ///< store a byte
-
- virtual bool file_exists(const String &p_path) override; ///< return true if a file exists
-
- virtual uint64_t _get_modified_time(const String &p_file) override;
- virtual uint32_t _get_unix_permissions(const String &p_file) override;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override;
-
- virtual void close() override;
-
- static void configure();
-
- FileAccessNetwork();
- ~FileAccessNetwork();
-};
-
-#endif // FILE_ACCESS_NETWORK_H
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 190edbfb82..09505ea05d 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -63,8 +63,9 @@ Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vecto
}
Error HTTPClient::_request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
- int size = p_body.length();
- return request(p_method, p_url, p_headers, size > 0 ? (const uint8_t *)p_body.utf8().get_data() : nullptr, size);
+ CharString body_utf8 = p_body.utf8();
+ int size = body_utf8.length();
+ return request(p_method, p_url, p_headers, size > 0 ? (const uint8_t *)body_utf8.get_data() : nullptr, size);
}
String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 3788fa501e..2f45238951 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -60,6 +60,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOp
}
ERR_FAIL_COND_V(tls_options.is_valid() && tls_options->is_server(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(tls_options.is_valid() && !StreamPeerTLS::is_available(), ERR_UNAVAILABLE, "HTTPS is not available in this build.");
ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
if (conn_port < 0) {
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 8d0fe53ed4..a6e054a9fe 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -47,13 +47,7 @@ const char *JSON::tk_name[TK_MAX] = {
};
String JSON::_make_indent(const String &p_indent, int p_size) {
- String indent_text = "";
- if (!p_indent.is_empty()) {
- for (int i = 0; i < p_size; i++) {
- indent_text += p_indent;
- }
- }
- return indent_text;
+ return p_indent.repeat(p_size);
}
String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) {
diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp
index 6c16401f17..ce4edb18fe 100644
--- a/core/io/packed_data_container.cpp
+++ b/core/io/packed_data_container.cpp
@@ -320,6 +320,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
}
Error PackedDataContainer::pack(const Variant &p_data) {
+ ERR_FAIL_COND_V_MSG(p_data.get_type() != Variant::ARRAY && p_data.get_type() != Variant::DICTIONARY, ERR_INVALID_DATA, "PackedDataContainer can pack only Array and Dictionary type.");
+
Vector<uint8_t> tmpdata;
HashMap<String, uint32_t> string_cache;
_pack(p_data, tmpdata, string_cache);
@@ -361,7 +363,9 @@ void PackedDataContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("pack", "value"), &PackedDataContainer::pack);
ClassDB::bind_method(D_METHOD("size"), &PackedDataContainer::size);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "__data__"), "_set_data", "_get_data");
+ BIND_METHOD_ERR_RETURN_DOC("pack", ERR_INVALID_DATA);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "__data__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
//////////////////
@@ -378,16 +382,11 @@ Variant PackedDataContainerRef::_iter_get(const Variant &p_iter) {
return from->_iter_get_ofs(p_iter, offset);
}
-bool PackedDataContainerRef::_is_dictionary() const {
- return from->_type_at_ofs(offset) == PackedDataContainer::TYPE_DICT;
-}
-
void PackedDataContainerRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("size"), &PackedDataContainerRef::size);
ClassDB::bind_method(D_METHOD("_iter_init"), &PackedDataContainerRef::_iter_init);
ClassDB::bind_method(D_METHOD("_iter_get"), &PackedDataContainerRef::_iter_get);
ClassDB::bind_method(D_METHOD("_iter_next"), &PackedDataContainerRef::_iter_next);
- ClassDB::bind_method(D_METHOD("_is_dictionary"), &PackedDataContainerRef::_is_dictionary);
}
Variant PackedDataContainerRef::getvar(const Variant &p_key, bool *r_valid) const {
diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h
index a77970a0bd..cc9996101e 100644
--- a/core/io/packed_data_container.h
+++ b/core/io/packed_data_container.h
@@ -94,7 +94,6 @@ public:
Variant _iter_init(const Array &p_iter);
Variant _iter_next(const Array &p_iter);
Variant _iter_get(const Variant &p_iter);
- bool _is_dictionary() const;
int size() const;
virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const override;
diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp
new file mode 100644
index 0000000000..f22e442a34
--- /dev/null
+++ b/core/io/remote_filesystem_client.cpp
@@ -0,0 +1,329 @@
+/**************************************************************************/
+/* remote_filesystem_client.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 "remote_filesystem_client.h"
+
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/io/stream_peer_tcp.h"
+#include "core/string/string_builder.h"
+
+#define FILESYSTEM_CACHE_VERSION 1
+#define FILESYSTEM_PROTOCOL_VERSION 1
+#define PASSWORD_LENGTH 32
+
+#define FILES_SUBFOLDER "remote_filesystem_files"
+#define FILES_CACHE_FILE "remote_filesystem.cache"
+
+Vector<RemoteFilesystemClient::FileCache> RemoteFilesystemClient::_load_cache_file() {
+ Ref<FileAccess> fa = FileAccess::open(cache_path.path_join(FILES_CACHE_FILE), FileAccess::READ);
+ if (!fa.is_valid()) {
+ return Vector<FileCache>(); // No cache, return empty
+ }
+
+ int version = fa->get_line().to_int();
+ if (version != FILESYSTEM_CACHE_VERSION) {
+ return Vector<FileCache>(); // Version mismatch, ignore everything.
+ }
+
+ String file_path = cache_path.path_join(FILES_SUBFOLDER);
+
+ Vector<FileCache> file_cache;
+
+ while (!fa->eof_reached()) {
+ String l = fa->get_line();
+ Vector<String> fields = l.split("::");
+ if (fields.size() != 3) {
+ break;
+ }
+ FileCache fc;
+ fc.path = fields[0];
+ fc.server_modified_time = fields[1].to_int();
+ fc.modified_time = fields[2].to_int();
+
+ String full_path = file_path.path_join(fc.path);
+ if (!FileAccess::exists(full_path)) {
+ continue; // File is gone.
+ }
+
+ if (FileAccess::get_modified_time(full_path) != fc.modified_time) {
+ DirAccess::remove_absolute(full_path); // Take the chance to remove this file and assume we no longer have it.
+ continue;
+ }
+
+ file_cache.push_back(fc);
+ }
+
+ return file_cache;
+}
+
+Error RemoteFilesystemClient::_store_file(const String &p_path, const LocalVector<uint8_t> &p_file, uint64_t &modified_time) {
+ modified_time = 0;
+ String full_path = cache_path.path_join(FILES_SUBFOLDER).path_join(p_path);
+ String base_file_dir = full_path.get_base_dir();
+
+ if (!validated_directories.has(base_file_dir)) {
+ // Verify that path exists before writing file, but only verify once for performance.
+ DirAccess::make_dir_recursive_absolute(base_file_dir);
+ validated_directories.insert(base_file_dir);
+ }
+
+ Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open file for writing to remote filesystem cache: " + p_path);
+ f->store_buffer(p_file.ptr(), p_file.size());
+ Error err = f->get_error();
+ if (err) {
+ return err;
+ }
+ f.unref(); // Unref to ensure file is not locked and modified time can be obtained.
+
+ modified_time = FileAccess::get_modified_time(full_path);
+ return OK;
+}
+
+Error RemoteFilesystemClient::_remove_file(const String &p_path) {
+ return DirAccess::remove_absolute(cache_path.path_join(FILES_SUBFOLDER).path_join(p_path));
+}
+Error RemoteFilesystemClient::_store_cache_file(const Vector<FileCache> &p_cache) {
+ String full_path = cache_path.path_join(FILES_CACHE_FILE);
+ String base_file_dir = full_path.get_base_dir();
+ Error err = DirAccess::make_dir_recursive_absolute(base_file_dir);
+ ERR_FAIL_COND_V_MSG(err != OK && err != ERR_ALREADY_EXISTS, err, "Unable to create base directory to store cache file: " + base_file_dir);
+
+ Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open the remote cache file for writing: " + full_path);
+ f->store_line(itos(FILESYSTEM_CACHE_VERSION));
+ for (int i = 0; i < p_cache.size(); i++) {
+ String l = p_cache[i].path + "::" + itos(p_cache[i].server_modified_time) + "::" + itos(p_cache[i].modified_time);
+ f->store_line(l);
+ }
+ return OK;
+}
+
+Error RemoteFilesystemClient::synchronize_with_server(const String &p_host, int p_port, const String &p_password, String &r_cache_path) {
+ Error err = _synchronize_with_server(p_host, p_port, p_password, r_cache_path);
+ // Ensure no memory is kept
+ validated_directories.reset();
+ cache_path = String();
+ return err;
+}
+
+void RemoteFilesystemClient::_update_cache_path(String &r_cache_path) {
+ r_cache_path = cache_path.path_join(FILES_SUBFOLDER);
+}
+
+Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int p_port, const String &p_password, String &r_cache_path) {
+ cache_path = r_cache_path;
+ {
+ Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ dir->change_dir(cache_path);
+ cache_path = dir->get_current_dir();
+ }
+
+ Ref<StreamPeerTCP> tcp_client;
+ tcp_client.instantiate();
+
+ IPAddress ip = p_host.is_valid_ip_address() ? IPAddress(p_host) : IP::get_singleton()->resolve_hostname(p_host);
+ ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_INVALID_PARAMETER, "Unable to resolve remote filesystem server hostname: " + p_host);
+ print_verbose(vformat("Remote Filesystem: Connecting to host %s, port %d.", ip, p_port));
+ Error err = tcp_client->connect_to_host(ip, p_port);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to open connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed.");
+
+ while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTING) {
+ tcp_client->poll();
+ OS::get_singleton()->delay_usec(100);
+ }
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ ERR_FAIL_V_MSG(ERR_CANT_CONNECT, "Connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed.");
+ }
+
+ // Connection OK, now send the current file state.
+ print_verbose("Remote Filesystem: Connection OK.");
+
+ // Header (GRFS) - Godot Remote File System
+ print_verbose("Remote Filesystem: Sending header");
+ tcp_client->put_u8('G');
+ tcp_client->put_u8('R');
+ tcp_client->put_u8('F');
+ tcp_client->put_u8('S');
+ // Protocol version
+ tcp_client->put_32(FILESYSTEM_PROTOCOL_VERSION);
+ print_verbose("Remote Filesystem: Sending password");
+ uint8_t password[PASSWORD_LENGTH]; // Send fixed size password, since it's easier and safe to validate.
+ for (int i = 0; i < PASSWORD_LENGTH; i++) {
+ if (i < p_password.length()) {
+ password[i] = p_password[i];
+ } else {
+ password[i] = 0;
+ }
+ }
+ tcp_client->put_data(password, PASSWORD_LENGTH);
+ print_verbose("Remote Filesystem: Tags.");
+ Vector<String> tags;
+ {
+ tags.push_back(OS::get_singleton()->get_identifier());
+ switch (OS::get_singleton()->get_preferred_texture_format()) {
+ case OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC: {
+ tags.push_back("bptc");
+ tags.push_back("s3tc");
+ } break;
+ case OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC: {
+ tags.push_back("etc2");
+ tags.push_back("astc");
+ } break;
+ }
+ }
+
+ tcp_client->put_32(tags.size());
+ for (int i = 0; i < tags.size(); i++) {
+ tcp_client->put_utf8_string(tags[i]);
+ }
+ // Size of compressed list of files
+ print_verbose("Remote Filesystem: Sending file list");
+
+ Vector<FileCache> file_cache = _load_cache_file();
+
+ // Encode file cache to send it via network.
+ Vector<uint8_t> file_cache_buffer;
+ if (file_cache.size()) {
+ StringBuilder sbuild;
+ for (int i = 0; i < file_cache.size(); i++) {
+ sbuild.append(file_cache[i].path);
+ sbuild.append("::");
+ sbuild.append(itos(file_cache[i].server_modified_time));
+ sbuild.append("\n");
+ }
+ String s = sbuild.as_string();
+ CharString cs = s.utf8();
+ file_cache_buffer.resize(Compression::get_max_compressed_buffer_size(cs.length(), Compression::MODE_ZSTD));
+ int res_len = Compression::compress(file_cache_buffer.ptrw(), (const uint8_t *)cs.ptr(), cs.length(), Compression::MODE_ZSTD);
+ file_cache_buffer.resize(res_len);
+
+ tcp_client->put_32(cs.length()); // Size of buffer uncompressed
+ tcp_client->put_32(file_cache_buffer.size()); // Size of buffer compressed
+ tcp_client->put_data(file_cache_buffer.ptr(), file_cache_buffer.size()); // Buffer
+ } else {
+ tcp_client->put_32(0); // No file cache buffer
+ }
+
+ tcp_client->poll();
+ ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected after sending header.");
+
+ uint32_t file_count = tcp_client->get_32();
+
+ ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected while waiting for file list");
+
+ LocalVector<uint8_t> file_buffer;
+
+ Vector<FileCache> temp_file_cache;
+
+ HashSet<String> files_processed;
+ for (uint32_t i = 0; i < file_count; i++) {
+ String file = tcp_client->get_utf8_string();
+ ERR_FAIL_COND_V_MSG(file == String(), ERR_CONNECTION_ERROR, "Invalid file name received from remote filesystem.");
+ uint64_t server_modified_time = tcp_client->get_u64();
+ ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected while waiting for file info.");
+
+ FileCache fc;
+ fc.path = file;
+ fc.server_modified_time = server_modified_time;
+ temp_file_cache.push_back(fc);
+
+ files_processed.insert(file);
+ }
+
+ Vector<FileCache> new_file_cache;
+
+ // Get the actual files. As a robustness measure, if the connection is interrupted here, any file not yet received will be considered removed.
+ // Since the file changed anyway, this makes it the easiest way to keep robustness.
+
+ bool server_disconnected = false;
+ for (uint32_t i = 0; i < file_count; i++) {
+ String file = temp_file_cache[i].path;
+
+ if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) {
+ // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway.
+ _remove_file(file);
+ continue;
+ }
+
+ uint64_t file_size = tcp_client->get_u64();
+ file_buffer.resize(file_size);
+
+ err = tcp_client->get_data(file_buffer.ptr(), file_size);
+ if (err != OK) {
+ ERR_PRINT("Error retrieving file from remote filesystem: " + file);
+ server_disconnected = true;
+ }
+
+ if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ // Early disconnect, stop accepting files.
+ server_disconnected = true;
+ }
+
+ if (server_disconnected) {
+ // No more server, transfer is invalid, remove this file.
+ _remove_file(file);
+ continue;
+ }
+
+ uint64_t modified_time = 0;
+ err = _store_file(file, file_buffer, modified_time);
+ if (err != OK) {
+ server_disconnected = true;
+ continue;
+ }
+ FileCache fc = temp_file_cache[i];
+ fc.modified_time = modified_time;
+ new_file_cache.push_back(fc);
+ }
+
+ print_verbose("Remote Filesystem: Updating the cache file.");
+
+ // Go through the list of local files read initially (file_cache) and see which ones are
+ // unchanged (not sent again from the server).
+ // These need to be re-saved in the new list (new_file_cache).
+
+ for (int i = 0; i < file_cache.size(); i++) {
+ if (files_processed.has(file_cache[i].path)) {
+ continue; // This was either added or removed, so skip.
+ }
+ new_file_cache.push_back(file_cache[i]);
+ }
+
+ err = _store_cache_file(new_file_cache);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Error writing the remote filesystem file cache.");
+
+ print_verbose("Remote Filesystem: Update success.");
+
+ _update_cache_path(r_cache_path);
+ return OK;
+}
diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h
new file mode 100644
index 0000000000..42eba98eb1
--- /dev/null
+++ b/core/io/remote_filesystem_client.h
@@ -0,0 +1,65 @@
+/**************************************************************************/
+/* remote_filesystem_client.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 REMOTE_FILESYSTEM_CLIENT_H
+#define REMOTE_FILESYSTEM_CLIENT_H
+
+#include "core/io/ip_address.h"
+#include "core/string/ustring.h"
+#include "core/templates/hash_set.h"
+#include "core/templates/local_vector.h"
+
+class RemoteFilesystemClient {
+ String cache_path;
+ HashSet<String> validated_directories;
+
+protected:
+ String _get_cache_path() { return cache_path; }
+ struct FileCache {
+ String path; // Local path (as in "folder/to/file.png")
+ uint64_t server_modified_time; // MD5 checksum.
+ uint64_t modified_time;
+ };
+ virtual bool _is_configured() { return !cache_path.is_empty(); }
+ // Can be re-implemented per platform. If so, feel free to ignore get_cache_path()
+ virtual Vector<FileCache> _load_cache_file();
+ virtual Error _store_file(const String &p_path, const LocalVector<uint8_t> &p_file, uint64_t &modified_time);
+ virtual Error _remove_file(const String &p_path);
+ virtual Error _store_cache_file(const Vector<FileCache> &p_cache);
+ virtual Error _synchronize_with_server(const String &p_host, int p_port, const String &p_password, String &r_cache_path);
+
+ virtual void _update_cache_path(String &r_cache_path);
+
+public:
+ Error synchronize_with_server(const String &p_host, int p_port, const String &p_password, String &r_cache_path);
+ virtual ~RemoteFilesystemClient() {}
+};
+
+#endif // REMOTE_FILESYSTEM_CLIENT_H
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 38f41d645c..b4da314e96 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -445,13 +445,12 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
WARN_PRINT("Broken external resource! (index out of size)");
r_v = Variant();
} else {
- if (external_resources[erindex].cache.is_null()) {
- //cache not here yet, wait for it?
- if (use_sub_threads) {
- Error err;
- external_resources.write[erindex].cache = ResourceLoader::load_threaded_get(external_resources[erindex].path, &err);
-
- if (err != OK || external_resources[erindex].cache.is_null()) {
+ Ref<ResourceLoader::LoadToken> &load_token = external_resources.write[erindex].load_token;
+ if (load_token.is_valid()) { // If not valid, it's OK since then we know this load accepts broken dependencies.
+ Error err;
+ Ref<Resource> res = ResourceLoader::_load_complete(*load_token.ptr(), &err);
+ if (res.is_null()) {
+ if (!ResourceLoader::is_cleaning_tasks()) {
if (!ResourceLoader::get_abort_on_missing_resources()) {
ResourceLoader::notify_dependency_error(local_path, external_resources[erindex].path, external_resources[erindex].type);
} else {
@@ -459,12 +458,11 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
ERR_FAIL_V_MSG(error, "Can't load dependency: " + external_resources[erindex].path + ".");
}
}
+ } else {
+ r_v = res;
}
}
-
- r_v = external_resources[erindex].cache;
}
-
} break;
default: {
ERR_FAIL_V(ERR_FILE_CORRUPT);
@@ -684,28 +682,13 @@ Error ResourceLoaderBinary::load() {
}
external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap
-
- if (!use_sub_threads) {
- external_resources.write[i].cache = ResourceLoader::load(path, external_resources[i].type);
-
- if (external_resources[i].cache.is_null()) {
- if (!ResourceLoader::get_abort_on_missing_resources()) {
- ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
- } else {
- error = ERR_FILE_MISSING_DEPENDENCIES;
- ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + ".");
- }
- }
-
- } else {
- Error err = ResourceLoader::load_threaded_request(path, external_resources[i].type, use_sub_threads, ResourceFormatLoader::CACHE_MODE_REUSE, local_path);
- if (err != OK) {
- if (!ResourceLoader::get_abort_on_missing_resources()) {
- ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
- } else {
- error = ERR_FILE_MISSING_DEPENDENCIES;
- ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + ".");
- }
+ external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, ResourceFormatLoader::CACHE_MODE_REUSE);
+ if (!external_resources[i].load_token.is_valid()) {
+ if (!ResourceLoader::get_abort_on_missing_resources()) {
+ ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
+ } else {
+ error = ERR_FILE_MISSING_DEPENDENCIES;
+ ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + ".");
}
}
}
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index add7cdf297..30f1664983 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -60,7 +60,7 @@ class ResourceLoaderBinary {
String path;
String type;
ResourceUID::ID uid = ResourceUID::INVALID_ID;
- Ref<Resource> cache;
+ Ref<ResourceLoader::LoadToken> load_token;
};
bool using_named_scene_ids = false;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 9af3a7daed..f852e8d382 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -202,20 +202,71 @@ void ResourceFormatLoader::_bind_methods() {
///////////////////////////////////
+// This should be robust enough to be called redundantly without issues.
+void ResourceLoader::LoadToken::clear() {
+ thread_load_mutex.lock();
+
+ WorkerThreadPool::TaskID task_to_await = 0;
+
+ if (!local_path.is_empty()) { // Empty is used for the special case where the load task is not registered.
+ DEV_ASSERT(thread_load_tasks.has(local_path));
+ ThreadLoadTask &load_task = thread_load_tasks[local_path];
+ if (!load_task.awaited) {
+ task_to_await = load_task.task_id;
+ load_task.awaited = true;
+ }
+ thread_load_tasks.erase(local_path);
+ local_path.clear();
+ }
+
+ if (!user_path.is_empty()) {
+ DEV_ASSERT(user_load_tokens.has(user_path));
+ user_load_tokens.erase(user_path);
+ user_path.clear();
+ }
+
+ thread_load_mutex.unlock();
+
+ // If task is unused, await it here, locally, now the token data is consistent.
+ if (task_to_await) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_to_await);
+ }
+}
+
+ResourceLoader::LoadToken::~LoadToken() {
+ clear();
+}
+
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
- bool found = false;
+ load_nesting++;
+ if (load_paths_stack.size()) {
+ thread_load_mutex.lock();
+ HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack[load_paths_stack.size() - 1]);
+ if (E) {
+ E->value.sub_tasks.insert(p_path);
+ }
+ thread_load_mutex.unlock();
+ }
+ load_paths_stack.push_back(p_path);
// Try all loaders and pick the first match for the type hint
+ bool found = false;
+ Ref<Resource> res;
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(p_path, p_type_hint)) {
continue;
}
found = true;
- Ref<Resource> res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
- if (res.is_null()) {
- continue;
+ res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
+ if (!res.is_null()) {
+ break;
}
+ }
+
+ load_paths_stack.resize(load_paths_stack.size() - 1);
+ load_nesting--;
+ if (!res.is_null()) {
return res;
}
@@ -232,47 +283,60 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
void ResourceLoader::_thread_load_function(void *p_userdata) {
ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata;
- load_task.loader_id = Thread::get_caller_id();
- if (load_task.cond_var) {
- //this is an actual thread, so wait for Ok from semaphore
- thread_load_semaphore->wait(); //wait until its ok to start loading
+ thread_load_mutex.lock();
+ caller_task_id = load_task.task_id;
+ if (cleaning_tasks) {
+ load_task.status = THREAD_LOAD_FAILED;
+ thread_load_mutex.unlock();
+ return;
}
- load_task.resource = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_task.error, load_task.use_sub_threads, &load_task.progress);
+ thread_load_mutex.unlock();
- load_task.progress = 1.0; //it was fully loaded at this point, so force progress to 1.0
+ // Thread-safe either if it's the current thread or a brand new one.
+ CallQueue *mq_override = nullptr;
+ if (load_nesting == 0) {
+ if (!load_task.dependent_path.is_empty()) {
+ load_paths_stack.push_back(load_task.dependent_path);
+ }
+ if (!Thread::is_main_thread()) {
+ mq_override = memnew(CallQueue);
+ MessageQueue::set_thread_singleton_override(mq_override);
+ }
+ } else {
+ DEV_ASSERT(load_task.dependent_path.is_empty());
+ }
+ // --
- thread_load_mutex->lock();
+ Ref<Resource> res = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_task.error, load_task.use_sub_threads, &load_task.progress);
+
+ thread_load_mutex.lock();
+
+ load_task.resource = res;
+
+ load_task.progress = 1.0; //it was fully loaded at this point, so force progress to 1.0
if (load_task.error != OK) {
load_task.status = THREAD_LOAD_FAILED;
} else {
load_task.status = THREAD_LOAD_LOADED;
}
- if (load_task.cond_var) {
- if (load_task.start_next && thread_waiting_count > 0) {
- thread_waiting_count--;
- //thread loading count remains constant, this ends but another one begins
- thread_load_semaphore->post();
- } else {
- thread_loading_count--; //no threads waiting, just reduce loading count
- }
-
- print_lt("END: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
+ if (load_task.cond_var) {
load_task.cond_var->notify_all();
memdelete(load_task.cond_var);
load_task.cond_var = nullptr;
}
if (load_task.resource.is_valid()) {
- load_task.resource->set_path(load_task.local_path);
+ if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ load_task.resource->set_path(load_task.local_path);
+ }
if (load_task.xl_remapped) {
load_task.resource->set_as_translation_remapped(true);
}
#ifdef TOOLS_ENABLED
-
load_task.resource->set_edited(false);
if (timestamp_on_load) {
uint64_t mt = FileAccess::get_modified_time(load_task.remapped_path);
@@ -286,7 +350,12 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
}
}
- thread_load_mutex->unlock();
+ thread_load_mutex.unlock();
+
+ if (load_nesting == 0 && mq_override) {
+ memdelete(mq_override);
+ MessageQueue::set_thread_singleton_override(nullptr);
+ }
}
static String _validate_local_path(const String &p_path) {
@@ -299,91 +368,127 @@ static String _validate_local_path(const String &p_path) {
return ProjectSettings::get_singleton()->localize_path(p_path);
}
}
-Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
- String local_path = _validate_local_path(p_path);
- thread_load_mutex->lock();
+Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) {
+ thread_load_mutex.lock();
+ if (user_load_tokens.has(p_path)) {
+ print_verbose("load_threaded_request(): Another threaded load for resource path '" + p_path + "' has been initiated. Not an error.");
+ user_load_tokens[p_path]->reference(); // Additional request.
+ thread_load_mutex.unlock();
+ return OK;
+ }
+ user_load_tokens[p_path] = nullptr;
+ thread_load_mutex.unlock();
+
+ Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode);
+ if (token.is_valid()) {
+ thread_load_mutex.lock();
+ token->user_path = p_path;
+ token->reference(); // First request.
+ user_load_tokens[p_path] = token.ptr();
+ print_lt("REQUEST: user load tokens: " + itos(user_load_tokens.size()));
+ thread_load_mutex.unlock();
+ return OK;
+ } else {
+ return FAILED;
+ }
+}
- if (!p_source_resource.is_empty()) {
- //must be loading from this resource
- if (!thread_load_tasks.has(p_source_resource)) {
- thread_load_mutex->unlock();
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "There is no thread loading source resource '" + p_source_resource + "'.");
- }
- //must not be already added as s sub tasks
- if (thread_load_tasks[p_source_resource].sub_tasks.has(local_path)) {
- thread_load_mutex->unlock();
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Thread loading source resource '" + p_source_resource + "' already is loading '" + local_path + "'.");
- }
+Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) {
+ if (r_error) {
+ *r_error = OK;
}
- if (thread_load_tasks.has(local_path)) {
- thread_load_tasks[local_path].requests++;
- if (!p_source_resource.is_empty()) {
- thread_load_tasks[p_source_resource].sub_tasks.insert(local_path);
+ Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, LOAD_THREAD_FROM_CURRENT, p_cache_mode);
+ if (!load_token.is_valid()) {
+ if (r_error) {
+ *r_error = FAILED;
}
- thread_load_mutex->unlock();
- return OK;
+ return Ref<Resource>();
}
- {
- //create load task
-
- ThreadLoadTask load_task;
+ Ref<Resource> res = _load_complete(*load_token.ptr(), r_error);
+ return res;
+}
- load_task.requests = 1;
- load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
- load_task.local_path = local_path;
- load_task.type_hint = p_type_hint;
- load_task.cache_mode = p_cache_mode;
- load_task.use_sub_threads = p_use_sub_threads;
+Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) {
+ String local_path = _validate_local_path(p_path);
- { //must check if resource is already loaded before attempting to load it in a thread
+ Ref<LoadToken> load_token;
+ bool must_not_register = false;
+ ThreadLoadTask unregistered_load_task; // Once set, must be valid up to the call to do the load.
+ ThreadLoadTask *load_task_ptr = nullptr;
+ bool run_on_current_thread = false;
+ {
+ MutexLock thread_load_lock(thread_load_mutex);
- if (load_task.loader_id == Thread::get_caller_id()) {
- thread_load_mutex->unlock();
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Attempted to load a resource already being loaded from this thread, cyclic reference?");
+ if (thread_load_tasks.has(local_path)) {
+ load_token = Ref<LoadToken>(thread_load_tasks[local_path].load_token);
+ if (!load_token.is_valid()) {
+ // The token is dying (reached 0 on another thread).
+ // Ensure it's killed now so the path can be safely reused right away.
+ thread_load_tasks[local_path].load_token->clear();
+ } else {
+ if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ return load_token;
+ }
}
+ }
- Ref<Resource> existing = ResourceCache::get_ref(local_path);
+ load_token.instantiate();
+ load_token->local_path = local_path;
- if (existing.is_valid()) {
- //referencing is fine
- load_task.resource = existing;
- load_task.status = THREAD_LOAD_LOADED;
- load_task.progress = 1.0;
+ //create load task
+ {
+ ThreadLoadTask load_task;
+
+ load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
+ load_task.load_token = load_token.ptr();
+ load_task.local_path = local_path;
+ load_task.type_hint = p_type_hint;
+ load_task.cache_mode = p_cache_mode;
+ load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE;
+ if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ Ref<Resource> existing = ResourceCache::get_ref(local_path);
+ if (existing.is_valid()) {
+ //referencing is fine
+ load_task.resource = existing;
+ load_task.status = THREAD_LOAD_LOADED;
+ load_task.progress = 1.0;
+ thread_load_tasks[local_path] = load_task;
+ return load_token;
+ }
}
- }
- if (!p_source_resource.is_empty()) {
- thread_load_tasks[p_source_resource].sub_tasks.insert(local_path);
- }
-
- thread_load_tasks[local_path] = load_task;
- }
+ // If we want to ignore cache, but there's another task loading it, we can't add this one to the map and we also have to finish unconditionally synchronously.
+ must_not_register = thread_load_tasks.has(local_path) && p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE;
+ if (must_not_register) {
+ load_token->local_path.clear();
+ unregistered_load_task = load_task;
+ } else {
+ thread_load_tasks[local_path] = load_task;
+ }
- ThreadLoadTask &load_task = thread_load_tasks[local_path];
+ load_task_ptr = must_not_register ? &unregistered_load_task : &thread_load_tasks[local_path];
+ }
- if (load_task.resource.is_null()) { //needs to be loaded in thread
+ run_on_current_thread = must_not_register || p_thread_mode == LOAD_THREAD_FROM_CURRENT;
- load_task.cond_var = memnew(ConditionVariable);
- if (thread_loading_count < thread_load_max) {
- thread_loading_count++;
- thread_load_semaphore->post(); //we have free threads, so allow one
+ if (run_on_current_thread) {
+ load_task_ptr->thread_id = Thread::get_caller_id();
+ if (must_not_register) {
+ load_token->res_if_unregistered = load_task_ptr->resource;
+ }
} else {
- thread_waiting_count++;
+ load_task_ptr->task_id = WorkerThreadPool::get_singleton()->add_native_task(&ResourceLoader::_thread_load_function, load_task_ptr);
}
-
- print_lt("REQUEST: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
-
- load_task.thread = memnew(Thread);
- load_task.thread->start(_thread_load_function, &thread_load_tasks[local_path]);
- load_task.loader_id = load_task.thread->get_id();
}
- thread_load_mutex->unlock();
+ if (run_on_current_thread) {
+ _thread_load_function(load_task_ptr);
+ }
- return OK;
+ return load_token;
}
float ResourceLoader::_dependency_get_progress(const String &p_path) {
@@ -409,13 +514,22 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) {
}
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) {
- String local_path = _validate_local_path(p_path);
+ MutexLock thread_load_lock(thread_load_mutex);
+
+ if (!user_load_tokens.has(p_path)) {
+ print_verbose("load_threaded_get_status(): No threaded load for resource path '" + p_path + "' has been initiated or its result has already been collected.");
+ return THREAD_LOAD_INVALID_RESOURCE;
+ }
- thread_load_mutex->lock();
+ String local_path = _validate_local_path(p_path);
if (!thread_load_tasks.has(local_path)) {
- thread_load_mutex->unlock();
+#ifdef DEV_ENABLED
+ CRASH_NOW();
+#endif
+ // On non-dev, be defensive and at least avoid crashing (at this point at least).
return THREAD_LOAD_INVALID_RESOURCE;
}
+
ThreadLoadTask &load_task = thread_load_tasks[local_path];
ThreadLoadStatus status;
status = load_task.status;
@@ -423,198 +537,120 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const
*r_progress = _dependency_get_progress(local_path);
}
- thread_load_mutex->unlock();
-
return status;
}
Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
- String local_path = _validate_local_path(p_path);
-
- MutexLock thread_load_lock(*thread_load_mutex);
- if (!thread_load_tasks.has(local_path)) {
- if (r_error) {
- *r_error = ERR_INVALID_PARAMETER;
- }
- return Ref<Resource>();
+ if (r_error) {
+ *r_error = OK;
}
- ThreadLoadTask &load_task = thread_load_tasks[local_path];
+ Ref<Resource> res;
+ {
+ MutexLock thread_load_lock(thread_load_mutex);
- if (load_task.status == THREAD_LOAD_IN_PROGRESS) {
- if (load_task.loader_id == Thread::get_caller_id()) {
- // Load is in progress, but it's precisely this thread the one in charge.
- // That means this is a cyclic load.
+ if (!user_load_tokens.has(p_path)) {
+ print_verbose("load_threaded_get(): No threaded load for resource path '" + p_path + "' has been initiated or its result has already been collected.");
if (r_error) {
- *r_error = ERR_BUSY;
+ *r_error = ERR_INVALID_PARAMETER;
}
return Ref<Resource>();
- } else if (!load_task.cond_var) {
- // Load is in progress, but a condition variable was never created for it.
- // That happens when a load has been initiated with subthreads disabled,
- // but now another load thread needs to interact with this one (either
- // because of subthreads being used this time, or because it's simply a
- // threaded load running on a different thread).
- // Since we want to be notified when the load ends, we must create the
- // condition variable now.
- load_task.cond_var = memnew(ConditionVariable);
}
- }
-
- //cond var still exists, meaning it's still loading, request poll
- if (load_task.cond_var) {
- {
- // As we got a cond var, this means we are going to have to wait
- // until the sub-resource is done loading
- //
- // As this thread will become 'blocked' we should "exchange" its
- // active status with a waiting one, to ensure load continues.
- //
- // This ensures loading is never blocked and that is also within
- // the maximum number of active threads.
-
- if (thread_waiting_count > 0) {
- thread_waiting_count--;
- thread_loading_count++;
- thread_load_semaphore->post();
-
- load_task.start_next = false; //do not start next since we are doing it here
- }
-
- thread_suspended_count++;
- print_lt("GET: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
- }
-
- bool still_valid = true;
- bool was_thread = load_task.thread;
- do {
- load_task.cond_var->wait(thread_load_lock);
- if (!thread_load_tasks.has(local_path)) { //may have been erased during unlock and this was always an invalid call
- still_valid = false;
- break;
- }
- } while (load_task.cond_var); // In case of spurious wakeup.
-
- if (was_thread) {
- thread_suspended_count--;
- }
-
- if (!still_valid) {
+ LoadToken *load_token = user_load_tokens[p_path];
+ if (!load_token) {
+ // This happens if requested from one thread and rapidly querying from another.
if (r_error) {
- *r_error = ERR_INVALID_PARAMETER;
+ *r_error = ERR_BUSY;
}
return Ref<Resource>();
}
+ res = _load_complete_inner(*load_token, r_error, thread_load_lock);
+ if (load_token->unreference()) {
+ memdelete(load_token);
+ }
}
- Ref<Resource> resource = load_task.resource;
- if (r_error) {
- *r_error = load_task.error;
- }
-
- load_task.requests--;
+ print_lt("GET: user load tokens: " + itos(user_load_tokens.size()));
- if (load_task.requests == 0) {
- if (load_task.thread) { //thread may not have been used
- load_task.thread->wait_to_finish();
- memdelete(load_task.thread);
- }
- thread_load_tasks.erase(local_path);
- }
+ return res;
+}
- return resource;
+Ref<Resource> ResourceLoader::_load_complete(LoadToken &p_load_token, Error *r_error) {
+ MutexLock thread_load_lock(thread_load_mutex);
+ return _load_complete_inner(p_load_token, r_error, thread_load_lock);
}
-Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) {
+Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Error *r_error, MutexLock<SafeBinaryMutex<BINARY_MUTEX_TAG>> &p_thread_load_lock) {
if (r_error) {
- *r_error = ERR_CANT_OPEN;
+ *r_error = OK;
}
- String local_path = _validate_local_path(p_path);
+ if (!p_load_token.local_path.is_empty()) {
+ if (!thread_load_tasks.has(p_load_token.local_path)) {
+#ifdef DEV_ENABLED
+ CRASH_NOW();
+#endif
+ // On non-dev, be defensive and at least avoid crashing (at this point at least).
+ if (r_error) {
+ *r_error = ERR_BUG;
+ }
+ return Ref<Resource>();
+ }
- if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
- thread_load_mutex->lock();
+ ThreadLoadTask &load_task = thread_load_tasks[p_load_token.local_path];
- //Is it already being loaded? poll until done
- if (thread_load_tasks.has(local_path)) {
- Error err = load_threaded_request(p_path, p_type_hint);
- if (err != OK) {
+ if (load_task.status == THREAD_LOAD_IN_PROGRESS) {
+ DEV_ASSERT((load_task.task_id == 0) != (load_task.thread_id == 0));
+
+ if ((load_task.task_id != 0 && load_task.task_id == caller_task_id) ||
+ (load_task.thread_id != 0 && load_task.thread_id == Thread::get_caller_id())) {
+ // Load is in progress, but it's precisely this thread the one in charge.
+ // That means this is a cyclic load.
if (r_error) {
- *r_error = err;
+ *r_error = ERR_BUSY;
}
- thread_load_mutex->unlock();
return Ref<Resource>();
}
- thread_load_mutex->unlock();
- return load_threaded_get(p_path, r_error);
- }
-
- //Is it cached?
-
- Ref<Resource> existing = ResourceCache::get_ref(local_path);
-
- if (existing.is_valid()) {
- thread_load_mutex->unlock();
-
- if (r_error) {
- *r_error = OK;
+ if (load_task.task_id != 0 && !load_task.awaited) {
+ // Loading thread is in the worker pool and still not awaited.
+ load_task.awaited = true;
+ thread_load_mutex.unlock();
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
+ thread_load_mutex.lock();
+ } else {
+ // Loading thread is main or user thread, or in the worker pool, but already awaited by some other thread.
+ if (!load_task.cond_var) {
+ load_task.cond_var = memnew(ConditionVariable);
+ }
+ do {
+ load_task.cond_var->wait(p_thread_load_lock);
+ DEV_ASSERT(thread_load_tasks.has(p_load_token.local_path) && p_load_token.get_reference_count());
+ } while (load_task.cond_var);
}
-
- return existing; //use cached
}
- //load using task (but this thread)
- ThreadLoadTask load_task;
-
- load_task.requests = 1;
- load_task.local_path = local_path;
- load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped);
- load_task.type_hint = p_type_hint;
- load_task.cache_mode = p_cache_mode; //ignore
- load_task.loader_id = Thread::get_caller_id();
-
- thread_load_tasks[local_path] = load_task;
-
- thread_load_mutex->unlock();
-
- _thread_load_function(&thread_load_tasks[local_path]);
-
- return load_threaded_get(p_path, r_error);
-
- } else {
- bool xl_remapped = false;
- String path = _path_remap(local_path, &xl_remapped);
-
- if (path.is_empty()) {
- ERR_FAIL_V_MSG(Ref<Resource>(), "Remapping '" + local_path + "' failed.");
+ if (cleaning_tasks) {
+ load_task.resource = Ref<Resource>();
+ load_task.error = FAILED;
}
- print_verbose("Loading resource: " + path);
- float p;
- Ref<Resource> res = _load(path, local_path, p_type_hint, p_cache_mode, r_error, false, &p);
-
- if (res.is_null()) {
- print_verbose("Failed loading resource: " + path);
- return Ref<Resource>();
- }
-
- if (xl_remapped) {
- res->set_as_translation_remapped(true);
+ Ref<Resource> resource = load_task.resource;
+ if (r_error) {
+ *r_error = load_task.error;
}
-
-#ifdef TOOLS_ENABLED
-
- res->set_edited(false);
- if (timestamp_on_load) {
- uint64_t mt = FileAccess::get_modified_time(path);
- //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt);
- res->set_last_modified_time(mt);
+ return resource;
+ } else {
+ // Special case of an unregistered task.
+ // The resource should have been loaded by now.
+ Ref<Resource> resource = p_load_token.res_if_unregistered;
+ if (!resource.is_valid()) {
+ if (r_error) {
+ *r_error = FAILED;
+ }
}
-#endif
-
- return res;
+ return resource;
}
}
@@ -958,32 +994,42 @@ void ResourceLoader::clear_translation_remaps() {
}
void ResourceLoader::clear_thread_load_tasks() {
- thread_load_mutex->lock();
-
- for (KeyValue<String, ResourceLoader::ThreadLoadTask> &E : thread_load_tasks) {
- switch (E.value.status) {
- case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_LOADED: {
- E.value.resource = Ref<Resource>();
- } break;
-
- case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_IN_PROGRESS: {
- if (E.value.thread != nullptr) {
- E.value.thread->wait_to_finish();
- memdelete(E.value.thread);
- E.value.thread = nullptr;
+ // Bring the thing down as quickly as possible without causing deadlocks or leaks.
+
+ thread_load_mutex.lock();
+ cleaning_tasks = true;
+
+ while (true) {
+ bool none_running = true;
+ if (thread_load_tasks.size()) {
+ for (KeyValue<String, ResourceLoader::ThreadLoadTask> &E : thread_load_tasks) {
+ if (E.value.status == THREAD_LOAD_IN_PROGRESS) {
+ if (E.value.cond_var) {
+ E.value.cond_var->notify_all();
+ memdelete(E.value.cond_var);
+ E.value.cond_var = nullptr;
+ }
+ none_running = false;
}
- E.value.resource = Ref<Resource>();
- } break;
-
- case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_FAILED:
- default: {
- // do nothing
}
}
+ if (none_running) {
+ break;
+ }
+ thread_load_mutex.unlock();
+ OS::get_singleton()->delay_usec(1000);
+ thread_load_mutex.lock();
+ }
+
+ for (KeyValue<String, LoadToken *> &E : user_load_tokens) {
+ memdelete(E.value);
}
+ user_load_tokens.clear();
+
thread_load_tasks.clear();
- thread_load_mutex->unlock();
+ cleaning_tasks = false;
+ thread_load_mutex.unlock();
}
void ResourceLoader::load_path_remaps() {
@@ -1080,41 +1126,33 @@ void ResourceLoader::remove_custom_loaders() {
}
}
-void ResourceLoader::initialize() {
- thread_load_mutex = memnew(SafeBinaryMutex<BINARY_MUTEX_TAG>);
- thread_load_max = OS::get_singleton()->get_processor_count();
- thread_loading_count = 0;
- thread_waiting_count = 0;
- thread_suspended_count = 0;
- thread_load_semaphore = memnew(Semaphore);
+bool ResourceLoader::is_cleaning_tasks() {
+ MutexLock lock(thread_load_mutex);
+ return cleaning_tasks;
}
-void ResourceLoader::finalize() {
- clear_thread_load_tasks();
- memdelete(thread_load_mutex);
- memdelete(thread_load_semaphore);
-}
+void ResourceLoader::initialize() {}
-ResourceLoadErrorNotify ResourceLoader::err_notify = nullptr;
-void *ResourceLoader::err_notify_ud = nullptr;
+void ResourceLoader::finalize() {}
+ResourceLoadErrorNotify ResourceLoader::err_notify = nullptr;
DependencyErrorNotify ResourceLoader::dep_err_notify = nullptr;
-void *ResourceLoader::dep_err_notify_ud = nullptr;
bool ResourceLoader::create_missing_resources_if_class_unavailable = false;
bool ResourceLoader::abort_on_missing_resource = true;
bool ResourceLoader::timestamp_on_load = false;
+thread_local int ResourceLoader::load_nesting = 0;
+thread_local WorkerThreadPool::TaskID ResourceLoader::caller_task_id = 0;
+thread_local Vector<String> ResourceLoader::load_paths_stack;
+
template <>
thread_local uint32_t SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG>::count = 0;
-SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG> *ResourceLoader::thread_load_mutex = nullptr;
+SafeBinaryMutex<ResourceLoader::BINARY_MUTEX_TAG> ResourceLoader::thread_load_mutex;
HashMap<String, ResourceLoader::ThreadLoadTask> ResourceLoader::thread_load_tasks;
-Semaphore *ResourceLoader::thread_load_semaphore = nullptr;
+bool ResourceLoader::cleaning_tasks = false;
-int ResourceLoader::thread_loading_count = 0;
-int ResourceLoader::thread_waiting_count = 0;
-int ResourceLoader::thread_suspended_count = 0;
-int ResourceLoader::thread_load_max = 0;
+HashMap<String, ResourceLoader::LoadToken *> ResourceLoader::user_load_tokens;
SelfList<Resource>::List ResourceLoader::remapped_list;
HashMap<String, Vector<String>> ResourceLoader::translation_remaps;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 72c1f90653..592befb603 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -34,6 +34,7 @@
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/object/script_language.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
@@ -88,8 +89,8 @@ public:
VARIANT_ENUM_CAST(ResourceFormatLoader::CacheMode)
-typedef void (*ResourceLoadErrorNotify)(void *p_ud, const String &p_text);
-typedef void (*DependencyErrorNotify)(void *p_ud, const String &p_loading, const String &p_which, const String &p_type);
+typedef void (*ResourceLoadErrorNotify)(const String &p_text);
+typedef void (*DependencyErrorNotify)(const String &p_loading, const String &p_which, const String &p_type);
typedef Error (*ResourceLoaderImport)(const String &p_path);
typedef void (*ResourceLoadedCallback)(Ref<Resource> p_resource, const String &p_path);
@@ -107,9 +108,30 @@ public:
THREAD_LOAD_LOADED
};
+ enum LoadThreadMode {
+ LOAD_THREAD_FROM_CURRENT,
+ LOAD_THREAD_SPAWN_SINGLE,
+ LOAD_THREAD_DISTRIBUTE,
+ };
+
+ struct LoadToken : public RefCounted {
+ String local_path;
+ String user_path;
+ Ref<Resource> res_if_unregistered;
+
+ void clear();
+
+ virtual ~LoadToken();
+ };
+
static const int BINARY_MUTEX_TAG = 1;
+ static Ref<LoadToken> _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode);
+ static Ref<Resource> _load_complete(LoadToken &p_load_token, Error *r_error);
+
private:
+ static Ref<Resource> _load_complete_inner(LoadToken &p_load_token, Error *r_error, MutexLock<SafeBinaryMutex<BINARY_MUTEX_TAG>> &p_thread_load_lock);
+
static Ref<ResourceFormatLoader> loader[MAX_LOADERS];
static int loader_count;
static bool timestamp_on_load;
@@ -129,8 +151,7 @@ private:
static SelfList<Resource>::List remapped_list;
friend class ResourceFormatImporter;
- friend class ResourceInteractiveLoader;
- // Internal load function.
+
static Ref<Resource> _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress);
static ResourceLoadedCallback _loaded_callback;
@@ -138,11 +159,14 @@ private:
static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path);
struct ThreadLoadTask {
- Thread *thread = nullptr;
- Thread::ID loader_id = 0;
- ConditionVariable *cond_var = nullptr;
+ WorkerThreadPool::TaskID task_id = 0; // Used if run on a worker thread from the pool.
+ Thread::ID thread_id = 0; // Used if running on an user thread (e.g., simple non-threaded load).
+ bool awaited = false; // If it's in the pool, this helps not awaiting from more than one dependent thread.
+ ConditionVariable *cond_var = nullptr; // In not in the worker pool or already awaiting, this is used as a secondary awaiting mechanism.
+ LoadToken *load_token = nullptr;
String local_path;
String remapped_path;
+ String dependent_path;
String type_hint;
float progress = 0.0;
ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS;
@@ -151,27 +175,29 @@ private:
Ref<Resource> resource;
bool xl_remapped = false;
bool use_sub_threads = false;
- bool start_next = true;
- int requests = 0;
HashSet<String> sub_tasks;
};
static void _thread_load_function(void *p_userdata);
- static SafeBinaryMutex<BINARY_MUTEX_TAG> *thread_load_mutex;
+
+ static thread_local int load_nesting;
+ static thread_local WorkerThreadPool::TaskID caller_task_id;
+ static thread_local Vector<String> load_paths_stack;
+ static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex;
static HashMap<String, ThreadLoadTask> thread_load_tasks;
- static Semaphore *thread_load_semaphore;
- static int thread_waiting_count;
- static int thread_loading_count;
- static int thread_suspended_count;
- static int thread_load_max;
+ static bool cleaning_tasks;
+
+ static HashMap<String, LoadToken *> user_load_tokens;
static float _dependency_get_progress(const String &p_path);
public:
- static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, const String &p_source_resource = String());
+ static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE);
static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr);
static Ref<Resource> load_threaded_get(const String &p_path, Error *r_error = nullptr);
+ static bool is_within_load() { return load_nesting > 0; };
+
static Ref<Resource> load(const String &p_path, const String &p_type_hint = "", ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, Error *r_error = nullptr);
static bool exists(const String &p_path, const String &p_type_hint = "");
@@ -192,24 +218,24 @@ public:
static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load = p_timestamp; }
static bool get_timestamp_on_load() { return timestamp_on_load; }
+ // Loaders can safely use this regardless which thread they are running on.
static void notify_load_error(const String &p_err) {
if (err_notify) {
- err_notify(err_notify_ud, p_err);
+ callable_mp_static(err_notify).bind(p_err).call_deferred();
}
}
- static void set_error_notify_func(void *p_ud, ResourceLoadErrorNotify p_err_notify) {
+ static void set_error_notify_func(ResourceLoadErrorNotify p_err_notify) {
err_notify = p_err_notify;
- err_notify_ud = p_ud;
}
+ // Loaders can safely use this regardless which thread they are running on.
static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) {
if (dep_err_notify) {
- dep_err_notify(dep_err_notify_ud, p_path, p_dependency, p_type);
+ callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type).call_deferred();
}
}
- static void set_dependency_error_notify_func(void *p_ud, DependencyErrorNotify p_err_notify) {
+ static void set_dependency_error_notify_func(DependencyErrorNotify p_err_notify) {
dep_err_notify = p_err_notify;
- dep_err_notify_ud = p_ud;
}
static void set_abort_on_missing_resources(bool p_abort) { abort_on_missing_resource = p_abort; }
@@ -237,6 +263,8 @@ public:
static void set_create_missing_resources_if_class_unavailable(bool p_enable);
_FORCE_INLINE_ static bool is_creating_missing_resources_if_class_unavailable_enabled() { return create_missing_resources_if_class_unavailable; }
+ static bool is_cleaning_tasks();
+
static void initialize();
static void finalize();
};
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index 139dc3afb1..63f7c80bdd 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -32,6 +32,8 @@
#include "core/variant/typed_array.h"
+#define GET_POINT_UNCHECKED(m_id) points[m_id.y - region.position.y][m_id.x - region.position.x]
+
static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
real_t dx = (real_t)ABS(p_to.x - p_from.x);
real_t dy = (real_t)ABS(p_to.y - p_from.y);
@@ -59,16 +61,29 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to)
static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev };
+void AStarGrid2D::set_region(const Rect2i &p_region) {
+ ERR_FAIL_COND(p_region.size.x < 0 || p_region.size.y < 0);
+ if (p_region != region) {
+ region = p_region;
+ dirty = true;
+ }
+}
+
+Rect2i AStarGrid2D::get_region() const {
+ return region;
+}
+
void AStarGrid2D::set_size(const Size2i &p_size) {
+ WARN_DEPRECATED_MSG(R"(The "size" property is deprecated, use "region" instead.)");
ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0);
- if (p_size != size) {
- size = p_size;
+ if (p_size != region.size) {
+ region.size = p_size;
dirty = true;
}
}
Size2i AStarGrid2D::get_size() const {
- return size;
+ return region.size;
}
void AStarGrid2D::set_offset(const Vector2 &p_offset) {
@@ -95,9 +110,11 @@ Size2 AStarGrid2D::get_cell_size() const {
void AStarGrid2D::update() {
points.clear();
- for (int64_t y = 0; y < size.y; y++) {
+ const int64_t end_x = region.position.x + region.size.width;
+ const int64_t end_y = region.position.y + region.size.height;
+ for (int64_t y = region.position.y; y < end_y; y++) {
LocalVector<Point> line;
- for (int64_t x = 0; x < size.x; x++) {
+ for (int64_t x = region.position.x; x < end_x; x++) {
line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size));
}
points.push_back(line);
@@ -106,11 +123,11 @@ void AStarGrid2D::update() {
}
bool AStarGrid2D::is_in_bounds(int p_x, int p_y) const {
- return p_x >= 0 && p_x < size.width && p_y >= 0 && p_y < size.height;
+ return region.has_point(Vector2i(p_x, p_y));
}
bool AStarGrid2D::is_in_boundsv(const Vector2i &p_id) const {
- return p_id.x >= 0 && p_id.x < size.width && p_id.y >= 0 && p_id.y < size.height;
+ return region.has_point(p_id);
}
bool AStarGrid2D::is_dirty() const {
@@ -154,27 +171,27 @@ AStarGrid2D::Heuristic AStarGrid2D::get_default_estimate_heuristic() const {
void AStarGrid2D::set_point_solid(const Vector2i &p_id, bool p_solid) {
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
- points[p_id.y][p_id.x].solid = p_solid;
+ ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point %s out of bounds %s.", p_id, region));
+ GET_POINT_UNCHECKED(p_id).solid = p_solid;
}
bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, false, "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
- return points[p_id.y][p_id.x].solid;
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point %s out of bounds %s.", p_id, region));
+ return GET_POINT_UNCHECKED(p_id).solid;
}
void AStarGrid2D::set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale) {
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
+ ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point %s out of bounds %s.", p_id, region));
ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
- points[p_id.y][p_id.x].weight_scale = p_weight_scale;
+ GET_POINT_UNCHECKED(p_id).weight_scale = p_weight_scale;
}
real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, 0, "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
- return points[p_id.y][p_id.x].weight_scale;
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point %s out of bounds %s.", p_id, region));
+ return GET_POINT_UNCHECKED(p_id).weight_scale;
}
AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) {
@@ -285,15 +302,15 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
bool has_left = false;
bool has_right = false;
- if (p_point->id.x - 1 >= 0) {
+ if (p_point->id.x - 1 >= region.position.x) {
left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y);
has_left = true;
}
- if (p_point->id.x + 1 < size.width) {
+ if (p_point->id.x + 1 < region.position.x + region.size.width) {
right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y);
has_right = true;
}
- if (p_point->id.y - 1 >= 0) {
+ if (p_point->id.y - 1 >= region.position.y) {
top = _get_point_unchecked(p_point->id.x, p_point->id.y - 1);
if (has_left) {
top_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y - 1);
@@ -302,7 +319,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
top_right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y - 1);
}
}
- if (p_point->id.y + 1 < size.height) {
+ if (p_point->id.y + 1 < region.position.y + region.size.height) {
bottom = _get_point_unchecked(p_point->id.x, p_point->id.y + 1);
if (has_left) {
bottom_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y + 1);
@@ -461,19 +478,19 @@ real_t AStarGrid2D::_compute_cost(const Vector2i &p_from_id, const Vector2i &p_t
void AStarGrid2D::clear() {
points.clear();
- size = Vector2i();
+ region = Rect2i();
}
Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
ERR_FAIL_COND_V_MSG(dirty, Vector2(), "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
- return points[p_id.y][p_id.x].pos;
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point %s out of bounds %s.", p_id, region));
+ return GET_POINT_UNCHECKED(p_id).pos;
}
Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
Point *a = _get_point(p_from_id.x, p_from_id.y);
Point *b = _get_point(p_to_id.x, p_to_id.y);
@@ -520,8 +537,8 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
Point *a = _get_point(p_from_id.x, p_from_id.y);
Point *b = _get_point(p_to_id.x, p_to_id.y);
@@ -565,6 +582,8 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
}
void AStarGrid2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_region", "region"), &AStarGrid2D::set_region);
+ ClassDB::bind_method(D_METHOD("get_region"), &AStarGrid2D::get_region);
ClassDB::bind_method(D_METHOD("set_size", "size"), &AStarGrid2D::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &AStarGrid2D::get_size);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &AStarGrid2D::set_offset);
@@ -596,6 +615,7 @@ void AStarGrid2D::_bind_methods() {
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2I, "region"), "set_region", "get_region");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size");
@@ -617,3 +637,5 @@ void AStarGrid2D::_bind_methods() {
BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES);
BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX);
}
+
+#undef GET_POINT_UNCHECKED
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index e4e62ec360..50df58e0e9 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -58,7 +58,7 @@ public:
};
private:
- Size2i size;
+ Rect2i region;
Vector2 offset;
Size2 cell_size = Size2(1, 1);
bool dirty = false;
@@ -107,21 +107,21 @@ private:
private: // Internal routines.
_FORCE_INLINE_ bool _is_walkable(int64_t p_x, int64_t p_y) const {
- if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) {
- return !points[p_y][p_x].solid;
+ if (region.has_point(Vector2i(p_x, p_y))) {
+ return !points[p_y - region.position.y][p_x - region.position.x].solid;
}
return false;
}
_FORCE_INLINE_ Point *_get_point(int64_t p_x, int64_t p_y) {
- if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) {
- return &points[p_y][p_x];
+ if (region.has_point(Vector2i(p_x, p_y))) {
+ return &points[p_y - region.position.y][p_x - region.position.x];
}
return nullptr;
}
_FORCE_INLINE_ Point *_get_point_unchecked(int64_t p_x, int64_t p_y) {
- return &points[p_y][p_x];
+ return &points[p_y - region.position.y][p_x - region.position.x];
}
void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors);
@@ -138,6 +138,9 @@ protected:
GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i)
public:
+ void set_region(const Rect2i &p_region);
+ Rect2i get_region() const;
+
void set_size(const Size2i &p_size);
Size2i get_size() const;
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 95a4187062..bfd902c7e2 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -807,8 +807,8 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
z = (rows[1][0] - rows[0][1]) / s;
r_axis = Vector3(x, y, z);
- // CLAMP to avoid NaN if the value passed to acos is not in [0,1].
- r_angle = Math::acos(CLAMP((rows[0][0] + rows[1][1] + rows[2][2] - 1) / 2, (real_t)0.0, (real_t)1.0));
+ // acos does clamping.
+ r_angle = Math::acos((rows[0][0] + rows[1][1] + rows[2][2] - 1) / 2);
}
void Basis::set_quaternion(const Quaternion &p_quaternion) {
diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc
index 2e519ceb3d..1964f2fa83 100644
--- a/core/math/bvh_debug.inc
+++ b/core/math/bvh_debug.inc
@@ -30,11 +30,7 @@ String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
void _debug_recursive_print_tree_node(uint32_t p_node_id, int depth = 0) const {
const TNode &tnode = _nodes[p_node_id];
- String sz = "";
- for (int n = 0; n < depth; n++) {
- sz += "\t";
- }
- sz += itos(p_node_id);
+ String sz = String("\t").repeat(depth) + itos(p_node_id);
if (tnode.is_leaf()) {
sz += " L";
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index a03438a339..76b3062944 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -596,9 +596,9 @@ private:
}
};
- enum Orientation { NONE,
- CLOCKWISE,
- COUNTER_CLOCKWISE };
+ enum Orientation { ORIENTATION_NONE,
+ ORIENTATION_CLOCKWISE,
+ ORIENTATION_COUNTER_CLOCKWISE };
Vector3 scaling;
Vector3 center;
@@ -1140,13 +1140,13 @@ ConvexHullInternal::Orientation ConvexHullInternal::get_orientation(const Edge *
CHULL_ASSERT(!m.is_zero());
int64_t dot = n.dot(m);
CHULL_ASSERT(dot != 0);
- return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE;
+ return (dot > 0) ? ORIENTATION_COUNTER_CLOCKWISE : ORIENTATION_CLOCKWISE;
}
- return COUNTER_CLOCKWISE;
+ return ORIENTATION_COUNTER_CLOCKWISE;
} else if (p_prev->prev == p_next) {
- return CLOCKWISE;
+ return ORIENTATION_CLOCKWISE;
} else {
- return NONE;
+ return ORIENTATION_NONE;
}
}
@@ -1176,7 +1176,7 @@ ConvexHullInternal::Edge *ConvexHullInternal::find_max_angle(bool p_ccw, const V
} else if ((cmp = cot.compare(p_min_cot)) < 0) {
p_min_cot = cot;
min_edge = e;
- } else if ((cmp == 0) && (p_ccw == (get_orientation(min_edge, e, p_s, t) == COUNTER_CLOCKWISE))) {
+ } else if ((cmp == 0) && (p_ccw == (get_orientation(min_edge, e, p_s, t) == ORIENTATION_COUNTER_CLOCKWISE))) {
min_edge = e;
}
}
@@ -1375,7 +1375,7 @@ void ConvexHullInternal::merge(IntermediateHull &p_h0, IntermediateHull &p_h1) {
int64_t dot = (*e->target - *c0).dot(normal);
CHULL_ASSERT(dot <= 0);
if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) {
- if (!start0 || (get_orientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) {
+ if (!start0 || (get_orientation(start0, e, s, Point32(0, 0, -1)) == ORIENTATION_CLOCKWISE)) {
start0 = e;
}
}
@@ -1390,7 +1390,7 @@ void ConvexHullInternal::merge(IntermediateHull &p_h0, IntermediateHull &p_h1) {
int64_t dot = (*e->target - *c1).dot(normal);
CHULL_ASSERT(dot <= 0);
if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) {
- if (!start1 || (get_orientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) {
+ if (!start1 || (get_orientation(start1, e, s, Point32(0, 0, -1)) == ORIENTATION_COUNTER_CLOCKWISE)) {
start1 = e;
}
}
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 078320d620..f96d3a909f 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -74,11 +74,13 @@ public:
static _ALWAYS_INLINE_ double tanh(double p_x) { return ::tanh(p_x); }
static _ALWAYS_INLINE_ float tanh(float p_x) { return ::tanhf(p_x); }
- static _ALWAYS_INLINE_ double asin(double p_x) { return ::asin(p_x); }
- static _ALWAYS_INLINE_ float asin(float p_x) { return ::asinf(p_x); }
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double asin(double p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asin(p_x)); }
+ static _ALWAYS_INLINE_ float asin(float p_x) { return p_x < -1 ? (-Math_PI / 2) : (p_x > 1 ? (Math_PI / 2) : ::asinf(p_x)); }
- static _ALWAYS_INLINE_ double acos(double p_x) { return ::acos(p_x); }
- static _ALWAYS_INLINE_ float acos(float p_x) { return ::acosf(p_x); }
+ // Always does clamping so always safe to use.
+ static _ALWAYS_INLINE_ double acos(double p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acos(p_x)); }
+ static _ALWAYS_INLINE_ float acos(float p_x) { return p_x < -1 ? Math_PI : (p_x > 1 ? 0 : ::acosf(p_x)); }
static _ALWAYS_INLINE_ double atan(double p_x) { return ::atan(p_x); }
static _ALWAYS_INLINE_ float atan(float p_x) { return ::atanf(p_x); }
diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp
index 34e212a5b6..e4ad17c8ef 100644
--- a/core/math/quaternion.cpp
+++ b/core/math/quaternion.cpp
@@ -35,7 +35,8 @@
real_t Quaternion::angle_to(const Quaternion &p_to) const {
real_t d = dot(p_to);
- return Math::acos(CLAMP(d * d * 2 - 1, -1, 1));
+ // acos does clamping.
+ return Math::acos(d * d * 2 - 1);
}
Vector3 Quaternion::get_euler(EulerOrder p_order) const {
diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h
index 1bafc29c57..c53868e12d 100644
--- a/core/math/static_raycaster.h
+++ b/core/math/static_raycaster.h
@@ -59,15 +59,15 @@ public:
/*! Constructs a ray from origin, direction, and ray segment. Near
* has to be smaller than far. */
- _FORCE_INLINE_ Ray(const Vector3 &org,
- const Vector3 &dir,
- float tnear = 0.0f,
- float tfar = INFINITY) :
- org(org),
- tnear(tnear),
- dir(dir),
+ _FORCE_INLINE_ Ray(const Vector3 &p_org,
+ const Vector3 &p_dir,
+ float p_tnear = 0.0f,
+ float p_tfar = INFINITY) :
+ org(p_org),
+ tnear(p_tnear),
+ dir(p_dir),
time(0.0f),
- tfar(tfar),
+ tfar(p_tfar),
mask(-1),
u(0.0),
v(0.0),
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index b9cababc90..cc4a29164d 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -56,6 +56,7 @@ ClassDB::APIType ClassDB::current_api = API_CORE;
HashMap<ClassDB::APIType, uint64_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.
current_api = p_api;
}
@@ -296,7 +297,12 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
}
hash = hash_fmix32(hash);
- api_hashes_cache[p_api] = hash;
+
+ // Extension API changes at runtime; let's just not cache them by now.
+ if (p_api != API_EXTENSION && p_api != API_EDITOR_EXTENSION) {
+ api_hashes_cache[p_api] = hash;
+ }
+
return hash;
#else
return 0;
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 83e0c4aea1..55ea5f5ecd 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,14 +35,23 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
+#define LOCK_MUTEX \
+ if (this != MessageQueue::thread_singleton) { \
+ mutex.lock(); \
+ }
+
+#define UNLOCK_MUTEX \
+ if (this != MessageQueue::thread_singleton) { \
+ mutex.unlock(); \
+ }
+
void CallQueue::_add_page() {
- if (pages_used == page_messages.size()) {
+ if (pages_used == page_bytes.size()) {
pages.push_back(allocator->alloc());
- page_messages.push_back(0);
+ page_bytes.push_back(0);
}
- page_messages[pages_used] = 0;
+ page_bytes[pages_used] = 0;
pages_used++;
- page_offset = 0;
}
Error CallQueue::push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
@@ -62,18 +71,19 @@ Error CallQueue::push_set(Object *p_object, const StringName &p_prop, const Vari
}
Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) {
- mutex.lock();
uint32_t room_needed = sizeof(Message) + sizeof(Variant) * p_argcount;
ERR_FAIL_COND_V_MSG(room_needed > uint32_t(PAGE_SIZE_BYTES), ERR_INVALID_PARAMETER, "Message is too large to fit on a page (" + itos(PAGE_SIZE_BYTES) + " bytes), consider passing less arguments.");
+ LOCK_MUTEX;
+
_ensure_first_page();
- if ((page_offset + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
- if (room_needed > uint32_t(PAGE_SIZE_BYTES) || pages_used == max_pages) {
+ if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
+ if (pages_used == max_pages) {
ERR_PRINT("Failed method: " + p_callable + ". Message queue out of memory. " + error_text);
statistics();
- mutex.unlock();
+ UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
}
_add_page();
@@ -81,7 +91,7 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar
Page *page = pages[pages_used - 1];
- uint8_t *buffer_end = &page->data[page_offset];
+ uint8_t *buffer_end = &page->data[page_bytes[pages_used - 1]];
Message *msg = memnew_placement(buffer_end, Message);
msg->args = p_argcount;
@@ -103,21 +113,20 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar
*v = *p_args[i];
}
- page_messages[pages_used - 1]++;
- page_offset += room_needed;
+ page_bytes[pages_used - 1] += room_needed;
- mutex.unlock();
+ UNLOCK_MUTEX;
return OK;
}
Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) {
- mutex.lock();
+ LOCK_MUTEX;
uint32_t room_needed = sizeof(Message) + sizeof(Variant);
_ensure_first_page();
- if ((page_offset + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
+ if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
String type;
if (ObjectDB::get_instance(p_id)) {
@@ -126,14 +135,14 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant
ERR_PRINT("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
statistics();
- mutex.unlock();
+ UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
}
_add_page();
}
Page *page = pages[pages_used - 1];
- uint8_t *buffer_end = &page->data[page_offset];
+ uint8_t *buffer_end = &page->data[page_bytes[pages_used - 1]];
Message *msg = memnew_placement(buffer_end, Message);
msg->args = 1;
@@ -145,32 +154,31 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant
Variant *v = memnew_placement(buffer_end, Variant);
*v = p_value;
- page_messages[pages_used - 1]++;
- page_offset += room_needed;
- mutex.unlock();
+ page_bytes[pages_used - 1] += room_needed;
+ UNLOCK_MUTEX;
return OK;
}
Error CallQueue::push_notification(ObjectID p_id, int p_notification) {
ERR_FAIL_COND_V(p_notification < 0, ERR_INVALID_PARAMETER);
- mutex.lock();
+ LOCK_MUTEX;
uint32_t room_needed = sizeof(Message);
_ensure_first_page();
- if ((page_offset + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
+ if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
ERR_PRINT("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
statistics();
- mutex.unlock();
+ UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
}
_add_page();
}
Page *page = pages[pages_used - 1];
- uint8_t *buffer_end = &page->data[page_offset];
+ uint8_t *buffer_end = &page->data[page_bytes[pages_used - 1]];
Message *msg = memnew_placement(buffer_end, Message);
@@ -179,9 +187,8 @@ Error CallQueue::push_notification(ObjectID p_id, int p_notification) {
//msg->target;
msg->notification = p_notification;
- page_messages[pages_used - 1]++;
- page_offset += room_needed;
- mutex.unlock();
+ page_bytes[pages_used - 1] += room_needed;
+ UNLOCK_MUTEX;
return OK;
}
@@ -204,26 +211,77 @@ void CallQueue::_call_function(const Callable &p_callable, const Variant *p_args
}
Error CallQueue::flush() {
- mutex.lock();
+ LOCK_MUTEX;
+
+ // Non-main threads are not meant to be flushed, but appended to the main one.
+ if (this != MessageQueue::main_singleton) {
+ 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] + dst_offset, pages[0], page_bytes[0]);
+ src_page++;
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ for (; src_page < pages_used; src_page++) {
+ mq->_add_page();
+ memcpy(mq->pages[mq->pages_used - 1], pages[src_page], page_bytes[src_page]);
+ mq->page_bytes[mq->pages_used - 1] = page_bytes[src_page];
+ }
+
+ mq->mutex.unlock();
+
+ page_bytes[0] = 0;
+ pages_used = 1;
+
+ return OK;
+ }
if (pages.size() == 0) {
// Never allocated
- mutex.unlock();
+ UNLOCK_MUTEX;
return OK; // Do nothing.
}
if (flushing) {
- mutex.unlock();
+ UNLOCK_MUTEX;
return ERR_BUSY;
}
flushing = true;
uint32_t i = 0;
- uint32_t j = 0;
uint32_t offset = 0;
- while (i < pages_used && j < page_messages[i]) {
+ while (i < pages_used && offset < page_bytes[i]) {
Page *page = pages[i];
//lock on each iteration, so a call can re-add itself to the message queue
@@ -240,7 +298,7 @@ Error CallQueue::flush() {
Object *target = message->callable.get_object();
- mutex.unlock();
+ UNLOCK_MUTEX;
switch (message->type & FLAG_MASK) {
case TYPE_CALL: {
@@ -271,35 +329,32 @@ Error CallQueue::flush() {
message->~Message();
- mutex.lock();
- j++;
- if (j == page_messages[i]) {
- j = 0;
+ LOCK_MUTEX;
+ if (offset == page_bytes[i]) {
i++;
offset = 0;
}
}
- page_messages[0] = 0;
- page_offset = 0;
+ page_bytes[0] = 0;
pages_used = 1;
flushing = false;
- mutex.unlock();
+ UNLOCK_MUTEX;
return OK;
}
void CallQueue::clear() {
- mutex.lock();
+ LOCK_MUTEX;
if (pages.size() == 0) {
- mutex.unlock();
+ UNLOCK_MUTEX;
return; // Nothing to clear.
}
for (uint32_t i = 0; i < pages_used; i++) {
uint32_t offset = 0;
- for (uint32_t j = 0; j < page_messages[i]; j++) {
+ while (offset < page_bytes[i]) {
Page *page = pages[i];
//lock on each iteration, so a call can re-add itself to the message queue
@@ -311,7 +366,6 @@ void CallQueue::clear() {
advance += sizeof(Variant) * message->args;
}
- //pre-advance so this function is reentrant
offset += advance;
if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
@@ -326,14 +380,13 @@ void CallQueue::clear() {
}
pages_used = 1;
- page_offset = 0;
- page_messages[0] = 0;
+ page_bytes[0] = 0;
- mutex.unlock();
+ UNLOCK_MUTEX;
}
void CallQueue::statistics() {
- mutex.lock();
+ LOCK_MUTEX;
HashMap<StringName, int> set_count;
HashMap<int, int> notify_count;
HashMap<Callable, int> call_count;
@@ -341,7 +394,7 @@ void CallQueue::statistics() {
for (uint32_t i = 0; i < pages_used; i++) {
uint32_t offset = 0;
- for (uint32_t j = 0; j < page_messages[i]; j++) {
+ while (offset < page_bytes[i]) {
Page *page = pages[i];
//lock on each iteration, so a call can re-add itself to the message queue
@@ -396,7 +449,6 @@ void CallQueue::statistics() {
null_count++;
}
- //pre-advance so this function is reentrant
offset += advance;
if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
@@ -425,13 +477,24 @@ void CallQueue::statistics() {
print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value));
}
- mutex.unlock();
+ UNLOCK_MUTEX;
}
bool CallQueue::is_flushing() const {
return flushing;
}
+bool CallQueue::has_messages() const {
+ if (pages_used == 0) {
+ return false;
+ }
+ if (pages_used == 1 && page_bytes[0] == 0) {
+ return false;
+ }
+
+ return true;
+}
+
int CallQueue::get_max_buffer_usage() const {
return pages.size() * PAGE_SIZE_BYTES;
}
@@ -461,16 +524,21 @@ CallQueue::~CallQueue() {
//////////////////////
-MessageQueue *MessageQueue::singleton = nullptr;
+CallQueue *MessageQueue::main_singleton = nullptr;
+thread_local CallQueue *MessageQueue::thread_singleton = nullptr;
+
+void MessageQueue::set_thread_singleton_override(CallQueue *p_thread_singleton) {
+ thread_singleton = p_thread_singleton;
+}
MessageQueue::MessageQueue() :
CallQueue(nullptr,
int(GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_mb", PROPERTY_HINT_RANGE, "1,512,1,or_greater"), 32)) * 1024 * 1024 / PAGE_SIZE_BYTES,
"Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_mb' in project settings.") {
- ERR_FAIL_COND_MSG(singleton != nullptr, "A MessageQueue singleton already exists.");
- singleton = this;
+ ERR_FAIL_COND_MSG(main_singleton != nullptr, "A MessageQueue singleton already exists.");
+ main_singleton = this;
}
MessageQueue::~MessageQueue() {
- singleton = nullptr;
+ main_singleton = nullptr;
}
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
index 68969dfd39..c6fcccbd58 100644
--- a/core/object/message_queue.h
+++ b/core/object/message_queue.h
@@ -45,6 +45,14 @@ public:
PAGE_SIZE_BYTES = 4096
};
+ struct Page {
+ uint8_t data[PAGE_SIZE_BYTES];
+ };
+
+ // Needs to be public to be able to define it outside the class.
+ // Needs to lock because there can be multiple of these allocators in several threads.
+ typedef PagedAllocator<Page, true> Allocator;
+
private:
enum {
TYPE_CALL,
@@ -56,21 +64,15 @@ private:
FLAG_MASK = FLAG_NULL_IS_OK - 1,
};
- struct Page {
- uint8_t data[PAGE_SIZE_BYTES];
- };
-
Mutex mutex;
- typedef PagedAllocator<Page, true> Allocator;
Allocator *allocator = nullptr;
bool allocator_is_custom = false;
LocalVector<Page *> pages;
- LocalVector<uint32_t> page_messages;
+ LocalVector<uint32_t> page_bytes;
uint32_t max_pages = 0;
uint32_t pages_used = 0;
- uint32_t page_offset = 0;
bool flushing = false;
struct Message {
@@ -85,7 +87,7 @@ private:
_FORCE_INLINE_ void _ensure_first_page() {
if (unlikely(pages.is_empty())) {
pages.push_back(allocator->alloc());
- page_messages.push_back(0);
+ page_bytes.push_back(0);
pages_used = 1;
}
}
@@ -140,6 +142,8 @@ public:
void clear();
void statistics();
+ bool has_messages() const;
+
bool is_flushing() const;
int get_max_buffer_usage() const;
@@ -148,10 +152,15 @@ public:
};
class MessageQueue : public CallQueue {
- static MessageQueue *singleton;
+ static CallQueue *main_singleton;
+ static thread_local CallQueue *thread_singleton;
+ friend class CallQueue;
public:
- _FORCE_INLINE_ static MessageQueue *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static CallQueue *get_singleton() { return thread_singleton ? thread_singleton : main_singleton; }
+
+ static void set_thread_singleton_override(CallQueue *p_thread_singleton);
+
MessageQueue();
~MessageQueue();
};
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index d37479f45b..84f0941b94 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -112,6 +112,8 @@ public:
_FORCE_INLINE_ int get_argument_count() const { return argument_count; };
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const = 0;
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const = 0;
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const = 0;
StringName get_name() const;
@@ -162,8 +164,12 @@ public:
}
#endif
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+ ERR_FAIL_MSG("Validated call can't be used with vararg methods. This is a bug.");
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
- ERR_FAIL(); // Can't call.
+ ERR_FAIL_MSG("ptrcall can't be used with vararg methods. This is a bug.");
}
virtual bool is_const() const { return false; }
@@ -253,6 +259,7 @@ public:
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error);
}
+
#if defined(SANITIZERS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
@@ -326,6 +333,14 @@ public:
return Variant();
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TYPED_METHOD_BIND
+ call_with_validated_object_instance_args(static_cast<T *>(p_object), method, p_args);
+#else
+ call_with_validated_object_instance_args(reinterpret_cast<MB_T *>(p_object), method, p_args);
+#endif
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args);
@@ -393,6 +408,14 @@ public:
return Variant();
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TYPED_METHOD_BIND
+ call_with_validated_object_instance_argsc(static_cast<T *>(p_object), method, p_args);
+#else
+ call_with_validated_object_instance_argsc(reinterpret_cast<MB_T *>(p_object), method, p_args);
+#endif
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args);
@@ -471,6 +494,14 @@ public:
return ret;
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TYPED_METHOD_BIND
+ call_with_validated_object_instance_args_ret(static_cast<T *>(p_object), method, p_args, r_ret);
+#else
+ call_with_validated_object_instance_args_ret(reinterpret_cast<MB_T *>(p_object), method, p_args, r_ret);
+#endif
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
@@ -550,6 +581,14 @@ public:
return ret;
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TYPED_METHOD_BIND
+ call_with_validated_object_instance_args_retc(static_cast<T *>(p_object), method, p_args, r_ret);
+#else
+ call_with_validated_object_instance_args_retc(reinterpret_cast<MB_T *>(p_object), method, p_args, r_ret);
+#endif
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
@@ -614,6 +653,10 @@ public:
return Variant();
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+ call_with_validated_variant_args_static_method(function, p_args);
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
(void)p_object;
(void)r_ret;
@@ -677,6 +720,10 @@ public:
return ret;
}
+ virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+ call_with_validated_variant_args_static_method_ret(function, p_args, r_ret);
+ }
+
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
(void)p_object;
call_with_ptr_args_static_method_ret(function, p_args, r_ret);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 2e1ea9ef5f..5a34328ec4 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -38,6 +38,7 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
+#include "core/templates/local_vector.h"
#include "core/variant/typed_array.h"
#ifdef DEBUG_ENABLED
@@ -200,6 +201,10 @@ bool Object::_predelete() {
return _predelete_ok;
}
+void Object::cancel_free() {
+ _predelete_ok = false;
+}
+
void Object::_postinitialize() {
_class_name_ptr = _get_class_namev(); // Set the direct pointer, which is much faster to obtain, but can only happen after postinitialize.
_initialize_classv();
@@ -883,8 +888,13 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
if (metadata.has(p_name)) {
metadata.erase(p_name);
- metadata_properties.erase("metadata/" + p_name.operator String());
- notify_property_list_changed();
+
+ const String &sname = p_name;
+ metadata_properties.erase("metadata/" + sname);
+ if (!sname.begins_with("_")) {
+ // Metadata starting with _ don't show up in the inspector, so no need to update.
+ notify_property_list_changed();
+ }
}
return;
}
@@ -895,8 +905,12 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) {
} else {
ERR_FAIL_COND(!p_name.operator String().is_valid_identifier());
Variant *V = &metadata.insert(p_name, p_value)->value;
- metadata_properties["metadata/" + p_name.operator String()] = V;
- notify_property_list_changed();
+
+ const String &sname = p_name;
+ metadata_properties["metadata/" + sname] = V;
+ if (!sname.begins_with("_")) {
+ notify_property_list_changed();
+ }
}
}
@@ -935,8 +949,8 @@ TypedArray<Dictionary> Object::_get_method_list_bind() const {
return ret;
}
-Vector<StringName> Object::_get_meta_list_bind() const {
- Vector<StringName> _metaret;
+TypedArray<StringName> Object::_get_meta_list_bind() const {
+ TypedArray<StringName> _metaret;
for (const KeyValue<StringName, Variant> &K : metadata) {
_metaret.push_back(K.key);
@@ -1019,20 +1033,23 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
List<_ObjectSignalDisconnectData> disconnect_data;
- //copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling.
- //this happens automatically and will not change the performance of calling.
- //awesome, isn't it?
- VMap<Callable, SignalData::Slot> slot_map = s->slot_map;
-
- int ssize = slot_map.size();
+ // Ensure that disconnecting the signal or even deleting the object
+ // will not affect the signal calling.
+ LocalVector<Connection> slot_conns;
+ slot_conns.resize(s->slot_map.size());
+ {
+ uint32_t idx = 0;
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ slot_conns[idx++] = slot_kv.value.conn;
+ }
+ DEV_ASSERT(idx == s->slot_map.size());
+ }
OBJ_DEBUG_LOCK
Error err = OK;
- for (int i = 0; i < ssize; i++) {
- const Connection &c = slot_map.getv(i).conn;
-
+ for (const Connection &c : slot_conns) {
Object *target = c.callable.get_object();
if (!target) {
// Target might have been deleted during signal callback, this is expected and OK.
@@ -1195,8 +1212,8 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
for (const KeyValue<StringName, SignalData> &E : signal_map) {
const SignalData *s = &E.value;
- for (int i = 0; i < s->slot_map.size(); i++) {
- p_connections->push_back(s->slot_map.getv(i).conn);
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ p_connections->push_back(slot_kv.value.conn);
}
}
}
@@ -1207,8 +1224,8 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect
return; //nothing
}
- for (int i = 0; i < s->slot_map.size(); i++) {
- p_connections->push_back(s->slot_map.getv(i).conn);
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ p_connections->push_back(slot_kv.value.conn);
}
}
@@ -1218,8 +1235,8 @@ int Object::get_persistent_signal_connection_count() const {
for (const KeyValue<StringName, SignalData> &E : signal_map) {
const SignalData *s = &E.value;
- for (int i = 0; i < s->slot_map.size(); i++) {
- if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ if (slot_kv.value.conn.flags & CONNECT_PERSIST) {
count += 1;
}
}
@@ -1319,28 +1336,28 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
_disconnect(p_signal, p_callable);
}
-void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
- ERR_FAIL_COND_MSG(p_callable.is_null(), "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
+bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
+ ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
Object *target_object = p_callable.get_object();
- ERR_FAIL_COND_MSG(!target_object, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
+ ERR_FAIL_COND_V_MSG(!target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) ||
(!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal));
- ERR_FAIL_COND_MSG(signal_is_valid, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");
+ ERR_FAIL_COND_V_MSG(signal_is_valid, false, "Attempt to disconnect a nonexistent connection from '" + to_string() + "'. Signal: '" + p_signal + "', callable: '" + p_callable + "'.");
}
- ERR_FAIL_COND_MSG(!s, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));
+ ERR_FAIL_COND_V_MSG(!s, false, vformat("Disconnecting nonexistent signal '%s' in %s.", p_signal, to_string()));
- ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
+ ERR_FAIL_COND_V_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), false, "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
SignalData::Slot *slot = &s->slot_map[*p_callable.get_base_comparator()];
if (!p_force) {
slot->reference_count--; // by default is zero, if it was not referenced it will go below it
if (slot->reference_count > 0) {
- return;
+ return false;
}
}
@@ -1351,6 +1368,8 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
//not user signal, delete
signal_map.erase(p_signal);
}
+
+ return true;
}
void Object::_set_bind(const StringName &p_set, const Variant &p_value) {
@@ -1555,6 +1574,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(""));
ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
+ ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free);
ClassDB::add_virtual_method("Object", MethodInfo("free"), false);
@@ -1798,26 +1818,30 @@ Object::~Object() {
ERR_PRINT("Object " + to_string() + " was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.");
}
+ // Drop all connections to the signals of this object.
while (signal_map.size()) {
// Avoid regular iteration so erasing is safe.
KeyValue<StringName, SignalData> &E = *signal_map.begin();
SignalData *s = &E.value;
- //brute force disconnect for performance
- int slot_count = s->slot_map.size();
- const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array();
-
- for (int i = 0; i < slot_count; i++) {
- slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE);
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ Object *target = slot_kv.value.conn.callable.get_object();
+ if (likely(target)) {
+ target->connections.erase(slot_kv.value.cE);
+ }
}
signal_map.erase(E.key);
}
- //signals from nodes that connect to this node
+ // Disconnect signals that connect to this object.
while (connections.size()) {
Connection c = connections.front()->get();
- c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
+ bool disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
+ if (unlikely(!disconnected)) {
+ // If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
+ connections.pop_front();
+ }
}
if (_instance_id != ObjectID()) {
diff --git a/core/object/object.h b/core/object/object.h
index 4226b5e67b..ae22851c15 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -41,7 +41,6 @@
#include "core/templates/list.h"
#include "core/templates/rb_map.h"
#include "core/templates/safe_refcount.h"
-#include "core/templates/vmap.h"
#include "core/variant/callable_bind.h"
#include "core/variant/variant.h"
@@ -86,6 +85,7 @@ enum PropertyHint {
PROPERTY_HINT_NODE_TYPE, ///< a node object type
PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor.
PROPERTY_HINT_PASSWORD,
+ PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_MAX,
};
@@ -119,6 +119,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected.
PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector.
+ PROPERTY_USAGE_SECRET = 1 << 29, // Export preset credentials that should be stored separately from the rest of the export config.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
@@ -587,7 +588,7 @@ private:
};
MethodInfo user;
- VMap<Callable, Slot> slot_map;
+ HashMap<Callable, Slot, HashableHasher<Callable>> slot_map;
};
HashMap<StringName, SignalData> signal_map;
@@ -717,7 +718,7 @@ protected:
return &_class_name_static;
}
- Vector<StringName> _get_meta_list_bind() const;
+ TypedArray<StringName> _get_meta_list_bind() const;
TypedArray<Dictionary> _get_property_list_bind() const;
TypedArray<Dictionary> _get_method_list_bind() const;
@@ -725,7 +726,7 @@ protected:
friend class ClassDB;
- void _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false);
+ bool _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false);
public: // Should be protected, but bug in clang++.
static void initialize_class();
@@ -924,6 +925,8 @@ public:
_ALWAYS_INLINE_ bool is_ref_counted() const { return type_is_reference; }
+ void cancel_free();
+
Object();
virtual ~Object();
};
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index ed48f4065c..cf0bd47bce 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -295,4 +295,16 @@ struct GetTypeInfo<const Ref<T> &> {
}
};
+template <class T>
+struct VariantInternalAccessor<Ref<T>> {
+ static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); }
+};
+
+template <class T>
+struct VariantInternalAccessor<const Ref<T> &> {
+ static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); }
+};
+
#endif // REF_COUNTED_H
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 721c8d0a10..e59ab3d6ae 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -140,9 +140,9 @@ void WorkerThreadPool::_process_task(Task *p_task) {
task_queue.add_last(&low_prio_task->task_elem);
post = true;
} else {
- low_priority_threads_used.decrement();
+ low_priority_threads_used--;
}
- task_mutex.lock();
+ task_mutex.unlock();
if (post) {
task_available_semaphore.post();
}
@@ -152,7 +152,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
void WorkerThreadPool::_thread_function(void *p_user) {
while (true) {
singleton->task_available_semaphore.wait();
- if (singleton->exit_threads.is_set()) {
+ if (singleton->exit_threads) {
break;
}
singleton->_process_task_queue();
@@ -165,17 +165,29 @@ void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) {
}
void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) {
+ // Fall back to processing on the calling thread if there are no worker threads.
+ // Separated into its own variable to make it easier to extend this logic
+ // in custom builds.
+ bool process_on_calling_thread = threads.size() == 0;
+ if (process_on_calling_thread) {
+ _process_task(p_task);
+ return;
+ }
+
task_mutex.lock();
p_task->low_priority = !p_high_priority;
if (!p_high_priority && use_native_low_priority_threads) {
- task_mutex.unlock();
p_task->low_priority_thread = native_thread_allocator.alloc();
- p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread.
+ task_mutex.unlock();
- } else if (p_high_priority || low_priority_threads_used.get() < max_low_priority_threads) {
+ if (p_task->group) {
+ p_task->group->low_priority_native_tasks.push_back(p_task);
+ }
+ p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread.
+ } else if (p_high_priority || low_priority_threads_used < max_low_priority_threads) {
task_queue.add_last(&p_task->task_elem);
if (!p_high_priority) {
- low_priority_threads_used.increment();
+ low_priority_threads_used++;
}
task_mutex.unlock();
task_available_semaphore.post();
@@ -251,30 +263,38 @@ void WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
if (use_native_low_priority_threads && task->low_priority) {
task->low_priority_thread->wait_to_finish();
+
+ task_mutex.lock();
native_thread_allocator.free(task->low_priority_thread);
} else {
int *index = thread_ids.getptr(Thread::get_caller_id());
if (index) {
// We are an actual process thread, we must not be blocked so continue processing stuff if available.
+ bool must_exit = false;
while (true) {
if (task->done_semaphore.try_wait()) {
// If done, exit
break;
}
- if (task_available_semaphore.try_wait()) {
- // Solve tasks while they are around.
- _process_task_queue();
- continue;
+ if (!must_exit && task_available_semaphore.try_wait()) {
+ if (exit_threads) {
+ must_exit = true;
+ } else {
+ // Solve tasks while they are around.
+ _process_task_queue();
+ continue;
+ }
}
OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance.
}
} else {
task->done_semaphore.wait();
}
+
+ task_mutex.lock();
}
- task_mutex.lock();
tasks.erase(p_task_id);
task_allocator.free(task);
task_mutex.unlock();
@@ -322,15 +342,8 @@ WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_ca
groups[id] = group;
task_mutex.unlock();
- if (!p_high_priority && use_native_low_priority_threads) {
- group->low_priority_native_tasks.resize(p_tasks);
- }
-
for (int i = 0; i < p_tasks; i++) {
_post_task(tasks_posted[i], p_high_priority);
- if (!p_high_priority && use_native_low_priority_threads) {
- group->low_priority_native_tasks[i] = tasks_posted[i];
- }
}
return id;
@@ -379,8 +392,8 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
if (group->low_priority_native_tasks.size() > 0) {
for (Task *task : group->low_priority_native_tasks) {
task->low_priority_thread->wait_to_finish();
- native_thread_allocator.free(task->low_priority_thread);
task_mutex.lock();
+ native_thread_allocator.free(task->low_priority_thread);
task_allocator.free(task);
task_mutex.unlock();
}
@@ -443,7 +456,7 @@ void WorkerThreadPool::finish() {
}
task_mutex.unlock();
- exit_threads.set_to(true);
+ exit_threads = true;
for (uint32_t i = 0; i < threads.size(); i++) {
task_available_semaphore.post();
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index c62e05fc28..d47c6ad714 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -107,7 +107,7 @@ private:
};
TightLocalVector<ThreadData> threads;
- SafeFlag exit_threads;
+ bool exit_threads = false;
HashMap<Thread::ID, int> thread_ids;
HashMap<TaskID, Task *> tasks;
@@ -115,7 +115,7 @@ private:
bool use_native_low_priority_threads = false;
uint32_t max_low_priority_threads = 0;
- SafeNumeric<uint32_t> low_priority_threads_used;
+ uint32_t low_priority_threads_used = 0;
uint64_t last_task = 1;
diff --git a/core/os/mutex.h b/core/os/mutex.h
index 90cc1632e8..cee0f8af74 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -119,8 +119,25 @@ class MutexLock {
public:
_ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) :
+ lock(p_mutex.mutex){};
+};
+
+// This specialization is needed so manual locking and MutexLock can be used
+// at the same time on a SafeBinaryMutex.
+template <int Tag>
+class MutexLock<SafeBinaryMutex<Tag>> {
+ friend class ConditionVariable;
+
+ std::unique_lock<std::mutex> lock;
+
+public:
+ _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
lock(p_mutex.mutex) {
- }
+ SafeBinaryMutex<Tag>::count++;
+ };
+ _ALWAYS_INLINE_ ~MutexLock() {
+ SafeBinaryMutex<Tag>::count--;
+ };
};
using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use
diff --git a/core/os/os.cpp b/core/os/os.cpp
index c82f87c731..4123a1d602 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -72,6 +72,10 @@ void OS::add_logger(Logger *p_logger) {
}
}
+String OS::get_identifier() const {
+ return get_name().to_lower();
+}
+
void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type) {
if (!_stderr_enabled) {
return;
@@ -357,13 +361,7 @@ void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
bool OS::has_feature(const String &p_feature) {
// Feature tags are always lowercase for consistency.
- if (p_feature == get_name().to_lower()) {
- return true;
- }
-
- // Catch-all `linuxbsd` feature tag that matches on both Linux and BSD.
- // This is the one exposed in the project settings dialog.
- if (p_feature == "linuxbsd" && (get_name() == "Linux" || get_name() == "FreeBSD" || get_name() == "NetBSD" || get_name() == "OpenBSD" || get_name() == "BSD")) {
+ if (p_feature == get_identifier()) {
return true;
}
@@ -569,6 +567,10 @@ void OS::add_frame_delay(bool p_can_draw) {
}
}
+Error OS::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
+ return default_rfs.synchronize_with_server(p_server_host, p_port, p_password, r_project_path);
+}
+
OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression.
diff --git a/core/os/os.h b/core/os/os.h
index 3248330c74..1652c1ed90 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -34,6 +34,7 @@
#include "core/config/engine.h"
#include "core/io/image.h"
#include "core/io/logger.h"
+#include "core/io/remote_filesystem_client.h"
#include "core/os/time_enums.h"
#include "core/string/ustring.h"
#include "core/templates/list.h"
@@ -72,6 +73,8 @@ class OS {
String _current_rendering_driver_name;
String _current_rendering_method;
+ RemoteFilesystemClient default_rfs;
+
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -134,6 +137,7 @@ public:
virtual String get_stdin_string() = 0;
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
+ virtual String get_system_ca_certificates() { return ""; } // Concatenated certificates in PEM format.
virtual PackedStringArray get_connected_midi_inputs();
virtual void open_midi_inputs();
@@ -172,6 +176,7 @@ public:
virtual void unset_environment(const String &p_var) const = 0;
virtual String get_name() const = 0;
+ virtual String get_identifier() const;
virtual String get_distribution_name() const = 0;
virtual String get_version() const = 0;
virtual List<String> get_cmdline_args() const { return _cmdline; }
@@ -292,6 +297,8 @@ public:
virtual void process_and_drop_events() {}
+ virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path);
+
enum PreferredTextureFormat {
PREFERRED_TEXTURE_FORMAT_S3TC_BPTC,
PREFERRED_TEXTURE_FORMAT_ETC2_ASTC
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index a992a4587d..66dfb3ee02 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -33,6 +33,9 @@
#include "core/error/error_list.h"
#include "core/typedefs.h"
+#ifdef DEBUG_ENABLED
+#include "core/error/error_macros.h"
+#endif
#include <condition_variable>
#include <mutex>
@@ -42,6 +45,9 @@ private:
mutable std::mutex mutex;
mutable std::condition_variable condition;
mutable uint32_t count = 0; // Initialized as locked.
+#ifdef DEBUG_ENABLED
+ mutable uint32_t awaiters = 0;
+#endif
public:
_ALWAYS_INLINE_ void post() const {
@@ -52,10 +58,16 @@ public:
_ALWAYS_INLINE_ void wait() const {
std::unique_lock lock(mutex);
+#ifdef DEBUG_ENABLED
+ ++awaiters;
+#endif
while (!count) { // Handle spurious wake-ups.
condition.wait(lock);
}
- count--;
+ --count;
+#ifdef DEBUG_ENABLED
+ --awaiters;
+#endif
}
_ALWAYS_INLINE_ bool try_wait() const {
@@ -67,6 +79,47 @@ public:
return false;
}
}
+
+#ifdef DEBUG_ENABLED
+ ~Semaphore() {
+ // Destroying an std::condition_variable when not all threads waiting on it have been notified
+ // invokes undefined behavior (e.g., it may be nicely destroyed or it may be awaited forever.)
+ // That means other threads could still be running the body of std::condition_variable::wait()
+ // but already past the safety checkpoint. That's the case for instance if that function is already
+ // waiting to lock again.
+ //
+ // We will make the rule a bit more restrictive and simpler to understand at the same time: there
+ // should not be any threads at any stage of the waiting by the time the semaphore is destroyed.
+ //
+ // We do so because of the following reasons:
+ // - We have the guideline that threads must be awaited (i.e., completed), so the waiting thread
+ // must be completely done by the time the thread controlling it finally destroys the semaphore.
+ // Therefore, only a coding mistake could make the program run into such a attempt at premature
+ // destruction of the semaphore.
+ // - In scripting, given that Semaphores are wrapped by RefCounted classes, in general it can't
+ // happen that a thread is trying to destroy a Semaphore while another is still doing whatever with
+ // it, so the simplification is mostly transparent to script writers.
+ // - The redefined rule can be checked for failure to meet it, which is what this implementation does.
+ // This is useful to detect a few cases of potential misuse; namely:
+ // a) In scripting:
+ // * The coder is naughtily dealing with the reference count causing a semaphore to die prematurely.
+ // * The coder is letting the project reach its termination without having cleanly finished threads
+ // that await on semaphores (or at least, let the usual semaphore-controlled loop exit).
+ // b) In the native side, where Semaphore is not a ref-counted beast and certain coding mistakes can
+ // lead to its premature destruction as well.
+ //
+ // Let's let users know they are doing it wrong, but apply a, somewhat hacky, countermeasure against UB
+ // in debug builds.
+ std::lock_guard lock(mutex);
+ if (awaiters) {
+ WARN_PRINT(
+ "A Semaphore object is being destroyed while one or more threads are still waiting on it.\n"
+ "Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup.");
+ // And now, the hacky countermeasure (i.e., leak the condition variable).
+ new (&condition) std::condition_variable();
+ }
+ }
+#endif
};
#endif // SEMAPHORE_H
diff --git a/core/os/spin_lock.h b/core/os/spin_lock.h
index 409154dbb5..93ea782b60 100644
--- a/core/os/spin_lock.h
+++ b/core/os/spin_lock.h
@@ -36,15 +36,15 @@
#include <atomic>
class SpinLock {
- std::atomic_flag locked = ATOMIC_FLAG_INIT;
+ mutable std::atomic_flag locked = ATOMIC_FLAG_INIT;
public:
- _ALWAYS_INLINE_ void lock() {
+ _ALWAYS_INLINE_ void lock() const {
while (locked.test_and_set(std::memory_order_acquire)) {
// Continue.
}
}
- _ALWAYS_INLINE_ void unlock() {
+ _ALWAYS_INLINE_ void unlock() const {
locked.clear(std::memory_order_release);
}
};
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 502f82aaef..03e2c5409d 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -66,11 +66,12 @@ void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_cal
}
}
-void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
- ERR_FAIL_COND_MSG(id != UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it.");
+Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
+ ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it.");
id = id_counter.increment();
std::thread new_thread(&Thread::callback, id, p_settings, p_callback, p_user);
thread.swap(new_thread);
+ return id;
}
bool Thread::is_started() const {
@@ -100,7 +101,9 @@ Thread::Thread() {
Thread::~Thread() {
if (id != UNASSIGNED_ID) {
#ifdef DEBUG_ENABLED
- WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
+ WARN_PRINT(
+ "A Thread object is being destroyed without its completion having been realized.\n"
+ "Please call wait_to_finish() on it to ensure correct cleanup.");
#endif
thread.detach();
}
diff --git a/core/os/thread.h b/core/os/thread.h
index 19e1376ca8..3e307adfff 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -105,9 +105,11 @@ public:
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; }
+ _FORCE_INLINE_ static bool is_main_thread() { return caller_id == MAIN_ID; } // Gain a tiny bit of perf here because there is no need to validate caller_id here, because only main thread will be set as 1.
+
static Error set_name(const String &p_name);
- void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
+ ID start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
bool is_started() const;
///< waits until thread is finished, and deallocates it.
void wait_to_finish();
diff --git a/core/os/time.cpp b/core/os/time.cpp
index 12e6f08525..038e4adc03 100644
--- a/core/os/time.cpp
+++ b/core/os/time.cpp
@@ -382,10 +382,10 @@ String Time::get_time_string_from_system(bool p_utc) const {
Dictionary Time::get_time_zone_from_system() const {
OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
- Dictionary timezone;
- timezone["bias"] = info.bias;
- timezone["name"] = info.name;
- return timezone;
+ Dictionary ret_timezone;
+ ret_timezone["bias"] = info.bias;
+ ret_timezone["name"] = info.name;
+ return ret_timezone;
}
double Time::get_unix_time_from_system() const {
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index a374e7c009..b8b8119618 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -302,15 +302,9 @@ void register_core_settings() {
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), (16));
GLOBAL_DEF(PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt"), "");
- int worker_threads = GLOBAL_DEF("threading/worker_pool/max_threads", -1);
- bool low_priority_use_system_threads = GLOBAL_DEF("threading/worker_pool/use_system_threads_for_low_priority_tasks", true);
- float low_property_ratio = GLOBAL_DEF("threading/worker_pool/low_priority_thread_ratio", 0.3);
-
- if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
- worker_thread_pool->init();
- } else {
- worker_thread_pool->init(worker_threads, low_priority_use_system_threads, low_property_ratio);
- }
+ GLOBAL_DEF("threading/worker_pool/max_threads", -1);
+ GLOBAL_DEF("threading/worker_pool/use_system_threads_for_low_priority_tasks", true);
+ GLOBAL_DEF("threading/worker_pool/low_priority_thread_ratio", 0.3);
}
void register_core_singletons() {
diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp
index 7b894d83bf..7b90710308 100644
--- a/core/string/print_string.cpp
+++ b/core/string/print_string.cpp
@@ -193,10 +193,8 @@ void print_error(String p_string) {
_global_unlock();
}
-void print_verbose(String p_string) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_line(p_string);
- }
+bool is_print_verbose_enabled() {
+ return OS::get_singleton()->is_stdout_verbose();
}
String stringify_variants(Variant p_var) {
diff --git a/core/string/print_string.h b/core/string/print_string.h
index 6496384b3f..7656e9bfa1 100644
--- a/core/string/print_string.h
+++ b/core/string/print_string.h
@@ -59,7 +59,15 @@ void remove_print_handler(const PrintHandlerList *p_handler);
extern void __print_line(String p_string);
extern void __print_line_rich(String p_string);
extern void print_error(String p_string);
-extern void print_verbose(String p_string);
+extern bool is_print_verbose_enabled();
+
+// This version avoids processing the text to be printed until it actually has to be printed, saving some CPU usage.
+#define print_verbose(m_text) \
+ { \
+ if (is_print_verbose_enabled()) { \
+ print_line(m_text); \
+ } \
+ }
inline void print_line(Variant v) {
__print_line(stringify_variants(v));
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 160bad14ab..3ca2e5ccdf 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -941,18 +941,11 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const
}
String TranslationServer::add_padding(const String &p_message, int p_length) const {
- String res;
- String prefix = pseudolocalization_prefix;
- String suffix;
- for (int i = 0; i < p_length * expansion_ratio / 2; i++) {
- prefix += "_";
- suffix += "_";
- }
- suffix += pseudolocalization_suffix;
- res += prefix;
- res += p_message;
- res += suffix;
- return res;
+ String underscores = String("_").repeat(p_length * expansion_ratio / 2);
+ String prefix = pseudolocalization_prefix + underscores;
+ String suffix = underscores + pseudolocalization_suffix;
+
+ return prefix + p_message + suffix;
}
const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 49c72a9dcf..c276f20f99 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -812,15 +812,15 @@ signed char String::nocasecmp_to(const String &p_str) const {
const char32_t *this_str = get_data();
while (true) {
- if (*that_str == 0 && *this_str == 0) {
- return 0; //we're equal
- } else if (*this_str == 0) {
- return -1; //if this is empty, and the other one is not, then we're less.. I think?
- } else if (*that_str == 0) {
- return 1; //otherwise the other one is smaller..
- } else if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than
+ if (*that_str == 0 && *this_str == 0) { // If both strings are at the end, they are equal.
+ return 0;
+ } else if (*this_str == 0) { // If at the end of this, and not of other, we are less.
+ return -1;
+ } else if (*that_str == 0) { // If at end of other, and not of this, we are greater.
+ return 1;
+ } else if (_find_upper(*this_str) < _find_upper(*that_str)) { // If current character in this is less, we are less.
return -1;
- } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than
+ } else if (_find_upper(*this_str) > _find_upper(*that_str)) { // If current character in this is greater, we are greater.
return 1;
}
@@ -844,15 +844,15 @@ signed char String::casecmp_to(const String &p_str) const {
const char32_t *this_str = get_data();
while (true) {
- if (*that_str == 0 && *this_str == 0) {
- return 0; //we're equal
- } else if (*this_str == 0) {
- return -1; //if this is empty, and the other one is not, then we're less.. I think?
- } else if (*that_str == 0) {
- return 1; //otherwise the other one is smaller..
- } else if (*this_str < *that_str) { //more than
+ if (*that_str == 0 && *this_str == 0) { // If both strings are at the end, they are equal.
+ return 0;
+ } else if (*this_str == 0) { // If at the end of this, and not of other, we are less.
+ return -1;
+ } else if (*that_str == 0) { // If at end of other, and not of this, we are greater.
+ return 1;
+ } else if (*this_str < *that_str) { // If current character in this is less, we are less.
return -1;
- } else if (*this_str > *that_str) { //less than
+ } else if (*this_str > *that_str) { // If current character in this is greater, we are greater.
return 1;
}
@@ -861,7 +861,48 @@ signed char String::casecmp_to(const String &p_str) const {
}
}
-signed char String::naturalnocasecmp_to(const String &p_str) const {
+static _FORCE_INLINE_ signed char natural_cmp_common(const char32_t *&r_this_str, const char32_t *&r_that_str) {
+ // Keep ptrs to start of numerical sequences.
+ const char32_t *this_substr = r_this_str;
+ const char32_t *that_substr = r_that_str;
+
+ // Compare lengths of both numerical sequences, ignoring leading zeros.
+ while (is_digit(*r_this_str)) {
+ r_this_str++;
+ }
+ while (is_digit(*r_that_str)) {
+ r_that_str++;
+ }
+ while (*this_substr == '0') {
+ this_substr++;
+ }
+ while (*that_substr == '0') {
+ that_substr++;
+ }
+ int this_len = r_this_str - this_substr;
+ int that_len = r_that_str - that_substr;
+
+ if (this_len < that_len) {
+ return -1;
+ } else if (this_len > that_len) {
+ return 1;
+ }
+
+ // If lengths equal, compare lexicographically.
+ while (this_substr != r_this_str && that_substr != r_that_str) {
+ if (*this_substr < *that_substr) {
+ return -1;
+ } else if (*this_substr > *that_substr) {
+ return 1;
+ }
+ this_substr++;
+ that_substr++;
+ }
+
+ return 0;
+}
+
+signed char String::naturalcasecmp_to(const String &p_str) const {
const char32_t *this_str = get_data();
const char32_t *that_str = p_str.get_data();
@@ -889,48 +930,69 @@ signed char String::naturalnocasecmp_to(const String &p_str) const {
return -1;
}
- // Keep ptrs to start of numerical sequences
- const char32_t *this_substr = this_str;
- const char32_t *that_substr = that_str;
-
- // Compare lengths of both numerical sequences, ignoring leading zeros
- while (is_digit(*this_str)) {
- this_str++;
+ signed char ret = natural_cmp_common(this_str, that_str);
+ if (ret) {
+ return ret;
}
- while (is_digit(*that_str)) {
- that_str++;
- }
- while (*this_substr == '0') {
- this_substr++;
- }
- while (*that_substr == '0') {
- that_substr++;
+ } else if (is_digit(*that_str)) {
+ return 1;
+ } else {
+ if (*this_str < *that_str) { // If current character in this is less, we are less.
+ return -1;
+ } else if (*this_str > *that_str) { // If current character in this is greater, we are greater.
+ return 1;
}
- int this_len = this_str - this_substr;
- int that_len = that_str - that_substr;
- if (this_len < that_len) {
+ this_str++;
+ that_str++;
+ }
+ }
+ if (*that_str) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+signed char String::naturalnocasecmp_to(const String &p_str) const {
+ const char32_t *this_str = get_data();
+ const char32_t *that_str = p_str.get_data();
+
+ if (this_str && that_str) {
+ while (*this_str == '.' || *that_str == '.') {
+ if (*this_str++ != '.') {
+ return 1;
+ }
+ if (*that_str++ != '.') {
+ return -1;
+ }
+ if (!*that_str) {
+ return 1;
+ }
+ if (!*this_str) {
+ return -1;
+ }
+ }
+
+ while (*this_str) {
+ if (!*that_str) {
+ return 1;
+ } else if (is_digit(*this_str)) {
+ if (!is_digit(*that_str)) {
return -1;
- } else if (this_len > that_len) {
- return 1;
}
- // If lengths equal, compare lexicographically
- while (this_substr != this_str && that_substr != that_str) {
- if (*this_substr < *that_substr) {
- return -1;
- } else if (*this_substr > *that_substr) {
- return 1;
- }
- this_substr++;
- that_substr++;
+ signed char ret = natural_cmp_common(this_str, that_str);
+ if (ret) {
+ return ret;
}
} else if (is_digit(*that_str)) {
return 1;
} else {
- if (_find_upper(*this_str) < _find_upper(*that_str)) { //more than
+ if (_find_upper(*this_str) < _find_upper(*that_str)) { // If current character in this is less, we are less.
return -1;
- } else if (_find_upper(*this_str) > _find_upper(*that_str)) { //less than
+ } else if (_find_upper(*this_str) > _find_upper(*that_str)) { // If current character in this is greater, we are greater.
return 1;
}
@@ -2857,6 +2919,12 @@ String String::insert(int p_at_pos, const String &p_string) const {
return pre + p_string + post;
}
+String String::erase(int p_pos, int p_chars) const {
+ ERR_FAIL_COND_V_MSG(p_pos < 0, "", vformat("Invalid starting position for `String.erase()`: %d. Starting position must be positive or zero.", p_pos));
+ ERR_FAIL_COND_V_MSG(p_chars < 0, "", vformat("Invalid character count for `String.erase()`: %d. Character count must be positive or zero.", p_chars));
+ return left(p_pos) + substr(p_pos + p_chars);
+}
+
String String::substr(int p_from, int p_chars) const {
if (p_chars == -1) {
p_chars = length() - p_from;
@@ -3524,6 +3592,14 @@ String String::replacen(const String &p_key, const String &p_with) const {
String String::repeat(int p_count) const {
ERR_FAIL_COND_V_MSG(p_count < 0, "", "Parameter count should be a positive number.");
+ if (p_count == 0) {
+ return "";
+ }
+
+ if (p_count == 1) {
+ return *this;
+ }
+
int len = length();
String new_string = *this;
new_string.resize(p_count * len + 1);
@@ -4161,13 +4237,11 @@ String String::pad_decimals(int p_digits) const {
}
if (s.length() - (c + 1) > p_digits) {
- s = s.substr(0, c + p_digits + 1);
+ return s.substr(0, c + p_digits + 1);
} else {
- while (s.length() - (c + 1) < p_digits) {
- s += "0";
- }
+ int zeros_to_add = p_digits - s.length() + (c + 1);
+ return s + String("0").repeat(zeros_to_add);
}
- return s;
}
String String::pad_zeros(int p_digits) const {
@@ -4192,12 +4266,8 @@ String String::pad_zeros(int p_digits) const {
return s;
}
- while (end - begin < p_digits) {
- s = s.insert(begin, "0");
- end++;
- }
-
- return s;
+ int zeros_to_add = p_digits - (end - begin);
+ return s.insert(begin, String("0").repeat(zeros_to_add));
}
String String::trim_prefix(const String &p_prefix) const {
@@ -4376,11 +4446,8 @@ String String::path_to(const String &p_path) const {
common_parent--;
- String dir;
-
- for (int i = src_dirs.size() - 1; i > common_parent; i--) {
- dir += "../";
- }
+ int dirs_to_backtrack = (src_dirs.size() - 1) - common_parent;
+ String dir = String("../").repeat(dirs_to_backtrack);
for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
dir += dst_dirs[i] + "/";
@@ -4669,11 +4736,8 @@ String String::rpad(int min_length, const String &character) const {
String s = *this;
int padding = min_length - s.length();
if (padding > 0) {
- for (int i = 0; i < padding; i++) {
- s = s + character;
- }
+ s += character.repeat(padding);
}
-
return s;
}
@@ -4682,11 +4746,8 @@ String String::lpad(int min_length, const String &character) const {
String s = *this;
int padding = min_length - s.length();
if (padding > 0) {
- for (int i = 0; i < padding; i++) {
- s = character + s;
- }
+ s = character.repeat(padding) + s;
}
-
return s;
}
diff --git a/core/string/ustring.h b/core/string/ustring.h
index e1512cfb26..782ca47507 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -262,6 +262,7 @@ public:
signed char casecmp_to(const String &p_str) const;
signed char nocasecmp_to(const String &p_str) const;
+ signed char naturalcasecmp_to(const String &p_str) const;
signed char naturalnocasecmp_to(const String &p_str) const;
const char32_t *get_data() const;
@@ -304,6 +305,7 @@ public:
String replacen(const String &p_key, const String &p_with) const;
String repeat(int p_count) const;
String insert(int p_at_pos, const String &p_string) const;
+ String erase(int p_pos, int p_chars = 1) const;
String pad_decimals(int p_digits) const;
String pad_zeros(int p_digits) const;
String trim_prefix(const String &p_prefix) const;
diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h
index 0b846dfaba..4da73f1cfb 100644
--- a/core/templates/hash_map.h
+++ b/core/templates/hash_map.h
@@ -97,7 +97,7 @@ private:
}
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
- if (elements == nullptr) {
+ if (elements == nullptr || num_elements == 0) {
return false; // Failed lookups, no elements
}
@@ -252,7 +252,7 @@ public:
}
void clear() {
- if (elements == nullptr) {
+ if (elements == nullptr || num_elements == 0) {
return;
}
uint32_t capacity = hash_table_size_primes[capacity_index];
diff --git a/core/templates/hash_set.h b/core/templates/hash_set.h
index 97f1b460aa..00f4acbc9c 100644
--- a/core/templates/hash_set.h
+++ b/core/templates/hash_set.h
@@ -80,7 +80,7 @@ private:
}
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
- if (keys == nullptr) {
+ if (keys == nullptr || num_elements == 0) {
return false; // Failed lookups, no elements
}
@@ -237,7 +237,7 @@ public:
}
void clear() {
- if (keys == nullptr) {
+ if (keys == nullptr || num_elements == 0) {
return;
}
uint32_t capacity = hash_table_size_primes[capacity_index];
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 95e6bad2f2..2a212f3dcb 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -386,6 +386,12 @@ struct HashMapHasherDefault {
}
};
+// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed
+template <class T>
+struct HashableHasher {
+ static _FORCE_INLINE_ uint32_t hash(const T &hashable) { return hashable.hash(); }
+};
+
template <typename T>
struct HashMapComparatorDefault {
static bool compare(const T &p_lhs, const T &p_rhs) {
diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h
index 5311a94987..b454821a8f 100644
--- a/core/templates/local_vector.h
+++ b/core/templates/local_vector.h
@@ -59,11 +59,7 @@ public:
_FORCE_INLINE_ void push_back(T p_elem) {
if (unlikely(count == capacity)) {
- if (capacity == 0) {
- capacity = 1;
- } else {
- capacity <<= 1;
- }
+ capacity = tight ? (capacity + 1) : MAX((U)1, capacity << 1);
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
}
@@ -87,7 +83,7 @@ public:
}
/// Removes the item copying the last value into the position of the one to
- /// remove. It's generally faster than `remove`.
+ /// remove. It's generally faster than `remove_at`.
void remove_at_unordered(U p_index) {
ERR_FAIL_INDEX(p_index, count);
count--;
@@ -99,11 +95,13 @@ public:
}
}
- void erase(const T &p_val) {
+ _FORCE_INLINE_ bool erase(const T &p_val) {
int64_t idx = find(p_val);
if (idx >= 0) {
remove_at(idx);
+ return true;
}
+ return false;
}
void invert() {
@@ -143,12 +141,7 @@ public:
count = p_size;
} else if (p_size > count) {
if (unlikely(p_size > capacity)) {
- if (capacity == 0) {
- capacity = 1;
- }
- while (capacity < p_size) {
- capacity <<= 1;
- }
+ capacity = tight ? p_size : nearest_power_of_2_templated(p_size);
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
}
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 1cd71ec16c..deb2937771 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -99,7 +99,8 @@ public:
}
}
- void reset(bool p_allow_unfreed = false) {
+private:
+ void _reset(bool p_allow_unfreed) {
if (!p_allow_unfreed || !std::is_trivially_destructible<T>::value) {
ERR_FAIL_COND(allocs_available < pages_allocated * page_size);
}
@@ -116,16 +117,41 @@ public:
allocs_available = 0;
}
}
+
+public:
+ void reset(bool p_allow_unfreed = false) {
+ if (thread_safe) {
+ spin_lock.lock();
+ }
+ _reset(p_allow_unfreed);
+ if (thread_safe) {
+ spin_lock.unlock();
+ }
+ }
+
bool is_configured() const {
- return page_size > 0;
+ if (thread_safe) {
+ spin_lock.lock();
+ }
+ bool result = page_size > 0;
+ if (thread_safe) {
+ spin_lock.unlock();
+ }
+ return result;
}
void configure(uint32_t p_page_size) {
+ if (thread_safe) {
+ spin_lock.lock();
+ }
ERR_FAIL_COND(page_pool != nullptr); //sanity check
ERR_FAIL_COND(p_page_size == 0);
page_size = nearest_power_of_2_templated(p_page_size);
page_mask = page_size - 1;
page_shift = get_shift_from_power_of_2(page_size);
+ if (thread_safe) {
+ spin_lock.unlock();
+ }
}
// Power of 2 recommended because of alignment with OS page sizes.
@@ -135,13 +161,20 @@ public:
}
~PagedAllocator() {
- if (allocs_available < pages_allocated * page_size) {
+ if (thread_safe) {
+ spin_lock.lock();
+ }
+ bool leaked = allocs_available < pages_allocated * page_size;
+ if (leaked) {
if (CoreGlobals::leak_reporting_enabled) {
- ERR_FAIL_COND_MSG(allocs_available < pages_allocated * page_size, String("Pages in use exist at exit in PagedAllocator: ") + String(typeid(T).name()));
+ ERR_PRINT(String("Pages in use exist at exit in PagedAllocator: ") + String(typeid(T).name()));
}
- return;
+ } else {
+ _reset(false);
+ }
+ if (thread_safe) {
+ spin_lock.unlock();
}
- reset();
}
};
diff --git a/core/templates/vector.h b/core/templates/vector.h
index ae58eb8b16..d8bac0870f 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -71,12 +71,15 @@ public:
void fill(T p_elem);
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
- void erase(const T &p_val) {
+ _FORCE_INLINE_ bool erase(const T &p_val) {
int idx = find(p_val);
if (idx >= 0) {
remove_at(idx);
+ return true;
}
+ return false;
}
+
void reverse();
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 81ac5adba7..9f8fb7e95e 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -83,50 +83,60 @@ struct VariantCaster<const T &> {
}
};
-#define VARIANT_ENUM_CAST(m_enum) \
- MAKE_ENUM_TYPE_INFO(m_enum) \
- template <> \
- struct VariantCaster<m_enum> { \
- static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \
- return (m_enum)p_variant.operator int64_t(); \
- } \
- }; \
- template <> \
- struct PtrToArg<m_enum> { \
- _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \
- return m_enum(*reinterpret_cast<const int64_t *>(p_ptr)); \
- } \
- typedef int64_t EncodeT; \
- _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \
- *(int64_t *)p_ptr = (int64_t)p_val; \
- } \
- }; \
- template <> \
- struct ZeroInitializer<m_enum> { \
- static void initialize(m_enum &value) { value = (m_enum)0; } \
+#define VARIANT_ENUM_CAST(m_enum) \
+ MAKE_ENUM_TYPE_INFO(m_enum) \
+ template <> \
+ struct VariantCaster<m_enum> { \
+ static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \
+ return (m_enum)p_variant.operator int64_t(); \
+ } \
+ }; \
+ template <> \
+ struct PtrToArg<m_enum> { \
+ _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \
+ return m_enum(*reinterpret_cast<const int64_t *>(p_ptr)); \
+ } \
+ typedef int64_t EncodeT; \
+ _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \
+ *(int64_t *)p_ptr = (int64_t)p_val; \
+ } \
+ }; \
+ template <> \
+ struct ZeroInitializer<m_enum> { \
+ static void initialize(m_enum &value) { value = (m_enum)0; } \
+ }; \
+ template <> \
+ struct VariantInternalAccessor<m_enum> { \
+ static _FORCE_INLINE_ m_enum get(const Variant *v) { return m_enum(*VariantInternal::get_int(v)); } \
+ static _FORCE_INLINE_ void set(Variant *v, m_enum p_value) { *VariantInternal::get_int(v) = (int64_t)p_value; } \
};
-#define VARIANT_BITFIELD_CAST(m_enum) \
- MAKE_BITFIELD_TYPE_INFO(m_enum) \
- template <> \
- struct VariantCaster<BitField<m_enum>> { \
- static _FORCE_INLINE_ BitField<m_enum> cast(const Variant &p_variant) { \
- return BitField<m_enum>(p_variant.operator int64_t()); \
- } \
- }; \
- template <> \
- struct PtrToArg<BitField<m_enum>> { \
- _FORCE_INLINE_ static BitField<m_enum> convert(const void *p_ptr) { \
- return BitField<m_enum>(*reinterpret_cast<const int64_t *>(p_ptr)); \
- } \
- typedef int64_t EncodeT; \
- _FORCE_INLINE_ static void encode(BitField<m_enum> p_val, const void *p_ptr) { \
- *(int64_t *)p_ptr = p_val; \
- } \
- }; \
- template <> \
- struct ZeroInitializer<BitField<m_enum>> { \
- static void initialize(BitField<m_enum> &value) { value = 0; } \
+#define VARIANT_BITFIELD_CAST(m_enum) \
+ MAKE_BITFIELD_TYPE_INFO(m_enum) \
+ template <> \
+ struct VariantCaster<BitField<m_enum>> { \
+ static _FORCE_INLINE_ BitField<m_enum> cast(const Variant &p_variant) { \
+ return BitField<m_enum>(p_variant.operator int64_t()); \
+ } \
+ }; \
+ template <> \
+ struct PtrToArg<BitField<m_enum>> { \
+ _FORCE_INLINE_ static BitField<m_enum> convert(const void *p_ptr) { \
+ return BitField<m_enum>(*reinterpret_cast<const int64_t *>(p_ptr)); \
+ } \
+ typedef int64_t EncodeT; \
+ _FORCE_INLINE_ static void encode(BitField<m_enum> p_val, const void *p_ptr) { \
+ *(int64_t *)p_ptr = p_val; \
+ } \
+ }; \
+ template <> \
+ struct ZeroInitializer<BitField<m_enum>> { \
+ static void initialize(BitField<m_enum> &value) { value = 0; } \
+ }; \
+ template <> \
+ struct VariantInternalAccessor<BitField<m_enum>> { \
+ static _FORCE_INLINE_ BitField<m_enum> get(const Variant *v) { return BitField<m_enum>(*VariantInternal::get_int(v)); } \
+ static _FORCE_INLINE_ void set(Variant *v, BitField<m_enum> p_value) { *VariantInternal::get_int(v) = p_value.operator int64_t(); } \
};
// Object enum casts must go here
@@ -597,6 +607,8 @@ void call_with_ptr_args_static_method(void (*p_method)(P...), const void **p_arg
call_with_ptr_args_static_method_helper<P...>(p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
}
+// Validated
+
template <class T, class... P>
void call_with_validated_variant_args(Variant *base, void (T::*p_method)(P...), const Variant **p_args) {
call_with_validated_variant_args_helper<T, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
@@ -632,6 +644,38 @@ void call_with_validated_variant_args_static_method_ret(R (*p_method)(P...), con
call_with_validated_variant_args_static_method_ret_helper<R, P...>(p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
}
+// Validated Object
+
+template <class T, class... P>
+void call_with_validated_object_instance_args(T *base, void (T::*p_method)(P...), const Variant **p_args) {
+ call_with_validated_variant_args_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class... P>
+void call_with_validated_object_instance_argsc(T *base, void (T::*p_method)(P...) const, const Variant **p_args) {
+ call_with_validated_variant_argsc_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class R, class... P>
+void call_with_validated_object_instance_args_ret(T *base, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret) {
+ call_with_validated_variant_args_ret_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class R, class... P>
+void call_with_validated_object_instance_args_retc(T *base, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret) {
+ call_with_validated_variant_args_retc_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class... P>
+void call_with_validated_object_instance_args_static(T *base, void (*p_method)(T *, P...), const Variant **p_args) {
+ call_with_validated_variant_args_static_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class R, class... P>
+void call_with_validated_object_instance_args_static_retc(T *base, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret) {
+ call_with_validated_variant_args_static_retc_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
+}
+
// GCC raises "parameter 'p_args' set but not used" when P = {},
// it's not clever enough to treat other P values as making this branch valid.
#if defined(__GNUC__) && !defined(__clang__)
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 03e557819b..98afc7e717 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -33,6 +33,7 @@
#include "core/object/object.h"
#include "core/variant/array.h"
+#include "core/variant/binder_common.h"
#include "core/variant/method_ptrcall.h"
#include "core/variant/type_info.h"
#include "core/variant/variant.h"
@@ -55,6 +56,17 @@ public:
}
};
+template <class T>
+struct VariantInternalAccessor<TypedArray<T>> {
+ static _FORCE_INLINE_ TypedArray<T> get(const Variant *v) { return *VariantInternal::get_array(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const TypedArray<T> &p_array) { *VariantInternal::get_array(v) = p_array; }
+};
+template <class T>
+struct VariantInternalAccessor<const TypedArray<T> &> {
+ static _FORCE_INLINE_ TypedArray<T> get(const Variant *v) { return *VariantInternal::get_array(v); }
+ static _FORCE_INLINE_ void set(Variant *v, const TypedArray<T> &p_array) { *VariantInternal::get_array(v) = p_array; }
+};
+
//specialization for the rest of variant types
#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \
@@ -117,6 +129,7 @@ MAKE_TYPED_ARRAY(Vector<String>, Variant::PACKED_STRING_ARRAY)
MAKE_TYPED_ARRAY(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPED_ARRAY(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY(IPAddress, Variant::STRING)
template <class T>
struct PtrToArg<TypedArray<T>> {
@@ -215,5 +228,6 @@ MAKE_TYPED_ARRAY_INFO(Vector<String>, Variant::PACKED_STRING_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING)
#endif // TYPED_ARRAY_H
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index e83b6dc183..0a836c125a 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1633,6 +1633,7 @@ static void _register_variant_builtin_methods() {
bind_string_method(casecmp_to, sarray("to"), varray());
bind_string_method(nocasecmp_to, sarray("to"), varray());
+ bind_string_method(naturalcasecmp_to, sarray("to"), varray());
bind_string_method(naturalnocasecmp_to, sarray("to"), varray());
bind_string_method(length, sarray(), varray());
bind_string_method(substr, sarray("from", "len"), varray(-1));
@@ -1659,6 +1660,7 @@ static void _register_variant_builtin_methods() {
bind_string_method(replacen, sarray("what", "forwhat"), varray());
bind_string_method(repeat, sarray("count"), varray());
bind_string_method(insert, sarray("position", "what"), varray());
+ bind_string_method(erase, sarray("position", "chars"), varray(1));
bind_string_method(capitalize, sarray(), varray());
bind_string_method(to_camel_case, sarray(), varray());
bind_string_method(to_pascal_case, sarray(), varray());
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index 950f4a62d8..3427950224 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -317,6 +317,17 @@ String Variant::get_constructor_argument_name(Variant::Type p_type, int p_constr
return construct_data[p_type][p_constructor].arg_names[p_argument];
}
+void VariantInternal::refcounted_object_assign(Variant *v, const RefCounted *rc) {
+ if (!rc || !const_cast<RefCounted *>(rc)->init_ref()) {
+ v->_get_obj().obj = nullptr;
+ v->_get_obj().id = ObjectID();
+ return;
+ }
+
+ v->_get_obj().obj = const_cast<RefCounted *>(rc);
+ v->_get_obj().id = rc->get_instance_id();
+}
+
void VariantInternal::object_assign(Variant *v, const Object *o) {
if (o) {
if (o->is_ref_counted()) {
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 0d55ee4ae2..b7bd2a9c8c 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -35,6 +35,9 @@
// For use when you want to access the internal pointer of a Variant directly.
// Use with caution. You need to be sure that the type is correct.
+
+class RefCounted;
+
class VariantInternal {
friend class Variant;
@@ -320,6 +323,7 @@ public:
}
static void object_assign(Variant *v, const Object *o); // Needs RefCounted, so it's implemented elsewhere.
+ static void refcounted_object_assign(Variant *v, const RefCounted *rc);
_FORCE_INLINE_ static void object_assign(Variant *v, const Variant *o) {
object_assign(v, o->_get_obj().obj);
@@ -820,28 +824,28 @@ VARIANT_ACCESSOR_NUMBER(int64_t)
VARIANT_ACCESSOR_NUMBER(uint64_t)
VARIANT_ACCESSOR_NUMBER(char32_t)
-// Bind enums to allow using them as return types.
-VARIANT_ACCESSOR_NUMBER(Error)
-VARIANT_ACCESSOR_NUMBER(Side)
-VARIANT_ACCESSOR_NUMBER(Vector2::Axis)
-VARIANT_ACCESSOR_NUMBER(Vector2i::Axis)
-VARIANT_ACCESSOR_NUMBER(Vector3::Axis)
-VARIANT_ACCESSOR_NUMBER(Vector3i::Axis)
-VARIANT_ACCESSOR_NUMBER(Vector4::Axis)
-VARIANT_ACCESSOR_NUMBER(Vector4i::Axis)
+template <>
+struct VariantInternalAccessor<ObjectID> {
+ static _FORCE_INLINE_ ObjectID get(const Variant *v) { return ObjectID(*VariantInternal::get_int(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, ObjectID p_value) { *VariantInternal::get_int(v) = p_value; }
+};
-VARIANT_ACCESSOR_NUMBER(Projection::Planes)
+template <class T>
+struct VariantInternalAccessor<T *> {
+ static _FORCE_INLINE_ T *get(const Variant *v) { return const_cast<T *>(static_cast<const T *>(*VariantInternal::get_object(v))); }
+ static _FORCE_INLINE_ void set(Variant *v, const T *p_value) { VariantInternal::object_assign(v, p_value); }
+};
-template <>
-struct VariantInternalAccessor<EulerOrder> {
- static _FORCE_INLINE_ EulerOrder get(const Variant *v) { return EulerOrder(*VariantInternal::get_int(v)); }
- static _FORCE_INLINE_ void set(Variant *v, EulerOrder p_value) { *VariantInternal::get_int(v) = (int64_t)p_value; }
+template <class T>
+struct VariantInternalAccessor<const T *> {
+ static _FORCE_INLINE_ const T *get(const Variant *v) { return static_cast<const T *>(*VariantInternal::get_object(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, const T *p_value) { VariantInternal::object_assign(v, p_value); }
};
template <>
-struct VariantInternalAccessor<ObjectID> {
- static _FORCE_INLINE_ ObjectID get(const Variant *v) { return ObjectID(*VariantInternal::get_int(v)); }
- static _FORCE_INLINE_ void set(Variant *v, ObjectID p_value) { *VariantInternal::get_int(v) = p_value; }
+struct VariantInternalAccessor<IPAddress> {
+ static _FORCE_INLINE_ IPAddress get(const Variant *v) { return IPAddress(*VariantInternal::get_string(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, IPAddress p_value) { *VariantInternal::get_string(v) = p_value; }
};
template <>
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index fd079dbeea..545825011a 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -804,6 +804,8 @@ struct VariantUtilityFunctions {
r_error.error = Callable::CallError::CALL_OK;
}
+#undef print_verbose
+
static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
if (OS::get_singleton()->is_stdout_verbose()) {
String s;
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index a0b623936a..ad68d2f818 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -65,7 +65,7 @@
<return type="float" />
<param index="0" name="x" type="float" />
<description>
- Returns the arc cosine of [param x] in radians. Use to get the angle of cosine [param x]. [param x] must be between [code]-1.0[/code] and [code]1.0[/code] (inclusive), otherwise, [method acos] will return [constant @GDScript.NAN].
+ Returns the arc cosine of [param x] in radians. Use to get the angle of cosine [param x]. [param x] will be clamped between [code]-1.0[/code] and [code]1.0[/code] (inclusive), in order to prevent [method acos] from returning [constant @GDScript.NAN].
[codeblock]
# c is 0.523599 or 30 degrees if converted with rad_to_deg(c)
var c = acos(0.866025)
@@ -76,7 +76,7 @@
<return type="float" />
<param index="0" name="x" type="float" />
<description>
- Returns the arc sine of [param x] in radians. Use to get the angle of sine [param x]. [param x] must be between [code]-1.0[/code] and [code]1.0[/code] (inclusive), otherwise, [method asin] will return [constant @GDScript.NAN].
+ Returns the arc sine of [param x] in radians. Use to get the angle of sine [param x]. [param x] will be clamped between [code]-1.0[/code] and [code]1.0[/code] (inclusive), in order to prevent [method asin] from returning [constant @GDScript.NAN].
[codeblock]
# s is 0.523599 or 30 degrees if converted with rad_to_deg(s)
var s = asin(0.5)
@@ -2695,6 +2695,9 @@
<constant name="PROPERTY_HINT_LAYERS_3D_NAVIGATION" value="12" enum="PropertyHint">
Hints that an [int] property is a bitmask using the optionally named 3D navigation layers.
</constant>
+ <constant name="PROPERTY_HINT_LAYERS_AVOIDANCE" value="37" enum="PropertyHint">
+ Hints that an integer property is a bitmask using the optionally named avoidance layers.
+ </constant>
<constant name="PROPERTY_HINT_FILE" value="13" enum="PropertyHint">
Hints that a [String] property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code].
</constant>
@@ -2764,7 +2767,7 @@
<constant name="PROPERTY_HINT_PASSWORD" value="36" enum="PropertyHint">
Hints that a string property is a password, and every character is replaced with the secret character.
</constant>
- <constant name="PROPERTY_HINT_MAX" value="37" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_MAX" value="38" enum="PropertyHint">
Represents the size of the [enum PropertyHint] enum.
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
@@ -2842,6 +2845,9 @@
<constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags" is_bitfield="true">
The property is read-only in the [EditorInspector].
</constant>
+ <constant name="PROPERTY_USAGE_SECRET" value="536870912" enum="PropertyUsageFlags" is_bitfield="true">
+ An export preset property with this flag contains confidential information and is stored separately from the rest of the export preset configuration.
+ </constant>
<constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true">
Default usage (storage, editor and network).
</constant>
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index e349f082d3..2a38d34cfb 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -5,11 +5,11 @@
</brief_description>
<description>
Compared to [AStar2D] you don't need to manually create points or connect them together. It also supports multiple type of heuristics and modes for diagonal movement. This class also provides a jumping mode which is faster to calculate than without it in the [AStar2D] class.
- In contrast to [AStar2D], you only need set the [member size] of the grid, optionally set the [member cell_size] and then call the [method update] method:
+ In contrast to [AStar2D], you only need set the [member region] of the grid, optionally set the [member cell_size] and then call the [method update] method:
[codeblocks]
[gdscript]
var astar_grid = AStarGrid2D.new()
- astar_grid.size = Vector2i(32, 32)
+ astar_grid.region = Rect2i(0, 0, 32, 32)
astar_grid.cell_size = Vector2(16, 16)
astar_grid.update()
print(astar_grid.get_id_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4)
@@ -49,7 +49,7 @@
<method name="clear">
<return type="void" />
<description>
- Clears the grid and sets the [member size] to [constant Vector2i.ZERO].
+ Clears the grid and sets the [member region] to [code]Rect2i(0, 0, 0, 0)[/code].
</description>
</method>
<method name="get_id_path">
@@ -132,7 +132,8 @@
<method name="update">
<return type="void" />
<description>
- Updates the internal state of the grid according to the parameters to prepare it to search the path. Needs to be called if parameters like [member size], [member cell_size] or [member offset] are changed. [method is_dirty] will return [code]true[/code] if this is the case and this needs to be called.
+ Updates the internal state of the grid according to the parameters to prepare it to search the path. Needs to be called if parameters like [member region], [member cell_size] or [member offset] are changed. [method is_dirty] will return [code]true[/code] if this is the case and this needs to be called.
+ [b]Note:[/b] All point data (solidity and weight scale) will be cleared.
</description>
</method>
</methods>
@@ -156,8 +157,12 @@
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
The offset of the grid which will be applied to calculate the resulting point position returned by [method get_point_path]. If changed, [method update] needs to be called before finding the next path.
</member>
+ <member name="region" type="Rect2i" setter="set_region" getter="get_region" default="Rect2i(0, 0, 0, 0)">
+ The region of grid cells available for pathfinding. If changed, [method update] needs to be called before finding the next path.
+ </member>
<member name="size" type="Vector2i" setter="set_size" getter="get_size" default="Vector2i(0, 0)">
The size of the grid (number of cells of size [member cell_size] on each axis). If changed, [method update] needs to be called before finding the next path.
+ [b]Note:[/b] This property is deprecated, use [member region] instead.
</member>
</members>
<constants>
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index c15289bde0..3e94f3d0bd 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Animation" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Contains data used to animate everything in the engine.
+ Holds data that can be used to animate anything in the engine.
</brief_description>
<description>
- An Animation resource contains data used to animate everything in the engine. Animations are divided into tracks, and each track must be linked to a node. The state of that node can be changed through time, by adding timed keys (events) to the track.
+ This resource holds data that can be used to animate anything in the engine. Animations are divided into tracks and each track must be linked to a node. The state of that node can be changed through time, by adding timed keys (events) to the track.
[codeblocks]
[gdscript]
# This creates an animation that makes the node "Enemy" move to the right by
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 80f9fd0803..cb6a4f30f9 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNode" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Base resource for [AnimationTree] nodes.
+ Base class for [AnimationTree] nodes. Not related to scene nodes.
</brief_description>
<description>
Base resource for [AnimationTree] nodes. In general, it's not used directly, but you can create custom ones with custom blending formulas.
Inherit this when creating nodes mainly for use in [AnimationNodeBlendTree], otherwise [AnimationRootNode] should be used instead.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<methods>
<method name="_get_caption" qualifiers="virtual const">
diff --git a/doc/classes/AnimationNodeAdd2.xml b/doc/classes/AnimationNodeAdd2.xml
index 1320e5de2d..68bbe8090b 100644
--- a/doc/classes/AnimationNodeAdd2.xml
+++ b/doc/classes/AnimationNodeAdd2.xml
@@ -7,6 +7,6 @@
A resource to add to an [AnimationNodeBlendTree]. Blends two animations additively based on an amount value in the [code][0.0, 1.0][/code] range.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml
index b93f1eadd1..1e51a8a4c5 100644
--- a/doc/classes/AnimationNodeAdd3.xml
+++ b/doc/classes/AnimationNodeAdd3.xml
@@ -11,7 +11,7 @@
- A +add animation to blend with when the blend amount is in the [code][0.0, 1.0][/code] range
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml
index 504160516d..ffefa89e35 100644
--- a/doc/classes/AnimationNodeAnimation.xml
+++ b/doc/classes/AnimationNodeAnimation.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeAnimation" inherits="AnimationRootNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Input animation to use in an [AnimationNodeBlendTree].
+ An input animation for an [AnimationNodeBlendTree].
</brief_description>
<description>
- A resource to add to an [AnimationNodeBlendTree]. Only features one output set using the [member animation] property. Use it as an input for [AnimationNode] that blend animations together.
+ A resource to add to an [AnimationNodeBlendTree]. Only has one output port using the [member animation] property. Used as an input for [AnimationNode]s that blend animations together.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml
index 694beb2780..1460f5f055 100644
--- a/doc/classes/AnimationNodeBlend2.xml
+++ b/doc/classes/AnimationNodeBlend2.xml
@@ -7,7 +7,7 @@
A resource to add to an [AnimationNodeBlendTree]. Blends two animations linearly based on an amount value in the [code][0.0, 1.0][/code] range.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
diff --git a/doc/classes/AnimationNodeBlend3.xml b/doc/classes/AnimationNodeBlend3.xml
index 90363a9718..34c4b8566a 100644
--- a/doc/classes/AnimationNodeBlend3.xml
+++ b/doc/classes/AnimationNodeBlend3.xml
@@ -11,6 +11,6 @@
- A +blend animation to blend with when the blend amount is in the [code][0.0, 1.0][/code] range
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml
index 3d5b70a69e..aa15ac03d9 100644
--- a/doc/classes/AnimationNodeBlendSpace1D.xml
+++ b/doc/classes/AnimationNodeBlendSpace1D.xml
@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlendSpace1D" inherits="AnimationRootNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Blends linearly between two of any number of [AnimationNode] of any type placed on a virtual axis.
+ A set of [AnimationRootNode]s placed on a virtual axis, crossfading between the two adjacent ones. Used by [AnimationTree].
</brief_description>
<description>
- A resource to add to an [AnimationNodeBlendTree].
- This is a virtual axis on which you can add any type of [AnimationNode] using [method add_blend_point].
- Outputs the linear blend of the two [AnimationNode]s closest to the node's current value.
- You can set the extents of the axis using the [member min_space] and [member max_space].
+ A resource used by [AnimationNodeBlendTree].
+ [AnimationNodeBlendSpace1D] represents a virtual axis on which any type of [AnimationRootNode]s can be added using [method add_blend_point]. Outputs the linear blend of the two [AnimationRootNode]s adjacent to the current value.
+ You can set the extents of the axis with [member min_space] and [member max_space].
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<methods>
<method name="add_blend_point">
diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml
index 7e9c2eed47..27dff11e88 100644
--- a/doc/classes/AnimationNodeBlendSpace2D.xml
+++ b/doc/classes/AnimationNodeBlendSpace2D.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlendSpace2D" inherits="AnimationRootNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Blends linearly between three [AnimationNode] of any type placed in a 2D space.
+ A set of [AnimationRootNode]s placed on 2D coordinates, crossfading between the three adjacent ones. Used by [AnimationTree].
</brief_description>
<description>
- A resource to add to an [AnimationNodeBlendTree].
- This node allows you to blend linearly between three animations using a [Vector2] weight.
- You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to create up the blend space by hand.
+ A resource used by [AnimationNodeBlendTree].
+ [AnimationNodeBlendSpace1D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value.
+ You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<methods>
diff --git a/doc/classes/AnimationNodeBlendTree.xml b/doc/classes/AnimationNodeBlendTree.xml
index 4be7f5e7f8..9ae34e8038 100644
--- a/doc/classes/AnimationNodeBlendTree.xml
+++ b/doc/classes/AnimationNodeBlendTree.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeBlendTree" inherits="AnimationRootNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- [AnimationTree] node resource that contains many blend type nodes.
+ A sub-tree of blend type [AnimationNode]s used for complex animations. Used by [AnimationTree].
</brief_description>
<description>
- This node may contain a sub-tree of any other blend type nodes, such as [AnimationNodeTransition], [AnimationNodeBlend2], [AnimationNodeBlend3], [AnimationNodeOneShot], etc. This is one of the most commonly used roots.
+ This node may contain a sub-tree of any other blend type nodes, such as [AnimationNodeTransition], [AnimationNodeBlend2], [AnimationNodeBlend3], [AnimationNodeOneShot], etc. This is one of the most commonly used animation node roots.
An [AnimationNodeOutput] node named [code]output[/code] is created by default.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<methods>
<method name="add_node">
diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml
index e95eab993c..bdf2f18431 100644
--- a/doc/classes/AnimationNodeOneShot.xml
+++ b/doc/classes/AnimationNodeOneShot.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeOneShot" inherits="AnimationNodeSync" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Plays an animation once in [AnimationNodeBlendTree].
+ Plays an animation once in an [AnimationNodeBlendTree].
</brief_description>
<description>
A resource to add to an [AnimationNodeBlendTree]. This node will execute a sub-animation and return once it finishes. Blend times for fading in and out can be customized, as well as filters.
@@ -18,10 +18,20 @@
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT
+ # Abort child animation with fading out connected to "shot" port.
+ animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT)
+ # Alternative syntax (same result as above).
+ animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT
+
# Get current state (read-only).
- animation_tree.get("parameters/OneShot/active"))
+ animation_tree.get("parameters/OneShot/active")
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/active"]
+
+ # Get current internal state (read-only).
+ animation_tree.get("parameters/OneShot/internal_active")
+ # Alternative syntax (same result as above).
+ animation_tree["parameters/OneShot/internal_active"]
[/gdscript]
[csharp]
// Play child animation connected to "shot" port.
@@ -30,13 +40,19 @@
// Abort child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT);
+ // Abort child animation with fading out connected to "shot" port.
+ animationTree.Set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FADE_OUT);
+
// Get current state (read-only).
animationTree.Get("parameters/OneShot/active");
+
+ // Get current internal state (read-only).
+ animationTree.Get("parameters/OneShot/internal_active");
[/csharp]
[/codeblocks]
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<members>
@@ -50,11 +66,17 @@
<member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay" default="0.0">
If [member autorestart] is [code]true[/code], a random additional delay (in seconds) between 0 and this value will be added to [member autorestart_delay].
</member>
+ <member name="fadein_curve" type="Curve" setter="set_fadein_curve" getter="get_fadein_curve">
+ Determines how cross-fading between animations is eased. If empty, the transition will be linear.
+ </member>
<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
- The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a crossfade that starts at 0 second and ends at 1 second during the animation.
+ The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation.
+ </member>
+ <member name="fadeout_curve" type="Curve" setter="set_fadeout_curve" getter="get_fadeout_curve">
+ Determines how cross-fading between animations is eased. If empty, the transition will be linear.
</member>
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
- The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a crossfade that starts at 4 second and ends at 5 second during the animation.
+ The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation.
</member>
<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
The blend type.
@@ -70,6 +92,9 @@
<constant name="ONE_SHOT_REQUEST_ABORT" value="2" enum="OneShotRequest">
The request to stop the animation connected to "shot" port.
</constant>
+ <constant name="ONE_SHOT_REQUEST_FADE_OUT" value="3" enum="OneShotRequest">
+ The request to fade out the animation connected to "shot" port.
+ </constant>
<constant name="MIX_MODE_BLEND" value="0" enum="MixMode">
Blends two animations. See also [AnimationNodeBlend2].
</constant>
diff --git a/doc/classes/AnimationNodeOutput.xml b/doc/classes/AnimationNodeOutput.xml
index 46b7169bba..0a9210ce37 100644
--- a/doc/classes/AnimationNodeOutput.xml
+++ b/doc/classes/AnimationNodeOutput.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeOutput" inherits="AnimationNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Generic output node to be added to [AnimationNodeBlendTree].
+ The animation output node of an [AnimationNodeBlendTree].
</brief_description>
<description>
+ A node created automatically in an [AnimationNodeBlendTree] that outputs the final animation.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml
index 13f88f79ef..9459c31b3b 100644
--- a/doc/classes/AnimationNodeStateMachine.xml
+++ b/doc/classes/AnimationNodeStateMachine.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeStateMachine" inherits="AnimationRootNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- State machine for control of animations.
+ A state machine with multiple [AnimationRootNode]s, used by [AnimationTree].
</brief_description>
<description>
- Contains multiple nodes representing animation states, connected in a graph. Node transitions can be configured to happen automatically or via code, using a shortest-path algorithm. Retrieve the [AnimationNodeStateMachinePlayback] object from the [AnimationTree] node to control it programmatically.
+ Contains multiple [AnimationRootNode]s representing animation states, connected in a graph. Node transitions can be configured to happen automatically or via code, using a shortest-path algorithm. Retrieve the [AnimationNodeStateMachinePlayback] object from the [AnimationTree] node to control it programmatically.
[b]Example:[/b]
[codeblocks]
[gdscript]
@@ -18,7 +18,7 @@
[/codeblocks]
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<methods>
<method name="add_node">
diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml
index 08634f650e..48d4f154d1 100644
--- a/doc/classes/AnimationNodeStateMachinePlayback.xml
+++ b/doc/classes/AnimationNodeStateMachinePlayback.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeStateMachinePlayback" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Playback control for [AnimationNodeStateMachine].
+ Provides playback control for an [AnimationNodeStateMachine].
</brief_description>
<description>
Allows control of [AnimationTree] state machines created with [AnimationNodeStateMachine]. Retrieve with [code]$AnimationTree.get("parameters/playback")[/code].
@@ -18,7 +18,7 @@
[/codeblocks]
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<methods>
<method name="get_current_length" qualifiers="const">
@@ -48,7 +48,7 @@
</description>
</method>
<method name="get_travel_path" qualifiers="const">
- <return type="PackedStringArray" />
+ <return type="StringName[]" />
<description>
Returns the current travel path as computed internally by the A* algorithm.
</description>
diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml
index e23c120cc4..7bb615c148 100644
--- a/doc/classes/AnimationNodeStateMachineTransition.xml
+++ b/doc/classes/AnimationNodeStateMachineTransition.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeStateMachineTransition" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A resource to connect each node to make a path for [AnimationNodeStateMachine].
+ A transition within an [AnimationNodeStateMachine] connecting two [AnimationRootNode]s.
</brief_description>
<description>
The path generated when using [method AnimationNodeStateMachinePlayback.travel] is limited to the nodes connected by [AnimationNodeStateMachineTransition].
You can set the timing and conditions of the transition in detail.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<members>
<member name="advance_condition" type="StringName" setter="set_advance_condition" getter="get_advance_condition" default="&amp;&quot;&quot;">
diff --git a/doc/classes/AnimationNodeSync.xml b/doc/classes/AnimationNodeSync.xml
index e72a0619f4..38f4069c7b 100644
--- a/doc/classes/AnimationNodeSync.xml
+++ b/doc/classes/AnimationNodeSync.xml
@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeSync" inherits="AnimationNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- The base class for [AnimationNode] which has more than two input ports and needs to synchronize them.
+ Base class for [AnimationNode]s with more than two input ports that must be synchronized.
</brief_description>
<description>
+ An animation node used to combine, mix, or blend two or more animations together while keeping them synchronized within an [AnimationTree].
</description>
<tutorials>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
<members>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync" default="false">
diff --git a/doc/classes/AnimationNodeTimeScale.xml b/doc/classes/AnimationNodeTimeScale.xml
index 5d1b8b109c..65795823b3 100644
--- a/doc/classes/AnimationNodeTimeScale.xml
+++ b/doc/classes/AnimationNodeTimeScale.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeTimeScale" inherits="AnimationNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A time-scaling animation node to be used with [AnimationTree].
+ A time-scaling animation node used in [AnimationTree].
</brief_description>
<description>
- Allows scaling the speed of the animation (or reversing it) in any children nodes. Setting it to 0 will pause the animation.
+ Allows to scale the speed of the animation (or reverse it) in any children [AnimationNode]s. Setting it to [code]0.0[/code] will pause the animation.
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationNodeTimeSeek.xml b/doc/classes/AnimationNodeTimeSeek.xml
index 978cd079c8..3098fa7662 100644
--- a/doc/classes/AnimationNodeTimeSeek.xml
+++ b/doc/classes/AnimationNodeTimeSeek.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeTimeSeek" inherits="AnimationNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A time-seeking animation node to be used with [AnimationTree].
+ A time-seeking animation node used in [AnimationTree].
</brief_description>
<description>
This node can be used to cause a seek command to happen to any sub-children of the animation graph. Use this node type to play an [Animation] from the start or a certain playback position inside the [AnimationNodeBlendTree].
@@ -28,6 +28,6 @@
[/codeblocks]
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml
index ccf14fcb50..481ea9ff59 100644
--- a/doc/classes/AnimationNodeTransition.xml
+++ b/doc/classes/AnimationNodeTransition.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationNodeTransition" inherits="AnimationNodeSync" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A generic animation transition node for [AnimationTree].
+ A transition within an [AnimationTree] connecting two [AnimationNode]s.
</brief_description>
<description>
Simple state machine for cases which don't require a more advanced [AnimationNodeStateMachine]. Animations can be connected to the inputs and transition times can be specified.
@@ -37,7 +37,7 @@
[/codeblocks]
</description>
<tutorials>
- <link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 77770a0f7e..f06731c376 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationPlayer" inherits="Node" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Player of [Animation] resources.
+ A node used for animation playback.
</brief_description>
<description>
- An animation player is used for general-purpose playback of [Animation] resources. It contains a dictionary of [AnimationLibrary] resources and custom blend times between animation transitions.
+ An animation player is used for general-purpose playback of animations. It contains a dictionary of [AnimationLibrary] resources and custom blend times between animation transitions.
Some methods and properties use a single key to reference an animation directly. These keys are formatted as the key for the library, followed by a forward slash, then the key for the animation within the library, for example [code]"movement/run"[/code]. If the library's key is an empty string (known as the default library), the forward slash is omitted, being the same key used by the library.
- [AnimationPlayer] is more suited than [Tween] for animations where you know the final values in advance. For example, fading a screen in and out is more easily done with an [AnimationPlayer] node thanks to the animation tools provided by the editor. That particular example can also be implemented with a [Tween], but it requires doing everything by code.
- Updating the target properties of animations occurs at process time.
+ [AnimationPlayer] is better-suited than [Tween] for more complex animations, for example ones with non-trivial timings. It can also be used over [Tween] if the animation track editor is more convenient than doing it in code.
+ Updating the target properties of animations occurs at the process frame.
</description>
<tutorials>
<link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link>
@@ -163,7 +163,8 @@
<param index="2" name="custom_speed" type="float" default="1.0" />
<param index="3" name="from_end" type="bool" default="false" />
<description>
- Plays the animation with key [param name]. Custom blend times and speed can be set. If [param custom_speed] is negative and [param from_end] is [code]true[/code], the animation will play backwards (which is equivalent to calling [method play_backwards]).
+ Plays the animation with key [param name]. Custom blend times and speed can be set.
+ The [param from_end] option only affects when switching to a new animation track, or if the same track but at the start or end. It does not affect resuming playback that was paused in the middle of an animation. If [param custom_speed] is negative and [param from_end] is [code]true[/code], the animation will play backwards (which is equivalent to calling [method play_backwards]).
The [AnimationPlayer] keeps track of its current or last played animation with [member assigned_animation]. If this method is called with that same animation [param name], or with no [param name] parameter, the assigned animation will resume playing if it was paused.
[b]Note:[/b] The animation will be updated the next time the [AnimationPlayer] is processed. If other variables are updated at the same time this is called, they may be updated too early. To perform the update immediately, call [code]advance(0)[/code].
</description>
diff --git a/doc/classes/AnimationRootNode.xml b/doc/classes/AnimationRootNode.xml
index 47c8c8f287..bf5122fe4f 100644
--- a/doc/classes/AnimationRootNode.xml
+++ b/doc/classes/AnimationRootNode.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationRootNode" inherits="AnimationNode" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- The [AnimationNode] which can be set as the root of an [AnimationTree].
+ Base class for [AnimationNode]s that hold one or multiple composite animations. Usually used for [member AnimationTree.tree_root].
</brief_description>
<description>
+ [AnimationRootNode] is a base class for [AnimationNode]s that hold a complete animation. A complete animation refers to the output of an [AnimationNodeOutput] in an [AnimationNodeBlendTree] or the output of another [AnimationRootNode]. Used for [member AnimationTree.tree_root] or in other [AnimationRootNode]s.
</description>
<tutorials>
+ <link title="Using AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 4de079c5a3..ef3c1a3f9e 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AnimationTree" inherits="Node" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A node to be used for advanced animation transitions in an [AnimationPlayer].
+ A node used for advanced animation transitions in an [AnimationPlayer].
</brief_description>
<description>
- A node to be used for advanced animation transitions in an [AnimationPlayer].
+ A node used for advanced animation transitions in an [AnimationPlayer].
[b]Note:[/b] When linked with an [AnimationPlayer], several properties and methods of the corresponding [AnimationPlayer] will not function as expected. Playback and transitions should be handled using only the [AnimationTree] and its constituent [AnimationNode](s). The [AnimationPlayer] node should be used solely for adding, deleting, and editing animations.
</description>
<tutorials>
diff --git a/doc/classes/AudioStreamGenerator.xml b/doc/classes/AudioStreamGenerator.xml
index fa4b2ae49f..7718cb78ea 100644
--- a/doc/classes/AudioStreamGenerator.xml
+++ b/doc/classes/AudioStreamGenerator.xml
@@ -1,16 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamGenerator" inherits="AudioStream" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Audio stream that generates sounds procedurally.
+ An audio stream with utilities for procedural sound generation.
</brief_description>
<description>
- This audio stream does not play back sounds, but expects a script to generate audio data for it instead. See also [AudioStreamGeneratorPlayback].
+ [AudioStreamGenerator] is a type of audio stream that does not play back sounds on its own; instead, it expects a script to generate audio data for it. See also [AudioStreamGeneratorPlayback].
+ Here's a sample on how to use it to generate a sine wave:
+ [codeblock]
+ var playback # Will hold the AudioStreamGeneratorPlayback.
+ @onready var sample_hz = $AudioStreamPlayer.stream.mix_rate
+ var pulse_hz = 440.0 # The frequency of the sound wave.
+
+ func _ready():
+ $AudioStreamPlayer.play()
+ playback = $AudioStreamPlayer.get_stream_playback()
+ fill_buffer()
+
+ func fill_buffer():
+ var phase = 0.0
+ var increment = pulse_hz / sample_hz
+ var frames_available = playback.get_frames_available()
+
+ for i in range(frames_available):
+ playback.push_frame(Vector2.ONE * sin(phase * TAU))
+ phase = fmod(phase + increment, 1.0)
+ [/codeblock]
+ In the example above, the "AudioStreamPlayer" node must use an [AudioStreamGenerator] as its stream. The [code]fill_buffer[/code] function provides audio data for approximating a sine wave.
See also [AudioEffectSpectrumAnalyzer] for performing real-time audio spectrum analysis.
[b]Note:[/b] Due to performance constraints, this class is best used from C# or from a compiled language via GDExtension. If you still want to use this class from GDScript, consider using a lower [member mix_rate] such as 11,025 Hz or 22,050 Hz.
</description>
<tutorials>
<link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link>
- <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link>
</tutorials>
<members>
<member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length" default="0.5">
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 989c76d5da..f5e14e29bf 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -356,13 +356,13 @@
[b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead.
</member>
<member name="subsurf_scatter_enabled" type="bool" setter="set_feature" getter="get_feature" default="false">
- If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges.
+ If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. Subsurface scattering quality is controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality].
</member>
<member name="subsurf_scatter_skin_mode" type="bool" setter="set_flag" getter="get_flag" default="false">
If [code]true[/code], subsurface scattering will use a special mode optimized for the color and density of human skin, such as boosting the intensity of the red channel in subsurface scattering.
</member>
<member name="subsurf_scatter_strength" type="float" setter="set_subsurface_scattering_strength" getter="get_subsurface_scattering_strength" default="0.0">
- The strength of the subsurface scattering effect.
+ The strength of the subsurface scattering effect. The depth of the effect is also controlled by [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_scale], which is set globally.
</member>
<member name="subsurf_scatter_texture" type="Texture2D" setter="set_texture" getter="get_texture">
Texture used to control the subsurface scattering strength. Stored in the red texture channel. Multiplied by [member subsurf_scatter_strength].
diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml
index 446c9d3702..93bdd9d527 100644
--- a/doc/classes/Bone2D.xml
+++ b/doc/classes/Bone2D.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Bone2D" inherits="Node2D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Joint used with [Skeleton2D] to control and animate other nodes.
+ A joint used with [Skeleton2D] to control and animate other nodes.
</brief_description>
<description>
- Use a hierarchy of [code]Bone2D[/code] bound to a [Skeleton2D] to control, and animate other [Node2D] nodes.
- You can use [code]Bone2D[/code] and [code]Skeleton2D[/code] nodes to animate 2D meshes created with the Polygon 2D UV editor.
+ A hierarchy of [Bone2D]s can be bound to a [Skeleton2D] to control and animate other [Node2D] nodes.
+ You can use [Bone2D] and [Skeleton2D] nodes to animate 2D meshes created with the [Polygon2D] UV editor.
Each bone has a [member rest] transform that you can reset to with [method apply_rest]. These rest poses are relative to the bone's parent.
If in the editor, you can set the rest pose of an entire skeleton using a menu option, from the code, you need to iterate over the bones to set their individual rest poses.
</description>
@@ -21,14 +21,14 @@
<method name="get_autocalculate_length_and_angle" qualifiers="const">
<return type="bool" />
<description>
- Returns whether this [code]Bone2D[/code] node is going to autocalculate its length and bone angle using its first [code]Bone2D[/code] child node, if one exists. If there are no [code]Bone2D[/code] children, then it cannot autocalculate these values and will print a warning.
+ Returns whether this [Bone2D] is going to autocalculate its length and bone angle using its first [Bone2D] child node, if one exists. If there are no [Bone2D] children, then it cannot autocalculate these values and will print a warning.
</description>
</method>
<method name="get_bone_angle" qualifiers="const">
<return type="float" />
<description>
- Returns the angle of the bone in the [code]Bone2D[/code] node.
- [b]Note:[/b] This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node.
+ Returns the angle of the bone in the [Bone2D].
+ [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform].
</description>
</method>
<method name="get_index_in_skeleton" qualifiers="const">
@@ -40,7 +40,7 @@
<method name="get_length" qualifiers="const">
<return type="float" />
<description>
- Returns the length of the bone in the [code]Bone2D[/code] node.
+ Returns the length of the bone in the [Bone2D] node.
</description>
</method>
<method name="get_skeleton_rest" qualifiers="const">
@@ -53,22 +53,22 @@
<return type="void" />
<param index="0" name="auto_calculate" type="bool" />
<description>
- When set to [code]true[/code], the [code]Bone2D[/code] node will attempt to automatically calculate the bone angle and length using the first child [code]Bone2D[/code] node, if one exists. If none exist, the [code]Bone2D[/code] cannot automatically calculate these values and will print a warning.
+ When set to [code]true[/code], the [Bone2D] node will attempt to automatically calculate the bone angle and length using the first child [Bone2D] node, if one exists. If none exist, the [Bone2D] cannot automatically calculate these values and will print a warning.
</description>
</method>
<method name="set_bone_angle">
<return type="void" />
<param index="0" name="angle" type="float" />
<description>
- Sets the bone angle for the [code]Bone2D[/code] node. This is typically set to the rotation from the [code]Bone2D[/code] node to a child [code]Bone2D[/code] node.
- [b]Note:[/b] This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node.
+ Sets the bone angle for the [Bone2D]. This is typically set to the rotation from the [Bone2D] to a child [Bone2D] node.
+ [b]Note:[/b] [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform].
</description>
</method>
<method name="set_length">
<return type="void" />
<param index="0" name="length" type="float" />
<description>
- Sets the length of the bone in the [code]Bone2D[/code] node.
+ Sets the length of the bone in the [Bone2D].
</description>
</method>
</methods>
diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml
index bcf7ed42e9..83f70fc331 100644
--- a/doc/classes/BoneAttachment3D.xml
+++ b/doc/classes/BoneAttachment3D.xml
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BoneAttachment3D" inherits="Node3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A node that will attach to a bone.
+ А node that dynamically copies or overrides the 3D transform of a bone in its parent [Skeleton3D].
</brief_description>
<description>
- This node will allow you to select a bone for this node to attach to. The BoneAttachment3D node can copy the transform of the select bone, or can override the transform of the selected bone.
- The BoneAttachment3D node must either be a child of a [Skeleton3D] node or be given an external [Skeleton3D] to use in order to function properly.
+ This node selects a bone in a [Skeleton3D] and attaches to it. This means that the [BoneAttachment3D] node will either dynamically copy or override the 3D transform of the selected bone.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/BoneMap.xml b/doc/classes/BoneMap.xml
index 1fd54a5019..032e2420ca 100644
--- a/doc/classes/BoneMap.xml
+++ b/doc/classes/BoneMap.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BoneMap" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Bone map for retargeting.
+ Describes a mapping of bone names for retargeting [Skeleton3D] into common names defined by a [SkeletonProfile].
</brief_description>
<description>
- This class contains a hashmap that uses a list of bone names in [SkeletonProfile] as key names.
+ This class contains a dictionary that uses a list of bone names in [SkeletonProfile] as key names.
By assigning the actual [Skeleton3D] bone name as the key value, it maps the [Skeleton3D] to the [SkeletonProfile].
</description>
<tutorials>
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index ec2447dbbc..1bf5c31818 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -58,7 +58,7 @@
To edit margin and spacing of the icon, use [theme_item h_separation] theme property and [code]content_margin_*[/code] properties of the used [StyleBox]es.
</member>
<member name="icon_alignment" type="int" setter="set_icon_alignment" getter="get_icon_alignment" enum="HorizontalAlignment" default="0">
- Specifies if the icon should be aligned to the left, right, or center of a button. Uses the same [enum HorizontalAlignment] constants as the text alignment. If centered, text will draw on top of the icon.
+ Specifies if the icon should be aligned horizontally to the left, right, or center of a button. Uses the same [enum HorizontalAlignment] constants as the text alignment. If centered horizontally and vertically, text will draw on top of the icon.
</member>
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
@@ -72,6 +72,9 @@
<member name="text_overrun_behavior" type="int" setter="set_text_overrun_behavior" getter="get_text_overrun_behavior" enum="TextServer.OverrunBehavior" default="0">
Sets the clipping behavior when the text exceeds the node's bounding rectangle. See [enum TextServer.OverrunBehavior] for a description of all modes.
</member>
+ <member name="vertical_icon_alignment" type="int" setter="set_vertical_icon_alignment" getter="get_vertical_icon_alignment" enum="VerticalAlignment" default="1">
+ Specifies if the icon should be aligned vertically to the top, bottom, or center of a button. Uses the same [enum VerticalAlignment] constants as the text alignment. If centered horizontally and vertically, text will draw on top of the icon.
+ </member>
</members>
<theme_items>
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index af1f2be76d..41804e5a75 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -237,7 +237,7 @@
<param index="2" name="uvs" type="PackedVector2Array" default="PackedVector2Array()" />
<param index="3" name="texture" type="Texture2D" default="null" />
<description>
- Draws a solid polygon of any number of points, convex or concave. Unlike [method draw_colored_polygon], each point's color can be changed individually. See also [method draw_polyline] and [method draw_polyline_colors].
+ Draws a solid polygon of any number of points, convex or concave. Unlike [method draw_colored_polygon], each point's color can be changed individually. See also [method draw_polyline] and [method draw_polyline_colors]. If you need more flexibility (such as being able to use bones), use [method RenderingServer.canvas_item_add_triangle_array] instead.
</description>
</method>
<method name="draw_polyline">
@@ -279,7 +279,7 @@
<param index="2" name="filled" type="bool" default="true" />
<param index="3" name="width" type="float" default="-1.0" />
<description>
- Draws a rectangle. If [param filled] is [code]true[/code], the rectangle will be filled with the [param color] specified. If [param filled] is [code]false[/code], the rectangle will be drawn as a stroke with the [param color] and [param width] specified.
+ Draws a rectangle. If [param filled] is [code]true[/code], the rectangle will be filled with the [param color] specified. If [param filled] is [code]false[/code], the rectangle will be drawn as a stroke with the [param color] and [param width] specified. See also [method draw_texture_rect].
If [param width] is negative, then two-point primitives will be drawn instead of a four-point ones. This means that when the CanvasItem is scaled, the lines will remain thin. If this behavior is not desired, then pass a positive [param width] like [code]1.0[/code].
[b]Note:[/b] [param width] is only effective if [param filled] is [code]false[/code].
[b]Note:[/b] Unfilled rectangles drawn with a negative [param width] may not display perfectly. For example, corners may be missing or brighter due to overlapping lines (for a translucent [param color]).
@@ -380,7 +380,7 @@
<param index="3" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<param index="4" name="transpose" type="bool" default="false" />
<description>
- Draws a textured rectangle at a given position, optionally modulated by a color. If [param transpose] is [code]true[/code], the texture will have its X and Y coordinates swapped.
+ Draws a textured rectangle at a given position, optionally modulated by a color. If [param transpose] is [code]true[/code], the texture will have its X and Y coordinates swapped. See also [method draw_rect] and [method draw_texture_rect_region].
</description>
</method>
<method name="draw_texture_rect_region">
@@ -392,7 +392,7 @@
<param index="4" name="transpose" type="bool" default="false" />
<param index="5" name="clip_uv" type="bool" default="true" />
<description>
- Draws a textured rectangle region at a given position, optionally modulated by a color. If [param transpose] is [code]true[/code], the texture will have its X and Y coordinates swapped.
+ Draws a textured rectangle from a texture's region (specified by [param src_rect]) at a given position, optionally modulated by a color. If [param transpose] is [code]true[/code], the texture will have its X and Y coordinates swapped. See also [method draw_texture_rect].
</description>
</method>
<method name="force_update_transform">
@@ -423,6 +423,7 @@
<return type="Vector2" />
<description>
Returns the mouse's position in the [CanvasLayer] that this [CanvasItem] is in using the coordinate system of the [CanvasLayer].
+ [b]Note:[/b] For screen-space coordinates (e.g. when using a non-embedded [Popup]), you can use [method DisplayServer.mouse_get_position].
</description>
</method>
<method name="get_global_transform" qualifiers="const">
@@ -502,7 +503,7 @@
<method name="is_visible_in_tree" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its antecedents are also visible. If any antecedent is hidden, this node will not be visible in the scene tree, and is consequently not drawn (see [method _draw]).
+ Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree, and is consequently not drawn (see [method _draw]).
</description>
</method>
<method name="make_canvas_position_local" qualifiers="const">
@@ -597,7 +598,7 @@
The rendering layer in which this [CanvasItem] is rendered by [Viewport] nodes. A [Viewport] will render a [CanvasItem] if it and all its parents share a layer with the [Viewport]'s canvas cull mask.
</member>
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
- If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
+ If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its ancestors are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
[b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead.
</member>
<member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
@@ -654,6 +655,9 @@
<constant name="NOTIFICATION_EXIT_CANVAS" value="33">
The [CanvasItem] has exited the canvas.
</constant>
+ <constant name="NOTIFICATION_WORLD_2D_CHANGED" value="36">
+ The [CanvasItem]'s active [World2D] changed.
+ </constant>
<constant name="TEXTURE_FILTER_PARENT_NODE" value="0" enum="TextureFilter">
The [CanvasItem] will inherit the filter from its parent.
</constant>
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 8642fcc0d0..84e1c80900 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -126,6 +126,15 @@
Inserts the selected entry into the text. If [param replace] is true, any existing text is replaced rather then merged.
</description>
</method>
+ <method name="convert_indent">
+ <return type="void" />
+ <param index="0" name="from_line" type="int" default="-1" />
+ <param index="1" name="to_line" type="int" default="-1" />
+ <description>
+ Converts the indents of lines between [param from_line] and [param to_line] to tabs or spaces as set by [member indent_use_spaces].
+ Values of [code]-1[/code] convert the entire text.
+ </description>
+ </method>
<method name="do_indent">
<return type="void" />
<description>
@@ -537,7 +546,7 @@
Marks the option as a member.
</constant>
<constant name="KIND_ENUM" value="5" enum="CodeCompletionKind">
- Marks the option as a enum entry.
+ Marks the option as an enum entry.
</constant>
<constant name="KIND_CONSTANT" value="6" enum="CodeCompletionKind">
Marks the option as a constant.
diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index 14656c649e..61a0606f79 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -141,6 +141,9 @@
</constant>
</constants>
<theme_items>
+ <theme_item name="center_slider_grabbers" data_type="constant" type="int" default="1">
+ Overrides the [theme_item HSlider.center_grabber] theme property of the sliders.
+ </theme_item>
<theme_item name="h_width" data_type="constant" type="int" default="30">
The width of the hue selection slider.
</theme_item>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index a52cdf7207..9a34bc4f99 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -217,6 +217,7 @@
<return type="void" />
<description>
Marks an input event as handled. Once you accept an input event, it stops propagating, even to nodes listening to [method Node._unhandled_input] or [method Node._unhandled_key_input].
+ [b]Note:[/b] This does not affect the methods in [Input], only the way events are propagated.
</description>
</method>
<method name="add_theme_color_override">
diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml
index 317a578559..52f79ecc9d 100644
--- a/doc/classes/Curve.xml
+++ b/doc/classes/Curve.xml
@@ -78,7 +78,7 @@
<return type="void" />
<param index="0" name="index" type="int" />
<description>
- Removes the point at [code]index[/code] from the curve.
+ Removes the point at [param index] from the curve.
</description>
</method>
<method name="sample" qualifiers="const">
diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml
index d545c144ab..abf71c5754 100644
--- a/doc/classes/Curve2D.xml
+++ b/doc/classes/Curve2D.xml
@@ -80,7 +80,7 @@
<return type="void" />
<param index="0" name="idx" type="int" />
<description>
- Deletes the point [code]idx[/code] from the curve. Sends an error to the console if [code]idx[/code] is out of bounds.
+ Deletes the point [param idx] from the curve. Sends an error to the console if [param idx] is out of bounds.
</description>
</method>
<method name="sample" qualifiers="const">
diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml
index 4134df25dd..4d1f5a7180 100644
--- a/doc/classes/Curve3D.xml
+++ b/doc/classes/Curve3D.xml
@@ -100,7 +100,7 @@
<return type="void" />
<param index="0" name="idx" type="int" />
<description>
- Deletes the point [code]idx[/code] from the curve. Sends an error to the console if [code]idx[/code] is out of bounds.
+ Deletes the point [param idx] from the curve. Sends an error to the console if [param idx] is out of bounds.
</description>
</method>
<method name="sample" qualifiers="const">
diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml
index 4dcdd9560a..7d530e45cf 100644
--- a/doc/classes/CurveTexture.xml
+++ b/doc/classes/CurveTexture.xml
@@ -15,7 +15,7 @@
</member>
<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_mode" type="int" setter="set_texture_mode" getter="get_texture_mode" enum="CurveTexture.TextureMode" default="0">
- The format the texture should be generated with. When passing a CurveTexture as a input to a [Shader], this may need to be adjusted.
+ The format the texture should be generated with. When passing a CurveTexture as an input to a [Shader], this may need to be adjusted.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="256">
The width of the texture (in pixels). Higher values make it possible to represent high-frequency data better (such as sudden direction changes), at the cost of increased generation time and memory usage.
diff --git a/doc/classes/Decal.xml b/doc/classes/Decal.xml
index b1ce4cef1a..ace1d026d4 100644
--- a/doc/classes/Decal.xml
+++ b/doc/classes/Decal.xml
@@ -107,7 +107,7 @@
<member name="texture_orm" type="Texture2D" setter="set_texture" getter="get_texture">
[Texture2D] storing ambient occlusion, roughness, and metallic for the decal. Use this to add extra detail to decals.
[b]Note:[/b] Unlike [BaseMaterial3D] whose filter mode can be adjusted on a per-material basis, the filter mode for [Decal] textures is set globally with [member ProjectSettings.rendering/textures/decals/filter].
- [b]Note:[/b] Setting this texture alone will not result in a visible decal, as [member texture_albedo] must also be set. To create a ORM-only decal, load an albedo texture into [member texture_albedo] and set [member albedo_mix] to [code]0.0[/code]. The albedo texture's alpha channel will be used to determine where the underlying surface's ORM map should be overridden (and its intensity).
+ [b]Note:[/b] Setting this texture alone will not result in a visible decal, as [member texture_albedo] must also be set. To create an ORM-only decal, load an albedo texture into [member texture_albedo] and set [member albedo_mix] to [code]0.0[/code]. The albedo texture's alpha channel will be used to determine where the underlying surface's ORM map should be overridden (and its intensity).
</member>
<member name="upper_fade" type="float" setter="set_upper_fade" getter="get_upper_fade" default="0.3">
Sets the curve over which the decal will fade as the surface gets further from the center of the [AABB]. Only positive values are valid (negative values will be clamped to [code]0.0[/code]). See also [member lower_fade].
diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml
index b6e17a1a2d..d26b839e64 100644
--- a/doc/classes/DirAccess.xml
+++ b/doc/classes/DirAccess.xml
@@ -173,6 +173,7 @@
<description>
Returns a [PackedStringArray] containing filenames of the directory contents, excluding directories. The array is sorted alphabetically.
Affected by [member include_hidden].
+ [b]Note:[/b] When used on a [code]res://[/code] path in an exported project, only the files actually included in the PCK at the given folder level are returned. In practice, this means that since imported resources are stored in a top-level [code].godot/[/code] folder, only paths to [code]*.gd[/code] and [code]*.import[/code] files are returned (plus a few files such as [code]project.godot[/code] or [code]project.binary[code] and the project icon). In an exported project, the list of returned files will also vary depending on whether [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code].
</description>
</method>
<method name="get_files_at" qualifiers="static">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 924e1d5d2f..513414e695 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -468,7 +468,7 @@
<param index="0" name="menu_root" type="String" />
<param index="1" name="idx" type="int" />
<description>
- Returns number of states of an multistate item. See [method global_menu_add_multistate_item] for details.
+ Returns number of states of a multistate item. See [method global_menu_add_multistate_item] for details.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
@@ -477,7 +477,7 @@
<param index="0" name="menu_root" type="String" />
<param index="1" name="idx" type="int" />
<description>
- Returns the state of an multistate item. See [method global_menu_add_multistate_item] for details.
+ Returns the state of a multistate item. See [method global_menu_add_multistate_item] for details.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
@@ -654,7 +654,7 @@
<param index="1" name="idx" type="int" />
<param index="2" name="max_states" type="int" />
<description>
- Sets number of state of an multistate item. See [method global_menu_add_multistate_item] for details.
+ Sets number of state of a multistate item. See [method global_menu_add_multistate_item] for details.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
@@ -675,7 +675,7 @@
<param index="1" name="idx" type="int" />
<param index="2" name="state" type="int" />
<description>
- Sets the state of an multistate item. See [method global_menu_add_multistate_item] for details.
+ Sets the state of a multistate item. See [method global_menu_add_multistate_item] for details.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
@@ -821,7 +821,7 @@
<method name="mouse_get_position" qualifiers="const">
<return type="Vector2i" />
<description>
- Returns the mouse cursor's current position.
+ Returns the mouse cursor's current position in screen coordinates.
</description>
</method>
<method name="mouse_set_mode">
@@ -1718,7 +1718,7 @@
[b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
</constant>
<constant name="WINDOW_FLAG_RESIZE_DISABLED" value="0" enum="WindowFlags">
- The window can't be resizing by dragging its resize grip. It's still possible to resize the window using [method window_set_size]. This flag is ignored for full screen windows.
+ The window can't be resized by dragging its resize grip. It's still possible to resize the window using [method window_set_size]. This flag is ignored for full screen windows.
</constant>
<constant name="WINDOW_FLAG_BORDERLESS" value="1" enum="WindowFlags">
The window do not have native title bar and other decorations. This flag is ignored for full-screen windows.
diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml
index 1484413536..e69c6489df 100644
--- a/doc/classes/EditorResourcePreviewGenerator.xml
+++ b/doc/classes/EditorResourcePreviewGenerator.xml
@@ -20,20 +20,24 @@
<return type="Texture2D" />
<param index="0" name="resource" type="Resource" />
<param index="1" name="size" type="Vector2i" />
+ <param index="2" name="metadata" type="Dictionary" />
<description>
Generate a preview from a given resource with the specified size. This must always be implemented.
Returning an empty texture is an OK way to fail and let another generator take care.
Care must be taken because this function is always called from a thread (not the main thread).
+ [param metadata] dictionary can modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.).
</description>
</method>
<method name="_generate_from_path" qualifiers="virtual const">
<return type="Texture2D" />
<param index="0" name="path" type="String" />
<param index="1" name="size" type="Vector2i" />
+ <param index="2" name="metadata" type="Dictionary" />
<description>
Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method _generate].
Returning an empty texture is an OK way to fail and let another generator take care.
Care must be taken because this function is always called from a thread (not the main thread).
+ [param metadata] dictionary can modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.).
</description>
</method>
<method name="_generate_small_preview_automatically" qualifiers="virtual const">
diff --git a/doc/classes/EditorResourceTooltipPlugin.xml b/doc/classes/EditorResourceTooltipPlugin.xml
new file mode 100644
index 0000000000..ada91c4e7c
--- /dev/null
+++ b/doc/classes/EditorResourceTooltipPlugin.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorResourceTooltipPlugin" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A plugin that advanced tooltip for its handled resource type.
+ </brief_description>
+ <description>
+ Resource tooltip plugins are used by [FileSystemDock] to generate customized tooltips for specific resources. E.g. tooltip for a [Texture2D] displays a bigger preview and the texture's dimensions.
+ A plugin must be first registered with [method FileSystemDock.add_resource_tooltip_plugin]. When the user hovers a resource in filesystem dock which is handled by the plugin, [method _make_tooltip_for_path] is called to create the tooltip. It works similarly to [method Control._make_custom_tooltip].
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_handles" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="type" type="String" />
+ <description>
+ Return [code]true[/code] if the plugin is going to handle the given [Resource] [param type].
+ </description>
+ </method>
+ <method name="_make_tooltip_for_path" qualifiers="virtual const">
+ <return type="Object" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="metadata" type="Dictionary" />
+ <description>
+ Create and return a tooltip that will be displayed when the user hovers resource under given [param path] in filesystem dock. For best results, use [method make_default_tooltip] as a base.
+ The [param metadata] dictionary is provided by preview generator (see method EditorResourcePreviewGenerator._generate]).
+ [b]Note:[/b] It's unadvised to use [method ResourceLoader.load], especially with heavy resources like models or textures, because it will make the editor unresponsive when creating the tooltip. You can use [method request_thumbnail] if you want to display a preview in your tooltip.
+ </description>
+ </method>
+ <method name="make_default_tooltip" qualifiers="static">
+ <return type="VBoxContainer" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Creates a default file tooltip. The tooltip includes file name, file size and [Resource] type if available.
+ </description>
+ </method>
+ <method name="request_thumbnail" qualifiers="const">
+ <return type="void" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="control" type="TextureRect" />
+ <description>
+ Requests a thumbnail for the given [TextureRect]. The thumbnail is created asynchronously by [EditorResourcePreview] and automatically set when available.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml
index ecaa21efb3..d77d11857b 100644
--- a/doc/classes/EditorScript.xml
+++ b/doc/classes/EditorScript.xml
@@ -48,13 +48,13 @@
[b]Warning:[/b] The implementation of this method is currently disabled.
</description>
</method>
- <method name="get_editor_interface">
+ <method name="get_editor_interface" qualifiers="const">
<return type="EditorInterface" />
<description>
Returns the [EditorInterface] singleton instance.
</description>
</method>
- <method name="get_scene">
+ <method name="get_scene" qualifiers="const">
<return type="Node" />
<description>
Returns the Editor's currently active scene.
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 14ef879f95..6a27bff763 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -295,7 +295,7 @@
If [code]true[/code], render the grid on an XZ plane.
</member>
<member name="editors/3d/grid_yz_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on an YZ plane. This can be useful for 3D side-scrolling games.
+ If [code]true[/code], render the grid on a YZ plane. This can be useful for 3D side-scrolling games.
</member>
<member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter="">
If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad.
@@ -416,6 +416,9 @@
<member name="editors/polygon_editor/show_previous_outline" type="bool" setter="" getter="">
If [code]true[/code], displays the polygon's previous shape in the 2D polygon editors with an opaque gray outline. This outline is displayed while dragging a point until the left mouse button is released.
</member>
+ <member name="editors/shader_editor/behavior/files/restore_shaders_on_load" type="bool" setter="" getter="">
+ If [code]true[/code], reopens shader files that were open in the shader editor when the project was last closed.
+ </member>
<member name="editors/tiles_editor/display_grid" type="bool" setter="" getter="">
If [code]true[/code], displays a grid while the TileMap editor is active. See also [member editors/tiles_editor/grid_color].
</member>
@@ -571,12 +574,26 @@
If [code]true[/code], editor main menu is using embedded [MenuBar] instead of system global menu.
Specific to the macOS platform.
</member>
+ <member name="interface/inspector/float_drag_speed" type="float" setter="" getter="">
+ Base speed for increasing/decreasing float values by dragging them in the inspector.
+ </member>
<member name="interface/inspector/max_array_dictionary_items_per_page" type="int" setter="" getter="">
The number of [Array] or [Dictionary] items to display on each "page" in the inspector. Higher values allow viewing more values per page, but take more time to load. This increased load time is noticeable when selecting nodes that have array or dictionary properties in the editor.
</member>
<member name="interface/inspector/show_low_level_opentype_features" type="bool" setter="" getter="">
If [code]true[/code], display OpenType features marked as [code]hidden[/code] by the font file in the [Font] editor.
</member>
+ <member name="interface/multi_window/enable" type="bool" setter="" getter="">
+ If [code]true[/code], the multi window support in editor is enabled. The following panels can become dedicated windows (made floating): Docks, Script editor, and Shader editor.
+ [b]Note:[/b] When [member interface/editor/single_window_mode] is [code]true[/code], the multi window support is always disabled.
+ </member>
+ <member name="interface/multi_window/maximize_window" type="bool" setter="" getter="">
+ If [code]true[/code], when panels are made floating they will be maximized.
+ If [code]false[/code], when panels are made floating their position and size will match the ones when they are attached (excluding window border) to the editor window.
+ </member>
+ <member name="interface/multi_window/restore_windows_on_load" type="bool" setter="" getter="">
+ If [code]true[/code], the floating panel position, size, and screen will be saved on editor exit. On next launch the panels that were floating will be made floating in the saved positions, sizes and screens, if possible.
+ </member>
<member name="interface/scene_tabs/display_close_button" type="int" setter="" getter="">
Controls when the Close (X) button is displayed on scene tabs at the top of the editor.
</member>
@@ -641,6 +658,10 @@
If [code]true[/code], increases the scrollbar touch area to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
</member>
+ <member name="interface/touchscreen/scale_gizmo_handles" type="float" setter="" getter="">
+ Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices.
+ [b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices.
+ </member>
<member name="network/debug/remote_host" type="String" setter="" getter="">
The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]).
</member>
@@ -701,7 +722,7 @@
If [code]true[/code], colors the background of the line the caret is currently on with [member text_editor/theme/highlighting/current_line_color].
</member>
<member name="text_editor/appearance/caret/type" type="int" setter="" getter="">
- The shape of the caret to use in the script editor. [b]Line[/b] displays a vertical line to the left of the current character, whereas [b]Block[/b] displays a outline over the current character.
+ The shape of the caret to use in the script editor. [b]Line[/b] displays a vertical line to the left of the current character, whereas [b]Block[/b] displays an outline over the current character.
</member>
<member name="text_editor/appearance/guidelines/line_length_guideline_hard_column" type="int" setter="" getter="">
The column at which to display a subtle line as a line length guideline for scripts. This should generally be greater than [member text_editor/appearance/guidelines/line_length_guideline_soft_column].
diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml
index d5dc6abe81..031d2a50c3 100644
--- a/doc/classes/EditorVCSInterface.xml
+++ b/doc/classes/EditorVCSInterface.xml
@@ -13,21 +13,21 @@
<return type="bool" />
<param index="0" name="branch_name" type="String" />
<description>
- Checks out a [code]branch_name[/code] in the VCS.
+ Checks out a [param branch_name] in the VCS.
</description>
</method>
<method name="_commit" qualifiers="virtual">
<return type="void" />
<param index="0" name="msg" type="String" />
<description>
- Commits the currently staged changes and applies the commit [code]msg[/code] to the resulting commit.
+ Commits the currently staged changes and applies the commit [param msg] to the resulting commit.
</description>
</method>
<method name="_create_branch" qualifiers="virtual">
<return type="void" />
<param index="0" name="branch_name" type="String" />
<description>
- Creates a new branch named [code]branch_name[/code] in the VCS.
+ Creates a new branch named [param branch_name] in the VCS.
</description>
</method>
<method name="_create_remote" qualifiers="virtual">
@@ -35,21 +35,21 @@
<param index="0" name="remote_name" type="String" />
<param index="1" name="remote_url" type="String" />
<description>
- Creates a new remote destination with name [code]remote_name[/code] and points it to [code]remote_url[/code]. This can be an HTTPS remote or an SSH remote.
+ Creates a new remote destination with name [param remote_name] and points it to [param remote_url]. This can be an HTTPS remote or an SSH remote.
</description>
</method>
<method name="_discard_file" qualifiers="virtual">
<return type="void" />
<param index="0" name="file_path" type="String" />
<description>
- Discards the changes made in a file present at [code]file_path[/code].
+ Discards the changes made in a file present at [param file_path].
</description>
</method>
<method name="_fetch" qualifiers="virtual">
<return type="void" />
<param index="0" name="remote" type="String" />
<description>
- Fetches new changes from the remote, but doesn't write changes to the current working directory. Equivalent to [code]git fetch[/code].
+ Fetches new changes from the [param remote], but doesn't write changes to the current working directory. Equivalent to [code]git fetch[/code].
</description>
</method>
<method name="_get_branch_list" qualifiers="virtual">
@@ -69,7 +69,7 @@
<param index="0" name="identifier" type="String" />
<param index="1" name="area" type="int" />
<description>
- Returns an array of [Dictionary] items (see [method create_diff_file], [method create_diff_hunk], [method create_diff_line], [method add_line_diffs_into_diff_hunk] and [method add_diff_hunks_into_diff_file]), each containing information about a diff. If [code]identifier[/code] is a file path, returns a file diff, and if it is a commit identifier, then returns a commit diff.
+ Returns an array of [Dictionary] items (see [method create_diff_file], [method create_diff_hunk], [method create_diff_line], [method add_line_diffs_into_diff_hunk] and [method add_diff_hunks_into_diff_file]), each containing information about a diff. If [param identifier] is a file path, returns a file diff, and if it is a commit identifier, then returns a commit diff.
</description>
</method>
<method name="_get_line_diff" qualifiers="virtual">
@@ -77,7 +77,7 @@
<param index="0" name="file_path" type="String" />
<param index="1" name="text" type="String" />
<description>
- Returns an [Array] of [Dictionary] items (see [method create_diff_hunk]), each containing a line diff between a file at [code]file_path[/code] and the [code]text[/code] which is passed in.
+ Returns an [Array] of [Dictionary] items (see [method create_diff_hunk]), each containing a line diff between a file at [param file_path] and the [param text] which is passed in.
</description>
</method>
<method name="_get_modified_files_data" qualifiers="virtual">
@@ -109,7 +109,7 @@
<return type="bool" />
<param index="0" name="project_path" type="String" />
<description>
- Initializes the VCS plugin when called from the editor. Returns whether or not the plugin was successfully initialized. A VCS project is initialized at [code]project_path[/code].
+ Initializes the VCS plugin when called from the editor. Returns whether or not the plugin was successfully initialized. A VCS project is initialized at [param project_path].
</description>
</method>
<method name="_pull" qualifiers="virtual">
@@ -149,7 +149,7 @@
<param index="3" name="ssh_private_key_path" type="String" />
<param index="4" name="ssh_passphrase" type="String" />
<description>
- Set user credentials in the underlying VCS. [code]username[/code] and [code]password[/code] are used only during HTTPS authentication unless not already mentioned in the remote URL. [code]ssh_public_key_path[/code], [code]ssh_private_key_path[/code], and [code]ssh_passphrase[/code] are only used during SSH authentication.
+ Set user credentials in the underlying VCS. [param username] and [param password] are used only during HTTPS authentication unless not already mentioned in the remote URL. [param ssh_public_key_path], [param ssh_private_key_path], and [param ssh_passphrase] are only used during SSH authentication.
</description>
</method>
<method name="_shut_down" qualifiers="virtual">
@@ -162,14 +162,14 @@
<return type="void" />
<param index="0" name="file_path" type="String" />
<description>
- Stages the file present at [code]file_path[/code] to the staged area.
+ Stages the file present at [param file_path] to the staged area.
</description>
</method>
<method name="_unstage_file" qualifiers="virtual">
<return type="void" />
<param index="0" name="file_path" type="String" />
<description>
- Unstages the file present at [code]file_path[/code] from the staged area to the unstaged area.
+ Unstages the file present at [param file_path] from the staged area to the unstaged area.
</description>
</method>
<method name="add_diff_hunks_into_diff_file">
@@ -177,7 +177,7 @@
<param index="0" name="diff_file" type="Dictionary" />
<param index="1" name="diff_hunks" type="Dictionary[]" />
<description>
- Helper function to add an array of [code]diff_hunks[/code] into a [code]diff_file[/code].
+ Helper function to add an array of [param diff_hunks] into a [param diff_file].
</description>
</method>
<method name="add_line_diffs_into_diff_hunk">
@@ -185,7 +185,7 @@
<param index="0" name="diff_hunk" type="Dictionary" />
<param index="1" name="line_diffs" type="Dictionary[]" />
<description>
- Helper function to add an array of [code]line_diffs[/code] into a [code]diff_hunk[/code].
+ Helper function to add an array of [param line_diffs] into a [param diff_hunk].
</description>
</method>
<method name="create_commit">
@@ -196,7 +196,7 @@
<param index="3" name="unix_timestamp" type="int" />
<param index="4" name="offset_minutes" type="int" />
<description>
- Helper function to create a commit [Dictionary] item. [code]msg[/code] is the commit message of the commit. [code]author[/code] is a single human-readable string containing all the author's details, e.g. the email and name configured in the VCS. [code]id[/code] is the identifier of the commit, in whichever format your VCS may provide an identifier to commits. [code]unix_timestamp[/code] is the UTC Unix timestamp of when the commit was created. [code]offset_minutes[/code] is the timezone offset in minutes, recorded from the system timezone where the commit was created.
+ Helper function to create a commit [Dictionary] item. [param msg] is the commit message of the commit. [param author] is a single human-readable string containing all the author's details, e.g. the email and name configured in the VCS. [param id] is the identifier of the commit, in whichever format your VCS may provide an identifier to commits. [param unix_timestamp] is the UTC Unix timestamp of when the commit was created. [param offset_minutes] is the timezone offset in minutes, recorded from the system timezone where the commit was created.
</description>
</method>
<method name="create_diff_file">
@@ -204,7 +204,7 @@
<param index="0" name="new_file" type="String" />
<param index="1" name="old_file" type="String" />
<description>
- Helper function to create a [code]Dictionary[/code] for storing old and new diff file paths.
+ Helper function to create a [Dictionary] for storing old and new diff file paths.
</description>
</method>
<method name="create_diff_hunk">
@@ -214,7 +214,7 @@
<param index="2" name="old_lines" type="int" />
<param index="3" name="new_lines" type="int" />
<description>
- Helper function to create a [code]Dictionary[/code] for storing diff hunk data. [code]old_start[/code] is the starting line number in old file. [code]new_start[/code] is the starting line number in new file. [code]old_lines[/code] is the number of lines in the old file. [code]new_lines[/code] is the number of lines in the new file.
+ Helper function to create a [Dictionary] for storing diff hunk data. [param old_start] is the starting line number in old file. [param new_start] is the starting line number in new file. [param old_lines] is the number of lines in the old file. [param new_lines] is the number of lines in the new file.
</description>
</method>
<method name="create_diff_line">
@@ -224,7 +224,7 @@
<param index="2" name="content" type="String" />
<param index="3" name="status" type="String" />
<description>
- Helper function to create a [code]Dictionary[/code] for storing a line diff. [code]new_line_no[/code] is the line number in the new file (can be [code]-1[/code] if the line is deleted). [code]old_line_no[/code] is the line number in the old file (can be [code]-1[/code] if the line is added). [code]content[/code] is the diff text. [code]status[/code] is a single character string which stores the line origin.
+ Helper function to create a [Dictionary] for storing a line diff. [param new_line_no] is the line number in the new file (can be [code]-1[/code] if the line is deleted). [param old_line_no] is the line number in the old file (can be [code]-1[/code] if the line is added). [param content] is the diff text. [param status] is a single character string which stores the line origin.
</description>
</method>
<method name="create_status_file">
@@ -233,7 +233,7 @@
<param index="1" name="change_type" type="int" enum="EditorVCSInterface.ChangeType" />
<param index="2" name="area" type="int" enum="EditorVCSInterface.TreeArea" />
<description>
- Helper function to create a [code]Dictionary[/code] used by editor to read the status of a file.
+ Helper function to create a [Dictionary] used by editor to read the status of a file.
</description>
</method>
<method name="popup_error">
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index 9ea59010cc..aa15f88afa 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -306,7 +306,7 @@
[b]Note:[/b] This property does not impact the editor's Errors tab when running a project from the editor.
</member>
<member name="time_scale" type="float" setter="set_time_scale" getter="get_time_scale" default="1.0">
- Controls how fast or slow the in-game clock ticks versus the real life one. It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life, whilst a value of 0.5 means the game moves at half the regular speed.
+ Controls how fast or slow the in-game clock ticks versus the real life one. It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life, whilst a value of 0.5 means the game moves at half the regular speed. This also affects [Timer] and [SceneTreeTimer] (see [method SceneTree.create_timer] for how to control this).
</member>
</members>
</class>
diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml
index f8ca5326a0..45317be394 100644
--- a/doc/classes/EngineDebugger.xml
+++ b/doc/classes/EngineDebugger.xml
@@ -50,7 +50,7 @@
<param index="1" name="enable" type="bool" />
<param index="2" name="arguments" type="Array" default="[]" />
<description>
- Calls the [code]toggle[/code] callable of the profiler with given [param name] and [param arguments]. Enables/Disables the same profiler depending on [code]enable[/code] argument.
+ Calls the [code]toggle[/code] callable of the profiler with given [param name] and [param arguments]. Enables/Disables the same profiler depending on [param enable] argument.
</description>
</method>
<method name="register_message_capture">
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
index 0796e837b2..ff04440a04 100644
--- a/doc/classes/Expression.xml
+++ b/doc/classes/Expression.xml
@@ -49,6 +49,7 @@
[/codeblocks]
</description>
<tutorials>
+ <link title="Evaluating Expressions">$DOCS_URL/tutorials/scripting/evaluating_expressions.html</link>
</tutorials>
<methods>
<method name="execute">
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 8dd73c79e5..11dbb00f10 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -94,6 +94,12 @@
[b]Note:[/b] Only call [method flush] when you actually need it. Otherwise, it will decrease performance due to constant disk writes.
</description>
</method>
+ <method name="get_8" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the next 8 bits from the file as an integer. See [method store_8] for details on what values can be stored and retrieved this way.
+ </description>
+ </method>
<method name="get_16" qualifiers="const">
<return type="int" />
<description>
@@ -112,12 +118,6 @@
Returns the next 64 bits from the file as an integer. See [method store_64] for details on what values can be stored and retrieved this way.
</description>
</method>
- <method name="get_8" qualifiers="const">
- <return type="int" />
- <description>
- Returns the next 8 bits from the file as an integer. See [method store_8] for details on what values can be stored and retrieved this way.
- </description>
- </method>
<method name="get_as_text" qualifiers="const">
<return type="String" />
<param index="0" name="skip_cr" type="bool" default="false" />
@@ -204,7 +204,7 @@
<return type="int" />
<param index="0" name="file" type="String" />
<description>
- Returns the last time the [param file] was modified in Unix timestamp format or returns a [String] "ERROR IN [code]file[/code]". This Unix timestamp can be converted to another format using the [Time] singleton.
+ Returns the last time the [param file] was modified in Unix timestamp format or returns a [String] "ERROR IN [param file]". This Unix timestamp can be converted to another format using the [Time] singleton.
</description>
</method>
<method name="get_open_error" qualifiers="static">
@@ -322,6 +322,15 @@
[b]Note:[/b] This is an offset, so you should use negative numbers or the cursor will be at the end of the file.
</description>
</method>
+ <method name="store_8">
+ <return type="void" />
+ <param index="0" name="value" type="int" />
+ <description>
+ Stores an integer as 8 bits in the file.
+ [b]Note:[/b] The [param value] should lie in the interval [code][0, 255][/code]. Any other value will overflow and wrap around.
+ To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example).
+ </description>
+ </method>
<method name="store_16">
<return type="void" />
<param index="0" name="value" type="int" />
@@ -380,15 +389,6 @@
[b]Note:[/b] The [param value] must lie in the interval [code][-2^63, 2^63 - 1][/code] (i.e. be a valid [int] value).
</description>
</method>
- <method name="store_8">
- <return type="void" />
- <param index="0" name="value" type="int" />
- <description>
- Stores an integer as 8 bits in the file.
- [b]Note:[/b] The [param value] should lie in the interval [code][0, 255][/code]. Any other value will overflow and wrap around.
- To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example).
- </description>
- </method>
<method name="store_buffer">
<return type="void" />
<param index="0" name="buffer" type="PackedByteArray" />
diff --git a/doc/classes/FileSystemDock.xml b/doc/classes/FileSystemDock.xml
index f8f6f26bfc..82a216d713 100644
--- a/doc/classes/FileSystemDock.xml
+++ b/doc/classes/FileSystemDock.xml
@@ -10,6 +10,13 @@
<tutorials>
</tutorials>
<methods>
+ <method name="add_resource_tooltip_plugin">
+ <return type="void" />
+ <param index="0" name="plugin" type="EditorResourceTooltipPlugin" />
+ <description>
+ Registers a new [EditorResourceTooltipPlugin].
+ </description>
+ </method>
<method name="navigate_to_path">
<return type="void" />
<param index="0" name="path" type="String" />
@@ -17,6 +24,13 @@
Sets the given [param path] as currently selected, ensuring that the selected file/directory is visible.
</description>
</method>
+ <method name="remove_resource_tooltip_plugin">
+ <return type="void" />
+ <param index="0" name="plugin" type="EditorResourceTooltipPlugin" />
+ <description>
+ Removes an [EditorResourceTooltipPlugin]. Fails if the plugin wasn't previously added.
+ </description>
+ </method>
</methods>
<signals>
<signal name="display_mode_changed">
diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml
index ed9906186c..4993d0dc42 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -12,7 +12,7 @@
- Bitmap font importer: AngelCode BMFont (.fnt, .font), text and binary (version 3) format variants.
- Monospace image font importer: All supported image formats.
[b]Note:[/b] A character is a symbol that represents an item (letter, digit etc.) in an abstract way.
- [b]Note:[/b] A glyph is a bitmap or shape used to draw a one or more characters in a context-dependent manner. Glyph indices are bound to the specific font data source.
+ [b]Note:[/b] A glyph is a bitmap or shape used to draw one or more characters in a context-dependent manner. Glyph indices are bound to the specific font data source.
[b]Note:[/b] If a none of the font data sources contain glyphs for a character used in a string, the character in question will be replaced with a box displaying its hexadecimal code.
[codeblocks]
[gdscript]
diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml
index 99ed5e7062..4c55aa225c 100644
--- a/doc/classes/GPUParticlesCollisionHeightField3D.xml
+++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml
@@ -50,7 +50,7 @@
Represents the size of the [enum Resolution] enum.
</constant>
<constant name="UPDATE_MODE_WHEN_MOVED" value="0" enum="UpdateMode">
- Only update the heightmap when the [GPUParticlesCollisionHeightField3D] node is moved, or when the camera moves if [member follow_camera_enabled] is [code]true[/code]. An update can be forced by slightly moving the [GPUParticlesCollisionHeightField3D] in any direction.
+ Only update the heightmap when the [GPUParticlesCollisionHeightField3D] node is moved, or when the camera moves if [member follow_camera_enabled] is [code]true[/code]. An update can be forced by slightly moving the [GPUParticlesCollisionHeightField3D] in any direction, or by calling [method RenderingServer.particles_collision_height_field_update].
</constant>
<constant name="UPDATE_MODE_ALWAYS" value="1" enum="UpdateMode">
Update the heightmap every frame. This has a significant performance cost. This update should only be used when geometry that particles can collide with changes significantly during gameplay.
diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml
index 22df6f7a6e..b2fb4251bb 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -88,7 +88,7 @@
<return type="PackedVector3Array" />
<param index="0" name="from" type="Vector3" />
<param index="1" name="to" type="Vector3" />
- <param index="2" name="planes" type="Array" />
+ <param index="2" name="planes" type="Plane[]" />
<description>
Given a convex hull defined though the [Plane]s in the array [param planes], tests if the segment ([param from], [param to]) intersects with that hull. If an intersection is found, returns a [PackedVector3Array] containing the point the intersection and the hull's normal. Otherwise, returns an empty array.
</description>
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index 14945922c6..edf2f9e18a 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -33,7 +33,7 @@
The selected shadow casting flag. See [enum ShadowCastingSetting] for possible values.
</member>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
- Overrides the bounding box of this node with a custom one. This can be used to avoid the expensive [AABB] recalculation that happens when a skeleton is used with a [MeshInstance3D] or to have fine control over the [MeshInstance3D]'s bounding box. To remove this, set value to an [AABB] with all fields set to zero.
+ Overrides the bounding box of this node with a custom one. This can be used to avoid the expensive [AABB] recalculation that happens when a skeleton is used with a [MeshInstance3D] or to have fine control over the [MeshInstance3D]'s bounding box. To use the default AABB, set value to an [AABB] with all fields set to [code]0.0[/code]. To avoid frustum culling, set [member custom_aabb] to a very large AABB that covers your entire game world such as [code]AABB(-10000, -10000, -10000, 20000, 20000, 20000)[/code]. To disable all forms of culling (including occlusion culling), call [method RenderingServer.instance_set_ignore_culling] on the [GeometryInstance3D]'s [RID].
</member>
<member name="extra_cull_margin" type="float" setter="set_extra_cull_margin" getter="get_extra_cull_margin" default="0.0">
The extra distance added to the GeometryInstance3D's bounding box ([AABB]) to increase its cull box.
@@ -47,6 +47,7 @@
</member>
<member name="ignore_occlusion_culling" type="bool" setter="set_ignore_occlusion_culling" getter="is_ignoring_occlusion_culling" default="false">
If [code]true[/code], disables occlusion culling for this instance. Useful for gizmos that must be rendered even when occlusion culling is in use.
+ [b]Note:[/b] [member ignore_occlusion_culling] does not affect frustum culling (which is what happens when an object is not visible given the camera's angle). To avoid frustum culling, set [member custom_aabb] to a very large AABB that covers your entire game world such as [code]AABB(-10000, -10000, -10000, 20000, 20000, 20000)[/code].
</member>
<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0">
Changes how quickly the mesh transitions to a lower level of detail. A value of 0 will force the mesh to its lowest level of detail, a value of 1 will use the default settings, and larger values will keep the mesh in a higher level of detail at farther distances.
@@ -70,14 +71,14 @@
</member>
<member name="visibility_range_begin_margin" type="float" setter="set_visibility_range_begin_margin" getter="get_visibility_range_begin_margin" default="0.0">
Margin for the [member visibility_range_begin] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_begin] threshold by this amount.
- If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_DISABLED], this acts as an hysteresis distance. If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_SELF] or [constant VISIBILITY_RANGE_FADE_DEPENDENCIES], this acts as a fade transition distance and must be set to a value greater than [code]0.0[/code] for the effect to be noticeable.
+ If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_DISABLED], this acts as a hysteresis distance. If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_SELF] or [constant VISIBILITY_RANGE_FADE_DEPENDENCIES], this acts as a fade transition distance and must be set to a value greater than [code]0.0[/code] for the effect to be noticeable.
</member>
<member name="visibility_range_end" type="float" setter="set_visibility_range_end" getter="get_visibility_range_end" default="0.0">
Distance from which the GeometryInstance3D will be hidden, taking [member visibility_range_end_margin] into account as well. The default value of 0 is used to disable the range check.
</member>
<member name="visibility_range_end_margin" type="float" setter="set_visibility_range_end_margin" getter="get_visibility_range_end_margin" default="0.0">
Margin for the [member visibility_range_end] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_end] threshold by this amount.
- If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_DISABLED], this acts as an hysteresis distance. If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_SELF] or [constant VISIBILITY_RANGE_FADE_DEPENDENCIES], this acts as a fade transition distance and must be set to a value greater than [code]0.0[/code] for the effect to be noticeable.
+ If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_DISABLED], this acts as a hysteresis distance. If [member visibility_range_fade_mode] is [constant VISIBILITY_RANGE_FADE_SELF] or [constant VISIBILITY_RANGE_FADE_DEPENDENCIES], this acts as a fade transition distance and must be set to a value greater than [code]0.0[/code] for the effect to be noticeable.
</member>
<member name="visibility_range_fade_mode" type="int" setter="set_visibility_range_fade_mode" getter="get_visibility_range_fade_mode" enum="GeometryInstance3D.VisibilityRangeFadeMode" default="0">
Controls which instances will be faded when approaching the limits of the visibility range. See [enum VisibilityRangeFadeMode] for possible values.
diff --git a/doc/classes/HSlider.xml b/doc/classes/HSlider.xml
index 5e11c1c3ab..2ab66a2e75 100644
--- a/doc/classes/HSlider.xml
+++ b/doc/classes/HSlider.xml
@@ -10,6 +10,9 @@
<tutorials>
</tutorials>
<theme_items>
+ <theme_item name="center_grabber" data_type="constant" type="int" default="0">
+ Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position.
+ </theme_item>
<theme_item name="grabber_offset" data_type="constant" type="int" default="0">
Vertical offset of the grabber.
</theme_item>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index de7e1521f7..130f848a73 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -382,17 +382,17 @@
Converts a standard RGBE (Red Green Blue Exponent) image to an sRGB image.
</description>
</method>
- <method name="rotate_180">
+ <method name="rotate_90">
<return type="void" />
+ <param index="0" name="direction" type="int" enum="ClockDirection" />
<description>
- Rotates the image by [code]180[/code] degrees. The width and height of the image must be greater than [code]1[/code].
+ Rotates the image in the specified [param direction] by [code]90[/code] degrees. The width and height of the image must be greater than [code]1[/code]. If the width and height are not equal, the image will be resized.
</description>
</method>
- <method name="rotate_90">
+ <method name="rotate_180">
<return type="void" />
- <param index="0" name="direction" type="int" enum="ClockDirection" />
<description>
- Rotates the image in the specified [param direction] by [code]90[/code] degrees. The width and height of the image must be greater than [code]1[/code]. If the width and height are not equal, the image will be resized.
+ Rotates the image by [code]180[/code] degrees. The width and height of the image must be greater than [code]1[/code].
</description>
</method>
<method name="save_exr" qualifiers="const">
diff --git a/doc/classes/ImageTexture3D.xml b/doc/classes/ImageTexture3D.xml
index ebfd38c3f4..4396039ab2 100644
--- a/doc/classes/ImageTexture3D.xml
+++ b/doc/classes/ImageTexture3D.xml
@@ -26,7 +26,7 @@
<return type="void" />
<param index="0" name="data" type="Image[]" />
<description>
- Replaces the texture's existing data with the layers specified in [code]data[/code]. The size of [code]data[/code] must match the parameters that were used for [method create]. In other words, the texture cannot be resized or have its format changed by calling [method update].
+ Replaces the texture's existing data with the layers specified in [param data]. The size of [param data] must match the parameters that were used for [method create]. In other words, the texture cannot be resized or have its format changed by calling [method update].
</description>
</method>
</methods>
diff --git a/doc/classes/ImageTextureLayered.xml b/doc/classes/ImageTextureLayered.xml
index 7f17c8e897..215ff6ac5d 100644
--- a/doc/classes/ImageTextureLayered.xml
+++ b/doc/classes/ImageTextureLayered.xml
@@ -22,7 +22,7 @@
<param index="0" name="image" type="Image" />
<param index="1" name="layer" type="int" />
<description>
- Replaces the existing [Image] data at the given [code]layer[/code] with this new image.
+ Replaces the existing [Image] data at the given [param layer] with this new image.
The given [Image] must have the same width, height, image format and mipmapping setting (a [code]bool[/code] value) as the rest of the referenced images.
If the image format is unsupported, it will be decompressed and converted to a similar and supported [enum Image.Format].
The update is immediate: it's synchronized with drawing.
diff --git a/doc/classes/ImmediateMesh.xml b/doc/classes/ImmediateMesh.xml
index 504076e3fe..cd6cb32d3e 100644
--- a/doc/classes/ImmediateMesh.xml
+++ b/doc/classes/ImmediateMesh.xml
@@ -4,9 +4,20 @@
Mesh optimized for creating geometry manually.
</brief_description>
<description>
- Mesh optimized for creating geometry manually, similar to OpenGL1.x immediate mode.
+ A mesh type optimized for creating geometry manually, similar to OpenGL 1.x immediate mode.
+ Here's a sample on how to generate a triangular face:
+ [codeblocks]
+ var mesh = ImmediateMesh.new()
+ mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES)
+ mesh.surface_add_vertex(Vector3.LEFT)
+ mesh.surface_add_vertex(Vector3.FORWARD)
+ mesh.surface_add_vertex(Vector3.ZERO)
+ mesh.surface_end()
+ [/codeblocks]
+ [b]Note:[/b] Generating complex geometries with [ImmediateMesh] is highly inefficient. Instead, it is designed to generate simple geometry that changes often.
</description>
<tutorials>
+ <link title="Using ImmediateMesh">$DOCS_URL/tutorials/3d/procedural_geometry/immediatemesh.html</link>
</tutorials>
<methods>
<method name="clear_surfaces">
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index ecc9c3d89a..5250b7a323 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
A singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the [b]Input Map[/b] tab in the [b]Project &gt; Project Settings[/b], or with the [InputMap] class.
+ [b]Note:[/b] The methods here reflect the global input state and are not affected by [method Control.accept_event] or [method Viewport.set_input_as_handled], which only deal with the way input is propagated in the [SceneTree].
</description>
<tutorials>
<link title="Inputs documentation index">$DOCS_URL/tutorials/inputs/index.html</link>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 2da5ad3cac..4aca92776f 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -64,6 +64,7 @@
<description>
Returns the item index at the given [param position].
When there is no item at that point, -1 will be returned if [param exact] is [code]true[/code], and the closest item index will be returned otherwise.
+ [b]Note:[/b] The returned value is unreliable if called right after modifying the [ItemList], before it redraws in the next frame.
</description>
</method>
<method name="get_item_custom_bg_color" qualifiers="const">
@@ -115,6 +116,15 @@
Returns the metadata value of the specified index.
</description>
</method>
+ <method name="get_item_rect" qualifiers="const">
+ <return type="Rect2" />
+ <param index="0" name="idx" type="int" />
+ <param index="1" name="expand" type="bool" default="true" />
+ <description>
+ Returns the position and size of the item with the specified index, in the coordinate system of the [ItemList] node. If [param expand] is [code]true[/code] the last column expands to fill the rest of the row.
+ [b]Note:[/b] The returned value is unreliable if called right after modifying the [ItemList], before it redraws in the next frame.
+ </description>
+ </method>
<method name="get_item_text" qualifiers="const">
<return type="String" />
<param index="0" name="idx" type="int" />
diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index 59549cecb8..26b97edc25 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -204,7 +204,7 @@
Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off.
</constant>
<constant name="BAKE_DYNAMIC" value="2" enum="BakeMode">
- Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC].
+ Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC]. When using SDFGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights].
</constant>
</constants>
</class>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index f63243e8d4..d040c51bb0 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -203,7 +203,7 @@
[b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components.
</member>
<member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled" default="false">
- If [code]true[/code], the [LineEdit] will show a clear button if [code]text[/code] is not empty, which can be used to clear the text quickly.
+ If [code]true[/code], the [LineEdit] will show a clear button if [member text] is not empty, which can be used to clear the text quickly.
</member>
<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled" default="true">
If [code]true[/code], the context menu will appear when right-clicked.
@@ -302,7 +302,7 @@
<signal name="text_change_rejected">
<param index="0" name="rejected_substring" type="String" />
<description>
- Emitted when appending text that overflows the [member max_length]. The appended text is truncated to fit [member max_length], and the part that couldn't fit is passed as the [code]rejected_substring[/code] argument.
+ Emitted when appending text that overflows the [member max_length]. The appended text is truncated to fit [member max_length], and the part that couldn't fit is passed as the [param rejected_substring] argument.
</description>
</signal>
<signal name="text_changed">
@@ -487,7 +487,7 @@
Texture for the clear button. See [member clear_button_enabled].
</theme_item>
<theme_item name="focus" data_type="style" type="StyleBox">
- Background used when [LineEdit] has GUI focus. The [code]focus[/code] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
+ Background used when [LineEdit] has GUI focus. The [theme_item focus] [StyleBox] is displayed [i]over[/i] the base [StyleBox], so a partially transparent [StyleBox] should be used to ensure the base [StyleBox] remains visible. A [StyleBox] that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign a [StyleBoxEmpty] resource. Note that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
</theme_item>
<theme_item name="normal" data_type="style" type="StyleBox">
Default background for the [LineEdit].
diff --git a/doc/classes/Marker2D.xml b/doc/classes/Marker2D.xml
index e4498663fb..16342669c8 100644
--- a/doc/classes/Marker2D.xml
+++ b/doc/classes/Marker2D.xml
@@ -4,7 +4,7 @@
Generic 2D position hint for editing.
</brief_description>
<description>
- Generic 2D position hint for editing. It's just like a plain [Node2D], but it displays as a cross in the 2D editor at all times. You can set cross' visual size by using the gizmo in the 2D editor while the node is selected.
+ Generic 2D position hint for editing. It's just like a plain [Node2D], but it displays as a cross in the 2D editor at all times. You can set the cross' visual size by using the gizmo in the 2D editor while the node is selected.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml
index 1362949a44..3b208f1fb7 100644
--- a/doc/classes/MeshDataTool.xml
+++ b/doc/classes/MeshDataTool.xml
@@ -19,7 +19,7 @@
vertex += mdt.get_vertex_normal(i)
# Save your change.
mdt.set_vertex(i, vertex)
- mesh.surface_remove(0)
+ mesh.clear_surfaces()
mdt.commit_to_surface(mesh)
var mi = MeshInstance.new()
mi.mesh = mesh
@@ -38,7 +38,7 @@
// Save your change.
mdt.SetVertex(i, vertex);
}
- mesh.SurfaceRemove(0);
+ mesh.ClearSurfaces();
mdt.CommitToSurface(mesh);
var mi = new MeshInstance();
mi.Mesh = mesh;
diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml
index 78202229ed..ab1fad9624 100644
--- a/doc/classes/Mutex.xml
+++ b/doc/classes/Mutex.xml
@@ -5,6 +5,11 @@
</brief_description>
<description>
A synchronization mutex (mutual exclusion). This is used to synchronize multiple [Thread]s, and is equivalent to a binary [Semaphore]. It guarantees that only one thread can ever acquire the lock at a time. A mutex can be used to protect a critical section; however, be careful to avoid deadlocks.
+ It's of the recursive kind, so it can be locked multiple times by one thread, provided it also unlocks it as many times.
+ [b]Warning:[/b]
+ To guarantee that the operating system is able to perform proper cleanup (no crashes, no deadlocks), these conditions must be met:
+ - By the time a [Mutex]'s reference count reaches zero and therefore it is destroyed, no threads (including the one on which the destruction will happen) must have it locked.
+ - By the time a [Thread]'s reference count reaches zero and therefore it is destroyed, it must not have any mutex locked.
</description>
<tutorials>
<link title="Using multiple threads">$DOCS_URL/tutorials/performance/using_multiple_threads.html</link>
@@ -29,6 +34,7 @@
<description>
Unlocks this [Mutex], leaving it to other threads.
[b]Note:[/b] If a thread called [method lock] or [method try_lock] multiple times while already having ownership of the mutex, it must also call [method unlock] the same number of times in order to unlock it correctly.
+ [b]Warning:[/b] Calling [method unlock] more times that [method lock] on a given thread, thus ending up trying to unlock a non-locked mutex, is wrong and may causes crashes or deadlocks.
</description>
</method>
</methods>
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 1c68058d2f..e401450e42 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
2D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent2D] is physics safe.
- [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
+ [b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
</description>
<tutorials>
<link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link>
@@ -17,6 +17,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
</description>
</method>
+ <method name="get_avoidance_layer_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
+ <method name="get_avoidance_mask_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="mask_number" type="int" />
+ <description>
+ Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
+ </description>
+ </method>
<method name="get_current_navigation_path" qualifiers="const">
<return type="PackedVector2Array" />
<description>
@@ -38,7 +52,7 @@
<method name="get_final_position">
<return type="Vector2" />
<description>
- Returns the reachable final position 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 can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
</description>
</method>
<method name="get_navigation_layer_value" qualifiers="const">
@@ -75,7 +89,7 @@
<method name="is_target_reachable">
<return type="bool" />
<description>
- Returns true if [member target_position] is reachable.
+ Returns true if [member target_position] is reachable. The target position is set using [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@@ -84,6 +98,22 @@
Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position].
</description>
</method>
+ <method name="set_avoidance_layer_value">
+ <return type="void" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
+ <method name="set_avoidance_mask_value">
+ <return type="void" />
+ <param index="0" name="mask_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
+ </description>
+ </method>
<method name="set_navigation_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
@@ -99,17 +129,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
</description>
</method>
- <method name="set_velocity">
+ <method name="set_velocity_forced">
<return type="void" />
<param index="0" name="velocity" type="Vector2" />
<description>
- Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
</methods>
<members>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
- If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [member velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ </member>
+ <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
+ A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
+ </member>
+ <member name="avoidance_mask" type="int" setter="set_avoidance_mask" getter="get_avoidance_mask" default="1">
+ A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
+ </member>
+ <member name="avoidance_priority" type="float" setter="set_avoidance_priority" getter="get_avoidance_priority" default="1.0">
+ The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
@@ -161,10 +200,16 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
</member>
<member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2(0, 0)">
- The user-defined target position. Setting this property will clear the current navigation path.
+ If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
+ </member>
+ <member name="time_horizon_agents" type="float" setter="set_time_horizon_agents" getter="get_time_horizon_agents" default="1.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+ </member>
+ <member name="time_horizon_obstacles" type="float" setter="set_time_horizon_obstacles" getter="get_time_horizon_obstacles" default="0.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</member>
- <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="1.0">
- The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive.
+ <member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
+ Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
</member>
</members>
<signals>
@@ -199,7 +244,7 @@
<signal name="velocity_computed">
<param index="0" name="safe_velocity" type="Vector2" />
<description>
- Notifies when the collision avoidance velocity is calculated. Emitted at the end of the physics frame in which [method set_velocity] is called. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
</description>
</signal>
<signal name="waypoint_reached">
diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index 6fdea5431d..00ef894378 100644
--- a/doc/classes/NavigationAgent3D.xml
+++ b/doc/classes/NavigationAgent3D.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
3D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent3D] is physics safe.
- [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
+ [b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
</description>
<tutorials>
<link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link>
@@ -17,6 +17,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
</description>
</method>
+ <method name="get_avoidance_layer_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
+ <method name="get_avoidance_mask_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="mask_number" type="int" />
+ <description>
+ Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
+ </description>
+ </method>
<method name="get_current_navigation_path" qualifiers="const">
<return type="PackedVector3Array" />
<description>
@@ -38,7 +52,7 @@
<method name="get_final_position">
<return type="Vector3" />
<description>
- Returns the reachable final position 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 navigation path is altered in any way. Because of this, it would be best to check this each frame.
</description>
</method>
<method name="get_navigation_layer_value" qualifiers="const">
@@ -75,7 +89,7 @@
<method name="is_target_reachable">
<return type="bool" />
<description>
- Returns true if [member target_position] is reachable.
+ Returns true if [member target_position] is reachable. The target position is set using [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@@ -84,6 +98,22 @@
Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position].
</description>
</method>
+ <method name="set_avoidance_layer_value">
+ <return type="void" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
+ <method name="set_avoidance_mask_value">
+ <return type="void" />
+ <param index="0" name="mask_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
+ </description>
+ </method>
<method name="set_navigation_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
@@ -99,20 +129,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
</description>
</method>
- <method name="set_velocity">
+ <method name="set_velocity_forced">
<return type="void" />
<param index="0" name="velocity" type="Vector3" />
<description>
- Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
</methods>
<members>
- <member name="agent_height_offset" type="float" setter="set_agent_height_offset" getter="get_agent_height_offset" default="0.0">
- The NavigationAgent height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
- </member>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
- If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [member velocity] is set and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ </member>
+ <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
+ A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
+ </member>
+ <member name="avoidance_mask" type="int" setter="set_avoidance_mask" getter="get_avoidance_mask" default="1">
+ A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
+ </member>
+ <member name="avoidance_priority" type="float" setter="set_avoidance_priority" getter="get_avoidance_priority" default="1.0">
+ The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
@@ -126,8 +162,8 @@
<member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false">
If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color.
</member>
- <member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="true">
- Ignores collisions on the Y axis. Must be true to move on a horizontal plane.
+ <member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
+ The height of the avoidance agent. Agent's will ignore other agents or obstacles that are above or below their current position + height in 2D avoidance. Does nothing in 3D avoidance which uses radius spheres alone.
</member>
<member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
The maximum number of neighbors for the agent to consider.
@@ -144,6 +180,9 @@
<member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="1.0">
The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update.
</member>
+ <member name="path_height_offset" type="float" setter="set_path_height_offset" getter="get_path_height_offset" default="0.0">
+ The height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
+ </member>
<member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="5.0">
The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
</member>
@@ -164,10 +203,20 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
</member>
<member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3(0, 0, 0)">
- The user-defined target position. Setting this property will clear the current navigation path.
+ If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
+ </member>
+ <member name="time_horizon_agents" type="float" setter="set_time_horizon_agents" getter="get_time_horizon_agents" default="1.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+ </member>
+ <member name="time_horizon_obstacles" type="float" setter="set_time_horizon_obstacles" getter="get_time_horizon_obstacles" default="0.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+ </member>
+ <member name="use_3d_avoidance" type="bool" setter="set_use_3d_avoidance" getter="get_use_3d_avoidance" default="false">
+ If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
+ If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including [member height] in 2D avoidance.
</member>
- <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="1.0">
- The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive.
+ <member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
+ Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
</member>
</members>
<signals>
@@ -202,7 +251,7 @@
<signal name="velocity_computed">
<param index="0" name="safe_velocity" type="Vector3" />
<description>
- Notifies when the collision avoidance velocity is calculated. Emitted at the end of the physics frame in which [method set_velocity] is called. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
</description>
</signal>
<signal name="waypoint_reached">
diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml
index 39509e10b7..a08f65da3c 100644
--- a/doc/classes/NavigationLink2D.xml
+++ b/doc/classes/NavigationLink2D.xml
@@ -26,7 +26,7 @@
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32.
+ 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="set_global_end_position">
@@ -48,7 +48,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32.
+ 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>
</methods>
@@ -65,7 +65,7 @@
The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius].
</member>
<member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0">
- When pathfinding enters this link from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path.
+ When pathfinding enters this link from another regions navigation mesh the [member enter_cost] value is added to the path distance for determining the shortest path.
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer2D.map_get_path].
@@ -76,7 +76,7 @@
The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius].
</member>
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
- When pathfinding moves along the link the traveled distance is multiplied with [code]travel_cost[/code] for determining the shortest path.
+ When pathfinding moves along the link the traveled distance is multiplied with [member travel_cost] for determining the shortest path.
</member>
</members>
</class>
diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml
index 49fdc774cd..70dfee9c10 100644
--- a/doc/classes/NavigationLink3D.xml
+++ b/doc/classes/NavigationLink3D.xml
@@ -26,7 +26,7 @@
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32.
+ 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="set_global_end_position">
@@ -48,7 +48,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32.
+ 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>
</methods>
@@ -65,7 +65,7 @@
The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius].
</member>
<member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0">
- When pathfinding enters this link from another regions navigation mesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path.
+ When pathfinding enters this link from another regions navigation mesh the [member enter_cost] value is added to the path distance for determining the shortest path.
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer3D.map_get_path].
@@ -76,7 +76,7 @@
The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius].
</member>
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
- When pathfinding moves along the link the traveled distance is multiplied with [code]travel_cost[/code] for determining the shortest path.
+ When pathfinding moves along the link the traveled distance is multiplied with [member travel_cost] for determining the shortest path.
</member>
</members>
</class>
diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml
index 06334e3dc7..e365d1aec5 100644
--- a/doc/classes/NavigationObstacle2D.xml
+++ b/doc/classes/NavigationObstacle2D.xml
@@ -1,42 +1,71 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NavigationObstacle2D" inherits="Node" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="NavigationObstacle2D" inherits="Node2D" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 2D Obstacle used in navigation for collision avoidance.
+ 2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
</brief_description>
<description>
- 2D Obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. [NavigationObstacle2D] is physics safe.
- Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
+ 2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
+ If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
+ Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
+ Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
</description>
<tutorials>
<link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
</tutorials>
<methods>
+ <method name="get_agent_rid" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the [RID] of this agent on the [NavigationServer2D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
+ </description>
+ </method>
+ <method name="get_avoidance_layer_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member avoidance_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 [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer.
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
</description>
</method>
- <method name="get_rid" qualifiers="const">
+ <method name="get_obstacle_rid" qualifiers="const">
<return type="RID" />
<description>
- Returns the [RID] of this obstacle on the [NavigationServer2D].
+ Returns the [RID] of this obstacle on the [NavigationServer2D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
+ </description>
+ </method>
+ <method name="set_avoidance_layer_value">
+ <return type="void" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified layer in the [member avoidance_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 NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer.
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
</description>
</method>
</methods>
<members>
- <member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
- Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
+ <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
+ A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0">
+ Sets the avoidance radius for the obstacle.
+ </member>
+ <member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
+ Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
- The radius of the agent. Used only if [member estimate_radius] is set to false.
+ <member name="vertices" type="PackedVector2Array" setter="set_vertices" getter="get_vertices" default="PackedVector2Array()">
+ The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
</member>
</members>
</class>
diff --git a/doc/classes/NavigationObstacle3D.xml b/doc/classes/NavigationObstacle3D.xml
index 2a2642fe8c..eb76d86fc0 100644
--- a/doc/classes/NavigationObstacle3D.xml
+++ b/doc/classes/NavigationObstacle3D.xml
@@ -1,42 +1,78 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NavigationObstacle3D" inherits="Node" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="NavigationObstacle3D" inherits="Node3D" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- 3D Obstacle used in navigation for collision avoidance.
+ 3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
</brief_description>
<description>
- 3D Obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. [NavigationObstacle3D] is physics safe.
- Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
+ 3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
+ If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
+ Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
+ Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
</description>
<tutorials>
<link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
</tutorials>
<methods>
+ <method name="get_agent_rid" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the [RID] of this agent on the [NavigationServer3D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
+ </description>
+ </method>
+ <method name="get_avoidance_layer_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member avoidance_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 [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer.
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
</description>
</method>
- <method name="get_rid" qualifiers="const">
+ <method name="get_obstacle_rid" qualifiers="const">
<return type="RID" />
<description>
- Returns the [RID] of this obstacle on the [NavigationServer3D].
+ Returns the [RID] of this obstacle on the [NavigationServer3D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
+ </description>
+ </method>
+ <method name="set_avoidance_layer_value">
+ <return type="void" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified layer in the [member avoidance_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 NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer.
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
</description>
</method>
</methods>
<members>
- <member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
- Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
+ <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
+ A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
+ </member>
+ <member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
+ Sets the obstacle height used in 2D avoidance. 2D avoidance using agent's ignore obstacles that are below or above them.
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0">
+ Sets the avoidance radius for the obstacle.
+ </member>
+ <member name="use_3d_avoidance" type="bool" setter="set_use_3d_avoidance" getter="get_use_3d_avoidance" default="false">
+ If [code]true[/code] the obstacle affects 3D avoidance using agent's with obstacle [member radius].
+ If [code]false[/code] the obstacle affects 2D avoidance using agent's with both obstacle [member vertices] as well as obstacle [member radius].
+ </member>
+ <member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
+ Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
- The radius of the agent. Used only if [member estimate_radius] is set to false.
+ <member name="vertices" type="PackedVector3Array" setter="set_vertices" getter="get_vertices" default="PackedVector3Array()">
+ The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
</member>
</members>
</class>
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index f05fd74685..1f1c0993d5 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -16,6 +16,13 @@
<link title="Using NavigationRegions">$DOCS_URL/tutorials/navigation/navigation_using_navigationregions.html</link>
</tutorials>
<methods>
+ <method name="get_avoidance_layer_value" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
<method name="get_navigation_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
@@ -29,6 +36,14 @@
Returns the [RID] of this region on the [NavigationServer2D]. Combined with [method NavigationServer2D.map_get_closest_point_owner] can be used to identify the [NavigationRegion2D] closest to a point on the merged navigation map.
</description>
</method>
+ <method name="set_avoidance_layer_value">
+ <return type="void" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
+ <description>
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+ </description>
+ </method>
<method name="set_navigation_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
@@ -39,6 +54,13 @@
</method>
</methods>
<members>
+ <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
+ A bitfield determining all avoidance layers for the avoidance constrain.
+ </member>
+ <member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false">
+ If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
+ [b]Experimental:[/b] This is an experimental feature and should not be used in production as agent's can get stuck on the navigation polygon corners and edges especially at high frame rate.
+ </member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationRegion2D] is enabled or disabled.
</member>
@@ -54,5 +76,8 @@
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
When pathfinding moves inside this region's navigation mesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path.
</member>
+ <member name="use_edge_connections" type="bool" setter="set_use_edge_connections" getter="get_use_edge_connections" default="true">
+ If enabled the navigation region will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ </member>
</members>
</class>
diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml
index a123381901..0988d07e8c 100644
--- a/doc/classes/NavigationRegion3D.xml
+++ b/doc/classes/NavigationRegion3D.xml
@@ -61,6 +61,9 @@
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
When pathfinding moves inside this region's navigation mesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path.
</member>
+ <member name="use_edge_connections" type="bool" setter="set_use_edge_connections" getter="get_use_edge_connections" default="true">
+ If enabled the navigation region will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ </member>
</members>
<signals>
<signal name="bake_finished">
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 75d648c540..e2293f2439 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -24,6 +24,13 @@
Creates the agent.
</description>
</method>
+ <method name="agent_get_avoidance_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="agent" type="RID" />
+ <description>
+ Return [code]true[/code] if the specified [param agent] uses avoidance.
+ </description>
+ </method>
<method name="agent_get_map" qualifiers="const">
<return type="RID" />
<param index="0" name="agent" type="RID" />
@@ -38,13 +45,46 @@
Returns true if the map got changed the previous frame.
</description>
</method>
- <method name="agent_set_callback">
+ <method name="agent_set_avoidance_callback">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="callback" type="Callable" />
<description>
- Sets the callback that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be passed as the first parameter just before the physics calculations.
- [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with an empty [Callable].
+ Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
+ [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
+ </description>
+ </method>
+ <method name="agent_set_avoidance_enabled">
+ <return type="void" />
+ <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.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_layers">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="layers" type="int" />
+ <description>
+ Set the agent's [code]avoidance_layers[/code] bitmask.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_mask">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="mask" type="int" />
+ <description>
+ Set the agent's [code]avoidance_mask[/code] bitmask.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_priority">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="priority" type="float" />
+ <description>
+ Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
+ The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</description>
</method>
<method name="agent_set_map">
@@ -95,20 +135,20 @@
Sets the radius of the agent.
</description>
</method>
- <method name="agent_set_target_velocity">
+ <method name="agent_set_time_horizon_agents">
<return type="void" />
<param index="0" name="agent" type="RID" />
- <param index="1" name="target_velocity" type="Vector2" />
+ <param index="1" name="time_horizon" type="float" />
<description>
- Sets the new target velocity.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
- <method name="agent_set_time_horizon">
+ <method name="agent_set_time_horizon_obstacles">
<return type="void" />
<param index="0" name="agent" type="RID" />
- <param index="1" name="time" type="float" />
+ <param index="1" name="time_horizon" type="float" />
<description>
- The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
<method name="agent_set_velocity">
@@ -116,7 +156,15 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector2" />
<description>
- Sets the current velocity of the agent.
+ Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position far away use [method agent_set_velocity_forced] instead to reset the internal velocity state.
+ </description>
+ </method>
+ <method name="agent_set_velocity_forced">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="velocity" type="Vector2" />
+ <description>
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position far away this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
<method name="free_rid">
@@ -148,7 +196,7 @@
<return type="Vector2" />
<param index="0" name="link" type="RID" />
<description>
- Returns the ending position of this [code]link[/code].
+ Returns the ending position of this [param link].
</description>
</method>
<method name="link_get_enter_cost" qualifiers="const">
@@ -162,14 +210,14 @@
<return type="RID" />
<param index="0" name="link" type="RID" />
<description>
- Returns the navigation map [RID] the requested [code]link[/code] is currently assigned to.
+ Returns the navigation map [RID] the requested [param link] is currently assigned to.
</description>
</method>
<method name="link_get_navigation_layers" qualifiers="const">
<return type="int" />
<param index="0" name="link" type="RID" />
<description>
- Returns the navigation layers for this [code]link[/code].
+ Returns the navigation layers for this [param link].
</description>
</method>
<method name="link_get_owner_id" qualifiers="const">
@@ -183,7 +231,7 @@
<return type="Vector2" />
<param index="0" name="link" type="RID" />
<description>
- Returns the starting position of this [code]link[/code].
+ Returns the starting position of this [param link].
</description>
</method>
<method name="link_get_travel_cost" qualifiers="const">
@@ -197,7 +245,7 @@
<return type="bool" />
<param index="0" name="link" type="RID" />
<description>
- Returns whether this [code]link[/code] can be travelled in both directions.
+ Returns whether this [param link] can be travelled in both directions.
</description>
</method>
<method name="link_set_bidirectional">
@@ -205,7 +253,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="bidirectional" type="bool" />
<description>
- Sets whether this [code]link[/code] can be travelled in both directions.
+ Sets whether this [param link] can be travelled in both directions.
</description>
</method>
<method name="link_set_end_position">
@@ -213,7 +261,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="position" type="Vector2" />
<description>
- Sets the exit position for the [code]link[/code].
+ Sets the exit position for the [param link].
</description>
</method>
<method name="link_set_enter_cost">
@@ -221,7 +269,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="enter_cost" type="float" />
<description>
- Sets the [code]enter_cost[/code] for this [code]link[/code].
+ Sets the [param enter_cost] for this [param link].
</description>
</method>
<method name="link_set_map">
@@ -253,7 +301,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="position" type="Vector2" />
<description>
- Sets the entry position for this [code]link[/code].
+ Sets the entry position for this [param link].
</description>
</method>
<method name="link_set_travel_cost">
@@ -261,7 +309,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="travel_cost" type="float" />
<description>
- Sets the [code]travel_cost[/code] for this [code]link[/code].
+ Sets the [param travel_cost] for this [param link].
</description>
</method>
<method name="map_create">
@@ -328,7 +376,14 @@
<return type="RID[]" />
<param index="0" name="map" type="RID" />
<description>
- Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
+ Returns all navigation link [RID]s that are currently assigned to the requested navigation [param map].
+ </description>
+ </method>
+ <method name="map_get_obstacles" qualifiers="const">
+ <return type="RID[]" />
+ <param index="0" name="map" type="RID" />
+ <description>
+ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_path" qualifiers="const">
@@ -349,6 +404,13 @@
Returns all navigation regions [RID]s that are currently assigned to the requested navigation [param map].
</description>
</method>
+ <method name="map_get_use_edge_connections" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="map" type="RID" />
+ <description>
+ Returns whether the navigation [param 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="map_is_active" qualifiers="const">
<return type="bool" />
<param index="0" name="map" type="RID" />
@@ -388,6 +450,58 @@
Set the map's link connection radius used to connect links to navigation polygons.
</description>
</method>
+ <method name="map_set_use_edge_connections">
+ <return type="void" />
+ <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.
+ </description>
+ </method>
+ <method name="obstacle_create">
+ <return type="RID" />
+ <description>
+ Creates a new navigation obstacle.
+ </description>
+ </method>
+ <method name="obstacle_get_map" qualifiers="const">
+ <return type="RID" />
+ <param index="0" name="obstacle" type="RID" />
+ <description>
+ Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
+ </description>
+ </method>
+ <method name="obstacle_set_avoidance_layers">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="layers" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="obstacle_set_map">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="map" type="RID" />
+ <description>
+ Sets the navigation map [RID] for the obstacle.
+ </description>
+ </method>
+ <method name="obstacle_set_position">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="position" type="Vector2" />
+ <description>
+ Sets the position of the obstacle in world space.
+ </description>
+ </method>
+ <method name="obstacle_set_vertices">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="vertices" type="PackedVector2Array" />
+ <description>
+ Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
+ </description>
+ </method>
<method name="query_path" qualifiers="const">
<return type="void" />
<param index="0" name="parameters" type="NavigationPathQueryParameters2D" />
@@ -460,6 +574,13 @@
Returns the travel cost of this [param region].
</description>
</method>
+ <method name="region_get_use_edge_connections" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="region" type="RID" />
+ <description>
+ Returns whether the navigation [param region] is set to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ </description>
+ </method>
<method name="region_owns_point" qualifiers="const">
<return type="bool" />
<param index="0" name="region" type="RID" />
@@ -526,6 +647,14 @@
Sets the [param travel_cost] for this [param region].
</description>
</method>
+ <method name="region_set_use_edge_connections">
+ <return type="void" />
+ <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.
+ </description>
+ </method>
<method name="set_debug_enabled">
<return type="void" />
<param index="0" name="enabled" type="bool" />
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index e6eed172f2..cc6b3b8ca4 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -24,6 +24,13 @@
Creates the agent.
</description>
</method>
+ <method name="agent_get_avoidance_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="agent" type="RID" />
+ <description>
+ Returns [code]true[/code] if the provided [param agent] has avoidance enabled.
+ </description>
+ </method>
<method name="agent_get_map" qualifiers="const">
<return type="RID" />
<param index="0" name="agent" type="RID" />
@@ -31,6 +38,13 @@
Returns the navigation map [RID] the requested [param agent] is currently assigned to.
</description>
</method>
+ <method name="agent_get_use_3d_avoidance" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="agent" type="RID" />
+ <description>
+ Returns [code]true[/code] if the provided [param agent] uses avoidance in 3D space Vector3(x,y,z) instead of horizontal 2D Vector2(x,y) / Vector3(x,0.0,z).
+ </description>
+ </method>
<method name="agent_is_map_changed" qualifiers="const">
<return type="bool" />
<param index="0" name="agent" type="RID" />
@@ -38,13 +52,54 @@
Returns true if the map got changed the previous frame.
</description>
</method>
- <method name="agent_set_callback">
+ <method name="agent_set_avoidance_callback">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="callback" type="Callable" />
<description>
- Sets the callback that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be passed as the first parameter just before the physics calculations.
- [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with an empty [Callable].
+ Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
+ [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
+ </description>
+ </method>
+ <method name="agent_set_avoidance_enabled">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [param enabled] the provided [param agent] calculates avoidance.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_layers">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="layers" type="int" />
+ <description>
+ Set the agent's [code]avoidance_layers[/code] bitmask.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_mask">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="mask" type="int" />
+ <description>
+ Set the agent's [code]avoidance_mask[/code] bitmask.
+ </description>
+ </method>
+ <method name="agent_set_avoidance_priority">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="priority" type="float" />
+ <description>
+ Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
+ The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
+ </description>
+ </method>
+ <method name="agent_set_height">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="height" type="float" />
+ <description>
+ Updates the provided [param agent] [param height].
</description>
</method>
<method name="agent_set_map">
@@ -95,20 +150,30 @@
Sets the radius of the agent.
</description>
</method>
- <method name="agent_set_target_velocity">
+ <method name="agent_set_time_horizon_agents">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="time_horizon" type="float" />
+ <description>
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+ </description>
+ </method>
+ <method name="agent_set_time_horizon_obstacles">
<return type="void" />
<param index="0" name="agent" type="RID" />
- <param index="1" name="target_velocity" type="Vector3" />
+ <param index="1" name="time_horizon" type="float" />
<description>
- Sets the new target velocity.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
- <method name="agent_set_time_horizon">
+ <method name="agent_set_use_3d_avoidance">
<return type="void" />
<param index="0" name="agent" type="RID" />
- <param index="1" name="time" type="float" />
+ <param index="1" name="enabled" type="bool" />
<description>
- The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ Sets if the agent uses the 2D avoidance or the 3D avoidance while avoidance is enabled.
+ If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
+ If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including the agents height in 2D avoidance.
</description>
</method>
<method name="agent_set_velocity">
@@ -116,7 +181,15 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector3" />
<description>
- Sets the current velocity of the agent.
+ Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method agent_set_velocity_forced] as well to reset the internal simulation velocity.
+ </description>
+ </method>
+ <method name="agent_set_velocity_forced">
+ <return type="void" />
+ <param index="0" name="agent" type="RID" />
+ <param index="1" name="velocity" type="Vector3" />
+ <description>
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
<method name="free_rid">
@@ -155,7 +228,7 @@
<return type="Vector3" />
<param index="0" name="link" type="RID" />
<description>
- Returns the ending position of this [code]link[/code].
+ Returns the ending position of this [param link].
</description>
</method>
<method name="link_get_enter_cost" qualifiers="const">
@@ -169,14 +242,14 @@
<return type="RID" />
<param index="0" name="link" type="RID" />
<description>
- Returns the navigation map [RID] the requested [code]link[/code] is currently assigned to.
+ Returns the navigation map [RID] the requested [param link] is currently assigned to.
</description>
</method>
<method name="link_get_navigation_layers" qualifiers="const">
<return type="int" />
<param index="0" name="link" type="RID" />
<description>
- Returns the navigation layers for this [code]link[/code].
+ Returns the navigation layers for this [param link].
</description>
</method>
<method name="link_get_owner_id" qualifiers="const">
@@ -190,7 +263,7 @@
<return type="Vector3" />
<param index="0" name="link" type="RID" />
<description>
- Returns the starting position of this [code]link[/code].
+ Returns the starting position of this [param link].
</description>
</method>
<method name="link_get_travel_cost" qualifiers="const">
@@ -204,7 +277,7 @@
<return type="bool" />
<param index="0" name="link" type="RID" />
<description>
- Returns whether this [code]link[/code] can be travelled in both directions.
+ Returns whether this [param link] can be travelled in both directions.
</description>
</method>
<method name="link_set_bidirectional">
@@ -212,7 +285,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="bidirectional" type="bool" />
<description>
- Sets whether this [code]link[/code] can be travelled in both directions.
+ Sets whether this [param link] can be travelled in both directions.
</description>
</method>
<method name="link_set_end_position">
@@ -220,7 +293,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="position" type="Vector3" />
<description>
- Sets the exit position for the [code]link[/code].
+ Sets the exit position for the [param link].
</description>
</method>
<method name="link_set_enter_cost">
@@ -228,7 +301,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="enter_cost" type="float" />
<description>
- Sets the [code]enter_cost[/code] for this [code]link[/code].
+ Sets the [param enter_cost] for this [param link].
</description>
</method>
<method name="link_set_map">
@@ -260,7 +333,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="position" type="Vector3" />
<description>
- Sets the entry position for this [code]link[/code].
+ Sets the entry position for this [param link].
</description>
</method>
<method name="link_set_travel_cost">
@@ -268,7 +341,7 @@
<param index="0" name="link" type="RID" />
<param index="1" name="travel_cost" type="float" />
<description>
- Sets the [code]travel_cost[/code] for this [code]link[/code].
+ Sets the [param travel_cost] for this [param link].
</description>
</method>
<method name="map_create">
@@ -353,7 +426,14 @@
<return type="RID[]" />
<param index="0" name="map" type="RID" />
<description>
- Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
+ Returns all navigation link [RID]s that are currently assigned to the requested navigation [param map].
+ </description>
+ </method>
+ <method name="map_get_obstacles" qualifiers="const">
+ <return type="RID[]" />
+ <param index="0" name="map" type="RID" />
+ <description>
+ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_path" qualifiers="const">
@@ -381,6 +461,13 @@
Returns the map's up direction.
</description>
</method>
+ <method name="map_get_use_edge_connections" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="map" type="RID" />
+ <description>
+ Returns true if the navigation [param 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="map_is_active" qualifiers="const">
<return type="bool" />
<param index="0" name="map" type="RID" />
@@ -428,6 +515,67 @@
Sets the map up direction.
</description>
</method>
+ <method name="map_set_use_edge_connections">
+ <return type="void" />
+ <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.
+ </description>
+ </method>
+ <method name="obstacle_create">
+ <return type="RID" />
+ <description>
+ Creates a new obstacle.
+ </description>
+ </method>
+ <method name="obstacle_get_map" qualifiers="const">
+ <return type="RID" />
+ <param index="0" name="obstacle" type="RID" />
+ <description>
+ Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
+ </description>
+ </method>
+ <method name="obstacle_set_avoidance_layers">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="layers" type="int" />
+ <description>
+ Set the obstacles's [code]avoidance_layers[/code] bitmask.
+ </description>
+ </method>
+ <method name="obstacle_set_height">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="height" type="float" />
+ <description>
+ Sets the [param height] for the [param obstacle]. In 3D agents will ignore obstacles that are above or below them while using 2D avoidance.
+ </description>
+ </method>
+ <method name="obstacle_set_map">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="map" type="RID" />
+ <description>
+ Assigns the [param obstacle] to a navigation map.
+ </description>
+ </method>
+ <method name="obstacle_set_position">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="position" type="Vector3" />
+ <description>
+ Updates the [param position] in world space for the [param obstacle].
+ </description>
+ </method>
+ <method name="obstacle_set_vertices">
+ <return type="void" />
+ <param index="0" name="obstacle" type="RID" />
+ <param index="1" name="vertices" type="PackedVector3Array" />
+ <description>
+ Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
+ </description>
+ </method>
<method name="query_path" qualifiers="const">
<return type="void" />
<param index="0" name="parameters" type="NavigationPathQueryParameters3D" />
@@ -508,6 +656,13 @@
Returns the travel cost of this [param region].
</description>
</method>
+ <method name="region_get_use_edge_connections" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="region" type="RID" />
+ <description>
+ Returns true if the navigation [param region] is set to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ </description>
+ </method>
<method name="region_owns_point" qualifiers="const">
<return type="bool" />
<param index="0" name="region" type="RID" />
@@ -574,6 +729,14 @@
Sets the [param travel_cost] for this [param region].
</description>
</method>
+ <method name="region_set_use_edge_connections">
+ <return type="void" />
+ <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.
+ </description>
+ </method>
<method name="set_active">
<return type="void" />
<param index="0" name="active" type="bool" />
@@ -590,6 +753,11 @@
</method>
</methods>
<signals>
+ <signal name="avoidance_debug_changed">
+ <description>
+ Emitted when avoidance debug settings are changed. Only available in debug builds.
+ </description>
+ </signal>
<signal name="map_changed">
<param index="0" name="map" type="RID" />
<description>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 42418b3e8d..3f79822ca9 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -182,6 +182,20 @@
[b]Note:[/b] For performance reasons, the order of node groups is [i]not[/i] guaranteed. The order of node groups should not be relied upon as it can vary across project runs.
</description>
</method>
+ <method name="call_deferred_thread_group" qualifiers="vararg">
+ <return type="Variant" />
+ <param index="0" name="method" type="StringName" />
+ <description>
+ This function is similar to [method Object.call_deferred] except that the call will take place when the node thread group is processed. If the node thread group processes in sub-threads, then the call will be done on that thread, right before [constant NOTIFICATION_PROCESS] or [constant NOTIFICATION_PHYSICS_PROCESS], the [method _process] or [method _physics_process] or their internal versions are called.
+ </description>
+ </method>
+ <method name="call_thread_safe" qualifiers="vararg">
+ <return type="Variant" />
+ <param index="0" name="method" type="StringName" />
+ <description>
+ This function ensures that the calling of this function will succeed, no matter whether it's being done from a thread or not. If called from a thread that is not allowed to call the function, the call will become deferred. Otherwise, the call will go through directly.
+ </description>
+ </method>
<method name="can_process" qualifiers="const">
<return type="bool" />
<description>
@@ -500,6 +514,13 @@
Returns [code]true[/code] if the local system is the multiplayer authority of this node.
</description>
</method>
+ <method name="is_node_ready" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the node is ready, i.e. it's inside scene tree and all its children are initialized.
+ [method request_ready] resets it back to [code]false[/code].
+ </description>
+ </method>
<method name="is_physics_processing" qualifiers="const">
<return type="bool" />
<description>
@@ -557,6 +578,20 @@
[b]Note:[/b] Internal children can only be moved within their expected "internal range" (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
+ <method name="notify_deferred_thread_group">
+ <return type="void" />
+ <param index="0" name="what" type="int" />
+ <description>
+ Similar to [method call_deferred_thread_group], but for notifications.
+ </description>
+ </method>
+ <method name="notify_thread_safe">
+ <return type="void" />
+ <param index="0" name="what" type="int" />
+ <description>
+ Similar to [method call_thread_safe], but for notifications.
+ </description>
+ </method>
<method name="print_orphan_nodes" qualifiers="static">
<return type="void" />
<description>
@@ -690,6 +725,14 @@
Sends a [method rpc] to a specific peer identified by [param peer_id] (see [method MultiplayerPeer.set_target_peer]). Returns [code]null[/code].
</description>
</method>
+ <method name="set_deferred_thread_group">
+ <return type="void" />
+ <param index="0" name="property" type="StringName" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Similar to [method call_deferred_thread_group], but for setting properties.
+ </description>
+ </method>
<method name="set_display_folded">
<return type="void" />
<param index="0" name="fold" type="bool" />
@@ -778,6 +821,14 @@
Sets whether this is an instance load placeholder. See [InstancePlaceholder].
</description>
</method>
+ <method name="set_thread_safe">
+ <return type="void" />
+ <param index="0" name="property" type="StringName" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Similar to [method call_thread_safe], but for setting properties.
+ </description>
+ </method>
<method name="update_configuration_warnings">
<return type="void" />
<description>
@@ -804,9 +855,24 @@
<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).
</member>
+ <member name="process_physics_priority" type="int" setter="set_physics_process_priority" getter="get_physics_process_priority" default="0">
+ Similar to [member process_priority] but for [constant NOTIFICATION_PHYSICS_PROCESS], [method _physics_process] or the internal version.
+ </member>
<member name="process_priority" type="int" setter="set_process_priority" getter="get_process_priority" default="0">
The node's priority in the execution order of the enabled processing callbacks (i.e. [constant NOTIFICATION_PROCESS], [constant NOTIFICATION_PHYSICS_PROCESS] and their internal counterparts). Nodes whose process priority value is [i]lower[/i] will have their processing callbacks executed first.
</member>
+ <member name="process_thread_group" type="int" setter="set_process_thread_group" getter="get_process_thread_group" enum="Node.ProcessThreadGroup" default="0">
+ Set the process thread group for this node (basically, whether it receives [constant NOTIFICATION_PROCESS], [constant NOTIFICATION_PHYSICS_PROCESS], [method _process] or [method _physics_process] (and the internal versions) on the main thread or in a sub-thread.
+ By default, the thread group is [constant PROCESS_THREAD_GROUP_INHERIT], which means that this node belongs to the same thread group as the parent node. The thread groups means that nodes in a specific thread group will process together, separate to other thread groups (depending on [member process_thread_group_order]). If the value is set is [constant PROCESS_THREAD_GROUP_SUB_THREAD], this thread group will occur on a sub thread (not the main thread), otherwise if set to [constant PROCESS_THREAD_GROUP_MAIN_THREAD] it will process on the main thread. If there is not a parent or grandparent node set to something other than inherit, the node will belong to the [i]default thread group[/i]. This default group will process on the main thread and its group order is 0.
+ During processing in a sub-thread, accessing most functions in nodes outside the thread group is forbidden (and it will result in an error in debug mode). Use [method Object.call_deferred], [method call_thread_safe], [method call_deferred_thread_group] and the likes in order to communicate from the thread groups to the main thread (or to other thread groups).
+ To better understand process thread groups, the idea is that any node set to any other value than [constant PROCESS_THREAD_GROUP_INHERIT] will include any children (and grandchildren) nodes set to inherit into its process thread group. this means that the processing of all the nodes in the group will happen together, at the same time as the node including them.
+ </member>
+ <member name="process_thread_group_order" type="int" setter="set_process_thread_group_order" getter="get_process_thread_group_order">
+ Change the process thread group order. Groups with a lesser order will process before groups with a greater order. This is useful when a large amount of nodes process in sub thread and, afterwards, another group wants to collect their result in the main thread, as an example.
+ </member>
+ <member name="process_thread_messages" type="int" setter="set_process_thread_messages" getter="get_process_thread_messages" enum="Node.ProcessThreadMessages">
+ Set whether the current thread group will process messages (calls to [method call_deferred_thread_group] on threads, and whether it wants to receive them during regular process or physics process callbacks.
+ </member>
<member name="scene_file_path" type="String" setter="set_scene_file_path" getter="get_scene_file_path">
If a scene is instantiated from a file, its topmost node contains the absolute file path from which it was loaded in [member scene_file_path] (e.g. [code]res://levels/1.tscn[/code]). Otherwise, [member scene_file_path] is set to an empty string.
</member>
@@ -1026,6 +1092,21 @@
<constant name="PROCESS_MODE_DISABLED" value="4" enum="ProcessMode">
Never process. Completely disables processing, ignoring the [SceneTree]'s paused property. This is the inverse of [constant PROCESS_MODE_ALWAYS].
</constant>
+ <constant name="PROCESS_THREAD_GROUP_INHERIT" value="0" enum="ProcessThreadGroup">
+ If the [member process_thread_group] property is sent to this, the node will belong to any parent (or grandparent) node that has a thread group mode that is not inherit. See [member process_thread_group] for more information.
+ </constant>
+ <constant name="PROCESS_THREAD_GROUP_MAIN_THREAD" value="1" enum="ProcessThreadGroup">
+ Process this node (and children nodes set to inherit) on the main thread. See [member process_thread_group] for more information.
+ </constant>
+ <constant name="PROCESS_THREAD_GROUP_SUB_THREAD" value="2" enum="ProcessThreadGroup">
+ Process this node (and children nodes set to inherit) on a sub-thread. See [member process_thread_group] for more information.
+ </constant>
+ <constant name="FLAG_PROCESS_THREAD_MESSAGES" value="1" enum="ProcessThreadMessages">
+ </constant>
+ <constant name="FLAG_PROCESS_THREAD_MESSAGES_PHYSICS" value="2" enum="ProcessThreadMessages">
+ </constant>
+ <constant name="FLAG_PROCESS_THREAD_MESSAGES_ALL" value="3" enum="ProcessThreadMessages">
+ </constant>
<constant name="DUPLICATE_SIGNALS" value="1" enum="DuplicateFlags">
Duplicate the node's signals.
</constant>
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index da898a5567..b4857bacde 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -7,6 +7,7 @@
Most basic 3D game object, with a [Transform3D] and visibility settings. All other 3D game objects inherit from Node3D. Use [Node3D] as a parent node to move, scale, rotate and show/hide children in a 3D project.
Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the [Node3D] object is set as top-level. Affine operations in this coordinate system correspond to direct affine operations on the [Node3D]'s transform. The word local below refers to this coordinate system. The coordinate system that is attached to the [Node3D] object itself is referred to as object-local coordinate system.
[b]Note:[/b] Unless otherwise specified, all methods that have angle parameters must have angles specified as [i]radians[/i]. To convert degrees to radians, use [method @GlobalScope.deg_to_rad].
+ [b]Note:[/b] Be aware that "Spatial" nodes are now called "Node3D" starting with Godot 4. Any Godot 3.x references to "Spatial" nodes refer to "Node3D" in Godot 4.
</description>
<tutorials>
<link title="Introduction to 3D">$DOCS_URL/tutorials/3d/introduction_to_3d.html</link>
@@ -105,7 +106,7 @@
<method name="is_visible_in_tree" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its antecedents are also visible. If any antecedent is hidden, this node will not be visible in the scene tree.
+ Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree.
</description>
</method>
<method name="look_at">
@@ -315,7 +316,7 @@
Defines the visibility range parent for this node and its subtree. The visibility parent must be a GeometryInstance3D. Any visual instance will only be visible if the visibility parent (and all of its visibility ancestors) is hidden by being closer to the camera than its own [member GeometryInstance3D.visibility_range_begin]. Nodes hidden via the [member Node3D.visible] property are essentially removed from the visibility dependency tree, so dependent instances will not take the hidden node or its ancestors into account.
</member>
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
- If [code]true[/code], this node is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
+ If [code]true[/code], this node is drawn. The node is only visible if all of its ancestors are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
</member>
</members>
<signals>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index a30c541b71..3bf4177549 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -91,7 +91,7 @@
<param index="3" name="read_stderr" type="bool" default="false" />
<param index="4" name="open_console" type="bool" default="false" />
<description>
- Executes a command. The file specified in [param path] must exist and be executable. Platform path resolution will be used. The [param arguments] are used in the given order and separated by a space. If an [param output] [Array] is provided, the complete shell output of the process will be appended as a single [String] element in [param output]. If [param read_stderr] is [code]true[/code], the output to the standard error stream will be included too.
+ Executes a command. The file specified in [param path] must exist and be executable. Platform path resolution will be used. The [param arguments] are used in the given order, separated by spaces, and wrapped in quotes. If an [param output] [Array] is provided, the complete shell output of the process will be appended as a single [String] element in [param output]. If [param read_stderr] is [code]true[/code], the output to the standard error stream will be included too.
On Windows, if [param open_console] is [code]true[/code] and the process is a console app, a new terminal window will be opened. This is ignored on other platforms.
If the command is successfully executed, the method will return the exit code of the command, or [code]-1[/code] if it fails.
[b]Note:[/b] The Godot thread will pause its execution until the executed command terminates. Use [Thread] to create a separate thread that will not pause the Godot thread, or use [method create_process] to create a completely independent process.
@@ -426,7 +426,7 @@
<description>
Returns an array of the system substitute font file paths, which are similar to the font with [param font_name] and style for the specified text, locale and script. Returns empty array if no matching fonts found.
The following aliases can be used to request default fonts: "sans-serif", "serif", "monospace", "cursive", and "fantasy".
- [b]Note:[/b] Depending on OS, it's not guaranteed that any of the returned fonts is suitable for rendering specified text. Fonts should be loaded and checked in the order they are returned, and the first suitable one used.
+ [b]Note:[/b] Depending on OS, it's not guaranteed that any of the returned fonts will be suitable for rendering specified text. Fonts should be loaded and checked in the order they are returned, and the first suitable one used.
[b]Note:[/b] Returned fonts might have different style if the requested style is not available or belong to a different font family.
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
</description>
@@ -481,7 +481,7 @@
<method name="get_video_adapter_driver_info" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns the video adapter driver name and version for the user's currently active graphics card.
+ Returns the video adapter driver name and version for the user's currently active graphics card. See also [method RenderingServer.get_video_adapter_api_version].
The first element holds the driver name, such as [code]nvidia[/code], [code]amdgpu[/code], etc.
The second element holds the driver version. For e.g. the [code]nvidia[/code] driver on a Linux/BSD platform, the version is in the format [code]510.85.02[/code]. For Windows, the driver's format is [code]31.0.15.1659[/code].
[b]Note:[/b] This method is only supported on the platforms Linux/BSD and Windows when not running in headless mode. It returns an empty array on other platforms.
@@ -654,8 +654,8 @@
<param index="0" name="file_or_dir_path" type="String" />
<param index="1" name="open_folder" type="bool" default="true" />
<description>
- Requests the OS to open file manager, then navigate to the given [param file_or_dir_path] and select the target file or folder.
- If [param file_or_dir_path] is a valid directory path, and [param open_folder] is [code]true[/code], the method will open explorer and enter the target folder without selecting anything.
+ Requests the OS to open the file manager, then navigate to the given [param file_or_dir_path] and select the target file or folder.
+ If [param file_or_dir_path] is a valid directory path, and [param open_folder] is [code]true[/code], the method will open the file manager and enter the target folder without selecting anything.
Use [method ProjectSettings.globalize_path] to convert a [code]res://[/code] or [code]user://[/code] path into a system path for use with this method.
[b]Note:[/b] Currently this method is only implemented on Windows. On other platforms, it will fallback to [method shell_open] with a directory path for [param file_or_dir_path].
</description>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index eea0161799..0a52df1e49 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -354,6 +354,12 @@
Returns [code]true[/code] if the object is allowed to translate messages with [method tr] and [method tr_n]. See also [method set_message_translation].
</description>
</method>
+ <method name="cancel_free">
+ <return type="void" />
+ <description>
+ If this method is called during [constant NOTIFICATION_PREDELETE], this object will reject being freed and will remain allocated. This is mostly an internal function used for error handling to avoid the user from freeing objects when they are not intended to.
+ </description>
+ </method>
<method name="connect">
<return type="int" enum="Error" />
<param index="0" name="signal" type="StringName" />
@@ -597,7 +603,7 @@
</description>
</method>
<method name="get_meta_list" qualifiers="const">
- <return type="PackedStringArray" />
+ <return type="StringName[]" />
<description>
Returns the object's metadata entry names as a [PackedStringArray].
</description>
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 431ccf634c..3926ea8beb 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -96,6 +96,13 @@
Decodes a 16-bit floating point number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0.0[/code] if a valid number can't be decoded.
</description>
</method>
+ <method name="decode_s8" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="byte_offset" type="int" />
+ <description>
+ Decodes a 8-bit signed integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
+ </description>
+ </method>
<method name="decode_s16" qualifiers="const">
<return type="int" />
<param index="0" name="byte_offset" type="int" />
@@ -117,11 +124,11 @@
Decodes a 64-bit signed integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
</description>
</method>
- <method name="decode_s8" qualifiers="const">
+ <method name="decode_u8" qualifiers="const">
<return type="int" />
<param index="0" name="byte_offset" type="int" />
<description>
- Decodes a 8-bit signed integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
+ Decodes a 8-bit unsigned integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
</description>
</method>
<method name="decode_u16" qualifiers="const">
@@ -145,13 +152,6 @@
Decodes a 64-bit unsigned integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
</description>
</method>
- <method name="decode_u8" qualifiers="const">
- <return type="int" />
- <param index="0" name="byte_offset" type="int" />
- <description>
- Decodes a 8-bit unsigned integer number from the bytes starting at [param byte_offset]. Fails if the byte count is insufficient. Returns [code]0[/code] if a valid number can't be decoded.
- </description>
- </method>
<method name="decode_var" qualifiers="const">
<return type="Variant" />
<param index="0" name="byte_offset" type="int" />
@@ -216,6 +216,14 @@
Encodes a 16-bit floating point number as bytes at the index of [param byte_offset] bytes. The array must have at least 2 bytes of space, starting at the offset.
</description>
</method>
+ <method name="encode_s8">
+ <return type="void" />
+ <param index="0" name="byte_offset" type="int" />
+ <param index="1" name="value" type="int" />
+ <description>
+ Encodes a 8-bit signed integer number (signed byte) at the index of [param byte_offset] bytes. The array must have at least 1 byte of space, starting at the offset.
+ </description>
+ </method>
<method name="encode_s16">
<return type="void" />
<param index="0" name="byte_offset" type="int" />
@@ -240,12 +248,12 @@
Encodes a 64-bit signed integer number as bytes at the index of [param byte_offset] bytes. The array must have at least 8 bytes of space, starting at the offset.
</description>
</method>
- <method name="encode_s8">
+ <method name="encode_u8">
<return type="void" />
<param index="0" name="byte_offset" type="int" />
<param index="1" name="value" type="int" />
<description>
- Encodes a 8-bit signed integer number (signed byte) at the index of [param byte_offset] bytes. The array must have at least 1 byte of space, starting at the offset.
+ Encodes a 8-bit unsigned integer number (byte) at the index of [param byte_offset] bytes. The array must have at least 1 byte of space, starting at the offset.
</description>
</method>
<method name="encode_u16">
@@ -272,14 +280,6 @@
Encodes a 64-bit unsigned integer number as bytes at the index of [param byte_offset] bytes. The array must have at least 8 bytes of space, starting at the offset.
</description>
</method>
- <method name="encode_u8">
- <return type="void" />
- <param index="0" name="byte_offset" type="int" />
- <param index="1" name="value" type="int" />
- <description>
- Encodes a 8-bit unsigned integer number (byte) at the index of [param byte_offset] bytes. The array must have at least 1 byte of space, starting at the offset.
- </description>
- </method>
<method name="encode_var">
<return type="int" />
<param index="0" name="byte_offset" type="int" />
@@ -310,22 +310,22 @@
Converts ASCII/Latin-1 encoded array to [String]. Fast alternative to [method get_string_from_utf8] if the content is ASCII/Latin-1 only. Unlike the UTF-8 function this function maps every byte to a character in the array. Multibyte sequences will not be interpreted correctly. For parsing user input always use [method get_string_from_utf8].
</description>
</method>
- <method name="get_string_from_utf16" qualifiers="const">
+ <method name="get_string_from_utf8" qualifiers="const">
<return type="String" />
<description>
- Converts UTF-16 encoded array to [String]. If the BOM is missing, system endianness is assumed. Returns empty string if source array is not valid UTF-16 string.
+ Converts UTF-8 encoded array to [String]. Slower than [method get_string_from_ascii] but supports UTF-8 encoded data. Use this function if you are unsure about the source of the data. For user input this function should always be preferred. Returns empty string if source array is not valid UTF-8 string.
</description>
</method>
- <method name="get_string_from_utf32" qualifiers="const">
+ <method name="get_string_from_utf16" qualifiers="const">
<return type="String" />
<description>
- Converts UTF-32 encoded array to [String]. System endianness is assumed. Returns empty string if source array is not valid UTF-32 string.
+ Converts UTF-16 encoded array to [String]. If the BOM is missing, system endianness is assumed. Returns empty string if source array is not valid UTF-16 string.
</description>
</method>
- <method name="get_string_from_utf8" qualifiers="const">
+ <method name="get_string_from_utf32" qualifiers="const">
<return type="String" />
<description>
- Converts UTF-8 encoded array to [String]. Slower than [method get_string_from_ascii] but supports UTF-8 encoded data. Use this function if you are unsure about the source of the data. For user input this function should always be preferred. Returns empty string if source array is not valid UTF-8 string.
+ Converts UTF-32 encoded array to [String]. System endianness is assumed. Returns empty string if source array is not valid UTF-32 string.
</description>
</method>
<method name="get_string_from_wchar" qualifiers="const">
diff --git a/doc/classes/PackedDataContainer.xml b/doc/classes/PackedDataContainer.xml
index addfdbed6b..8b500b41bd 100644
--- a/doc/classes/PackedDataContainer.xml
+++ b/doc/classes/PackedDataContainer.xml
@@ -1,26 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PackedDataContainer" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Efficiently packs and serializes [Array] or [Dictionary].
</brief_description>
<description>
+ [PackedDataContainer] can be used to efficiently store data from untyped containers. The data is packed into raw bytes and can be saved to file. Only [Array] and [Dictionary] can be stored this way.
+ You can retrieve the data by iterating on the container, which will work as if iterating on the packed data itself. If the packed container is a [Dictionary], the data can be retrieved by key names ([String]/[StringName] only).
+ [codeblock]
+ var data = { "key": "value", "another_key": 123, "lock": Vector2() }
+ var packed = PackedDataContainer.new()
+ packed.pack(data)
+ ResourceSaver.save(packed, "packed_data.res")
+ [/codeblock]
+ [codeblock]
+ var container = load("packed_data.res")
+ for key in container:
+ prints(key, container[key])
+
+ # Prints:
+ # key value
+ # lock (0, 0)
+ # another_key 123
+ [/codeblock]
+ Nested containers will be packed recursively. While iterating, they will be returned as [PackedDataContainerRef].
</description>
<tutorials>
</tutorials>
<methods>
<method name="pack">
<return type="int" enum="Error" />
+ <returns_error number="0"/>
+ <returns_error number="30"/>
<param index="0" name="value" type="Variant" />
<description>
+ Packs the given container into a binary representation. The [param value] must be either [Array] or [Dictionary], any other type will result in invalid data error.
+ [b]Note:[/b] Subsequent calls to this method will overwrite the existing data.
</description>
</method>
<method name="size" qualifiers="const">
<return type="int" />
<description>
+ Returns the size of the packed container (see [method Array.size] and [method Dictionary.size]).
</description>
</method>
</methods>
- <members>
- <member name="__data__" type="PackedByteArray" setter="_set_data" getter="_get_data" default="PackedByteArray()">
- </member>
- </members>
</class>
diff --git a/doc/classes/PackedDataContainerRef.xml b/doc/classes/PackedDataContainerRef.xml
index 47285fa8d6..440673d9b0 100644
--- a/doc/classes/PackedDataContainerRef.xml
+++ b/doc/classes/PackedDataContainerRef.xml
@@ -1,9 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PackedDataContainerRef" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Reference-counted version of [PackedDataContainer].
+ Internal class used by [PackedDataContainer].
</brief_description>
<description>
+ When packing nested containers using [PackedDataContainer], they are recursively packed into [PackedDataContainerRef] (only applies to [Array] and [Dictionary]). Their data can be retrieved the same way as from [PackedDataContainer].
+ [codeblock]
+ var packed = PackedDataContainer.new()
+ packed.pack([1, 2, 3, ["abc", "def"], 4, 5, 6])
+
+ for element in packed:
+ if element is PackedDataContainerRef:
+ for subelement in element:
+ print("::", subelement)
+ else:
+ print(element)
+
+ # Prints:
+ # 1
+ # 2
+ # 3
+ # ::abc
+ # ::def
+ # 4
+ # 5
+ # 6
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -11,6 +33,7 @@
<method name="size" qualifiers="const">
<return type="int" />
<description>
+ Returns the size of the packed container (see [method Array.size] and [method Dictionary.size]).
</description>
</method>
</methods>
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml
index 6d549e1b67..5497effc75 100644
--- a/doc/classes/ParticleProcessMaterial.xml
+++ b/doc/classes/ParticleProcessMaterial.xml
@@ -282,7 +282,8 @@
Minimum equivalent of [member tangential_accel_max].
</member>
<member name="turbulence_enabled" type="bool" setter="set_turbulence_enabled" getter="get_turbulence_enabled" default="false">
- Enables and disables Turbulence for the particle system.
+ If [code]true[/code], enables turbulence for the particle system. Turbulence can be used to vary particle movement according to its position (based on a 3D noise pattern). In 3D, [GPUParticlesAttractorVectorField3D] with [NoiseTexture3D] can be used as an alternative to turbulence that works in world space and with multiple particle systems reacting in the same way.
+ [b]Note:[/b] Enabling turbulence has a high performance cost on the GPU. Only enable turbulence on a few particle systems at once at most, and consider disabling it when targeting mobile/web platforms.
</member>
<member name="turbulence_influence_max" type="float" setter="set_param_max" getter="get_param_max" default="0.1">
Maximum turbulence influence on each particle.
@@ -296,11 +297,11 @@
Each particle's amount of turbulence will be influenced along this [CurveTexture] over its life time.
</member>
<member name="turbulence_initial_displacement_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
- Maximum displacement of each particles spawn position by the turbulence.
+ Maximum displacement of each particle's spawn position by the turbulence.
The actual amount of displacement will be a factor of the underlying turbulence multiplied by a random value between [member turbulence_initial_displacement_min] and [member turbulence_initial_displacement_max].
</member>
<member name="turbulence_initial_displacement_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0">
- Minimum displacement of each particles spawn position by the turbulence.
+ Minimum displacement of each particle's spawn position by the turbulence.
The actual amount of displacement will be a factor of the underlying turbulence multiplied by a random value between [member turbulence_initial_displacement_min] and [member turbulence_initial_displacement_max].
</member>
<member name="turbulence_noise_scale" type="float" setter="set_turbulence_noise_scale" getter="get_turbulence_noise_scale" default="9.0">
@@ -312,10 +313,10 @@
A value of [code]Vector3(0.0, 0.0, 0.0)[/code] will freeze the turbulence pattern in place.
</member>
<member name="turbulence_noise_speed_random" type="float" setter="set_turbulence_noise_speed_random" getter="get_turbulence_noise_speed_random" default="0.0">
- Use to influence the noise speed in a random pattern. This helps to break up visible movement patterns.
+ Use to influence the noise speed in a random pattern. This helps break up visible movement patterns.
</member>
<member name="turbulence_noise_strength" type="float" setter="set_turbulence_noise_strength" getter="get_turbulence_noise_strength" default="1.0">
- The turbulence noise strength. Increasing this will result in a stronger, more contrasting, noise pattern.
+ The turbulence noise strength. Increasing this will result in a stronger, more contrasting noise pattern.
</member>
</members>
<constants>
diff --git a/doc/classes/PathFollow3D.xml b/doc/classes/PathFollow3D.xml
index bf8194a7cf..41727a7bd8 100644
--- a/doc/classes/PathFollow3D.xml
+++ b/doc/classes/PathFollow3D.xml
@@ -15,7 +15,7 @@
<param index="0" name="transform" type="Transform3D" />
<param index="1" name="rotation_mode" type="int" enum="PathFollow3D.RotationMode" />
<description>
- Correct the [code]transform[/code]. [code]rotation_mode[/code] implicitly specifies how posture (forward, up and sideway direction) is calculated.
+ Correct the [param transform]. [param rotation_mode] implicitly specifies how posture (forward, up and sideway direction) is calculated.
</description>
</method>
</methods>
diff --git a/doc/classes/PhysicalBone2D.xml b/doc/classes/PhysicalBone2D.xml
index d0bfbbd998..f201016f9c 100644
--- a/doc/classes/PhysicalBone2D.xml
+++ b/doc/classes/PhysicalBone2D.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalBone2D" inherits="RigidBody2D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A 2D node that can be used for physically aware bones in 2D.
+ A [RigidBody2D]-derived node used to make [Bone2D]s in a [Skeleton2D] react to physics.
</brief_description>
<description>
- The [code]PhysicalBone2D[/code] node is a [RigidBody2D]-based node that can be used to make [Bone2D] nodes in a [Skeleton2D] react to physics. This node is very similar to the [PhysicalBone3D] node, just for 2D instead of 3D.
- [b]Note:[/b] To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes.
- [b]Note:[/b] The PhysicalBone2D node does not automatically create a [Joint2D] node to keep [code]PhysicalBone2D[/code] nodes together. You will need to create these manually. For most cases, you want to use a [PinJoint2D] node. The [code]PhysicalBone2D[/code] node can automatically configure the [Joint2D] node once it's been created as a child node.
+ The [PhysicalBone2D] node is a [RigidBody2D]-based node that can be used to make [Bone2D]s in a [Skeleton2D] react to physics.
+ [b]Note:[/b] To make the [Bone2D]s visually follow the [PhysicalBone2D] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] parent.
+ [b]Note:[/b] The [PhysicalBone2D] node does not automatically create a [Joint2D] node to keep [PhysicalBone2D] nodes together. They must be created manually. For most cases, you want to use a [PinJoint2D] node. The [PhysicalBone2D] node will automatically configure the [Joint2D] node once it's been added as a child node.
</description>
<tutorials>
</tutorials>
@@ -14,32 +14,32 @@
<method name="get_joint" qualifiers="const">
<return type="Joint2D" />
<description>
- Returns the first [Joint2D] child node, if one exists. This is mainly a helper function to make it easier to get the [Joint2D] that the [code]PhysicalBone2D[/code] is autoconfiguring.
+ Returns the first [Joint2D] child node, if one exists. This is mainly a helper function to make it easier to get the [Joint2D] that the [PhysicalBone2D] is autoconfiguring.
</description>
</method>
<method name="is_simulating_physics" qualifiers="const">
<return type="bool" />
<description>
- Returns a boolean that indicates whether the [code]PhysicalBone2D[/code] node is running and simulating using the Godot 2D physics engine. When [code]true[/code], the PhysicalBone2D node is using physics.
+ Returns a boolean that indicates whether the [PhysicalBone2D] is running and simulating using the Godot 2D physics engine. When [code]true[/code], the PhysicalBone2D node is using physics.
</description>
</method>
</methods>
<members>
<member name="auto_configure_joint" type="bool" setter="set_auto_configure_joint" getter="get_auto_configure_joint" default="true">
- If [code]true[/code], the [code]PhysicalBone2D[/code] node will automatically configure the first [Joint2D] child node. The automatic configuration is limited to setting up the node properties and positioning the [Joint2D].
+ If [code]true[/code], the [PhysicalBone2D] will automatically configure the first [Joint2D] child node. The automatic configuration is limited to setting up the node properties and positioning the [Joint2D].
</member>
<member name="bone2d_index" type="int" setter="set_bone2d_index" getter="get_bone2d_index" default="-1">
- The index of the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating.
+ The index of the [Bone2D] that this [PhysicalBone2D] should simulate.
</member>
<member name="bone2d_nodepath" type="NodePath" setter="set_bone2d_nodepath" getter="get_bone2d_nodepath" default="NodePath(&quot;&quot;)">
- The [NodePath] to the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating.
+ The [NodePath] to the [Bone2D] that this [PhysicalBone2D] isshould simulate.
</member>
<member name="follow_bone_when_simulating" type="bool" setter="set_follow_bone_when_simulating" getter="get_follow_bone_when_simulating" default="false">
- If [code]true[/code], the [code]PhysicalBone2D[/code] will keep the transform of the bone it is bound to when simulating physics.
+ If [code]true[/code], the [PhysicalBone2D] will keep the transform of the bone it is bound to when simulating physics.
</member>
<member name="simulate_physics" type="bool" setter="set_simulate_physics" getter="get_simulate_physics" default="false">
- If [code]true[/code], the [code]PhysicalBone2D[/code] will start simulating using physics. If [code]false[/code], the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node.
- [b]Note:[/b] To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes.
+ If [code]true[/code], the [PhysicalBone2D] will start simulating using physics. If [code]false[/code], the [PhysicalBone2D] will follow the transform of the [Bone2D] node.
+ [b]Note:[/b] To have the [Bone2D]s visually follow the [PhysicalBone2D], use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes.
</member>
</members>
</class>
diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml
index 7d5d0f1691..06941c0a50 100644
--- a/doc/classes/PhysicalBone3D.xml
+++ b/doc/classes/PhysicalBone3D.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalBone3D" inherits="PhysicsBody3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A physics body used to make bones in a [Skeleton3D] react to physics.
</brief_description>
<description>
- [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
+ The [PhysicalBone3D] node is a physics body that can be used to make bones in a [Skeleton3D] react to physics.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml
index aedd9373b1..bfc25e0bd4 100644
--- a/doc/classes/PhysicsDirectBodyState2D.xml
+++ b/doc/classes/PhysicsDirectBodyState2D.xml
@@ -127,7 +127,7 @@
<return type="Vector2" />
<param index="0" name="contact_idx" type="int" />
<description>
- Returns the contact position in the collider.
+ Returns the position of the contact point on the collider in the global coordinate system.
</description>
</method>
<method name="get_contact_collider_shape" qualifiers="const">
@@ -169,7 +169,7 @@
<return type="Vector2" />
<param index="0" name="contact_idx" type="int" />
<description>
- Returns the local position of the contact point.
+ Returns the position of the contact point on the body in the global coordinate system.
</description>
</method>
<method name="get_contact_local_shape" qualifiers="const">
diff --git a/doc/classes/PhysicsDirectBodyState3D.xml b/doc/classes/PhysicsDirectBodyState3D.xml
index def9ca7a50..77ebd58ad9 100644
--- a/doc/classes/PhysicsDirectBodyState3D.xml
+++ b/doc/classes/PhysicsDirectBodyState3D.xml
@@ -127,7 +127,7 @@
<return type="Vector3" />
<param index="0" name="contact_idx" type="int" />
<description>
- Returns the contact position in the collider.
+ Returns the position of the contact point on the collider in the global coordinate system.
</description>
</method>
<method name="get_contact_collider_shape" qualifiers="const">
@@ -169,7 +169,7 @@
<return type="Vector3" />
<param index="0" name="contact_idx" type="int" />
<description>
- Returns the local position of the contact point.
+ Returns the position of the contact point on the body in the global coordinate system.
</description>
</method>
<method name="get_contact_local_shape" qualifiers="const">
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index 010a73a8e5..4ed65e7bfb 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -595,7 +595,7 @@
Sets the function used to calculate physics for the body, if that body allows it (see [method body_set_omit_force_integration]).
The force integration function takes the following two parameters:
1. a [PhysicsDirectBodyState2D] [code]state[/code]: used to retrieve and modify the body's state,
- 2. a [Variant] [code]userdata[/code]: optional user data.
+ 2. a [Variant] [param userdata]: optional user data.
[b]Note:[/b] This callback is currently not called in Godot Physics.
</description>
</method>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 6996061bd8..c278f53a38 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -209,6 +209,13 @@
Returns the maximum allowed width of the icon for the item at the given [param index].
</description>
</method>
+ <method name="get_item_icon_modulate" qualifiers="const">
+ <return type="Color" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns a [Color] modulating the item's icon at the given [param index].
+ </description>
+ </method>
<method name="get_item_id" qualifiers="const">
<return type="int" />
<param index="0" name="index" type="int" />
@@ -412,6 +419,14 @@
Sets the maximum allowed width of the icon for the item at the given [param index]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio.
</description>
</method>
+ <method name="set_item_icon_modulate">
+ <return type="void" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="modulate" type="Color" />
+ <description>
+ Sets a modulating [Color] of the item's icon at the given [param index].
+ </description>
+ </method>
<method name="set_item_id">
<return type="void" />
<param index="0" name="index" type="int" />
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 6eed8434ac..818e402067 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -20,9 +20,9 @@
<param index="0" name="hint" type="Dictionary" />
<description>
Adds a custom property info to a property. The dictionary must contain:
- - [code]name[/code]: [String] (the property's name)
- - [code]type[/code]: [int] (see [enum Variant.Type])
- - optionally [code]hint[/code]: [int] (see [enum PropertyHint]) and [code]hint_string[/code]: [String]
+ - [code]"name"[/code]: [String] (the property's name)
+ - [code]"type"[/code]: [int] (see [enum Variant.Type])
+ - optionally [code]"hint"[/code]: [int] (see [enum PropertyHint]) and [code]"hint_string"[/code]: [String]
[b]Example:[/b]
[codeblocks]
[gdscript]
@@ -456,6 +456,9 @@
<member name="debug/gdscript/warnings/redundant_await" 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 function that is not a coroutine is called with await.
</member>
+ <member name="debug/gdscript/warnings/redundant_static_unload" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables.
+ </member>
<member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1">
When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs.
</member>
@@ -543,6 +546,66 @@
<member name="debug/settings/stdout/verbose_stdout" type="bool" setter="" getter="" default="false">
Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc. This can also be enabled using the [code]--verbose[/code] or [code]-v[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], even on an exported project. See also [method OS.is_stdout_verbose] and [method @GlobalScope.print_verbose].
</member>
+ <member name="debug/shader_language/warnings/device_limit_exceeded" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when the shader exceeds certain device limits. Currently, the only device limit checked is the limit on uniform buffer size. More device limits will be added in the future.
+ </member>
+ <member name="debug/shader_language/warnings/enable" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], enables specific shader warnings (see [code]debug/shader_language/warnings/*[/code] settings). If [code]false[/code], disables all shader warnings.
+ </member>
+ <member name="debug/shader_language/warnings/float_comparison" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when two floating point numbers are compared directly with the [code]==[/code] operator or the [code]!=[/code] operator.
+ </member>
+ <member name="debug/shader_language/warnings/formatting_error" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning upon encountering certain formatting errors. Currently this only checks for empty statements. More formatting errors may be added over time.
+ </member>
+ <member name="debug/shader_language/warnings/treat_warnings_as_errors" type="bool" setter="" getter="" default="false">
+ When set to [code]true[/code], warnings are treated as errors.
+ </member>
+ <member name="debug/shader_language/warnings/unused_constant" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a constant is never used.
+ </member>
+ <member name="debug/shader_language/warnings/unused_function" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a function is never used.
+ </member>
+ <member name="debug/shader_language/warnings/unused_local_variable" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a local variable is never used.
+ </member>
+ <member name="debug/shader_language/warnings/unused_struct" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a struct is never used.
+ </member>
+ <member name="debug/shader_language/warnings/unused_uniform" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a uniform is never used.
+ </member>
+ <member name="debug/shader_language/warnings/unused_varying" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when a varying is never used.
+ </member>
+ <member name="debug/shapes/avoidance/agents_radius_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 0.25)">
+ Color of the avoidance agents radius, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/enable_agents_radius" type="bool" setter="" getter="" default="true">
+ If enabled, displays avoidance agents radius when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/enable_obstacles_radius" type="bool" setter="" getter="" default="true">
+ If enabled, displays avoidance obstacles radius when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/enable_obstacles_static" type="bool" setter="" getter="" default="true">
+ If enabled, displays static avoidance obstacles when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/obstacles_radius_color" type="Color" setter="" getter="" default="Color(1, 0.5, 0, 0.25)">
+ Color of the avoidance obstacles radius, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/obstacles_static_edge_pushin_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 1)">
+ Color of the static avoidance obstacles edges when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/obstacles_static_edge_pushout_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 1)">
+ Color of the static avoidance obstacles edges when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/obstacles_static_face_pushin_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 0)">
+ Color of the static avoidance obstacles faces when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
+ <member name="debug/shapes/avoidance/obstacles_static_face_pushout_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 0.5)">
+ Color of the static avoidance obstacles faces when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
+ </member>
<member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color(1, 0.2, 0.1, 0.8)">
Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
</member>
@@ -731,7 +794,8 @@
Changing this value allows setting up a multi-project scenario where there are multiple [code].csproj[/code]. Keep in mind that the Godot project is considered one of the C# projects in the workspace and it's root directory should contain the [code]project.godot[/code] and [code].csproj[/code] next to each other.
</member>
<member name="editor/export/convert_text_resources_to_binary" type="bool" setter="" getter="" default="true">
- If [code]true[/code] text resources are converted to binary format on export.
+ If [code]true[/code], text resources are converted to a binary format on export. This decreases file sizes and speeds up loading slightly.
+ [b]Note:[/b] If [member editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to return the converted files in an exported project. Some file paths within the exported PCK will also change, such as [code]project.godot[/code] becoming [code]project.binary[/code]. If you rely on run-time loading of files present within the PCK, set [member editor/export/convert_text_resources_to_binary] to [code]false[/code].
</member>
<member name="editor/import/reimport_missing_imported_files" type="bool" setter="" getter="" default="true">
</member>
@@ -821,6 +885,7 @@
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
</member>
<member name="gui/common/snap_controls_to_pixels" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], snaps [Control] node vertices to the nearest pixel to ensure they remain crisp even when the camera moves or zooms.
</member>
<member name="gui/common/swap_cancel_ok" type="bool" setter="" getter="">
If [code]true[/code], swaps [b]Cancel[/b] and [b]OK[/b] buttons in dialogs on Windows and UWP to follow interface conventions. [method DisplayServer.get_swap_cancel_ok] can be used to query whether buttons are swapped at run-time.
@@ -1238,6 +1303,30 @@
<member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1".
</member>
+ <member name="layer_names/2d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 2. If left empty, the layer will display as "Layer 2".
+ </member>
+ <member name="layer_names/2d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 3. If left empty, the layer will display as "Layer 3".
+ </member>
+ <member name="layer_names/2d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4".
+ </member>
+ <member name="layer_names/2d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 5. If left empty, the layer will display as "Layer 5".
+ </member>
+ <member name="layer_names/2d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 6. If left empty, the layer will display as "Layer 6".
+ </member>
+ <member name="layer_names/2d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/2d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/2d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D navigation layer 9. If left empty, the layer will display as "Layer 9".
+ </member>
<member name="layer_names/2d_navigation/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 10. If left empty, the layer will display as "Layer 10".
</member>
@@ -1268,9 +1357,6 @@
<member name="layer_names/2d_navigation/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/2d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/2d_navigation/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 20. If left empty, the layer will display as "Layer 20".
</member>
@@ -1301,9 +1387,6 @@
<member name="layer_names/2d_navigation/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 29. If left empty, the layer will display as "Layer 29".
</member>
- <member name="layer_names/2d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 3. If left empty, the layer will display as "Layer 3".
- </member>
<member name="layer_names/2d_navigation/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 30. If left empty, the layer will display as "Layer 30".
</member>
@@ -1313,26 +1396,32 @@
<member name="layer_names/2d_navigation/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 32. If left empty, the layer will display as "Layer 32".
</member>
- <member name="layer_names/2d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/2d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/2d_physics/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/2d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/2d_physics/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/2d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/2d_physics/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/2d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/2d_physics/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/2d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D navigation layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/2d_physics/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 1. If left empty, the layer will display as "Layer 1".
+ <member name="layer_names/2d_physics/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/2d_physics/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/2d_physics/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D physics layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/2d_physics/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 10. If left empty, the layer will display as "Layer 10".
@@ -1364,9 +1453,6 @@
<member name="layer_names/2d_physics/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/2d_physics/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/2d_physics/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 20. If left empty, the layer will display as "Layer 20".
</member>
@@ -1397,9 +1483,6 @@
<member name="layer_names/2d_physics/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 29. If left empty, the layer will display as "Layer 29".
</member>
- <member name="layer_names/2d_physics/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 3. If left empty, the layer will display as "Layer 3".
- </member>
<member name="layer_names/2d_physics/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 30. If left empty, the layer will display as "Layer 30".
</member>
@@ -1409,26 +1492,32 @@
<member name="layer_names/2d_physics/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 32. If left empty, the layer will display as "Layer 32".
</member>
- <member name="layer_names/2d_physics/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/2d_render/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/2d_physics/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/2d_render/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/2d_physics/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/2d_render/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/2d_physics/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/2d_render/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/2d_physics/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/2d_render/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/2d_physics/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D physics layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/2d_render/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/2d_render/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 1. If left empty, the layer will display as "Layer 1".
+ <member name="layer_names/2d_render/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/2d_render/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 2D render layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/2d_render/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D render layer 10. If left empty, the layer will display as "Layer 10".
@@ -1460,35 +1549,35 @@
<member name="layer_names/2d_render/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D render layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/2d_render/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/2d_render/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D render layer 20. If left empty, the layer will display as "Layer 20".
</member>
- <member name="layer_names/2d_render/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 3. If left empty, the layer will display as "Layer 3".
+ <member name="layer_names/3d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/2d_render/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/3d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/2d_render/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/3d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/2d_render/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/3d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/2d_render/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/3d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/2d_render/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/3d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 2D render layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/3d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 7. If left empty, the layer will display as "Layer 7".
</member>
- <member name="layer_names/3d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 1. If left empty, the layer will display as "Layer 1".
+ <member name="layer_names/3d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/3d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D navigation layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/3d_navigation/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 10. If left empty, the layer will display as "Layer 10".
@@ -1520,9 +1609,6 @@
<member name="layer_names/3d_navigation/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/3d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/3d_navigation/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 20. If left empty, the layer will display as "Layer 20".
</member>
@@ -1553,9 +1639,6 @@
<member name="layer_names/3d_navigation/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 29. If left empty, the layer will display as "Layer 29".
</member>
- <member name="layer_names/3d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 3. If left empty, the layer will display as "Layer 3".
- </member>
<member name="layer_names/3d_navigation/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 30. If left empty, the layer will display as "Layer 30".
</member>
@@ -1565,26 +1648,32 @@
<member name="layer_names/3d_navigation/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 32. If left empty, the layer will display as "Layer 32".
</member>
- <member name="layer_names/3d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/3d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/3d_physics/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/3d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/3d_physics/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/3d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/3d_physics/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/3d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/3d_physics/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/3d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D navigation layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/3d_physics/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 1. If left empty, the layer will display as "Layer 1".
+ <member name="layer_names/3d_physics/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/3d_physics/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/3d_physics/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D physics layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/3d_physics/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 10. If left empty, the layer will display as "Layer 10".
@@ -1616,9 +1705,6 @@
<member name="layer_names/3d_physics/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/3d_physics/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/3d_physics/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 20. If left empty, the layer will display as "Layer 20".
</member>
@@ -1649,9 +1735,6 @@
<member name="layer_names/3d_physics/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 29. If left empty, the layer will display as "Layer 29".
</member>
- <member name="layer_names/3d_physics/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 3. If left empty, the layer will display as "Layer 3".
- </member>
<member name="layer_names/3d_physics/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 30. If left empty, the layer will display as "Layer 30".
</member>
@@ -1661,26 +1744,32 @@
<member name="layer_names/3d_physics/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 32. If left empty, the layer will display as "Layer 32".
</member>
- <member name="layer_names/3d_physics/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/3d_render/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/3d_physics/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/3d_render/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/3d_physics/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/3d_render/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/3d_physics/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/3d_render/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/3d_physics/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/3d_render/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/3d_physics/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D physics layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/3d_render/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/3d_render/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 1. If left empty, the layer will display as "Layer 1".
+ <member name="layer_names/3d_render/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/3d_render/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/3d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the 3D render layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/3d_render/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D render layer 10. If left empty, the layer will display as "Layer 10".
@@ -1712,32 +1801,104 @@
<member name="layer_names/3d_render/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D render layer 19. If left empty, the layer will display as "Layer 19".
</member>
- <member name="layer_names/3d_render/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 2. If left empty, the layer will display as "Layer 2".
- </member>
<member name="layer_names/3d_render/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D render layer 20. If left empty, the layer will display as "Layer 20".
</member>
- <member name="layer_names/3d_render/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 3. If left empty, the layer will display as "Layer 3".
+ <member name="layer_names/avoidance/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 1. If left empty, the layer will display as "Layer 1".
</member>
- <member name="layer_names/3d_render/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 4. If left empty, the layer will display as "Layer 4".
+ <member name="layer_names/avoidance/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 2. If left empty, the layer will display as "Layer 2".
</member>
- <member name="layer_names/3d_render/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 5. If left empty, the layer will display as "Layer 5".
+ <member name="layer_names/avoidance/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 3. If left empty, the layer will display as "Layer 3".
</member>
- <member name="layer_names/3d_render/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 6. If left empty, the layer will display as "Layer 6".
+ <member name="layer_names/avoidance/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 4. If left empty, the layer will display as "Layer 4".
</member>
- <member name="layer_names/3d_render/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 7. If left empty, the layer will display as "Layer 7".
+ <member name="layer_names/avoidance/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 5. If left empty, the layer will display as "Layer 5".
</member>
- <member name="layer_names/3d_render/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 8. If left empty, the layer will display as "Layer 8".
+ <member name="layer_names/avoidance/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 6. If left empty, the layer will display as "Layer 6".
</member>
- <member name="layer_names/3d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
- Optional name for the 3D render layer 9. If left empty, the layer will display as "Layer 9".
+ <member name="layer_names/avoidance/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 7. If left empty, the layer will display as "Layer 7".
+ </member>
+ <member name="layer_names/avoidance/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 8. If left empty, the layer will display as "Layer 8".
+ </member>
+ <member name="layer_names/avoidance/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 9. If left empty, the layer will display as "Layer 9".
+ </member>
+ <member name="layer_names/avoidance/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 10. If left empty, the layer will display as "Layer 10".
+ </member>
+ <member name="layer_names/avoidance/layer_11" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 11. If left empty, the layer will display as "Layer 11".
+ </member>
+ <member name="layer_names/avoidance/layer_12" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 12. If left empty, the layer will display as "Layer 12".
+ </member>
+ <member name="layer_names/avoidance/layer_13" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 13. If left empty, the layer will display as "Layer 13".
+ </member>
+ <member name="layer_names/avoidance/layer_14" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 14. If left empty, the layer will display as "Layer 14".
+ </member>
+ <member name="layer_names/avoidance/layer_15" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 15. If left empty, the layer will display as "Layer 15".
+ </member>
+ <member name="layer_names/avoidance/layer_16" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 16. If left empty, the layer will display as "Layer 16".
+ </member>
+ <member name="layer_names/avoidance/layer_17" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 17. If left empty, the layer will display as "Layer 17".
+ </member>
+ <member name="layer_names/avoidance/layer_18" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 18. If left empty, the layer will display as "Layer 18".
+ </member>
+ <member name="layer_names/avoidance/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 19. If left empty, the layer will display as "Layer 19".
+ </member>
+ <member name="layer_names/avoidance/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 20. If left empty, the layer will display as "Layer 20".
+ </member>
+ <member name="layer_names/avoidance/layer_21" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 21. If left empty, the layer will display as "Layer 21".
+ </member>
+ <member name="layer_names/avoidance/layer_22" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 22. If left empty, the layer will display as "Layer 22".
+ </member>
+ <member name="layer_names/avoidance/layer_23" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 23. If left empty, the layer will display as "Layer 23".
+ </member>
+ <member name="layer_names/avoidance/layer_24" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 24. If left empty, the layer will display as "Layer 24".
+ </member>
+ <member name="layer_names/avoidance/layer_25" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 25. If left empty, the layer will display as "Layer 25".
+ </member>
+ <member name="layer_names/avoidance/layer_26" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 26. If left empty, the layer will display as "Layer 26".
+ </member>
+ <member name="layer_names/avoidance/layer_27" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 27. If left empty, the layer will display as "Layer 27".
+ </member>
+ <member name="layer_names/avoidance/layer_28" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 28. If left empty, the layer will display as "Layer 28".
+ </member>
+ <member name="layer_names/avoidance/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 29. If left empty, the layer will display as "Layer 29".
+ </member>
+ <member name="layer_names/avoidance/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 30. If left empty, the layer will display as "Layer 30".
+ </member>
+ <member name="layer_names/avoidance/layer_31" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 31. If left empty, the layer will display as "Layer 31".
+ </member>
+ <member name="layer_names/avoidance/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
+ Optional name for the navigation avoidance layer 32. If left empty, the layer will display as "Layer 32".
</member>
<member name="memory/limits/message_queue/max_size_mb" type="int" setter="" getter="" default="32">
Godot uses a message queue to defer some function calls. If you run out of space on it (you will see an error), you can increase the size here.
@@ -1754,6 +1915,9 @@
<member name="navigation/2d/default_link_connection_radius" type="int" setter="" getter="" default="4">
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">
+ If enabled 2D 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 World2D default navigation maps.
+ </member>
<member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.25">
Default cell size for 3D navigation maps. See [method NavigationServer3D.map_set_cell_size].
</member>
@@ -1763,6 +1927,15 @@
<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/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>
+ <member name="navigation/avoidance/thread_model/avoidance_use_high_priority_threads" type="bool" setter="" getter="" default="true">
+ If enabled and avoidance calculations use multiple threads the threads run with high priority.
+ </member>
+ <member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
+ If enabled the avoidance calculations use multiple threads.
+ </member>
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
</member>
@@ -1784,12 +1957,6 @@
<member name="network/limits/webrtc/max_channel_in_buffer_kb" type="int" setter="" getter="" default="64">
Maximum size (in kiB) for the [WebRTCDataChannel] input buffer.
</member>
- <member name="network/remote_fs/page_read_ahead" type="int" setter="" getter="" default="4">
- Amount of read ahead used by remote filesystem. Higher values decrease the effects of latency at the cost of higher bandwidth usage.
- </member>
- <member name="network/remote_fs/page_size" type="int" setter="" getter="" default="65536">
- Page size used by remote filesystem (in bytes).
- </member>
<member name="network/tls/certificate_bundle_override" type="String" setter="" getter="" default="&quot;&quot;">
The CA certificates bundle to use for TLS connections. If this is set to a non-empty value, this will [i]override[/i] Godot's default [url=https://github.com/godotengine/godot/blob/master/thirdparty/certs/ca-certificates.crt]Mozilla certificate bundle[/url]. If left empty, the default certificate bundle will be used.
If in doubt, leave this setting empty.
@@ -1952,23 +2119,34 @@
[b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value.
</member>
<member name="rendering/2d/sdf/oversize" type="int" setter="" getter="" default="1">
+ Controls how much of the original viewport size should be covered by the 2D signed distance field. This SDF can be sampled in [CanvasItem] shaders and is used for [GPUParticles2D] collision. Higher values allow portions of occluders located outside the viewport to still be taken into account in the generated signed distance field, at the cost of performance. If you notice particles falling through [LightOccluder2D]s as the occluders leave the viewport, increase this setting.
+ The percentage specified is added on each axis and on both sides. For example, with the default setting of 120%, the signed distance field will cover 20% of the viewport's size outside the viewport on each side (top, right, bottom, left).
+ [b]Note:[/b] This property is only read when the project starts. To change the 2D SDF oversizing percentage at runtime, use [method RenderingServer.viewport_set_sdf_oversize_and_scale] instead.
</member>
<member name="rendering/2d/sdf/scale" type="int" setter="" getter="" default="1">
+ The resolution scale to use for the 2D signed distance field. Higher values lead to a more precise and more stable signed distance field as the camera moves, at the cost of performance. The default value (50%) renders at half the resolution of the viewport size on each axis, which means the SDF is generated with 25% of the viewport's pixel count.
+ [b]Note:[/b] This property is only read when the project starts. To change the 2D SDF resolution scale at runtime, use [method RenderingServer.viewport_set_sdf_oversize_and_scale] instead.
</member>
<member name="rendering/2d/shadow_atlas/size" type="int" setter="" getter="" default="2048">
+ The size of the 2D shadow atlas in pixels. Higher values result in more precise [Light2D] shadows, at the cost of performance and video memory usage. The specified value is rounded up to the nearest power of 2.
+ [b]Note:[/b] This property is only read when the project starts. To change the 2D shadow atlas size at runtime, use [method RenderingServer.canvas_set_shadow_texture_size] instead.
</member>
<member name="rendering/2d/snap/snap_2d_transforms_to_pixel" type="bool" setter="" getter="" default="false">
- If [code]true[/code], [CanvasItem] nodes will internally snap to full pixels. Their position can still be sub-pixel, but the decimals will not have effect.
+ If [code]true[/code], [CanvasItem] nodes will internally snap to full pixels. Their position can still be sub-pixel, but the decimals will not have effect. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
+ [b]Note:[/b] This property is only read when the project starts. To toggle 2D transform snapping at runtime, use [method RenderingServer.viewport_set_snap_2d_transforms_to_pixel] on the root [Viewport] instead.
+ [b]Note:[/b] [Control] nodes are snapped to the nearest pixel by default. This is controlled by [member gui/common/snap_controls_to_pixels].
</member>
<member name="rendering/2d/snap/snap_2d_vertices_to_pixel" type="bool" setter="" getter="" default="false">
- If [code]true[/code], vertices of [CanvasItem] nodes will snap to full pixels. Only affects the final vertex positions, not the transforms.
+ If [code]true[/code], vertices of [CanvasItem] nodes will snap to full pixels. Only affects the final vertex positions, not the transforms. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
+ [b]Note:[/b] This property is only read when the project starts. To toggle 2D vertex snapping at runtime, use [method RenderingServer.viewport_set_snap_2d_vertices_to_pixel] on the root [Viewport] instead.
+ [b]Note:[/b] [Control] nodes are snapped to the nearest pixel by default. This is controlled by [member gui/common/snap_controls_to_pixels].
</member>
<member name="rendering/anti_aliasing/quality/msaa_2d" type="int" setter="" getter="" default="0">
- Sets the number of MSAA samples to use for 2D/Canvas rendering (as a power of two). MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware. This has no effect on shader-induced aliasing or texture aliasing.
+ Sets the number of MSAA samples to use for 2D/Canvas rendering (as a power of two). MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware, especially integrated graphics due to their limited memory bandwidth. This has no effect on shader-induced aliasing or texture aliasing.
[b]Note:[/b] MSAA is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
</member>
<member name="rendering/anti_aliasing/quality/msaa_3d" type="int" setter="" getter="" default="0">
- Sets the number of MSAA samples to use for 3D rendering (as a power of two). MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware. See also bilinear scaling 3d [member rendering/scaling_3d/mode] for supersampling, which provides higher quality but is much more expensive. This has no effect on shader-induced aliasing or texture aliasing.
+ Sets the number of MSAA samples to use for 3D rendering (as a power of two). MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware, especially integrated graphics due to their limited memory bandwidth. See also [member rendering/scaling_3d/mode] for supersampling, which provides higher quality but is much more expensive. This has no effect on shader-induced aliasing or texture aliasing.
[b]Note:[/b] MSAA is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
</member>
<member name="rendering/anti_aliasing/quality/screen_space_aa" type="int" setter="" getter="" default="0">
@@ -1987,12 +2165,15 @@
[b]Note:[/b] TAA is only supported in the Forward+ rendering method, not Mobile or Compatibility.
</member>
<member name="rendering/anti_aliasing/screen_space_roughness_limiter/amount" type="float" setter="" getter="" default="0.25">
+ [b]Note:[/b] This property is only read when the project starts. To control the screen-space roughness limiter at runtime, call [method RenderingServer.screen_space_roughness_limiter_set_active] instead.
</member>
<member name="rendering/anti_aliasing/screen_space_roughness_limiter/enabled" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables a spatial filter to limit roughness in areas with high-frequency detail. This can help reduce specular aliasing to an extent, though not as much as enabling [member rendering/anti_aliasing/quality/use_taa]. This filter has a small performance cost, so consider disabling it if it doesn't benefit your scene noticeably.
- [b]Note:[/b] TAA is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
+ [b]Note:[/b] The screen-space roughness limiter is only supported in the Forward+ and Mobile rendering methods, not Compatibility.
+ [b]Note:[/b] This property is only read when the project starts. To control the screen-space roughness limiter at runtime, call [method RenderingServer.screen_space_roughness_limiter_set_active] instead.
</member>
<member name="rendering/anti_aliasing/screen_space_roughness_limiter/limit" type="float" setter="" getter="" default="0.18">
+ [b]Note:[/b] This property is only read when the project starts. To control the screen-space roughness limiter at runtime, call [method RenderingServer.screen_space_roughness_limiter_set_active] instead.
</member>
<member name="rendering/camera/depth_of_field/depth_of_field_bokeh_quality" type="int" setter="" getter="" default="1">
Sets the quality of the depth of field effect. Higher quality takes more samples, which is slower but looks smoother.
@@ -2065,13 +2246,16 @@
Sets the quality of the screen-space indirect lighting effect. Higher values take more samples and so will result in better quality, at the cost of performance. Setting to [code]Ultra[/code] will use the [member rendering/environment/ssil/adaptive_target] setting.
</member>
<member name="rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale" type="float" setter="" getter="" default="0.01">
- Scales the depth over which the subsurface scattering effect is applied. A high value may allow light to scatter into a part of the mesh or another mesh that is close in screen space but far in depth.
+ Scales the depth over which the subsurface scattering effect is applied. A high value may allow light to scatter into a part of the mesh or another mesh that is close in screen space but far in depth. See also [member rendering/environment/subsurface_scattering/subsurface_scattering_scale].
+ [b]Note:[/b] This property is only read when the project starts. To set the subsurface scattering depth scale at runtime, call [method RenderingServer.sub_surface_scattering_set_scale] instead.
</member>
<member name="rendering/environment/subsurface_scattering/subsurface_scattering_quality" type="int" setter="" getter="" default="1">
- Sets the quality of the subsurface scattering effect. Higher values are slower but look nicer.
+ Sets the quality of the subsurface scattering effect. Higher values are slower but look nicer. This affects the rendering of materials that have [member BaseMaterial3D.subsurf_scatter_enabled] set to [code]true[/code], along with [ShaderMaterial]s that set [code]SSS_STRENGTH[/code].
+ [b]Note:[/b] This property is only read when the project starts. To set the subsurface scattering quality at runtime, call [method RenderingServer.sub_surface_scattering_set_quality] instead.
</member>
<member name="rendering/environment/subsurface_scattering/subsurface_scattering_scale" type="float" setter="" getter="" default="0.05">
- Scales the distance over which samples are taken for subsurface scattering effect. Changing this does not impact performance, but higher values will result in significant artifacts as the samples will become obviously spread out. A lower value results in a smaller spread of scattered light.
+ Scales the distance over which samples are taken for subsurface scattering effect. Changing this does not impact performance, but higher values will result in significant artifacts as the samples will become obviously spread out. A lower value results in a smaller spread of scattered light. See also [member rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale].
+ [b]Note:[/b] This property is only read when the project starts. To set the subsurface scattering scale at runtime, call [method RenderingServer.sub_surface_scattering_set_scale] instead.
</member>
<member name="rendering/environment/volumetric_fog/use_filter" type="int" setter="" getter="" default="1">
Enables filtering of the volumetric fog effect prior to integration. This substantially blurs the fog which reduces fine details but also smooths out harsh edges and aliasing artifacts. Disable when more detail is required.
@@ -2111,12 +2295,21 @@
[b]Note:[/b] This property is only read when the project starts. To set half-resolution GI at run-time, call [method RenderingServer.gi_set_use_half_resolution] instead.
</member>
<member name="rendering/global_illumination/sdfgi/frames_to_converge" type="int" setter="" getter="" default="5">
+ The number of frames to use for converging signed distance field global illumination. Higher values lead to a less noisy result, at the cost of taking a longer time to fully converge. This means the scene's global illumination will be too dark for a longer period of time, especially when the camera moves fast. The actual convergence speed depends on rendered framerate. For example, with the default setting of 30 frames, rendering at 60 FPS will make SDFGI fully converge after 0.5 seconds. See also [member rendering/global_illumination/sdfgi/frames_to_update_lights] and [member rendering/global_illumination/sdfgi/probe_ray_count].
+ [b]Note:[/b] This property is only read when the project starts. To control SDFGI convergence speed at runtime, call [method RenderingServer.environment_set_sdfgi_frames_to_converge] instead.
</member>
<member name="rendering/global_illumination/sdfgi/frames_to_update_lights" type="int" setter="" getter="" default="2">
+ The number of frames over which dynamic lights should be updated in signed distance field global illumination. Higher values take more time to update indirect lighting coming from dynamic lights, but result in better performance when many dynamic lights are present. See also [member rendering/global_illumination/sdfgi/frames_to_converge] and [member rendering/global_illumination/sdfgi/probe_ray_count].
+ [b]Note:[/b] This only affects [Light3D] nodes whose [member Light3D.light_bake_mode] is [constant Light3D.BAKE_DYNAMIC] (which is the default). Consider making non-moving lights use the [constant Light3D.BAKE_STATIC] bake mode to improve performance.
+ [b]Note:[/b] This property is only read when the project starts. To control SDFGI light update speed at runtime, call [method RenderingServer.environment_set_sdfgi_frames_to_update_light] instead.
</member>
<member name="rendering/global_illumination/sdfgi/probe_ray_count" type="int" setter="" getter="" default="1">
+ The number of rays to throw per frame when computing signed distance field global illumination. Higher values lead to a less noisy result, at the cost of performance. See also [member rendering/global_illumination/sdfgi/frames_to_converge] and [member rendering/global_illumination/sdfgi/frames_to_update_lights].
+ [b]Note:[/b] This property is only read when the project starts. To control SDFGI quality at runtime, call [method RenderingServer.environment_set_sdfgi_ray_count] instead.
</member>
<member name="rendering/global_illumination/voxel_gi/quality" type="int" setter="" getter="" default="0">
+ The VoxelGI quality to use. High quality leads to more precise lighting and better reflections, but is slower to render. This setting does not affect the baked data and doesn't require baking the [VoxelGI] again to apply.
+ [b]Note:[/b] This property is only read when the project starts. To control VoxelGI quality at runtime, call [method RenderingServer.voxel_gi_set_quality] instead.
</member>
<member name="rendering/lightmapping/bake_performance/max_rays_per_pass" type="int" setter="" getter="" default="32">
The maximum number of rays that can be thrown per pass when baking lightmaps with [LightmapGI]. Depending on the scene, adjusting this value may result in higher GPU utilization when baking lightmaps, leading to faster bake times.
@@ -2158,10 +2351,10 @@
The framerate-independent update speed when representing dynamic object lighting from [LightmapProbe]s. Higher values make dynamic object lighting update faster. Higher values can prevent fast-moving objects from having "outdated" indirect lighting displayed on them, at the cost of possible flickering when an object moves from a bright area to a shaded area.
</member>
<member name="rendering/lights_and_shadows/directional_shadow/16_bits" type="bool" setter="" getter="" default="true">
- Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
+ Use 16 bits for the directional shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
</member>
<member name="rendering/lights_and_shadows/directional_shadow/size" type="int" setter="" getter="" default="4096">
- The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value will be rounded up to the nearest power of 2.
+ The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value is rounded up to the nearest power of 2.
</member>
<member name="rendering/lights_and_shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048">
Lower-end override for [member rendering/lights_and_shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support.
@@ -2175,7 +2368,7 @@
Lower-end override for [member rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/lights_and_shadows/positional_shadow/atlas_16_bits" type="bool" setter="" getter="" default="true">
- Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
+ Use 16 bits for the omni/spot shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
</member>
<member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv" type="int" setter="" getter="" default="2">
Subdivision quadrant size for shadow mapping. See shadow mapping documentation.
@@ -2239,10 +2432,12 @@
[b]Note:[/b] This property is only read when the project starts. To adjust the automatic LOD threshold at runtime, set [member Viewport.mesh_lod_threshold] on the root [Viewport].
</member>
<member name="rendering/occlusion_culling/bvh_build_quality" type="int" setter="" getter="" default="2">
- The [url=https://en.wikipedia.org/wiki/Bounding_volume_hierarchy]BVH[/url] quality to use when rendering the occlusion culling buffer. Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage.
+ The [url=https://en.wikipedia.org/wiki/Bounding_volume_hierarchy]Bounding Volume Hierarchy[/url] quality to use when rendering the occlusion culling buffer. Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage. See also [member rendering/occlusion_culling/occlusion_rays_per_thread].
+ [b]Note:[/b] This property is only read when the project starts. To adjust the BVH build quality at runtime, use [method RenderingServer.viewport_set_occlusion_culling_build_quality].
</member>
<member name="rendering/occlusion_culling/occlusion_rays_per_thread" type="int" setter="" getter="" default="512">
- Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage. The occlusion culling buffer's pixel count is roughly equal to [code]occlusion_rays_per_thread * number_of_logical_cpu_cores[/code], so it will depend on the system's CPU. Therefore, CPUs with fewer cores will use a lower resolution to attempt keeping performance costs even across devices.
+ The number of occlusion rays traced per CPU thread. Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage. The occlusion culling buffer's pixel count is roughly equal to [code]occlusion_rays_per_thread * number_of_logical_cpu_cores[/code], so it will depend on the system's CPU. Therefore, CPUs with fewer cores will use a lower resolution to attempt keeping performance costs even across devices. See also [member rendering/occlusion_culling/bvh_build_quality].
+ [b]Note:[/b] This property is only read when the project starts. To adjust the number of occlusion rays traced per thread at runtime, use [method RenderingServer.viewport_set_occlusion_rays_per_thread].
</member>
<member name="rendering/occlusion_culling/use_occlusion_culling" type="bool" setter="" getter="" default="false">
If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D in the root viewport. In custom viewports, [member Viewport.use_occlusion_culling] must be set to [code]true[/code] instead.
diff --git a/doc/classes/RDAttachmentFormat.xml b/doc/classes/RDAttachmentFormat.xml
index 4d912da1ef..1c32041f4b 100644
--- a/doc/classes/RDAttachmentFormat.xml
+++ b/doc/classes/RDAttachmentFormat.xml
@@ -1,17 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDAttachmentFormat" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Attachment format (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="36">
+ The attachment's data format.
</member>
<member name="samples" type="int" setter="set_samples" getter="get_samples" enum="RenderingDevice.TextureSamples" default="0">
+ The number of samples used when sampling the attachment.
</member>
<member name="usage_flags" type="int" setter="set_usage_flags" getter="get_usage_flags" default="0">
+ The attachment's usage flags, which determine what can be done with it.
</member>
</members>
</class>
diff --git a/doc/classes/RDFramebufferPass.xml b/doc/classes/RDFramebufferPass.xml
index b78658d559..5a296da19c 100644
--- a/doc/classes/RDFramebufferPass.xml
+++ b/doc/classes/RDFramebufferPass.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDFramebufferPass" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Framebuffer pass attachment description.
+ Framebuffer pass attachment description (used by [RenderingDevice]).
</brief_description>
<description>
This class contains the list of attachment descriptions for a framebuffer pass. Each points with an index to a previously supplied list of texture attachments.
- Multipass framebuffers can optimize some configurations in mobile, on desktop they provide little to no advantage.
+ Multipass framebuffers can optimize some configurations in mobile. On desktop, they provide little to no advantage.
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
@@ -28,6 +29,7 @@
</members>
<constants>
<constant name="ATTACHMENT_UNUSED" value="-1">
+ Attachment is unused.
</constant>
</constants>
</class>
diff --git a/doc/classes/RDPipelineColorBlendState.xml b/doc/classes/RDPipelineColorBlendState.xml
index 7ce5157cc5..363349927e 100644
--- a/doc/classes/RDPipelineColorBlendState.xml
+++ b/doc/classes/RDPipelineColorBlendState.xml
@@ -1,19 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineColorBlendState" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline color blend state (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="attachments" type="RDPipelineColorBlendStateAttachment[]" setter="set_attachments" getter="get_attachments" default="[]">
+ The attachments that are blended together.
</member>
<member name="blend_constant" type="Color" setter="set_blend_constant" getter="get_blend_constant" default="Color(0, 0, 0, 1)">
+ The constant color to blend with. See also [method RenderingDevice.draw_list_set_blend_constants].
</member>
<member name="enable_logic_op" type="bool" setter="set_enable_logic_op" getter="get_enable_logic_op" default="false">
+ If [code]true[/code], performs the logic operation defined in [member logic_op].
</member>
<member name="logic_op" type="int" setter="set_logic_op" getter="get_logic_op" enum="RenderingDevice.LogicOperation" default="0">
+ The logic operation to perform for blending. Only effective if [member enable_logic_op] is [code]true[/code].
</member>
</members>
</class>
diff --git a/doc/classes/RDPipelineColorBlendStateAttachment.xml b/doc/classes/RDPipelineColorBlendStateAttachment.xml
index ff52be9b23..4892536814 100644
--- a/doc/classes/RDPipelineColorBlendStateAttachment.xml
+++ b/doc/classes/RDPipelineColorBlendStateAttachment.xml
@@ -1,8 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineColorBlendStateAttachment" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline color blend state attachment (used by [RenderingDevice]).
</brief_description>
<description>
+ Controls how blending between source and destination fragments is performed when using [RenderingDevice].
+ For reference, this is how common user-facing blend modes are implemented in Godot's 2D renderer:
+ [b]Mix:[/b]
+ [codeblock]
+ var attachment = RDPipelineColorBlendStateAttachment.new()
+ attachment.enable_blend = true
+ attachment.color_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.src_color_blend_factor = RenderingDevice.BLEND_FACTOR_SRC_ALPHA
+ attachment.dst_color_blend_factor = RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ attachment.alpha_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.src_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ attachment.dst_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ [/codeblock]
+ [b]Add:[/b]
+ [codeblock]
+ var attachment = RDPipelineColorBlendStateAttachment.new()
+ attachment.enable_blend = true
+ attachment.alpha_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.color_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.src_color_blend_factor = RenderingDevice.BLEND_FACTOR_SRC_ALPHA
+ attachment.dst_color_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ attachment.src_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_SRC_ALPHA
+ attachment.dst_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ [/codeblock]
+ [b]Subtract:[/b]
+ [codeblock]
+ var attachment = RDPipelineColorBlendStateAttachment.new()
+ attachment.enable_blend = true
+ attachment.alpha_blend_op = RenderingDevice.BLEND_OP_SUBTRACT
+ attachment.color_blend_op = RenderingDevice.BLEND_OP_SUBTRACT
+ attachment.src_color_blend_factor = RenderingDevice.BLEND_FACTOR_SRC_ALPHA
+ attachment.dst_color_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ attachment.src_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_SRC_ALPHA
+ attachment.dst_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ [/codeblock]
+ [b]Multiply:[/b]
+ [codeblock]
+ var attachment = RDPipelineColorBlendStateAttachment.new()
+ attachment.enable_blend = true
+ attachment.alpha_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.color_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.src_color_blend_factor = RenderingDevice.BLEND_FACTOR_DST_COLOR
+ attachment.dst_color_blend_factor = RenderingDevice.BLEND_FACTOR_ZERO
+ attachment.src_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_DST_ALPHA
+ attachment.dst_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ZERO
+ [/codeblock]
+ [b]Pre-multiplied alpha:[/b]
+ [codeblock]
+ var attachment = RDPipelineColorBlendStateAttachment.new()
+ attachment.enable_blend = true
+ attachment.alpha_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.color_blend_op = RenderingDevice.BLEND_OP_ADD
+ attachment.src_color_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ attachment.dst_color_blend_factor = RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ attachment.src_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE
+ attachment.dst_alpha_blend_factor = RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -10,31 +68,43 @@
<method name="set_as_mix">
<return type="void" />
<description>
+ Convenience method to perform standard mix blending with straight (non-premultiplied) alpha. This sets [member enable_blend] to [code]true[/code], [member src_color_blend_factor] to [constant RenderingDevice.BLEND_FACTOR_SRC_ALPHA], [member dst_color_blend_factor] to [constant RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA], [member src_alpha_blend_factor] to [constant RenderingDevice.BLEND_FACTOR_SRC_ALPHA] and [member dst_alpha_blend_factor] to [constant RenderingDevice.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA].
</description>
</method>
</methods>
<members>
<member name="alpha_blend_op" type="int" setter="set_alpha_blend_op" getter="get_alpha_blend_op" enum="RenderingDevice.BlendOperation" default="0">
+ The blend mode to use for the alpha channel.
</member>
<member name="color_blend_op" type="int" setter="set_color_blend_op" getter="get_color_blend_op" enum="RenderingDevice.BlendOperation" default="0">
+ The blend mode to use for the red/green/blue color channels.
</member>
<member name="dst_alpha_blend_factor" type="int" setter="set_dst_alpha_blend_factor" getter="get_dst_alpha_blend_factor" enum="RenderingDevice.BlendFactor" default="0">
+ Controls how the blend factor for the alpha channel is determined based on the destination's fragments.
</member>
<member name="dst_color_blend_factor" type="int" setter="set_dst_color_blend_factor" getter="get_dst_color_blend_factor" enum="RenderingDevice.BlendFactor" default="0">
+ Controls how the blend factor for the color channels is determined based on the destination's fragments.
</member>
<member name="enable_blend" type="bool" setter="set_enable_blend" getter="get_enable_blend" default="false">
+ If [code]true[/code], performs blending between the source and destination according to the factors defined in [member src_color_blend_factor], [member dst_color_blend_factor], [member src_alpha_blend_factor] and [member dst_alpha_blend_factor]. The blend modes [member color_blend_op] and [member alpha_blend_op] are also taken into account, with [member write_r], [member write_g], [member write_b] and [member write_a] controlling the output.
</member>
<member name="src_alpha_blend_factor" type="int" setter="set_src_alpha_blend_factor" getter="get_src_alpha_blend_factor" enum="RenderingDevice.BlendFactor" default="0">
+ Controls how the blend factor for the alpha channel is determined based on the source's fragments.
</member>
<member name="src_color_blend_factor" type="int" setter="set_src_color_blend_factor" getter="get_src_color_blend_factor" enum="RenderingDevice.BlendFactor" default="0">
+ Controls how the blend factor for the color channels is determined based on the source's fragments.
</member>
<member name="write_a" type="bool" setter="set_write_a" getter="get_write_a" default="true">
+ If [code]true[/code], writes the new alpha channel to the final result.
</member>
<member name="write_b" type="bool" setter="set_write_b" getter="get_write_b" default="true">
+ If [code]true[/code], writes the new blue color channel to the final result.
</member>
<member name="write_g" type="bool" setter="set_write_g" getter="get_write_g" default="true">
+ If [code]true[/code], writes the new green color channel to the final result.
</member>
<member name="write_r" type="bool" setter="set_write_r" getter="get_write_r" default="true">
+ If [code]true[/code], writes the new red color channel to the final result.
</member>
</members>
</class>
diff --git a/doc/classes/RDPipelineDepthStencilState.xml b/doc/classes/RDPipelineDepthStencilState.xml
index 65a83a962a..3893e0188d 100644
--- a/doc/classes/RDPipelineDepthStencilState.xml
+++ b/doc/classes/RDPipelineDepthStencilState.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineDepthStencilState" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline depth/stencil state (used by [RenderingDevice]).
</brief_description>
<description>
+ [RDPipelineDepthStencilState] controls the way depth and stencil comparisons are performed when sampling those values using [RenderingDevice].
</description>
<tutorials>
</tutorials>
@@ -30,6 +32,7 @@
<member name="enable_depth_range" type="bool" setter="set_enable_depth_range" getter="get_enable_depth_range" default="false">
</member>
<member name="enable_depth_test" type="bool" setter="set_enable_depth_test" getter="get_enable_depth_test" default="false">
+ If [code]true[/code], enables depth testing which allows objects to be automatically occluded by other objects based on their depth. This also allows objects to be partially occluded by other objects. If [code]false[/code], objects will appear in the order they were drawn (like in Godot's 2D renderer).
</member>
<member name="enable_depth_write" type="bool" setter="set_enable_depth_write" getter="get_enable_depth_write" default="false">
</member>
diff --git a/doc/classes/RDPipelineMultisampleState.xml b/doc/classes/RDPipelineMultisampleState.xml
index 2e72ca2bae..635a2480de 100644
--- a/doc/classes/RDPipelineMultisampleState.xml
+++ b/doc/classes/RDPipelineMultisampleState.xml
@@ -1,23 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineMultisampleState" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline multisample state (used by [RenderingDevice]).
</brief_description>
<description>
+ [RDPipelineMultisampleState] is used to control how multisample or supersample antialiasing is being performed when rendering using [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="enable_alpha_to_coverage" type="bool" setter="set_enable_alpha_to_coverage" getter="get_enable_alpha_to_coverage" default="false">
+ If [code]true[/code], alpha to coverage is enabled. This generates a temporary coverage value based on the alpha component of the fragment's first color output. This allows alpha transparency to make use of multisample antialiasing.
</member>
<member name="enable_alpha_to_one" type="bool" setter="set_enable_alpha_to_one" getter="get_enable_alpha_to_one" default="false">
+ If [code]true[/code], alpha is forced to either [code]0.0[/code] or [code]1.0[/code]. This allows hardening the edges of antialiased alpha transparencies. Only relevant if [member enable_alpha_to_coverage] is [code]true[/code].
</member>
<member name="enable_sample_shading" type="bool" setter="set_enable_sample_shading" getter="get_enable_sample_shading" default="false">
+ If [code]true[/code], enables per-sample shading which replaces MSAA by SSAA. This provides higher quality antialiasing that works with transparent (alpha scissor) edges. This has a very high performance cost. See also [member min_sample_shading]. See the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#primsrast-sampleshading]per-sample shading Vulkan documentation[/url] for more details.
</member>
<member name="min_sample_shading" type="float" setter="set_min_sample_shading" getter="get_min_sample_shading" default="0.0">
+ The multiplier of [member sample_count] that determines how many samples are performed for each fragment. Must be between [code]0.0[/code] and [code]1.0[/code] (inclusive). Only effective if [member enable_sample_shading] is [code]true[/code]. If [member min_sample_shading] is [code]1.0[/code], fragment invocation must only read from the coverage index sample. Tile image access must not be used if [member enable_sample_shading] is [i]not[/i] [code]1.0[/code].
</member>
<member name="sample_count" type="int" setter="set_sample_count" getter="get_sample_count" enum="RenderingDevice.TextureSamples" default="0">
+ The number of MSAA samples (or SSAA samples if [member enable_sample_shading] is [code]true[/code]) to perform. Higher values result in better antialiasing, at the cost of performance.
</member>
<member name="sample_masks" type="int[]" setter="set_sample_masks" getter="get_sample_masks" default="[]">
+ The sampleSee the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-samplemask]sample mask Vulkan documentation[/url] for more details.
</member>
</members>
</class>
diff --git a/doc/classes/RDPipelineRasterizationState.xml b/doc/classes/RDPipelineRasterizationState.xml
index 80aef26d2b..79d0eeef48 100644
--- a/doc/classes/RDPipelineRasterizationState.xml
+++ b/doc/classes/RDPipelineRasterizationState.xml
@@ -1,13 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineRasterizationState" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline rasterization state (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="cull_mode" type="int" setter="set_cull_mode" getter="get_cull_mode" enum="RenderingDevice.PolygonCullMode" default="0">
+ The cull mode to use when drawing polygons, which determines whether front faces or backfaces are hidden.
</member>
<member name="depth_bias_clamp" type="float" setter="set_depth_bias_clamp" getter="get_depth_bias_clamp" default="0.0">
</member>
@@ -18,16 +21,21 @@
<member name="depth_bias_slope_factor" type="float" setter="set_depth_bias_slope_factor" getter="get_depth_bias_slope_factor" default="0.0">
</member>
<member name="discard_primitives" type="bool" setter="set_discard_primitives" getter="get_discard_primitives" default="false">
+ If [code]true[/code], primitives are discarded immediately before the rasterization stage.
</member>
<member name="enable_depth_clamp" type="bool" setter="set_enable_depth_clamp" getter="get_enable_depth_clamp" default="false">
</member>
<member name="front_face" type="int" setter="set_front_face" getter="get_front_face" enum="RenderingDevice.PolygonFrontFace" default="0">
+ The winding order to use to determine which face of a triangle is considered its front face.
</member>
<member name="line_width" type="float" setter="set_line_width" getter="get_line_width" default="1.0">
+ THe line width to use when drawing lines (in pixels). Thick lines may not be supported on all hardware.
</member>
<member name="patch_control_points" type="int" setter="set_patch_control_points" getter="get_patch_control_points" default="1">
+ The number of control points to use when drawing a patch with tessellation enabled. Higher values result in higher quality at the cost of performance.
</member>
<member name="wireframe" type="bool" setter="set_wireframe" getter="get_wireframe" default="false">
+ If [code]true[/code], performs wireframe rendering for triangles instead of flat or textured rendering.
</member>
</members>
</class>
diff --git a/doc/classes/RDPipelineSpecializationConstant.xml b/doc/classes/RDPipelineSpecializationConstant.xml
index 3c1c8b3a76..9b1ca98708 100644
--- a/doc/classes/RDPipelineSpecializationConstant.xml
+++ b/doc/classes/RDPipelineSpecializationConstant.xml
@@ -1,15 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDPipelineSpecializationConstant" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Pipeline specialization constant (used by [RenderingDevice]).
</brief_description>
<description>
+ A [i]specialization constant[/i] is a way to create additional variants of shaders without actually increasing the number of shader versions that are compiled. This allows improving performance by reducing the number of shader versions and reducing [code]if[/code] branching, while still allowing shaders to be flexible for different use cases.
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="constant_id" type="int" setter="set_constant_id" getter="get_constant_id" default="0">
+ The identifier of the specialization constant. This is a value starting from [code]0[/code] and that increments for every different specialization constant for a given shader.
</member>
<member name="value" type="Variant" setter="set_value" getter="get_value">
+ The specialization constant's value. Only [bool], [int] and [float] types are valid for specialization constants.
</member>
</members>
</class>
diff --git a/doc/classes/RDSamplerState.xml b/doc/classes/RDSamplerState.xml
index 3ddc73a05e..464fe61543 100644
--- a/doc/classes/RDSamplerState.xml
+++ b/doc/classes/RDSamplerState.xml
@@ -1,41 +1,57 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDSamplerState" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Sampler state (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="anisotropy_max" type="float" setter="set_anisotropy_max" getter="get_anisotropy_max" default="1.0">
+ Maximum anisotropy that can be used when sampling. Only effective if [member use_anisotropy] is [code]true[/code]. Higher values result in a sharper sampler at oblique angles, at the cost of performance (due to memory bandwidth). This value may be limited by the graphics hardware in use. Most graphics hardware only supports values up to [code]16.0[/code].
+ If [member anisotropy_max] is [code]1.0[/code], forcibly disables anisotropy even if [member use_anisotropy] is [code]true[/code].
</member>
<member name="border_color" type="int" setter="set_border_color" getter="get_border_color" enum="RenderingDevice.SamplerBorderColor" default="2">
+ The border color that will be returned when sampling outside the sampler's bounds and the [member repeat_u], [member repeat_v] or [member repeat_w] modes have repeating disabled.
</member>
<member name="compare_op" type="int" setter="set_compare_op" getter="get_compare_op" enum="RenderingDevice.CompareOperator" default="7">
+ The compare operation to use. Only effective if [member enable_compare] is [code]true[/code].
</member>
<member name="enable_compare" type="bool" setter="set_enable_compare" getter="get_enable_compare" default="false">
+ If [code]true[/code], returned values will be based on the comparison operation defined in [member compare_op]. This is a hardware-based approach and is therefore faster than performing this manually in a shader. For example, compare operations are used for shadow map rendering by comparing depth values from a shadow sampler.
</member>
<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="0.0">
+ The mipmap LOD bias to use. Positive values will make the sampler blurrier at a given distance, while negative values will make the sampler sharper at a given distance (at the risk of looking grainy). Recommended values are between [code]-0.5[/code] and [code]0.0[/code]. Only effective if the sampler has mipmaps available.
</member>
<member name="mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" enum="RenderingDevice.SamplerFilter" default="0">
+ The sampler's magnification filter.
</member>
<member name="max_lod" type="float" setter="set_max_lod" getter="get_max_lod" default="1e+20">
+ The maximum mipmap LOD bias to display (lowest resolution). Only effective if the sampler has mipmaps available.
</member>
<member name="min_filter" type="int" setter="set_min_filter" getter="get_min_filter" enum="RenderingDevice.SamplerFilter" default="0">
</member>
<member name="min_lod" type="float" setter="set_min_lod" getter="get_min_lod" default="0.0">
+ The minimum mipmap LOD bias to display (highest resolution). Only effective if the sampler has mipmaps available.
</member>
<member name="mip_filter" type="int" setter="set_mip_filter" getter="get_mip_filter" enum="RenderingDevice.SamplerFilter" default="0">
+ The filtering method to use for mipmaps.
</member>
<member name="repeat_u" type="int" setter="set_repeat_u" getter="get_repeat_u" enum="RenderingDevice.SamplerRepeatMode" default="2">
+ The repeat mode to use along the U axis of UV coordinates. This affects the returned values if sampling outside the UV bounds.
</member>
<member name="repeat_v" type="int" setter="set_repeat_v" getter="get_repeat_v" enum="RenderingDevice.SamplerRepeatMode" default="2">
+ The repeat mode to use along the V axis of UV coordinates. This affects the returned values if sampling outside the UV bounds.
</member>
<member name="repeat_w" type="int" setter="set_repeat_w" getter="get_repeat_w" enum="RenderingDevice.SamplerRepeatMode" default="2">
+ The repeat mode to use along the W axis of UV coordinates. This affects the returned values if sampling outside the UV bounds. Only effective for 3D samplers.
</member>
<member name="unnormalized_uvw" type="bool" setter="set_unnormalized_uvw" getter="get_unnormalized_uvw" default="false">
</member>
<member name="use_anisotropy" type="bool" setter="set_use_anisotropy" getter="get_use_anisotropy" default="false">
+ If [code]true[/code], perform anisotropic sampling. See [member anisotropy_max].
</member>
</members>
</class>
diff --git a/doc/classes/RDShaderFile.xml b/doc/classes/RDShaderFile.xml
index 0d2217b507..98c719e6c8 100644
--- a/doc/classes/RDShaderFile.xml
+++ b/doc/classes/RDShaderFile.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDShaderFile" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Compiled shader file in SPIR-V form (used by [RenderingDevice]). Not to be confused with Godot's own [Shader].
</brief_description>
<description>
+ Compiled shader file in SPIR-V form.
+ See also [RDShaderSource]. [RDShaderFile] is only meant to be used with the [RenderingDevice] API. It should not be confused with Godot's own [Shader] resource, which is what Godot's various nodes use for high-level shader programming.
</description>
<tutorials>
</tutorials>
@@ -11,11 +14,13 @@
<return type="RDShaderSPIRV" />
<param index="0" name="version" type="StringName" default="&amp;&quot;&quot;" />
<description>
+ Returns the SPIR-V intermediate representation for the specified shader [param version].
</description>
</method>
<method name="get_version_list" qualifiers="const">
- <return type="PackedStringArray" />
+ <return type="StringName[]" />
<description>
+ Returns the list of compiled versions for this shader.
</description>
</method>
<method name="set_bytecode">
@@ -23,11 +28,13 @@
<param index="0" name="bytecode" type="RDShaderSPIRV" />
<param index="1" name="version" type="StringName" default="&amp;&quot;&quot;" />
<description>
+ Sets the SPIR-V [param bytecode] that will be compiled for the specified [param version].
</description>
</method>
</methods>
<members>
<member name="base_error" type="String" setter="set_base_error" getter="get_base_error" default="&quot;&quot;">
+ The base compilation error message, which indicates errors not related to a specific shader stage if non-empty. If empty, shader compilation is not necessarily successful (check [RDShaderSPIRV]'s error message members).
</member>
</members>
</class>
diff --git a/doc/classes/RDShaderSPIRV.xml b/doc/classes/RDShaderSPIRV.xml
index c17699882b..3fc69431d2 100644
--- a/doc/classes/RDShaderSPIRV.xml
+++ b/doc/classes/RDShaderSPIRV.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDShaderSPIRV" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ SPIR-V intermediate representation as part of a [RDShaderFile] (used by [RenderingDevice]).
</brief_description>
<description>
+ [RDShaderSPIRV] represents a [RDShaderFile]'s [url=https://www.khronos.org/spir/]SPIR-V[/url] code for various shader stages, as well as possible compilation error messages. SPIR-V a low-level intermediate shader representation. This intermediate representation is not used directly by GPUs for rendering, but it can be compiled into binary shaders that GPUs can understand. Unlike compiled shaders, SPIR-V is portable across GPU models and driver versions.
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
@@ -11,12 +14,14 @@
<return type="PackedByteArray" />
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<description>
+ Equivalent to getting one of [member bytecode_compute], [member bytecode_fragment], [member bytecode_tesselation_control], [member bytecode_tesselation_evaluation], [member bytecode_vertex].
</description>
</method>
<method name="get_stage_compile_error" qualifiers="const">
<return type="String" />
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<description>
+ Returns the compilation error message for the given shader [param stage]. Equivalent to getting one of [member compile_error_compute], [member compile_error_fragment], [member compile_error_tesselation_control], [member compile_error_tesselation_evaluation], [member compile_error_vertex].
</description>
</method>
<method name="set_stage_bytecode">
@@ -24,6 +29,7 @@
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<param index="1" name="bytecode" type="PackedByteArray" />
<description>
+ Sets the SPIR-V [param bytecode] for the given shader [param stage]. Equivalent to setting one of [member bytecode_compute], [member bytecode_fragment], [member bytecode_tesselation_control], [member bytecode_tesselation_evaluation], [member bytecode_vertex].
</description>
</method>
<method name="set_stage_compile_error">
@@ -31,29 +37,40 @@
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<param index="1" name="compile_error" type="String" />
<description>
+ Sets the compilation error message for the given shader [param stage] to [param compile_error]. Equivalent to setting one of [member compile_error_compute], [member compile_error_fragment], [member compile_error_tesselation_control], [member compile_error_tesselation_evaluation], [member compile_error_vertex].
</description>
</method>
</methods>
<members>
<member name="bytecode_compute" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
+ The SPIR-V bytecode for the compute shader stage.
</member>
<member name="bytecode_fragment" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
+ The SPIR-V bytecode for the fragment shader stage.
</member>
<member name="bytecode_tesselation_control" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
+ The SPIR-V bytecode for the tessellation control shader stage.
</member>
<member name="bytecode_tesselation_evaluation" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
+ The SPIR-V bytecode for the tessellation evaluation shader stage.
</member>
<member name="bytecode_vertex" type="PackedByteArray" setter="set_stage_bytecode" getter="get_stage_bytecode" default="PackedByteArray()">
+ The SPIR-V bytecode for the vertex shader stage.
</member>
<member name="compile_error_compute" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
+ The compilation error message for the compute shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_fragment" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
+ The compilation error message for the fragment shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_tesselation_control" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
+ The compilation error message for the tessellation control shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_tesselation_evaluation" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
+ The compilation error message for the tessellation evaluation shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
<member name="compile_error_vertex" type="String" setter="set_stage_compile_error" getter="get_stage_compile_error" default="&quot;&quot;">
+ The compilation error message for the vertex shader stage (set by the SPIR-V compiler and Godot). If empty, shader compilation was successful.
</member>
</members>
</class>
diff --git a/doc/classes/RDShaderSource.xml b/doc/classes/RDShaderSource.xml
index d7fbb6a1ee..054e882b91 100644
--- a/doc/classes/RDShaderSource.xml
+++ b/doc/classes/RDShaderSource.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDShaderSource" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Shader source code (used by [RenderingDevice]).
</brief_description>
<description>
+ Shader source code in text form.
+ See also [RDShaderFile]. [RDShaderSource] is only meant to be used with the [RenderingDevice] API. It should not be confused with Godot's own [Shader] resource, which is what Godot's various nodes use for high-level shader programming.
</description>
<tutorials>
</tutorials>
@@ -11,6 +14,7 @@
<return type="String" />
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<description>
+ Returns source code for the specified shader [param stage]. Equivalent to getting one of [member source_compute], [member source_fragment], [member source_tesselation_control], [member source_tesselation_evaluation] or [member source_vertex].
</description>
</method>
<method name="set_stage_source">
@@ -18,21 +22,28 @@
<param index="0" name="stage" type="int" enum="RenderingDevice.ShaderStage" />
<param index="1" name="source" type="String" />
<description>
+ Sets [param source] code for the specified shader [param stage]. Equivalent to setting one of [member source_compute], [member source_fragment], [member source_tesselation_control], [member source_tesselation_evaluation] or [member source_vertex].
</description>
</method>
</methods>
<members>
<member name="language" type="int" setter="set_language" getter="get_language" enum="RenderingDevice.ShaderLanguage" default="0">
+ The language the shader is written in.
</member>
<member name="source_compute" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
+ Source code for the shader's compute stage.
</member>
<member name="source_fragment" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
+ Source code for the shader's fragment stage.
</member>
<member name="source_tesselation_control" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
+ Source code for the shader's tessellation control stage.
</member>
<member name="source_tesselation_evaluation" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
+ Source code for the shader's tessellation evaluation stage.
</member>
<member name="source_vertex" type="String" setter="set_stage_source" getter="get_stage_source" default="&quot;&quot;">
+ Source code for the shader's vertex stage.
</member>
</members>
</class>
diff --git a/doc/classes/RDTextureFormat.xml b/doc/classes/RDTextureFormat.xml
index 6ba5a62113..ff674a8d7e 100644
--- a/doc/classes/RDTextureFormat.xml
+++ b/doc/classes/RDTextureFormat.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDTextureFormat" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Texture format (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
@@ -22,22 +24,31 @@
</methods>
<members>
<member name="array_layers" type="int" setter="set_array_layers" getter="get_array_layers" default="1">
+ The number of layers in the texture. Only relevant for 2D texture arrays.
</member>
<member name="depth" type="int" setter="set_depth" getter="get_depth" default="1">
+ The texture's depth (in pixels). This is always [code]1[/code] for 2D textures.
</member>
<member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="8">
+ The texture's pixel data format.
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="1">
+ The texture's height (in pixels).
</member>
<member name="mipmaps" type="int" setter="set_mipmaps" getter="get_mipmaps" default="1">
+ The number of mipmaps available in the texture.
</member>
<member name="samples" type="int" setter="set_samples" getter="get_samples" enum="RenderingDevice.TextureSamples" default="0">
+ The number of samples used when sampling the texture.
</member>
<member name="texture_type" type="int" setter="set_texture_type" getter="get_texture_type" enum="RenderingDevice.TextureType" default="1">
+ The texture type.
</member>
<member name="usage_bits" type="int" setter="set_usage_bits" getter="get_usage_bits" enum="RenderingDevice.TextureUsageBits" default="0">
+ The texture's usage bits, which determine what can be done using the texture.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="1">
+ The texture's width (in pixels).
</member>
</members>
</class>
diff --git a/doc/classes/RDTextureView.xml b/doc/classes/RDTextureView.xml
index 064bd11319..4e4e93f88a 100644
--- a/doc/classes/RDTextureView.xml
+++ b/doc/classes/RDTextureView.xml
@@ -1,21 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDTextureView" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Texture view (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
<members>
<member name="format_override" type="int" setter="set_format_override" getter="get_format_override" enum="RenderingDevice.DataFormat" default="218">
+ Optional override for the data format to return sampled values in. The default value of [constant RenderingDevice.DATA_FORMAT_MAX] does not override the format.
</member>
<member name="swizzle_a" type="int" setter="set_swizzle_a" getter="get_swizzle_a" enum="RenderingDevice.TextureSwizzle" default="6">
+ The channel to sample when sampling the alpha channel.
</member>
<member name="swizzle_b" type="int" setter="set_swizzle_b" getter="get_swizzle_b" enum="RenderingDevice.TextureSwizzle" default="5">
+ The channel to sample when sampling the blue color channel.
</member>
<member name="swizzle_g" type="int" setter="set_swizzle_g" getter="get_swizzle_g" enum="RenderingDevice.TextureSwizzle" default="4">
+ The channel to sample when sampling the green color channel.
</member>
<member name="swizzle_r" type="int" setter="set_swizzle_r" getter="get_swizzle_r" enum="RenderingDevice.TextureSwizzle" default="3">
+ The channel to sample when sampling the red color channel.
</member>
</members>
</class>
diff --git a/doc/classes/RDUniform.xml b/doc/classes/RDUniform.xml
index 9fb2979676..a2d05502e7 100644
--- a/doc/classes/RDUniform.xml
+++ b/doc/classes/RDUniform.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDUniform" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Shader uniform (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
@@ -26,8 +28,10 @@
</methods>
<members>
<member name="binding" type="int" setter="set_binding" getter="get_binding" default="0">
+ The uniform's binding.
</member>
<member name="uniform_type" type="int" setter="set_uniform_type" getter="get_uniform_type" enum="RenderingDevice.UniformType" default="3">
+ The uniform's data type.
</member>
</members>
</class>
diff --git a/doc/classes/RDVertexAttribute.xml b/doc/classes/RDVertexAttribute.xml
index f5c7d08ac9..9347edf9db 100644
--- a/doc/classes/RDVertexAttribute.xml
+++ b/doc/classes/RDVertexAttribute.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RDVertexAttribute" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Vertex attribute (used by [RenderingDevice]).
</brief_description>
<description>
+ This object is used by [RenderingDevice].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index 143c199cac..c402040c65 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -154,7 +154,7 @@
<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 [code]b[/code] (i.e. they have at least one point in common).
+ 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.
</description>
</method>
diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml
index 44c7345edb..ba20bb60e6 100644
--- a/doc/classes/Rect2i.xml
+++ b/doc/classes/Rect2i.xml
@@ -143,7 +143,7 @@
<return type="Rect2i" />
<param index="0" name="b" type="Rect2i" />
<description>
- Returns the intersection of this [Rect2i] and [code]b[/code].
+ Returns the intersection of this [Rect2i] and [param b].
If the rectangles do not intersect, an empty [Rect2i] is returned.
</description>
</method>
@@ -151,7 +151,7 @@
<return type="bool" />
<param index="0" name="b" type="Rect2i" />
<description>
- Returns [code]true[/code] if the [Rect2i] overlaps with [code]b[/code] (i.e. they have at least one point in common).
+ Returns [code]true[/code] if the [Rect2i] overlaps with [param b] (i.e. they have at least one point in common).
</description>
</method>
<method name="merge" qualifiers="const">
diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml
index f890831fa4..42922d51e2 100644
--- a/doc/classes/ReflectionProbe.xml
+++ b/doc/classes/ReflectionProbe.xml
@@ -15,13 +15,13 @@
</tutorials>
<members>
<member name="ambient_color" type="Color" setter="set_ambient_color" getter="get_ambient_color" default="Color(0, 0, 0, 1)">
- The custom ambient color to use within the [ReflectionProbe]'s [member size]. Only effective if [member ambient_mode] is [constant AMBIENT_COLOR].
+ The custom ambient color to use within the [ReflectionProbe]'s box defined by its [member size]. Only effective if [member ambient_mode] is [constant AMBIENT_COLOR].
</member>
<member name="ambient_color_energy" type="float" setter="set_ambient_color_energy" getter="get_ambient_color_energy" default="1.0">
- The custom ambient color energy to use within the [ReflectionProbe]'s [member size]. Only effective if [member ambient_mode] is [constant AMBIENT_COLOR].
+ The custom ambient color energy to use within the [ReflectionProbe]'s box defined by its [member size]. Only effective if [member ambient_mode] is [constant AMBIENT_COLOR].
</member>
<member name="ambient_mode" type="int" setter="set_ambient_mode" getter="get_ambient_mode" enum="ReflectionProbe.AmbientMode" default="1">
- The ambient color to use within the [ReflectionProbe]'s [member size]. The ambient color will smoothly blend with other [ReflectionProbe]s and the rest of the scene (outside the [ReflectionProbe]'s [member size]).
+ The ambient color to use within the [ReflectionProbe]'s box defined by its [member size]. The ambient color will smoothly blend with other [ReflectionProbe]s and the rest of the scene (outside the [ReflectionProbe]'s box defined by its [member size]).
</member>
<member name="box_projection" type="bool" setter="set_enable_box_projection" getter="is_box_projection_enabled" default="false">
If [code]true[/code], enables box projection. This makes reflections look more correct in rectangle-shaped rooms by offsetting the reflection center depending on the camera's location.
@@ -41,7 +41,7 @@
</member>
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
The maximum distance away from the [ReflectionProbe] an object can be before it is culled. Decrease this to improve performance, especially when using the [constant UPDATE_ALWAYS] [member update_mode].
- [b]Note:[/b] The maximum reflection distance is always at least equal to the probe's extents. This means that decreasing [member max_distance] will not always cull objects from reflections, especially if the reflection probe's [member size] is already large.
+ [b]Note:[/b] The maximum reflection distance is always at least equal to the probe's extents. This means that decreasing [member max_distance] will not always cull objects from reflections, especially if the reflection probe's box defined by its [member size] is already large.
</member>
<member name="mesh_lod_threshold" type="float" setter="set_mesh_lod_threshold" getter="get_mesh_lod_threshold" default="1.0">
The automatic LOD bias to use for meshes rendered within the [ReflectionProbe] (this is analog to [member Viewport.mesh_lod_threshold]). Higher values will use less detailed versions of meshes that have LOD variations generated. If set to [code]0.0[/code], automatic LOD is disabled. Increase [member mesh_lod_threshold] to improve performance at the cost of geometry detail, especially when using the [constant UPDATE_ALWAYS] [member update_mode].
@@ -66,13 +66,13 @@
Update the probe every frame. This provides better results for fast-moving dynamic objects (such as cars). However, it has a significant performance cost. Due to the cost, it's recommended to only use one ReflectionProbe with [constant UPDATE_ALWAYS] at most per scene. For all other use cases, use [constant UPDATE_ONCE].
</constant>
<constant name="AMBIENT_DISABLED" value="0" enum="AmbientMode">
- Do not apply any ambient lighting inside the [ReflectionProbe]'s [member size].
+ Do not apply any ambient lighting inside the [ReflectionProbe]'s box defined by its [member size].
</constant>
<constant name="AMBIENT_ENVIRONMENT" value="1" enum="AmbientMode">
- Apply automatically-sourced environment lighting inside the [ReflectionProbe]'s [member size].
+ Apply automatically-sourced environment lighting inside the [ReflectionProbe]'s box defined by its [member size].
</constant>
<constant name="AMBIENT_COLOR" value="2" enum="AmbientMode">
- Apply custom ambient lighting inside the [ReflectionProbe]'s [member size]. See [member ambient_color] and [member ambient_color_energy].
+ Apply custom ambient lighting inside the [ReflectionProbe]'s box defined by its [member size]. See [member ambient_color] and [member ambient_color_energy].
</constant>
</constants>
</class>
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index f597cb7efc..98e2730740 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -4,13 +4,14 @@
Abstraction for working with modern low-level graphics APIs.
</brief_description>
<description>
- [RenderingDevice] is an abstraction for working with modern low-level graphics APIs such as Vulkan.
- On startup, Godot creates a global [RenderingDevice] which can be retrieved using [method RenderingServer.get_rendering_device]. This global RenderingDevice performs drawing to the screen.
- Internally, [RenderingDevice] is used in Godot to provide support for several modern low-level graphics APIs while reducing the amount of code duplication required.
+ [RenderingDevice] is an abstraction for working with modern low-level graphics APIs such as Vulkan. Compared to [RenderingServer] (which works with Godot's own rendering subsystems), [RenderingDevice] is much lower-level and allows working more directly with the underlying graphics APIs. [RenderingDevice] is used in Godot to provide support for several modern low-level graphics APIs while reducing the amount of code duplication required. [RenderingDevice] can also be used in your own projects to perform things that are not exposed by [RenderingServer] or high-level nodes, such as using compute shaders.
+ On startup, Godot creates a global [RenderingDevice] which can be retrieved using [method RenderingServer.get_rendering_device]. This global [RenderingDevice] performs drawing to the screen.
[b]Local RenderingDevices:[/b] Using [method RenderingServer.create_local_rendering_device], you can create "secondary" rendering devices to perform drawing and GPU compute operations on separate threads.
+ [b]Note:[/b] [RenderingDevice] assumes intermediate knowledge of modern graphics APIs such as Vulkan, Direct3D 12, Metal or WebGPU. These graphics APIs are lower-level than OpenGL or Direct3D 11, requiring you to perform what was previously done by the graphics driver itself. If you have difficulty understanding the concepts used in this class, follow the [url=https://vulkan-tutorial.com/]Vulkan Tutorial[/url] or [url=https://vkguide.dev/]Vulkan Guide[/url]. It's recommended to have existing modern OpenGL or Direct3D 11 knowledge before attempting to learn a low-level graphics API.
[b]Note:[/b] [RenderingDevice] is not available when running in headless mode or when using the Compatibility rendering method.
</description>
<tutorials>
+ <link title="Using compute shaders">https://docs.godotengine.org/en/latest/tutorials/shaders/compute_shaders.html</link>
</tutorials>
<methods>
<method name="barrier">
@@ -18,6 +19,7 @@
<param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<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>
</method>
<method name="buffer_clear">
@@ -52,6 +54,7 @@
<return type="void" />
<param index="0" name="name" type="String" />
<description>
+ Creates a timestamp marker with the specified [param name]. This is used for performance reporting with the [method get_captured_timestamp_cpu_time], [method get_captured_timestamp_gpu_time] and [method get_captured_timestamp_name] methods.
</description>
</method>
<method name="compute_list_add_barrier">
@@ -64,6 +67,24 @@
<return type="int" />
<param index="0" name="allow_draw_overlap" type="bool" default="false" />
<description>
+ Starts a list of compute commands created with the [code]compute_*[/code] methods. The returned value should be passed to other [code]compute_list_*[/code] functions.
+ If [code]allow_draw_overlap[/code] is true, you may have one draw list running at the same time as one compute list. Multiple compute lists cannot be created at the same time; you must finish the previous compute list first using [method compute_list_end].
+ A simple compute operation might look like this (code is not a complete example):
+ [codeblock]
+ var rd = RenderingDevice.new()
+ var compute_list = rd.compute_list_begin()
+
+ rd.compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline)
+ rd.compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0)
+ rd.compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1)
+
+ for i in atlas_slices:
+ rd.compute_list_set_push_constant(compute_list, push_constant, push_constant.size())
+ rd.compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z)
+ # No barrier, let them run all together.
+
+ rd.compute_list_end()
+ [/codeblock]
</description>
</method>
<method name="compute_list_bind_compute_pipeline">
@@ -88,12 +109,14 @@
<param index="2" name="y_groups" type="int" />
<param index="3" name="z_groups" type="int" />
<description>
+ Submits the compute list for processing on the GPU. This is the compute equivalent to [method draw_list_draw].
</description>
</method>
<method name="compute_list_end">
<return type="void" />
<param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<description>
+ Finishes a list of compute commands created with the [code]compute_*[/code] methods.
</description>
</method>
<method name="compute_list_set_push_constant">
@@ -102,6 +125,7 @@
<param index="1" name="buffer" type="PackedByteArray" />
<param index="2" name="size_bytes" type="int" />
<description>
+ Sets the push constant data to [param buffer] for the specified [param compute_list]. The shader determines how this binary data is used. The buffer's size in bytes must also be specified in [param size_bytes] (this can be obtained by calling the [method PackedByteArray.size] method on the passed [param buffer]).
</description>
</method>
<method name="compute_pipeline_create">
@@ -109,17 +133,21 @@
<param index="0" name="shader" type="RID" />
<param index="1" name="specialization_constants" type="RDPipelineSpecializationConstant[]" default="[]" />
<description>
+ Creates a new compute pipeline. 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.
</description>
</method>
<method name="compute_pipeline_is_valid">
<return type="bool" />
<param index="0" name="compute_pipeline" type="RID" />
<description>
+ Returns [code]true[/code] if the compute pipeline specified by the [param compute_pipeline] RID is valid, [code]false[/code] otherwise.
</description>
</method>
<method name="create_local_device">
<return type="RenderingDevice" />
<description>
+ Create a new local [RenderingDevice]. This is most useful for performing compute operations on the GPU independently from the rest of the engine.
</description>
</method>
<method name="draw_command_begin_label">
@@ -127,11 +155,14 @@
<param index="0" name="name" type="String" />
<param index="1" name="color" type="Color" />
<description>
+ Create a command buffer debug label region that can be displayed in third-party tools such as [url=https://renderdoc.org/]RenderDoc[/url]. All regions must be ended with a [method draw_command_end_label] call. When viewed from the linear series of submissions to a single queue, calls to [method draw_command_begin_label] and [method draw_command_end_label] must be matched and balanced.
+ The [code]VK_EXT_DEBUG_UTILS_EXTENSION_NAME[/code] Vulkan extension must be available and enabled for command buffer debug label region to work. See also [method draw_command_insert_label] and [method draw_command_end_label].
</description>
</method>
<method name="draw_command_end_label">
<return type="void" />
<description>
+ Ends the command buffer debug label region started by a [method draw_command_begin_label] call.
</description>
</method>
<method name="draw_command_insert_label">
@@ -139,6 +170,7 @@
<param index="0" name="name" type="String" />
<param index="1" name="color" type="Color" />
<description>
+ Inserts a command buffer debug label region in the current command buffer. Unlike [method draw_command_begin_label], this region should not be ended with a [method draw_command_end_label] call.
</description>
</method>
<method name="draw_list_begin">
@@ -152,8 +184,29 @@
<param index="6" name="clear_depth" type="float" default="1.0" />
<param index="7" name="clear_stencil" type="int" default="0" />
<param index="8" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
- <param index="9" name="storage_textures" type="Array" default="[]" />
- <description>
+ <param index="9" name="storage_textures" type="RID[]" default="[]" />
+ <description>
+ Starts a list of raster drawing commands created with the [code]draw_*[/code] methods. The returned value should be passed to other [code]draw_list_*[/code] functions.
+ Multiple draw lists cannot be created at the same time; you must finish the previous draw list first using [method draw_list_end].
+ A simple drawing operation might look like this (code is not a complete example):
+ [codeblock]
+ var rd = RenderingDevice.new()
+ var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)]
+ var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors)
+
+ # Draw opaque.
+ rd.draw_list_bind_render_pipeline(draw_list, raster_pipeline)
+ rd.draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0)
+ rd.draw_list_set_push_constant(draw_list, raster_push_constant, raster_push_constant.size())
+ rd.draw_list_draw(draw_list, false, 1, slice_triangle_count[i] * 3)
+ # Draw wire.
+ rd.draw_list_bind_render_pipeline(draw_list, raster_pipeline_wire)
+ rd.draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0)
+ rd.draw_list_set_push_constant(draw_list, raster_push_constant, raster_push_constant.size())
+ rd.draw_list_draw(draw_list, false, 1, slice_triangle_count[i] * 3)
+
+ rd.draw_list_end()
+ [/codeblock]
</description>
</method>
<method name="draw_list_begin_for_screen">
@@ -161,6 +214,8 @@
<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.
+ [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>
<method name="draw_list_begin_split">
@@ -177,6 +232,7 @@
<param index="9" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
<param index="10" name="storage_textures" type="RID[]" default="[]" />
<description>
+ Variant of [method draw_list_begin] with support for multiple splits. The [param splits] parameter determines how many splits are created.
</description>
</method>
<method name="draw_list_bind_index_array">
@@ -184,6 +240,7 @@
<param index="0" name="draw_list" type="int" />
<param index="1" name="index_array" type="RID" />
<description>
+ Binds [param index_array] to the specified [param draw_list].
</description>
</method>
<method name="draw_list_bind_render_pipeline">
@@ -191,6 +248,7 @@
<param index="0" name="draw_list" type="int" />
<param index="1" name="render_pipeline" type="RID" />
<description>
+ Binds [param render_pipeline] to the specified [param draw_list].
</description>
</method>
<method name="draw_list_bind_uniform_set">
@@ -199,6 +257,7 @@
<param index="1" name="uniform_set" type="RID" />
<param index="2" name="set_index" type="int" />
<description>
+ Binds [param uniform_set] to the specified [param draw_list]. A [param set_index] must also be specified, which is an identifier starting from [code]0[/code] that must match the one expected by the draw list.
</description>
</method>
<method name="draw_list_bind_vertex_array">
@@ -206,12 +265,14 @@
<param index="0" name="draw_list" type="int" />
<param index="1" name="vertex_array" type="RID" />
<description>
+ Binds [param vertex_array] to the specified [param draw_list].
</description>
</method>
<method name="draw_list_disable_scissor">
<return type="void" />
<param index="0" name="draw_list" type="int" />
<description>
+ Removes and disables the scissor rectangle for the specified [param draw_list]. See also [method draw_list_enable_scissor].
</description>
</method>
<method name="draw_list_draw">
@@ -221,6 +282,7 @@
<param index="2" name="instances" type="int" />
<param index="3" name="procedural_vertex_count" type="int" default="0" />
<description>
+ Submits [param draw_list] for rendering on the GPU. This is the raster equivalent to [method compute_list_dispatch].
</description>
</method>
<method name="draw_list_enable_scissor">
@@ -228,12 +290,15 @@
<param index="0" name="draw_list" type="int" />
<param index="1" name="rect" type="Rect2" default="Rect2(0, 0, 0, 0)" />
<description>
+ Creates a scissor rectangle and enables it for the specified [param draw_list]. Scissor rectangles are used for clipping by discarding fragments that fall outside a specified rectangular portion of the screen. See also [method draw_list_disable_scissor].
+ [b]Note:[/b] The specified [param rect] is automatically intersected with the screen's dimensions, which means it cannot exceed the screen's dimensions.
</description>
</method>
<method name="draw_list_end">
<return type="void" />
<param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<description>
+ Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods.
</description>
</method>
<method name="draw_list_set_blend_constants">
@@ -241,7 +306,7 @@
<param index="0" name="draw_list" type="int" />
<param index="1" name="color" type="Color" />
<description>
- Sets blend constants for draw list, blend constants are used only if the graphics pipeline is created with [constant DYNAMIC_STATE_BLEND_CONSTANTS] flag set.
+ Sets blend constants for the specified [param draw_list] to [param color]. Blend constants are used only if the graphics pipeline is created with [constant DYNAMIC_STATE_BLEND_CONSTANTS] flag set.
</description>
</method>
<method name="draw_list_set_push_constant">
@@ -250,17 +315,20 @@
<param index="1" name="buffer" type="PackedByteArray" />
<param index="2" name="size_bytes" type="int" />
<description>
+ Sets the push constant data to [param buffer] for the specified [param draw_list]. The shader determines how this binary data is used. The buffer's size in bytes must also be specified in [param size_bytes] (this can be obtained by calling the [method PackedByteArray.size] method on the passed [param buffer]).
</description>
</method>
<method name="draw_list_switch_to_next_pass">
<return type="int" />
<description>
+ Switches to the next draw pass and returns the split's ID. Equivalent to [method draw_list_switch_to_next_pass_split] with [code]splits[/code] set to [code]1[/code].
</description>
</method>
<method name="draw_list_switch_to_next_pass_split">
<return type="PackedInt64Array" />
<param index="0" name="splits" type="int" />
<description>
+ Switches to the next draw pass, with the number of splits allocated specified in [param splits]. The return value is an array containing the ID of each split. For single-split usage, see [method draw_list_switch_to_next_pass].
</description>
</method>
<method name="framebuffer_create">
@@ -269,6 +337,8 @@
<param index="1" name="validate_with_format" type="int" default="-1" />
<param index="2" name="view_count" type="int" default="1" />
<description>
+ Creates a new framebuffer. 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.
</description>
</method>
<method name="framebuffer_create_empty">
@@ -277,6 +347,8 @@
<param index="1" name="samples" type="int" enum="RenderingDevice.TextureSamples" default="0" />
<param index="2" name="validate_with_format" type="int" default="-1" />
<description>
+ Creates a new empty framebuffer. 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.
</description>
</method>
<method name="framebuffer_create_multipass">
@@ -286,6 +358,8 @@
<param index="2" name="validate_with_format" type="int" default="-1" />
<param index="3" name="view_count" type="int" default="1" />
<description>
+ Creates a new multipass framebuffer. 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.
</description>
</method>
<method name="framebuffer_format_create">
@@ -293,12 +367,15 @@
<param index="0" name="attachments" type="RDAttachmentFormat[]" />
<param index="1" name="view_count" type="int" default="1" />
<description>
+ Creates a new framebuffer format with the specified [param attachments] and [param view_count]. Returns the new framebuffer's unique framebuffer format ID.
+ If [code]view_count[/code] is greater than or equal to [code]2[/code], enables multiview which is used for VR rendering. This requires support for the Vulkan multiview extension.
</description>
</method>
<method name="framebuffer_format_create_empty">
<return type="int" />
<param index="0" name="samples" type="int" enum="RenderingDevice.TextureSamples" default="0" />
<description>
+ Creates a new empty framebuffer format with the specified number of [param samples] and returns its ID.
</description>
</method>
<method name="framebuffer_format_create_multipass">
@@ -307,6 +384,7 @@
<param index="1" name="passes" type="RDFramebufferPass[]" />
<param index="2" name="view_count" type="int" default="1" />
<description>
+ Creates a multipass framebuffer format with the specified [param attachments], [param passes] and [param view_count] and returns its ID. If [code]view_count[/code] is greater than or equal to [code]2[/code], enables multiview which is used for VR rendering. This requires support for the Vulkan multiview extension.
</description>
</method>
<method name="framebuffer_format_get_texture_samples">
@@ -314,72 +392,85 @@
<param index="0" name="format" type="int" />
<param index="1" name="render_pass" type="int" default="0" />
<description>
+ Returns the number of texture samples used for the given framebuffer [param format] ID (returned by [method framebuffer_get_format]).
</description>
</method>
<method name="framebuffer_get_format">
<return type="int" />
<param index="0" name="framebuffer" type="RID" />
<description>
+ Returns the format ID of the framebuffer specified by the [param framebuffer] RID. This ID is guaranteed to be unique for the same formats and does not need to be freed.
</description>
</method>
<method name="framebuffer_is_valid" qualifiers="const">
<return type="bool" />
<param index="0" name="framebuffer" type="RID" />
<description>
+ Returns [code]true[/code] if the framebuffer specified by the [param framebuffer] RID is valid, [code]false[/code] otherwise.
</description>
</method>
<method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
+ Tries to free an object in the RenderingDevice. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingDevice directly.
</description>
</method>
<method name="full_barrier">
<return type="void" />
<description>
+ Puts a [i]full[/i] memory barrier in place. This is a memory [method barrier] with all flags enabled. [method full_barrier] it should only be used for debugging as it can severely impact performance.
</description>
</method>
<method name="get_captured_timestamp_cpu_time" qualifiers="const">
<return type="int" />
<param index="0" name="index" type="int" />
<description>
+ Returns the timestamp in CPU time for the rendering step specified by [param index] (in microseconds since the engine started). See also [method get_captured_timestamp_gpu_time] and [method capture_timestamp].
</description>
</method>
<method name="get_captured_timestamp_gpu_time" qualifiers="const">
<return type="int" />
<param index="0" name="index" type="int" />
<description>
+ Returns the timestamp in GPU time for the rendering step specified by [param index] (in microseconds since the engine started). See also [method get_captured_timestamp_cpu_time] and [method capture_timestamp].
</description>
</method>
<method name="get_captured_timestamp_name" qualifiers="const">
<return type="String" />
<param index="0" name="index" type="int" />
<description>
+ Returns the timestamp's name for the rendering step specified by [param index]. See also [method capture_timestamp].
</description>
</method>
<method name="get_captured_timestamps_count" qualifiers="const">
<return type="int" />
<description>
+ Returns the total number of timestamps (rendering steps) available for profiling.
</description>
</method>
<method name="get_captured_timestamps_frame" qualifiers="const">
<return type="int" />
<description>
+ Returns the index of the last frame rendered that has rendering timestamps available for querying.
</description>
</method>
<method name="get_device_name" qualifiers="const">
<return type="String" />
<description>
+ Returns the name of the video adapter (e.g. "GeForce GTX 1080/PCIe/SSE2"). Equivalent to [method RenderingServer.get_video_adapter_name]. See also [method get_device_vendor_name].
</description>
</method>
<method name="get_device_pipeline_cache_uuid" qualifiers="const">
<return type="String" />
<description>
+ Returns the universally unique identifier for the pipeline cache. This is used to cache shader files on disk, which avoids shader recompilations on subsequent engine runs. This UUID varies depending on the graphics card model, but also the driver version. Therefore, updating graphics drivers will invalidate the shader cache.
</description>
</method>
<method name="get_device_vendor_name" qualifiers="const">
<return type="String" />
<description>
+ Returns the vendor of the video adapter (e.g. "NVIDIA Corporation"). Equivalent to [method RenderingServer.get_video_adapter_vendor]. See also [method get_device_name].
</description>
</method>
<method name="get_driver_resource">
@@ -388,17 +479,20 @@
<param index="1" name="rid" type="RID" />
<param index="2" name="index" type="int" />
<description>
+ Returns the unique identifier of the driver [param resource] for the specified [param rid]. Some driver resource types ignore the specified [param rid] (see [enum DriverResource] descriptions). [param index] is always ignored but must be specified anyway.
</description>
</method>
<method name="get_frame_delay" qualifiers="const">
<return type="int" />
<description>
+ Returns the frame count kept by the graphics API. Higher values result in higher input lag, but with more consistent throughput. For the main [RenderingDevice], frames are cycled (usually 3 with triple-buffered V-Sync enabled). However, local [RenderingDevice]s only have 1 frame.
</description>
</method>
<method name="get_memory_usage" qualifiers="const">
<return type="int" />
<param index="0" name="type" type="int" enum="RenderingDevice.MemoryType" />
<description>
+ Returns the memory usage in bytes corresponding to the given [param type]. When using Vulkan, these statistics are calculated by [url=https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator]Vulkan Memory Allocator[/url].
</description>
</method>
<method name="index_array_create">
@@ -407,6 +501,8 @@
<param index="1" name="index_offset" type="int" />
<param index="2" name="index_count" type="int" />
<description>
+ Creates a new index array. 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.
</description>
</method>
<method name="index_buffer_create">
@@ -416,12 +512,16 @@
<param index="2" name="data" type="PackedByteArray" default="PackedByteArray()" />
<param index="3" name="use_restart_indices" type="bool" default="false" />
<description>
+ Creates a new index buffer. 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.
</description>
</method>
<method name="limit_get" qualifiers="const">
<return type="int" />
<param index="0" name="limit" type="int" enum="RenderingDevice.Limit" />
<description>
+ Returns the value of the specified [param limit]. This limit varies depending on the current graphics hardware (and sometimes the driver version). If the given limit is exceeded, rendering errors will occur.
+ Limits for various graphics hardware can be found in the [url=https://vulkan.gpuinfo.org/]Vulkan Hardware Database[/url].
</description>
</method>
<method name="render_pipeline_create">
@@ -438,35 +538,54 @@
<param index="9" name="for_render_pass" type="int" default="0" />
<param index="10" name="specialization_constants" type="RDPipelineSpecializationConstant[]" default="[]" />
<description>
+ Creates a new render pipeline. 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.
</description>
</method>
<method name="render_pipeline_is_valid">
<return type="bool" />
<param index="0" name="render_pipeline" type="RID" />
<description>
+ Returns [code]true[/code] if the render pipeline specified by the [param render_pipeline] RID is valid, [code]false[/code] otherwise.
</description>
</method>
<method name="sampler_create">
<return type="RID" />
<param index="0" name="state" type="RDSamplerState" />
<description>
+ Creates a new sampler. 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.
+ </description>
+ </method>
+ <method name="sampler_is_format_supported_for_filter" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="format" type="int" enum="RenderingDevice.DataFormat" />
+ <param index="1" name="sampler_filter" type="int" enum="RenderingDevice.SamplerFilter" />
+ <description>
+ Returns [code]true[/code] if implementation supports using a texture of [param format] with the given [param sampler_filter].
</description>
</method>
<method name="screen_get_framebuffer_format" qualifiers="const">
<return type="int" />
<description>
+ Returns the screen's framebuffer format.
+ [b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a format. If called on a local [RenderingDevice], this method prints an error and returns [constant INVALID_ID].
</description>
</method>
<method name="screen_get_height" qualifiers="const">
<return type="int" />
<param index="0" name="screen" type="int" default="0" />
<description>
+ Returns the window height matching the graphics API context for the given window ID (in pixels). Despite the parameter being named [param screen], this returns the [i]window[/i] size. See also [method screen_get_width].
+ [b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a height. If called on a local [RenderingDevice], this method prints an error and returns [constant INVALID_ID].
</description>
</method>
<method name="screen_get_width" qualifiers="const">
<return type="int" />
<param index="0" name="screen" type="int" default="0" />
<description>
+ Returns the window width matching the graphics API context for the given window ID (in pixels). Despite the parameter being named [param screen], this returns the [i]window[/i] size. See also [method screen_get_height].
+ [b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a width. If called on a local [RenderingDevice], this method prints an error and returns [constant INVALID_ID].
</description>
</method>
<method name="set_resource_name">
@@ -474,6 +593,9 @@
<param index="0" name="id" type="RID" />
<param index="1" name="name" type="String" />
<description>
+ Sets the resource name for [param id] to [param name]. This is used for debugging with third-party tools such as [url=https://renderdoc.org/]RenderDoc[/url].
+ The following types of resources can be named: texture, sampler, vertex buffer, index buffer, uniform buffer, texture buffer, storage buffer, uniform set buffer, shader, render pipeline and compute pipeline. Framebuffers cannot be named. Attempting to name an incompatible resource type will print an error.
+ [b]Note:[/b] Resource names are only set when the engine runs in verbose mode ([method OS.is_stdout_verbose] = [code]true[/code]), or when using an engine build compiled with the [code]dev_mode=yes[/code] SCons option. The graphics driver must also support the [code]VK_EXT_DEBUG_UTILS_EXTENSION_NAME[/code] Vulkan extension for named resources to work.
</description>
</method>
<method name="shader_compile_binary_from_spirv">
@@ -481,6 +603,8 @@
<param index="0" name="spirv_data" type="RDShaderSPIRV" />
<param index="1" name="name" type="String" default="&quot;&quot;" />
<description>
+ Compiles a binary shader from [param spirv_data] and returns the compiled binary data as a [PackedByteArray]. This compiled shader is specific to the GPU model and driver version used; it will not work on different GPU models or even different driver versions. See also [method shader_compile_spirv_from_source].
+ [param name] is an optional human-readable name that can be given to the compiled shader for organizational purposes.
</description>
</method>
<method name="shader_compile_spirv_from_source">
@@ -488,12 +612,16 @@
<param index="0" name="shader_source" type="RDShaderSource" />
<param index="1" name="allow_cache" type="bool" default="true" />
<description>
+ Compiles a SPIR-V from the shader source code in [param shader_source] and returns the SPIR-V as a [RDShaderSPIRV]. This intermediate language shader is portable across different GPU models and driver versions, but cannot be run directly by GPUs until compiled into a binary shader using [method shader_compile_binary_from_spirv].
+ If [param allow_cache] is [code]true[/code], make use of the shader cache generated by Godot. This avoids a potentially lengthy shader compilation step if the shader is already in cache. If [param allow_cache] is [code]false[/code], Godot's shader cache is ignored and the shader will always be recompiled.
</description>
</method>
<method name="shader_create_from_bytecode">
<return type="RID" />
<param index="0" name="binary_data" type="PackedByteArray" />
<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].
</description>
</method>
<method name="shader_create_from_spirv">
@@ -501,6 +629,8 @@
<param index="0" name="spirv_data" type="RDShaderSPIRV" />
<param index="1" name="name" type="String" default="&quot;&quot;" />
<description>
+ Creates a new shader instance from SPIR-V intermediate code. 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_spirv_from_source] and [method shader_create_from_bytecode].
</description>
</method>
<method name="shader_get_vertex_input_attribute_mask">
@@ -515,16 +645,23 @@
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
<param index="2" name="usage" type="int" enum="RenderingDevice.StorageBufferUsage" default="0" />
<description>
+ Creates a [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]storage buffer[/url] with the specified [param data] and [param usage]. 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.
</description>
</method>
<method name="submit">
<return type="void" />
<description>
+ Pushes the frame setup and draw command buffers then marks the local device as currently processing (which allows calling [method sync]).
+ [b]Note:[/b] Only available in local RenderingDevices.
</description>
</method>
<method name="sync">
<return type="void" />
<description>
+ Forces a synchronization between the CPU and GPU, which may be required in certain cases. Only call this when needed, as CPU-GPU synchronization has a performance cost.
+ [b]Note:[/b] Only available in local RenderingDevices.
+ [b]Note:[/b] [method sync] can only be called after a [method submit].
</description>
</method>
<method name="texture_buffer_create">
@@ -533,6 +670,8 @@
<param index="1" name="format" type="int" enum="RenderingDevice.DataFormat" />
<param index="2" name="data" type="PackedByteArray" default="PackedByteArray()" />
<description>
+ Creates a new texture buffer. 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.
</description>
</method>
<method name="texture_clear">
@@ -545,6 +684,8 @@
<param index="5" name="layer_count" type="int" />
<param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<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.
</description>
</method>
<method name="texture_copy">
@@ -560,6 +701,12 @@
<param index="8" name="dst_layer" type="int" />
<param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<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.
+ [b]Note:[/b] [param from_texture] texture requires the [constant TEXTURE_USAGE_CAN_COPY_FROM_BIT] to be retrieved.
+ [b]Note:[/b] [param to_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.
+ [b]Note:[/b] [param to_texture] requires the [constant TEXTURE_USAGE_CAN_COPY_TO_BIT] to be retrieved.
+ [b]Note:[/b] [param from_texture] and [param to_texture] must be of the same type (color or depth).
</description>
</method>
<method name="texture_create">
@@ -568,6 +715,9 @@
<param index="1" name="view" type="RDTextureView" />
<param index="2" name="data" type="PackedByteArray[]" default="[]" />
<description>
+ Creates a new texture. 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.
+ [b]Note:[/b] Not to be confused with [method RenderingServer.texture_2d_create], which creates the Godot-specific [Texture2D] resource as opposed to the graphics API's own texture type.
</description>
</method>
<method name="texture_create_shared">
@@ -575,6 +725,7 @@
<param index="0" name="view" type="RDTextureView" />
<param index="1" name="with_texture" type="RID" />
<description>
+ Creates a shared texture using the specified [param view] and the texture information from [param with_texture].
</description>
</method>
<method name="texture_create_shared_from_slice">
@@ -586,6 +737,9 @@
<param index="4" name="mipmaps" type="int" default="1" />
<param index="5" name="slice_type" type="int" enum="RenderingDevice.TextureSliceType" default="0" />
<description>
+ Creates a shared texture using the specified [param view] and the texture information from [param with_texture]'s [param layer] and [param mipmap]. The number of included mipmaps from the original texture can be controlled using the [param mipmaps] parameter. Only relevant for textures with multiple layers, such as 3D textures, texture arrays and cubemaps. For single-layer textures, use [method texture_create_shared]
+ For 2D textures (which only have one layer), [param layer] must be [code]0[/code].
+ [b]Note:[/b] Layer slicing is only supported for 2D texture arrays, not 3D textures or cubemaps.
</description>
</method>
<method name="texture_get_data">
@@ -593,6 +747,9 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="layer" type="int" />
<description>
+ Returns the [param texture] data for the specified [param layer] as raw binary data. For 2D textures (which only have one layer), [param layer] must be [code]0[/code].
+ [b]Note:[/b] [param texture] can't be retrieved 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 retrieve this texture. Otherwise, an error is printed and a empty [PackedByteArray] is returned.
+ [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_is_format_supported_for_usage" qualifiers="const">
@@ -600,18 +757,21 @@
<param index="0" name="format" type="int" enum="RenderingDevice.DataFormat" />
<param index="1" name="usage_flags" type="int" enum="RenderingDevice.TextureUsageBits" />
<description>
+ Returns [code]true[/code] if the specified [param format] is supported for the given [param usage_flags], [code]false[/code] otherwise.
</description>
</method>
<method name="texture_is_shared">
<return type="bool" />
<param index="0" name="texture" type="RID" />
<description>
+ Returns [code]true[/code] if the [param texture] is shared, [code]false[/code] otherwise. See [RDTextureView].
</description>
</method>
<method name="texture_is_valid">
<return type="bool" />
<param index="0" name="texture" type="RID" />
<description>
+ Returns [code]true[/code] if the [param texture] is valid, [code]false[/code] otherwise.
</description>
</method>
<method name="texture_resolve_multisample">
@@ -620,6 +780,14 @@
<param index="1" name="to_texture" type="RID" />
<param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<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).
+ [b]Note:[/b] [param from_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 resolve this texture.
+ [b]Note:[/b] [param from_texture] requires the [constant TEXTURE_USAGE_CAN_COPY_FROM_BIT] to be retrieved.
+ [b]Note:[/b] [param from_texture] must be multisampled and must also be 2D (or a slice of a 3D/cubemap texture).
+ [b]Note:[/b] [param to_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 resolve this texture.
+ [b]Note:[/b] [param to_texture] texture requires the [constant TEXTURE_USAGE_CAN_COPY_TO_BIT] to be retrieved.
+ [b]Note:[/b] [param to_texture] texture must [b]not[/b] be multisampled and must also be 2D (or a slice of a 3D/cubemap texture).
</description>
</method>
<method name="texture_update">
@@ -629,6 +797,10 @@
<param index="2" name="data" type="PackedByteArray" />
<param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" />
<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.
+ [b]Note:[/b] The existing [param texture] can't be updated 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 update this texture.
+ [b]Note:[/b] The existing [param texture] requires the [constant TEXTURE_USAGE_CAN_UPDATE_BIT] to be updatable.
</description>
</method>
<method name="uniform_buffer_create">
@@ -636,6 +808,8 @@
<param index="0" name="size_bytes" type="int" />
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
<description>
+ 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.
</description>
</method>
<method name="uniform_set_create">
@@ -644,6 +818,8 @@
<param index="1" name="shader" type="RID" />
<param index="2" name="shader_set" type="int" />
<description>
+ 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.
</description>
</method>
<method name="uniform_set_is_valid">
@@ -668,12 +844,15 @@
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
<param index="2" name="use_as_storage" type="bool" default="false" />
<description>
+ 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.
</description>
</method>
<method name="vertex_format_create">
<return type="int" />
<param index="0" name="vertex_descriptions" type="RDVertexAttribute[]" />
<description>
+ Creates a new vertex format with the specified [param vertex_descriptions]. Returns a unique vertex format ID corresponding to the newly created vertex format.
</description>
</method>
</methods>
@@ -697,478 +876,716 @@
Represents the size of the [enum DeviceType] enum.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_DEVICE" value="0" enum="DriverResource">
+ Vulkan device driver resource. This is a "global" resource and ignores the RID passed in
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE" value="1" enum="DriverResource">
+ Physical device (graphics card) driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_INSTANCE" value="2" enum="DriverResource">
+ Vulkan instance driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_QUEUE" value="3" enum="DriverResource">
+ Vulkan queue driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX" value="4" enum="DriverResource">
+ Vulkan queue family index driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_IMAGE" value="5" enum="DriverResource">
+ Vulkan image driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_IMAGE_VIEW" value="6" enum="DriverResource">
+ Vulkan image view driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT" value="7" enum="DriverResource">
+ Vulkan image native texture format driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_SAMPLER" value="8" enum="DriverResource">
+ Vulkan sampler driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET" value="9" enum="DriverResource">
+ Vulkan [url=https://vkguide.dev/docs/chapter-4/descriptors/]descriptor set[/url] driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_BUFFER" value="10" enum="DriverResource">
+ Vulkan buffer driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE" value="11" enum="DriverResource">
+ Vulkan compute pipeline driver resource.
</constant>
<constant name="DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE" value="12" enum="DriverResource">
+ Vulkan render pipeline driver resource.
</constant>
<constant name="DATA_FORMAT_R4G4_UNORM_PACK8" value="0" enum="DataFormat">
+ 4-bit-per-channel red/green channel data format, packed into 8 bits. Values are in the [code][0.0, 1.0][/code] range.
+ [b]Note:[/b] More information on all data formats can be found on the [url=https://registry.khronos.org/vulkan/specs/1.1/html/vkspec.html#_identification_of_formats]Identification of formats[/url] section of the Vulkan specification, as well as the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html]VkFormat[/url] enum.
</constant>
<constant name="DATA_FORMAT_R4G4B4A4_UNORM_PACK16" value="1" enum="DataFormat">
+ 4-bit-per-channel red/green/blue/alpha channel data format, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B4G4R4A4_UNORM_PACK16" value="2" enum="DataFormat">
+ 4-bit-per-channel blue/green/red/alpha channel data format, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R5G6B5_UNORM_PACK16" value="3" enum="DataFormat">
+ Red/green/blue channel data format with 5 bits of red, 6 bits of green and 5 bits of blue, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B5G6R5_UNORM_PACK16" value="4" enum="DataFormat">
+ Blue/green/red channel data format with 5 bits of blue, 6 bits of green and 5 bits of red, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R5G5B5A1_UNORM_PACK16" value="5" enum="DataFormat">
+ Red/green/blue/alpha channel data format with 5 bits of red, 6 bits of green, 5 bits of blue and 1 bit of alpha, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B5G5R5A1_UNORM_PACK16" value="6" enum="DataFormat">
+ Blue/green/red/alpha channel data format with 5 bits of blue, 6 bits of green, 5 bits of red and 1 bit of alpha, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A1R5G5B5_UNORM_PACK16" value="7" enum="DataFormat">
+ Alpha/red/green/blue channel data format with 1 bit of alpha, 5 bits of red, 6 bits of green and 5 bits of blue, packed into 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_UNORM" value="8" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_SNORM" value="9" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_USCALED" value="10" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_SSCALED" value="11" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_UINT" value="12" enum="DataFormat">
+ 8-bit-per-channel unsigned integer red channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_SINT" value="13" enum="DataFormat">
+ 8-bit-per-channel signed integer red channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_R8_SRGB" value="14" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_UNORM" value="15" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_SNORM" value="16" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_USCALED" value="17" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_SSCALED" value="18" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_UINT" value="19" enum="DataFormat">
+ 8-bit-per-channel unsigned integer red/green channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_SINT" value="20" enum="DataFormat">
+ 8-bit-per-channel signed integer red/green channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8_SRGB" value="21" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_UNORM" value="22" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_SNORM" value="23" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green/blue channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_USCALED" value="24" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_SSCALED" value="25" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green/blue channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_UINT" value="26" enum="DataFormat">
+ 8-bit-per-channel unsigned integer red/green/blue channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_SINT" value="27" enum="DataFormat">
+ 8-bit-per-channel signed integer red/green/blue channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8_SRGB" value="28" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue/blue channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_UNORM" value="29" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_SNORM" value="30" enum="DataFormat">
+ 8-bit-per-channel signed floating-point blue/green/red channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_USCALED" value="31" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_SSCALED" value="32" enum="DataFormat">
+ 8-bit-per-channel signed floating-point blue/green/red channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_UINT" value="33" enum="DataFormat">
+ 8-bit-per-channel unsigned integer blue/green/red channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_SINT" value="34" enum="DataFormat">
+ 8-bit-per-channel signed integer blue/green/red channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8_SRGB" value="35" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_UNORM" value="36" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_SNORM" value="37" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green/blue/alpha channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_USCALED" value="38" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_SSCALED" value="39" enum="DataFormat">
+ 8-bit-per-channel signed floating-point red/green/blue/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_UINT" value="40" enum="DataFormat">
+ 8-bit-per-channel unsigned integer red/green/blue/alpha channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_SINT" value="41" enum="DataFormat">
+ 8-bit-per-channel signed integer red/green/blue/alpha channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_R8G8B8A8_SRGB" value="42" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_UNORM" value="43" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_SNORM" value="44" enum="DataFormat">
+ 8-bit-per-channel signed floating-point blue/green/red/alpha channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_USCALED" value="45" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_SSCALED" value="46" enum="DataFormat">
+ 8-bit-per-channel signed floating-point blue/green/red/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_UINT" value="47" enum="DataFormat">
+ 8-bit-per-channel unsigned integer blue/green/red/alpha channel data format. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_SINT" value="48" enum="DataFormat">
+ 8-bit-per-channel signed integer blue/green/red/alpha channel data format. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_B8G8R8A8_SRGB" value="49" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_UNORM_PACK32" value="50" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_SNORM_PACK32" value="51" enum="DataFormat">
+ 8-bit-per-channel signed floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_USCALED_PACK32" value="52" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point alpha/red/green/blue channel data format with scaled value (value is converted from integer to float), packed in 32 bits. Values are in the [code][0.0, 255.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_SSCALED_PACK32" value="53" enum="DataFormat">
+ 8-bit-per-channel signed floating-point alpha/red/green/blue channel data format with scaled value (value is converted from integer to float), packed in 32 bits. Values are in the [code][-127.0, 127.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_UINT_PACK32" value="54" enum="DataFormat">
+ 8-bit-per-channel unsigned integer alpha/red/green/blue channel data format, packed in 32 bits. Values are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_SINT_PACK32" value="55" enum="DataFormat">
+ 8-bit-per-channel signed integer alpha/red/green/blue channel data format, packed in 32 bits. Values are in the [code][-127, 127][/code] range.
</constant>
<constant name="DATA_FORMAT_A8B8G8R8_SRGB_PACK32" value="56" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point alpha/red/green/blue channel data format with normalized value and non-linear sRGB encoding, packed in 32 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_UNORM_PACK32" value="57" enum="DataFormat">
+ Unsigned floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_SNORM_PACK32" value="58" enum="DataFormat">
+ Signed floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_USCALED_PACK32" value="59" enum="DataFormat">
+ Unsigned floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][0.0, 1023.0][/code] range for red/green/blue and [code][0.0, 3.0][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_SSCALED_PACK32" value="60" enum="DataFormat">
+ Signed floating-point alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][-511.0, 511.0][/code] range for red/green/blue and [code][-1.0, 1.0][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_UINT_PACK32" value="61" enum="DataFormat">
+ Unsigned integer alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][0, 1023][/code] range for red/green/blue and [code][0, 3][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2R10G10B10_SINT_PACK32" value="62" enum="DataFormat">
+ Signed integer alpha/red/green/blue channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of red, 10 bits of green and 10 bits of blue. Values are in the [code][-511, 511][/code] range for red/green/blue and [code][-1, 1][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_UNORM_PACK32" value="63" enum="DataFormat">
+ Unsigned floating-point alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_SNORM_PACK32" value="64" enum="DataFormat">
+ Signed floating-point alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_USCALED_PACK32" value="65" enum="DataFormat">
+ Unsigned floating-point alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][0.0, 1023.0][/code] range for blue/green/red and [code][0.0, 3.0][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_SSCALED_PACK32" value="66" enum="DataFormat">
+ Signed floating-point alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][-511.0, 511.0][/code] range for blue/green/red and [code][-1.0, 1.0][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_UINT_PACK32" value="67" enum="DataFormat">
+ Unsigned integer alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][0, 1023][/code] range for blue/green/red and [code][0, 3][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_A2B10G10R10_SINT_PACK32" value="68" enum="DataFormat">
+ Signed integer alpha/blue/green/red channel data format with normalized value, packed in 32 bits. Format contains 2 bits of alpha, 10 bits of blue, 10 bits of green and 10 bits of red. Values are in the [code][-511, 511][/code] range for blue/green/red and [code][-1, 1][/code] for alpha.
</constant>
<constant name="DATA_FORMAT_R16_UNORM" value="69" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_SNORM" value="70" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_USCALED" value="71" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 65535.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_SSCALED" value="72" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red channel data format with scaled value (value is converted from integer to float). Values are in the [code][-32767.0, 32767.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_UINT" value="73" enum="DataFormat">
+ 16-bit-per-channel unsigned integer red channel data format. Values are in the [code][0.0, 65535][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_SINT" value="74" enum="DataFormat">
+ 16-bit-per-channel signed integer red channel data format. Values are in the [code][-32767, 32767][/code] range.
</constant>
<constant name="DATA_FORMAT_R16_SFLOAT" value="75" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R16G16_UNORM" value="76" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_SNORM" value="77" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_USCALED" value="78" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 65535.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_SSCALED" value="79" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green channel data format with scaled value (value is converted from integer to float). Values are in the [code][-32767.0, 32767.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_UINT" value="80" enum="DataFormat">
+ 16-bit-per-channel unsigned integer red/green channel data format. Values are in the [code][0.0, 65535][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_SINT" value="81" enum="DataFormat">
+ 16-bit-per-channel signed integer red/green channel data format. Values are in the [code][-32767, 32767][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16_SFLOAT" value="82" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R16G16B16_UNORM" value="83" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green/blue channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_SNORM" value="84" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_USCALED" value="85" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green/blue channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 65535.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_SSCALED" value="86" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue channel data format with scaled value (value is converted from integer to float). Values are in the [code][-32767.0, 32767.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_UINT" value="87" enum="DataFormat">
+ 16-bit-per-channel unsigned integer red/green/blue channel data format. Values are in the [code][0.0, 65535][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_SINT" value="88" enum="DataFormat">
+ 16-bit-per-channel signed integer red/green/blue channel data format. Values are in the [code][-32767, 32767][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16_SFLOAT" value="89" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_UNORM" value="90" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_SNORM" value="91" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue/alpha channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_USCALED" value="92" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point red/green/blue/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][0.0, 65535.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_SSCALED" value="93" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue/alpha channel data format with scaled value (value is converted from integer to float). Values are in the [code][-32767.0, 32767.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_UINT" value="94" enum="DataFormat">
+ 16-bit-per-channel unsigned integer red/green/blue/alpha channel data format. Values are in the [code][0.0, 65535][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_SINT" value="95" enum="DataFormat">
+ 16-bit-per-channel signed integer red/green/blue/alpha channel data format. Values are in the [code][-32767, 32767][/code] range.
</constant>
<constant name="DATA_FORMAT_R16G16B16A16_SFLOAT" value="96" enum="DataFormat">
+ 16-bit-per-channel signed floating-point red/green/blue/alpha channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R32_UINT" value="97" enum="DataFormat">
+ 32-bit-per-channel unsigned integer red channel data format. Values are in the [code][0, 2^32 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32_SINT" value="98" enum="DataFormat">
+ 32-bit-per-channel signed integer red channel data format. Values are in the [code][2^31 + 1, 2^31 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32_SFLOAT" value="99" enum="DataFormat">
+ 32-bit-per-channel signed floating-point red channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R32G32_UINT" value="100" enum="DataFormat">
+ 32-bit-per-channel unsigned integer red/green channel data format. Values are in the [code][0, 2^32 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32_SINT" value="101" enum="DataFormat">
+ 32-bit-per-channel signed integer red/green channel data format. Values are in the [code][2^31 + 1, 2^31 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32_SFLOAT" value="102" enum="DataFormat">
+ 32-bit-per-channel signed floating-point red/green channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R32G32B32_UINT" value="103" enum="DataFormat">
+ 32-bit-per-channel unsigned integer red/green/blue channel data format. Values are in the [code][0, 2^32 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32B32_SINT" value="104" enum="DataFormat">
+ 32-bit-per-channel signed integer red/green/blue channel data format. Values are in the [code][2^31 + 1, 2^31 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32B32_SFLOAT" value="105" enum="DataFormat">
+ 32-bit-per-channel signed floating-point red/green/blue channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R32G32B32A32_UINT" value="106" enum="DataFormat">
+ 32-bit-per-channel unsigned integer red/green/blue/alpha channel data format. Values are in the [code][0, 2^32 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32B32A32_SINT" value="107" enum="DataFormat">
+ 32-bit-per-channel signed integer red/green/blue/alpha channel data format. Values are in the [code][2^31 + 1, 2^31 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R32G32B32A32_SFLOAT" value="108" enum="DataFormat">
+ 32-bit-per-channel signed floating-point red/green/blue/alpha channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R64_UINT" value="109" enum="DataFormat">
+ 64-bit-per-channel unsigned integer red channel data format. Values are in the [code][0, 2^64 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64_SINT" value="110" enum="DataFormat">
+ 64-bit-per-channel signed integer red channel data format. Values are in the [code][2^63 + 1, 2^63 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64_SFLOAT" value="111" enum="DataFormat">
+ 64-bit-per-channel signed floating-point red channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R64G64_UINT" value="112" enum="DataFormat">
+ 64-bit-per-channel unsigned integer red/green channel data format. Values are in the [code][0, 2^64 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64_SINT" value="113" enum="DataFormat">
+ 64-bit-per-channel signed integer red/green channel data format. Values are in the [code][2^63 + 1, 2^63 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64_SFLOAT" value="114" enum="DataFormat">
+ 64-bit-per-channel signed floating-point red/green channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R64G64B64_UINT" value="115" enum="DataFormat">
+ 64-bit-per-channel unsigned integer red/green/blue channel data format. Values are in the [code][0, 2^64 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64B64_SINT" value="116" enum="DataFormat">
+ 64-bit-per-channel signed integer red/green/blue channel data format. Values are in the [code][2^63 + 1, 2^63 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64B64_SFLOAT" value="117" enum="DataFormat">
+ 64-bit-per-channel signed floating-point red/green/blue channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_R64G64B64A64_UINT" value="118" enum="DataFormat">
+ 64-bit-per-channel unsigned integer red/green/blue/alpha channel data format. Values are in the [code][0, 2^64 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64B64A64_SINT" value="119" enum="DataFormat">
+ 64-bit-per-channel signed integer red/green/blue/alpha channel data format. Values are in the [code][2^63 + 1, 2^63 - 1][/code] range.
</constant>
<constant name="DATA_FORMAT_R64G64B64A64_SFLOAT" value="120" enum="DataFormat">
+ 64-bit-per-channel signed floating-point red/green/blue/alpha channel data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_B10G11R11_UFLOAT_PACK32" value="121" enum="DataFormat">
+ Unsigned floating-point blue/green/red data format with the value stored as-is, packed in 32 bits. The format's precision is 10 bits of blue channel, 11 bits of green channel and 11 bits of red channel.
</constant>
<constant name="DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32" value="122" enum="DataFormat">
+ Unsigned floating-point exposure/blue/green/red data format with the value stored as-is, packed in 32 bits. The format's precision is 5 bits of exposure, 9 bits of blue channel, 9 bits of green channel and 9 bits of red channel.
</constant>
<constant name="DATA_FORMAT_D16_UNORM" value="123" enum="DataFormat">
+ 16-bit unsigned floating-point depth data format with normalized value. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_X8_D24_UNORM_PACK32" value="124" enum="DataFormat">
+ 24-bit unsigned floating-point depth data format with normalized value, plus 8 unused bits, packed in 32 bits. Values for depth are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_D32_SFLOAT" value="125" enum="DataFormat">
+ 32-bit signed floating-point depth data format with the value stored as-is.
</constant>
<constant name="DATA_FORMAT_S8_UINT" value="126" enum="DataFormat">
+ 8-bit unsigned integer stencil data format.
</constant>
<constant name="DATA_FORMAT_D16_UNORM_S8_UINT" value="127" enum="DataFormat">
+ 16-bit unsigned floating-point depth data format with normalized value, plus 8 bits of stencil in unsigned integer format. Values for depth are in the [code][0.0, 1.0][/code] range. Values for stencil are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_D24_UNORM_S8_UINT" value="128" enum="DataFormat">
+ 24-bit unsigned floating-point depth data format with normalized value, plus 8 bits of stencil in unsigned integer format. Values for depth are in the [code][0.0, 1.0][/code] range. Values for stencil are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_D32_SFLOAT_S8_UINT" value="129" enum="DataFormat">
+ 32-bit signed floating-point depth data format with the value stored as-is, plus 8 bits of stencil in unsigned integer format. Values for stencil are in the [code][0, 255][/code] range.
</constant>
<constant name="DATA_FORMAT_BC1_RGB_UNORM_BLOCK" value="130" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel and 5 bits of blue channel. Using BC1 texture compression (also known as S3TC DXT1).
</constant>
<constant name="DATA_FORMAT_BC1_RGB_SRGB_BLOCK" value="131" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel and 5 bits of blue channel. Using BC1 texture compression (also known as S3TC DXT1).
</constant>
<constant name="DATA_FORMAT_BC1_RGBA_UNORM_BLOCK" value="132" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 1 bit of alpha channel. Using BC1 texture compression (also known as S3TC DXT1).
</constant>
<constant name="DATA_FORMAT_BC1_RGBA_SRGB_BLOCK" value="133" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 1 bit of alpha channel. Using BC1 texture compression (also known as S3TC DXT1).
</constant>
<constant name="DATA_FORMAT_BC2_UNORM_BLOCK" value="134" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 4 bits of alpha channel. Using BC2 texture compression (also known as S3TC DXT3).
</constant>
<constant name="DATA_FORMAT_BC2_SRGB_BLOCK" value="135" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 4 bits of alpha channel. Using BC2 texture compression (also known as S3TC DXT3).
</constant>
<constant name="DATA_FORMAT_BC3_UNORM_BLOCK" value="136" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 8 bits of alpha channel. Using BC3 texture compression (also known as S3TC DXT5).
</constant>
<constant name="DATA_FORMAT_BC3_SRGB_BLOCK" value="137" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 5 bits of red channel, 6 bits of green channel, 5 bits of blue channel and 8 bits of alpha channel. Using BC3 texture compression (also known as S3TC DXT5).
</constant>
<constant name="DATA_FORMAT_BC4_UNORM_BLOCK" value="138" enum="DataFormat">
+ VRAM-compressed unsigned red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 8 bits of red channel. Using BC4 texture compression.
</constant>
<constant name="DATA_FORMAT_BC4_SNORM_BLOCK" value="139" enum="DataFormat">
+ VRAM-compressed signed red channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range. The format's precision is 8 bits of red channel. Using BC4 texture compression.
</constant>
<constant name="DATA_FORMAT_BC5_UNORM_BLOCK" value="140" enum="DataFormat">
+ VRAM-compressed unsigned red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is 8 bits of red channel and 8 bits of green channel. Using BC5 texture compression (also known as S3TC RGTC).
</constant>
<constant name="DATA_FORMAT_BC5_SNORM_BLOCK" value="141" enum="DataFormat">
+ VRAM-compressed signed red/green channel data format with normalized value. Values are in the [code][-1.0, 1.0][/code] range. The format's precision is 8 bits of red channel and 8 bits of green channel. Using BC5 texture compression (also known as S3TC RGTC).
</constant>
<constant name="DATA_FORMAT_BC6H_UFLOAT_BLOCK" value="142" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue channel data format with the floating-point value stored as-is. The format's precision is 8 bits of red channel and 8 bits of green channel. Using BC6H texture compression (also known as BPTC HDR).
</constant>
<constant name="DATA_FORMAT_BC6H_SFLOAT_BLOCK" value="143" enum="DataFormat">
+ VRAM-compressed signed red/green/blue channel data format with the floating-point value stored as-is. The format's precision is between 4 and 7 bits for the red/green/blue channels and between 0 and 8 bits for the alpha channel. Using BC7 texture compression (also known as BPTC HDR).
</constant>
<constant name="DATA_FORMAT_BC7_UNORM_BLOCK" value="144" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. The format's precision is between 4 and 7 bits for the red/green/blue channels and between 0 and 8 bits for the alpha channel. Also known as BPTC LDR.
</constant>
<constant name="DATA_FORMAT_BC7_SRGB_BLOCK" value="145" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. The format's precision is between 4 and 7 bits for the red/green/blue channels and between 0 and 8 bits for the alpha channel. Also known as BPTC LDR.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK" value="146" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK" value="147" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK" value="148" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Red/green/blue use 8 bit of precision each, with alpha using 1 bit of precision. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK" value="149" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. Red/green/blue use 8 bit of precision each, with alpha using 1 bit of precision. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK" value="150" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Red/green/blue use 8 bits of precision each, with alpha using 8 bits of precision. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK" value="151" enum="DataFormat">
+ VRAM-compressed unsigned red/green/blue/alpha channel data format with normalized value and non-linear sRGB encoding. Values are in the [code][0.0, 1.0][/code] range. Red/green/blue use 8 bits of precision each, with alpha using 8 bits of precision. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11_UNORM_BLOCK" value="152" enum="DataFormat">
+ 11-bit VRAM-compressed unsigned red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11_SNORM_BLOCK" value="153" enum="DataFormat">
+ 11-bit VRAM-compressed signed red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11G11_UNORM_BLOCK" value="154" enum="DataFormat">
+ 11-bit VRAM-compressed unsigned red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_EAC_R11G11_SNORM_BLOCK" value="155" enum="DataFormat">
+ 11-bit VRAM-compressed signed red/green channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Using ETC2 texture compression.
</constant>
<constant name="DATA_FORMAT_ASTC_4x4_UNORM_BLOCK" value="156" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 4×4 blocks (highest quality). Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_4x4_SRGB_BLOCK" value="157" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 4×4 blocks (highest quality). Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_5x4_UNORM_BLOCK" value="158" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 5×4 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_5x4_SRGB_BLOCK" value="159" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 5×4 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_5x5_UNORM_BLOCK" value="160" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 5×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_5x5_SRGB_BLOCK" value="161" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 5×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_6x5_UNORM_BLOCK" value="162" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 6×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_6x5_SRGB_BLOCK" value="163" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 6×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_6x6_UNORM_BLOCK" value="164" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 6×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_6x6_SRGB_BLOCK" value="165" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 6×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x5_UNORM_BLOCK" value="166" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 8×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x5_SRGB_BLOCK" value="167" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 8×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x6_UNORM_BLOCK" value="168" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 8×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x6_SRGB_BLOCK" value="169" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 8×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x8_UNORM_BLOCK" value="170" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 8×8 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_8x8_SRGB_BLOCK" value="171" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 8×8 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x5_UNORM_BLOCK" value="172" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 10×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x5_SRGB_BLOCK" value="173" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 10×5 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x6_UNORM_BLOCK" value="174" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 10×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x6_SRGB_BLOCK" value="175" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 10×6 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x8_UNORM_BLOCK" value="176" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 10×8 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x8_SRGB_BLOCK" value="177" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 10×8 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x10_UNORM_BLOCK" value="178" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 10×10 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_10x10_SRGB_BLOCK" value="179" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 10×10 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_12x10_UNORM_BLOCK" value="180" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 12×10 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_12x10_SRGB_BLOCK" value="181" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 12×10 blocks. Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_12x12_UNORM_BLOCK" value="182" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value, packed in 12 blocks (lowest quality). Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_ASTC_12x12_SRGB_BLOCK" value="183" enum="DataFormat">
+ VRAM-compressed unsigned floating-point data format with normalized value and non-linear sRGB encoding, packed in 12 blocks (lowest quality). Values are in the [code][0.0, 1.0][/code] range. Using ASTC compression.
</constant>
<constant name="DATA_FORMAT_G8B8G8R8_422_UNORM" value="184" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_B8G8R8G8_422_UNORM" value="185" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point blue/green/red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G8_B8_R8_3PLANE_420_UNORM" value="186" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G8_B8R8_2PLANE_420_UNORM" value="187" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, stored across 2 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G8_B8_R8_3PLANE_422_UNORM" value="188" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, stored across 2 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G8_B8R8_2PLANE_422_UNORM" value="189" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, stored across 2 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G8_B8_R8_3PLANE_444_UNORM" value="190" enum="DataFormat">
+ 8-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, stored across 3 separate planes. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R10X6_UNORM_PACK16" value="191" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point red channel data with normalized value, plus 6 unused bits, packed in 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R10X6G10X6_UNORM_2PACK16" value="192" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point red/green channel data with normalized value, plus 6 unused bits after each channel, packed in 2×16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16" value="193" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point red/green/blue/alpha channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16" value="194" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/green/red channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel). The green channel is listed twice, but contains different values to allow it to be represented at full resolution.
</constant>
<constant name="DATA_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16" value="195" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point blue/green/red/green channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel). The green channel is listed twice, but contains different values to allow it to be represented at full resolution.
</constant>
<constant name="DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16" value="196" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 2 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16" value="197" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 2 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16" value="198" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16" value="199" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16" value="200" enum="DataFormat">
+ 10-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R12X4_UNORM_PACK16" value="201" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point red channel data with normalized value, plus 6 unused bits, packed in 16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R12X4G12X4_UNORM_2PACK16" value="202" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point red/green channel data with normalized value, plus 6 unused bits after each channel, packed in 2×16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16" value="203" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point red/green/blue/alpha channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16" value="204" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/green/red channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel). The green channel is listed twice, but contains different values to allow it to be represented at full resolution.
</constant>
<constant name="DATA_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16" value="205" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point blue/green/red/green channel data with normalized value, plus 6 unused bits after each channel, packed in 4×16 bits. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel). The green channel is listed twice, but contains different values to allow it to be represented at full resolution.
</constant>
<constant name="DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16" value="206" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 2 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16" value="207" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 2 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16" value="208" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16" value="209" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16" value="210" enum="DataFormat">
+ 12-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Packed in 3×16 bits and stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="DATA_FORMAT_G16B16G16R16_422_UNORM" value="211" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_B16G16R16G16_422_UNORM" value="212" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point blue/green/red channel data format with normalized value. Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G16_B16_R16_3PLANE_420_UNORM" value="213" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 2 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G16_B16R16_2PLANE_420_UNORM" value="214" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 2 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal and vertical resolution (i.e. 2×2 adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM" value="215" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM" value="216" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 3 separate planes (green + blue/red). Values are in the [code][0.0, 1.0][/code] range. Blue and red channel data is stored at halved horizontal resolution (i.e. 2 horizontally adjacent pixels will share the same value for the blue/red channel).
</constant>
<constant name="DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM" value="217" enum="DataFormat">
+ 16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range.
</constant>
<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>
<constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true">
+ Compute barrier mask.
</constant>
<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>
<constant name="BARRIER_MASK_NO_BARRIER" value="8" enum="BarrierMask" is_bitfield="true">
+ No barrier for any type.
</constant>
<constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType">
1-dimensional texture.
@@ -1195,63 +1612,91 @@
Represents the size of the [enum TextureType] enum.
</constant>
<constant name="TEXTURE_SAMPLES_1" value="0" enum="TextureSamples">
+ Perform 1 texture sample (this is the fastest but lowest-quality for antialiasing).
</constant>
<constant name="TEXTURE_SAMPLES_2" value="1" enum="TextureSamples">
+ Perform 2 texture samples.
</constant>
<constant name="TEXTURE_SAMPLES_4" value="2" enum="TextureSamples">
+ Perform 4 texture samples.
</constant>
<constant name="TEXTURE_SAMPLES_8" value="3" enum="TextureSamples">
+ Perform 8 texture samples. Not supported on mobile GPUs (including Apple Silicon).
</constant>
<constant name="TEXTURE_SAMPLES_16" value="4" enum="TextureSamples">
+ Perform 16 texture samples. Not supported on mobile GPUs and many desktop GPUs.
</constant>
<constant name="TEXTURE_SAMPLES_32" value="5" enum="TextureSamples">
+ Perform 32 texture samples. Not supported on most GPUs.
</constant>
<constant name="TEXTURE_SAMPLES_64" value="6" enum="TextureSamples">
+ Perform 64 texture samples (this is the slowest but highest-quality for antialiasing). Not supported on most GPUs.
</constant>
<constant name="TEXTURE_SAMPLES_MAX" value="7" enum="TextureSamples">
Represents the size of the [enum TextureSamples] enum.
</constant>
<constant name="TEXTURE_USAGE_SAMPLING_BIT" value="1" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be sampled.
</constant>
<constant name="TEXTURE_USAGE_COLOR_ATTACHMENT_BIT" value="2" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be used as a color attachment in a framebuffer.
</constant>
<constant name="TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT" value="4" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be used as a depth/stencil attachment in a framebuffer.
</constant>
<constant name="TEXTURE_USAGE_STORAGE_BIT" value="8" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be used as a [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#descriptorsets-storageimage]storage image[/url].
</constant>
<constant name="TEXTURE_USAGE_STORAGE_ATOMIC_BIT" value="16" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be used as a [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#descriptorsets-storageimage]storage image[/url] with support for atomic operations.
</constant>
<constant name="TEXTURE_USAGE_CPU_READ_BIT" value="32" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be read back on the CPU using [method texture_get_data] faster than without this bit, since it is always kept in the system memory.
</constant>
<constant name="TEXTURE_USAGE_CAN_UPDATE_BIT" value="64" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be updated using [method texture_update].
</constant>
<constant name="TEXTURE_USAGE_CAN_COPY_FROM_BIT" value="128" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be a source for [method texture_copy].
</constant>
<constant name="TEXTURE_USAGE_CAN_COPY_TO_BIT" value="256" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be a destination for [method texture_copy].
</constant>
<constant name="TEXTURE_USAGE_INPUT_ATTACHMENT_BIT" value="512" enum="TextureUsageBits" is_bitfield="true">
+ Texture can be used as a [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#descriptorsets-inputattachment]input attachment[/url] in a framebuffer.
</constant>
<constant name="TEXTURE_SWIZZLE_IDENTITY" value="0" enum="TextureSwizzle">
+ Return the sampled value as-is.
</constant>
<constant name="TEXTURE_SWIZZLE_ZERO" value="1" enum="TextureSwizzle">
+ Always return [code]0.0[/code] when sampling.
</constant>
<constant name="TEXTURE_SWIZZLE_ONE" value="2" enum="TextureSwizzle">
+ Always return [code]1.0[/code] when sampling.
</constant>
<constant name="TEXTURE_SWIZZLE_R" value="3" enum="TextureSwizzle">
+ Sample the red color channel.
</constant>
<constant name="TEXTURE_SWIZZLE_G" value="4" enum="TextureSwizzle">
+ Sample the green color channel.
</constant>
<constant name="TEXTURE_SWIZZLE_B" value="5" enum="TextureSwizzle">
+ Sample the blue color channel.
</constant>
<constant name="TEXTURE_SWIZZLE_A" value="6" enum="TextureSwizzle">
+ Sample the alpha channel.
</constant>
<constant name="TEXTURE_SWIZZLE_MAX" value="7" enum="TextureSwizzle">
+ Represents the size of the [enum TextureSwizzle] enum.
</constant>
<constant name="TEXTURE_SLICE_2D" value="0" enum="TextureSliceType">
+ 2-dimensional texture slice.
</constant>
<constant name="TEXTURE_SLICE_CUBEMAP" value="1" enum="TextureSliceType">
+ Cubemap texture slice.
</constant>
<constant name="TEXTURE_SLICE_3D" value="2" enum="TextureSliceType">
+ 3-dimensional texture slice.
</constant>
<constant name="SAMPLER_FILTER_NEAREST" value="0" enum="SamplerFilter">
Nearest-neighbor sampler filtering. Sampling at higher resolutions than the source will result in a pixelated look.
@@ -1260,209 +1705,311 @@
Bilinear sampler filtering. Sampling at higher resolutions than the source will result in a blurry look.
</constant>
<constant name="SAMPLER_REPEAT_MODE_REPEAT" value="0" enum="SamplerRepeatMode">
+ Sample with repeating enabled.
</constant>
<constant name="SAMPLER_REPEAT_MODE_MIRRORED_REPEAT" value="1" enum="SamplerRepeatMode">
+ Sample with mirrored repeating enabled. When sampling outside the [code][0.0, 1.0][/code] range, return a mirrored version of the sampler. This mirrored version is mirrored again if sampling further away, with the pattern repeating indefinitely.
</constant>
<constant name="SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE" value="2" enum="SamplerRepeatMode">
+ Sample with repeating disabled. When sampling outside the [code][0.0, 1.0][/code] range, return the color of the last pixel on the edge.
</constant>
<constant name="SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER" value="3" enum="SamplerRepeatMode">
+ Sample with repeating disabled. When sampling outside the [code][0.0, 1.0][/code] range, return the specified [member RDSamplerState.border_color].
</constant>
<constant name="SAMPLER_REPEAT_MODE_MIRROR_CLAMP_TO_EDGE" value="4" enum="SamplerRepeatMode">
+ Sample with mirrored repeating enabled, but only once. When sampling in the [code][-1.0, 0.0][/code] range, return a mirrored version of the sampler. When sampling outside the [code][-1.0, 1.0][/code] range, return the color of the last pixel on the edge.
</constant>
<constant name="SAMPLER_REPEAT_MODE_MAX" value="5" enum="SamplerRepeatMode">
+ Represents the size of the [enum SamplerRepeatMode] enum.
</constant>
<constant name="SAMPLER_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK" value="0" enum="SamplerBorderColor">
+ Return a floating-point transparent black color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_INT_TRANSPARENT_BLACK" value="1" enum="SamplerBorderColor">
+ Return a integer transparent black color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_BLACK" value="2" enum="SamplerBorderColor">
+ Return a floating-point opaque black color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_INT_OPAQUE_BLACK" value="3" enum="SamplerBorderColor">
+ Return a integer opaque black color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_FLOAT_OPAQUE_WHITE" value="4" enum="SamplerBorderColor">
+ Return a floating-point opaque white color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_INT_OPAQUE_WHITE" value="5" enum="SamplerBorderColor">
+ Return a integer opaque white color when sampling outside the [code][0.0, 1.0][/code] range. Only effective if the sampler repeat mode is [constant SAMPLER_REPEAT_MODE_CLAMP_TO_BORDER].
</constant>
<constant name="SAMPLER_BORDER_COLOR_MAX" value="6" enum="SamplerBorderColor">
+ Represents the size of the [enum SamplerBorderColor] enum.
</constant>
<constant name="VERTEX_FREQUENCY_VERTEX" value="0" enum="VertexFrequency">
+ Vertex attribute addressing is a function of the vertex. This is used to specify the rate at which vertex attributes are pulled from buffers.
</constant>
<constant name="VERTEX_FREQUENCY_INSTANCE" value="1" enum="VertexFrequency">
+ Vertex attribute addressing is a function of the instance index. This is used to specify the rate at which vertex attributes are pulled from buffers.
</constant>
<constant name="INDEX_BUFFER_FORMAT_UINT16" value="0" enum="IndexBufferFormat">
+ Index buffer in 16-bit unsigned integer format. This limits the maximum index that can be specified to [code]65535[/code].
</constant>
<constant name="INDEX_BUFFER_FORMAT_UINT32" value="1" enum="IndexBufferFormat">
+ Index buffer in 32-bit unsigned integer format. This limits the maximum index that can be specified to [code]4294967295[/code].
</constant>
<constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true">
</constant>
<constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType">
+ Sampler uniform. TODO: Difference between sampler and texture uniform
</constant>
<constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE" value="1" enum="UniformType">
+ Sampler uniform with a texture.
</constant>
<constant name="UNIFORM_TYPE_TEXTURE" value="2" enum="UniformType">
+ Texture uniform.
</constant>
<constant name="UNIFORM_TYPE_IMAGE" value="3" enum="UniformType">
+ Image uniform. TODO: Difference between texture and image uniform
</constant>
<constant name="UNIFORM_TYPE_TEXTURE_BUFFER" value="4" enum="UniformType">
+ Texture buffer uniform. TODO: Difference between texture and texture buffe uniformr
</constant>
<constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER" value="5" enum="UniformType">
+ Sampler uniform with a texture buffer. TODO: Difference between texture and texture buffer uniform
</constant>
<constant name="UNIFORM_TYPE_IMAGE_BUFFER" value="6" enum="UniformType">
+ Image buffer uniform. TODO: Difference between texture and image uniforms
</constant>
<constant name="UNIFORM_TYPE_UNIFORM_BUFFER" value="7" enum="UniformType">
+ Uniform buffer uniform.
</constant>
<constant name="UNIFORM_TYPE_STORAGE_BUFFER" value="8" enum="UniformType">
+ [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]Storage buffer[/url] uniform.
</constant>
<constant name="UNIFORM_TYPE_INPUT_ATTACHMENT" value="9" enum="UniformType">
+ Input attachment uniform.
</constant>
<constant name="UNIFORM_TYPE_MAX" value="10" enum="UniformType">
+ Represents the size of the [enum UniformType] enum.
</constant>
<constant name="RENDER_PRIMITIVE_POINTS" value="0" enum="RenderPrimitive">
Point rendering primitive (with constant size, regardless of distance from camera).
</constant>
<constant name="RENDER_PRIMITIVE_LINES" value="1" enum="RenderPrimitive">
- Line rendering primitive.
+ Line list rendering primitive. Lines are drawn separated from each other.
</constant>
<constant name="RENDER_PRIMITIVE_LINES_WITH_ADJACENCY" value="2" enum="RenderPrimitive">
+ [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#drawing-line-lists-with-adjacency]Line list rendering primitive with adjacency.[/url]
+ [b]Note:[/b] Adjacency is only useful with geometry shaders, which Godot does not expose.
</constant>
<constant name="RENDER_PRIMITIVE_LINESTRIPS" value="3" enum="RenderPrimitive">
+ Line strip rendering primitive. Lines drawn are connected to the previous vertex.
</constant>
<constant name="RENDER_PRIMITIVE_LINESTRIPS_WITH_ADJACENCY" value="4" enum="RenderPrimitive">
+ [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#drawing-line-strips-with-adjacency]Line strip rendering primitive with adjacency.[/url]
+ [b]Note:[/b] Adjacency is only useful with geometry shaders, which Godot does not expose.
</constant>
<constant name="RENDER_PRIMITIVE_TRIANGLES" value="5" enum="RenderPrimitive">
+ Triangle list rendering primitive. Triangles are drawn separated from each other.
</constant>
<constant name="RENDER_PRIMITIVE_TRIANGLES_WITH_ADJACENCY" value="6" enum="RenderPrimitive">
+ [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#drawing-triangle-lists-with-adjacency]Triangle list rendering primitive with adjacency.[/url]
+ [b]Note:[/b] Adjacency is only useful with geometry shaders, which Godot does not expose.
</constant>
<constant name="RENDER_PRIMITIVE_TRIANGLE_STRIPS" value="7" enum="RenderPrimitive">
+ Triangle strip rendering primitive. Triangles drawn are connected to the previous triangle.
</constant>
<constant name="RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_AJACENCY" value="8" enum="RenderPrimitive">
+ [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#drawing-triangle-strips-with-adjacency]Triangle strip rendering primitive with adjacency.[/url]
+ [b]Note:[/b] Adjacency is only useful with geometry shaders, which Godot does not expose.
</constant>
<constant name="RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX" value="9" enum="RenderPrimitive">
+ Triangle strip rendering primitive with [i]primitive restart[/i] enabled. Triangles drawn are connected to the previous triangle, but a primitive restart index can be specified before drawing to create a second triangle strip after the specified index.
+ [b]Note:[/b] Only compatible with indexed draws.
</constant>
<constant name="RENDER_PRIMITIVE_TESSELATION_PATCH" value="10" enum="RenderPrimitive">
+ Tessellation patch rendering primitive. Only useful with tessellation shaders, which can be used to deform these patches.
</constant>
<constant name="RENDER_PRIMITIVE_MAX" value="11" enum="RenderPrimitive">
+ Represents the size of the [enum RenderPrimitive] enum.
</constant>
<constant name="POLYGON_CULL_DISABLED" value="0" enum="PolygonCullMode">
+ Do not use polygon front face or backface culling.
</constant>
<constant name="POLYGON_CULL_FRONT" value="1" enum="PolygonCullMode">
+ Use polygon frontface culling (faces pointing towards the camera are hidden).
</constant>
<constant name="POLYGON_CULL_BACK" value="2" enum="PolygonCullMode">
+ Use polygon backface culling (faces pointing away from the camera are hidden).
</constant>
<constant name="POLYGON_FRONT_FACE_CLOCKWISE" value="0" enum="PolygonFrontFace">
+ Clockwise winding order to determine which face of a polygon is its front face.
</constant>
<constant name="POLYGON_FRONT_FACE_COUNTER_CLOCKWISE" value="1" enum="PolygonFrontFace">
+ Counter-clockwise winding order to determine which face of a polygon is its front face.
</constant>
<constant name="STENCIL_OP_KEEP" value="0" enum="StencilOperation">
+ Keep the current stencil value.
</constant>
<constant name="STENCIL_OP_ZERO" value="1" enum="StencilOperation">
+ Set the stencil value to [code]0[/code].
</constant>
<constant name="STENCIL_OP_REPLACE" value="2" enum="StencilOperation">
+ Replace the existing stencil value with the new one.
</constant>
<constant name="STENCIL_OP_INCREMENT_AND_CLAMP" value="3" enum="StencilOperation">
+ Increment the existing stencil value and clamp to the maximum representable unsigned value if reached. Stencil bits are considered as an unsigned integer.
</constant>
<constant name="STENCIL_OP_DECREMENT_AND_CLAMP" value="4" enum="StencilOperation">
+ Decrement the existing stencil value and clamp to the minimum value if reached. Stencil bits are considered as an unsigned integer.
</constant>
<constant name="STENCIL_OP_INVERT" value="5" enum="StencilOperation">
+ Bitwise-invert the existing stencil value.
</constant>
<constant name="STENCIL_OP_INCREMENT_AND_WRAP" value="6" enum="StencilOperation">
+ Increment the stencil value and wrap around to [code]0[/code] if reaching the maximum representable unsigned. Stencil bits are considered as an unsigned integer.
</constant>
<constant name="STENCIL_OP_DECREMENT_AND_WRAP" value="7" enum="StencilOperation">
+ Decrement the stencil value and wrap around to the maximum representable unsigned if reaching the minimum. Stencil bits are considered as an unsigned integer.
</constant>
<constant name="STENCIL_OP_MAX" value="8" enum="StencilOperation">
+ Represents the size of the [enum StencilOperation] enum.
</constant>
<constant name="COMPARE_OP_NEVER" value="0" enum="CompareOperator">
+ "Never" comparison (opposite of [constant COMPARE_OP_ALWAYS]).
</constant>
<constant name="COMPARE_OP_LESS" value="1" enum="CompareOperator">
+ "Less than" comparison.
</constant>
<constant name="COMPARE_OP_EQUAL" value="2" enum="CompareOperator">
+ "Equal" comparison.
</constant>
<constant name="COMPARE_OP_LESS_OR_EQUAL" value="3" enum="CompareOperator">
+ "Less than or equal" comparison.
</constant>
<constant name="COMPARE_OP_GREATER" value="4" enum="CompareOperator">
+ "Greater than" comparison.
</constant>
<constant name="COMPARE_OP_NOT_EQUAL" value="5" enum="CompareOperator">
+ "Not equal" comparison.
</constant>
<constant name="COMPARE_OP_GREATER_OR_EQUAL" value="6" enum="CompareOperator">
+ "Greater than or equal" comparison.
</constant>
<constant name="COMPARE_OP_ALWAYS" value="7" enum="CompareOperator">
+ "Always" comparison (opposite of [constant COMPARE_OP_NEVER]).
</constant>
<constant name="COMPARE_OP_MAX" value="8" enum="CompareOperator">
+ Represents the size of the [enum CompareOperator] enum.
</constant>
<constant name="LOGIC_OP_CLEAR" value="0" enum="LogicOperation">
+ Clear logic operation (result is always [code]0[/code]). See also [constant LOGIC_OP_SET].
</constant>
<constant name="LOGIC_OP_AND" value="1" enum="LogicOperation">
+ AND logic operation.
</constant>
<constant name="LOGIC_OP_AND_REVERSE" value="2" enum="LogicOperation">
+ AND logic operation with the [i]destination[/i] operand being inverted. See also [constant LOGIC_OP_AND_INVERTED].
</constant>
<constant name="LOGIC_OP_COPY" value="3" enum="LogicOperation">
+ Copy logic operation (keeps the [i]source[/i] value as-is). See also [constant LOGIC_OP_COPY_INVERTED] and [constant LOGIC_OP_NO_OP].
</constant>
<constant name="LOGIC_OP_AND_INVERTED" value="4" enum="LogicOperation">
+ AND logic operation with the [i]source[/i] operand being inverted. See also [constant LOGIC_OP_AND_REVERSE].
</constant>
<constant name="LOGIC_OP_NO_OP" value="5" enum="LogicOperation">
+ No-op logic operation (keeps the [i]destination[/i] value as-is). See also [constant LOGIC_OP_COPY].
</constant>
<constant name="LOGIC_OP_XOR" value="6" enum="LogicOperation">
Exclusive or (XOR) logic operation.
</constant>
<constant name="LOGIC_OP_OR" value="7" enum="LogicOperation">
+ OR logic operation.
</constant>
<constant name="LOGIC_OP_NOR" value="8" enum="LogicOperation">
+ Not-OR (NOR) logic operation.
</constant>
<constant name="LOGIC_OP_EQUIVALENT" value="9" enum="LogicOperation">
+ Not-XOR (XNOR) logic operation.
</constant>
<constant name="LOGIC_OP_INVERT" value="10" enum="LogicOperation">
+ Invert logic operation.
</constant>
<constant name="LOGIC_OP_OR_REVERSE" value="11" enum="LogicOperation">
+ OR logic operation with the [i]destination[/i] operand being inverted. See also [constant LOGIC_OP_OR_REVERSE].
</constant>
<constant name="LOGIC_OP_COPY_INVERTED" value="12" enum="LogicOperation">
+ NOT logic operation (inverts the value). See also [constant LOGIC_OP_COPY].
</constant>
<constant name="LOGIC_OP_OR_INVERTED" value="13" enum="LogicOperation">
+ OR logic operation with the [i]source[/i] operand being inverted. See also [constant LOGIC_OP_OR_REVERSE].
</constant>
<constant name="LOGIC_OP_NAND" value="14" enum="LogicOperation">
+ Not-AND (NAND) logic operation.
</constant>
<constant name="LOGIC_OP_SET" value="15" enum="LogicOperation">
+ SET logic operation (result is always [code]1[/code]). See also [constant LOGIC_OP_CLEAR].
</constant>
<constant name="LOGIC_OP_MAX" value="16" enum="LogicOperation">
+ Represents the size of the [enum LogicOperation] enum.
</constant>
<constant name="BLEND_FACTOR_ZERO" value="0" enum="BlendFactor">
+ Constant [code]0.0[/code] blend factor.
</constant>
<constant name="BLEND_FACTOR_ONE" value="1" enum="BlendFactor">
+ Constant [code]1.0[/code] blend factor.
</constant>
<constant name="BLEND_FACTOR_SRC_COLOR" value="2" enum="BlendFactor">
+ Color blend factor is [code]source color[/code]. Alpha blend factor is [code]source alpha[/code].
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_SRC_COLOR" value="3" enum="BlendFactor">
+ Color blend factor is [code]1.0 - source color[/code]. Alpha blend factor is [code]1.0 - source alpha[/code].
</constant>
<constant name="BLEND_FACTOR_DST_COLOR" value="4" enum="BlendFactor">
+ Color blend factor is [code]destination color[/code]. Alpha blend factor is [code]destination alpha[/code].
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_DST_COLOR" value="5" enum="BlendFactor">
+ Color blend factor is [code]1.0 - destination color[/code]. Alpha blend factor is [code]1.0 - destination alpha[/code].
</constant>
<constant name="BLEND_FACTOR_SRC_ALPHA" value="6" enum="BlendFactor">
+ Color and alpha blend factor is [code]source alpha[/code].
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_SRC_ALPHA" value="7" enum="BlendFactor">
+ Color and alpha blend factor is [code]1.0 - source alpha[/code].
</constant>
<constant name="BLEND_FACTOR_DST_ALPHA" value="8" enum="BlendFactor">
+ Color and alpha blend factor is [code]destination alpha[/code].
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_DST_ALPHA" value="9" enum="BlendFactor">
+ Color and alpha blend factor is [code]1.0 - destination alpha[/code].
</constant>
<constant name="BLEND_FACTOR_CONSTANT_COLOR" value="10" enum="BlendFactor">
+ Color blend factor is [code]blend constant color[/code]. Alpha blend factor is [code]blend constant alpha[/code] (see [method draw_list_set_blend_constants]).
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR" value="11" enum="BlendFactor">
+ Color blend factor is [code]1.0 - blend constant color[/code]. Alpha blend factor is [code]1.0 - blend constant alpha[/code] (see [method draw_list_set_blend_constants]).
</constant>
<constant name="BLEND_FACTOR_CONSTANT_ALPHA" value="12" enum="BlendFactor">
+ Color and alpha blend factor is [code]blend constant alpha[/code] (see [method draw_list_set_blend_constants]).
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA" value="13" enum="BlendFactor">
+ Color and alpha blend factor is [code]1.0 - blend constant alpha[/code] (see [method draw_list_set_blend_constants]).
</constant>
<constant name="BLEND_FACTOR_SRC_ALPHA_SATURATE" value="14" enum="BlendFactor">
+ Color blend factor is [code]min(source alpha, 1.0 - destination alpha)[/code]. Alpha blend factor is [code]1.0[/code].
</constant>
<constant name="BLEND_FACTOR_SRC1_COLOR" value="15" enum="BlendFactor">
+ Color blend factor is [code]second source color[/code]. Alpha blend factor is [code]second source alpha[/code]. Only relevant for dual-source blending.
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_SRC1_COLOR" value="16" enum="BlendFactor">
+ Color blend factor is [code]1.0 - second source color[/code]. Alpha blend factor is [code]1.0 - second source alpha[/code]. Only relevant for dual-source blending.
</constant>
<constant name="BLEND_FACTOR_SRC1_ALPHA" value="17" enum="BlendFactor">
+ Color and alpha blend factor is [code]second source alpha[/code]. Only relevant for dual-source blending.
</constant>
<constant name="BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA" value="18" enum="BlendFactor">
+ Color and alpha blend factor is [code]1.0 - second source alpha[/code]. Only relevant for dual-source blending.
</constant>
<constant name="BLEND_FACTOR_MAX" value="19" enum="BlendFactor">
+ Represents the size of the [enum BlendFactor] enum.
</constant>
<constant name="BLEND_OP_ADD" value="0" enum="BlendOperation">
Additive blending operation ([code]source + destination[/code]).
@@ -1509,68 +2056,100 @@
<constant name="INITIAL_ACTION_CONTINUE" value="5" enum="InitialAction">
</constant>
<constant name="INITIAL_ACTION_MAX" value="6" enum="InitialAction">
+ Represents the size of the [enum InitialAction] enum.
</constant>
<constant name="FINAL_ACTION_READ" value="0" enum="FinalAction">
+ Store the texture for reading and make it read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit (only applies to color, depth and stencil attachments).
</constant>
<constant name="FINAL_ACTION_DISCARD" value="1" enum="FinalAction">
+ Discard the texture data and make it read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit (only applies to color, depth and stencil attachments).
</constant>
<constant name="FINAL_ACTION_CONTINUE" value="2" enum="FinalAction">
+ Store the texture and continue for further processing. Similar to [constant FINAL_ACTION_READ], but does not make the texture read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit.
</constant>
<constant name="FINAL_ACTION_MAX" value="3" enum="FinalAction">
+ Represents the size of the [enum FinalAction] enum.
</constant>
<constant name="SHADER_STAGE_VERTEX" value="0" enum="ShaderStage">
+ Vertex shader stage. This can be used to manipulate vertices from a shader (but not create new vertices).
</constant>
<constant name="SHADER_STAGE_FRAGMENT" value="1" enum="ShaderStage">
+ Fragment shader stage (called "pixel shader" in Direct3D). This can be used to manipulate pixels from a shader.
</constant>
<constant name="SHADER_STAGE_TESSELATION_CONTROL" value="2" enum="ShaderStage">
+ Tessellation control shader stage. This can be used to create additional geometry from a shader.
</constant>
<constant name="SHADER_STAGE_TESSELATION_EVALUATION" value="3" enum="ShaderStage">
+ Tessellation evaluation shader stage. This can be used to create additional geometry from a shader.
</constant>
<constant name="SHADER_STAGE_COMPUTE" value="4" enum="ShaderStage">
+ Compute shader stage. This can be used to run arbitrary computing tasks in a shader, performing them on the GPU instead of the CPU.
</constant>
<constant name="SHADER_STAGE_MAX" value="5" enum="ShaderStage">
+ Represents the size of the [enum ShaderStage] enum.
</constant>
<constant name="SHADER_STAGE_VERTEX_BIT" value="1" enum="ShaderStage">
+ Vertex shader stage bit (see also [constant SHADER_STAGE_VERTEX]).
</constant>
<constant name="SHADER_STAGE_FRAGMENT_BIT" value="2" enum="ShaderStage">
+ Fragment shader stage bit (see also [constant SHADER_STAGE_FRAGMENT]).
</constant>
<constant name="SHADER_STAGE_TESSELATION_CONTROL_BIT" value="4" enum="ShaderStage">
+ Tessellation control shader stage bit (see also [constant SHADER_STAGE_TESSELATION_CONTROL]).
</constant>
<constant name="SHADER_STAGE_TESSELATION_EVALUATION_BIT" value="8" enum="ShaderStage">
+ Tessellation evaluation shader stage bit (see also [constant SHADER_STAGE_TESSELATION_EVALUATION]).
</constant>
<constant name="SHADER_STAGE_COMPUTE_BIT" value="16" enum="ShaderStage">
+ Compute shader stage bit (see also [constant SHADER_STAGE_COMPUTE]).
</constant>
<constant name="SHADER_LANGUAGE_GLSL" value="0" enum="ShaderLanguage">
+ Khronos' GLSL shading language (used natively by OpenGL and Vulkan). This is the language used for core Godot shaders.
</constant>
<constant name="SHADER_LANGUAGE_HLSL" value="1" enum="ShaderLanguage">
+ Microsoft's High-Level Shading Language (used natively by Direct3D, but can also be used in Vulkan).
</constant>
<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL" value="0" enum="PipelineSpecializationConstantType">
+ Boolean specialization constant.
</constant>
<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT" value="1" enum="PipelineSpecializationConstantType">
+ Integer specialization constant.
</constant>
<constant name="PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT" value="2" enum="PipelineSpecializationConstantType">
+ Floating-point specialization constant.
</constant>
<constant name="LIMIT_MAX_BOUND_UNIFORM_SETS" value="0" enum="Limit">
+ Maximum number of uniform sets that can be bound at a given time.
</constant>
<constant name="LIMIT_MAX_FRAMEBUFFER_COLOR_ATTACHMENTS" value="1" enum="Limit">
+ Maximum number of color framebuffer attachments that can be used at a given time.
</constant>
<constant name="LIMIT_MAX_TEXTURES_PER_UNIFORM_SET" value="2" enum="Limit">
+ Maximum number of textures that can be used per uniform set.
</constant>
<constant name="LIMIT_MAX_SAMPLERS_PER_UNIFORM_SET" value="3" enum="Limit">
+ Maximum number of samplers that can be used per uniform set.
</constant>
<constant name="LIMIT_MAX_STORAGE_BUFFERS_PER_UNIFORM_SET" value="4" enum="Limit">
+ Maximum number of [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]storage buffers[/url] per uniform set.
</constant>
<constant name="LIMIT_MAX_STORAGE_IMAGES_PER_UNIFORM_SET" value="5" enum="Limit">
+ Maximum number of storage images per uniform set.
</constant>
<constant name="LIMIT_MAX_UNIFORM_BUFFERS_PER_UNIFORM_SET" value="6" enum="Limit">
+ Maximum number of uniform buffers per uniform set.
</constant>
<constant name="LIMIT_MAX_DRAW_INDEXED_INDEX" value="7" enum="Limit">
+ Maximum index for an indexed draw command.
</constant>
<constant name="LIMIT_MAX_FRAMEBUFFER_HEIGHT" value="8" enum="Limit">
+ Maximum height of a framebuffer (in pixels).
</constant>
<constant name="LIMIT_MAX_FRAMEBUFFER_WIDTH" value="9" enum="Limit">
+ Maximum width of a framebuffer (in pixels).
</constant>
<constant name="LIMIT_MAX_TEXTURE_ARRAY_LAYERS" value="10" enum="Limit">
+ Maximum number of texture array layers.
</constant>
<constant name="LIMIT_MAX_TEXTURE_SIZE_1D" value="11" enum="Limit">
Maximum supported 1-dimensional texture size (in pixels on a single axis).
@@ -1585,48 +2164,70 @@
Maximum supported cubemap texture size (in pixels on a single axis of a single face).
</constant>
<constant name="LIMIT_MAX_TEXTURES_PER_SHADER_STAGE" value="15" enum="Limit">
+ Maximum number of textures per shader stage.
</constant>
<constant name="LIMIT_MAX_SAMPLERS_PER_SHADER_STAGE" value="16" enum="Limit">
+ Maximum number of samplers per shader stage.
</constant>
<constant name="LIMIT_MAX_STORAGE_BUFFERS_PER_SHADER_STAGE" value="17" enum="Limit">
+ Maximum number of [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]storage buffers[/url] per shader stage.
</constant>
<constant name="LIMIT_MAX_STORAGE_IMAGES_PER_SHADER_STAGE" value="18" enum="Limit">
+ Maximum number of storage images per shader stage.
</constant>
<constant name="LIMIT_MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE" value="19" enum="Limit">
+ Maximum number of uniform buffers per uniform set.
</constant>
<constant name="LIMIT_MAX_PUSH_CONSTANT_SIZE" value="20" enum="Limit">
+ Maximum size of a push constant. A lot of devices are limited to 128 bytes, so try to avoid exceeding 128 bytes in push constants to ensure compatibility even if your GPU is reporting a higher value.
</constant>
<constant name="LIMIT_MAX_UNIFORM_BUFFER_SIZE" value="21" enum="Limit">
+ Maximum size of a uniform buffer.
</constant>
<constant name="LIMIT_MAX_VERTEX_INPUT_ATTRIBUTE_OFFSET" value="22" enum="Limit">
+ Maximum vertex input attribute offset.
</constant>
<constant name="LIMIT_MAX_VERTEX_INPUT_ATTRIBUTES" value="23" enum="Limit">
+ Maximum number of vertex input attributes.
</constant>
<constant name="LIMIT_MAX_VERTEX_INPUT_BINDINGS" value="24" enum="Limit">
+ Maximum number of vertex input bindings.
</constant>
<constant name="LIMIT_MAX_VERTEX_INPUT_BINDING_STRIDE" value="25" enum="Limit">
+ Maximum vertex input binding stride.
</constant>
<constant name="LIMIT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT" value="26" enum="Limit">
+ Minimum uniform buffer offset alignment.
</constant>
<constant name="LIMIT_MAX_COMPUTE_SHARED_MEMORY_SIZE" value="27" enum="Limit">
+ Maximum shared memory size for compute shaders.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X" value="28" enum="Limit">
+ Maximum number of workgroups for compute shaders on the X axis.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Y" value="29" enum="Limit">
+ Maximum number of workgroups for compute shaders on the Y axis.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_Z" value="30" enum="Limit">
+ Maximum number of workgroups for compute shaders on the Z axis.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_INVOCATIONS" value="31" enum="Limit">
+ Maximum number of workgroup invocations for compute shaders.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X" value="32" enum="Limit">
+ Maximum workgroup size for compute shaders on the X axis.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y" value="33" enum="Limit">
+ Maximum workgroup size for compute shaders on the Y axis.
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z" value="34" enum="Limit">
+ Maximum workgroup size for compute shaders on the Z axis.
</constant>
<constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_X" value="35" enum="Limit">
+ Maximum viewport width (in pixels).
</constant>
<constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_Y" value="36" enum="Limit">
+ Maximum viewport height (in pixels).
</constant>
<constant name="MEMORY_TEXTURES" value="0" enum="MemoryType">
Memory taken by textures.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 5f9abe2cda..59286acccc 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -4,15 +4,14 @@
Server for anything visible.
</brief_description>
<description>
- The rendering server is the API backend for everything visible. The whole scene system mounts on it to display.
- The rendering server is completely opaque, the internals are entirely implementation specific and cannot be accessed.
- The rendering server can be used to bypass the scene/[Node] system entirely.
+ The rendering server is the API backend for everything visible. The whole scene system mounts on it to display. The rendering server is completely opaque: the internals are entirely implementation-specific and cannot be accessed.
+ The rendering server can be used to bypass the scene/[Node] system entirely. This can improve performance in cases where the scene system is the bottleneck, but won't improve performance otherwise (for instance, if the GPU is already fully utilized).
Resources are created using the [code]*_create[/code] functions. These functions return [RID]s which are not references to the objects themselves, but opaque [i]pointers[/i] towards these objects.
All objects are drawn to a viewport. You can use the [Viewport] attached to the [SceneTree] or you can create one yourself with [method viewport_create]. When using a custom scenario or canvas, the scenario or canvas needs to be attached to the viewport using [method viewport_set_scenario] or [method viewport_attach_canvas].
- In 3D, all visual objects must be associated with a scenario. The scenario is a visual representation of the world. If accessing the rendering server from a running game, the scenario can be accessed from the scene tree from any [Node3D] node with [method Node3D.get_world_3d]. Otherwise, a scenario can be created with [method scenario_create].
+ [b]Scenarios:[/b] In 3D, all visual objects must be associated with a scenario. The scenario is a visual representation of the world. If accessing the rendering server from a running game, the scenario can be accessed from the scene tree from any [Node3D] node with [method Node3D.get_world_3d]. Otherwise, a scenario can be created with [method scenario_create].
Similarly, in 2D, a canvas is needed to draw all canvas items.
- In 3D, all visible objects are comprised of a resource and an instance. A resource can be a mesh, a particle system, a light, or any other 3D object. In order to be visible resources must be attached to an instance using [method instance_set_base]. The instance must also be attached to the scenario using [method instance_set_scenario] in order to be visible.
- In 2D, all visible objects are some form of canvas item. In order to be visible, a canvas item needs to be the child of a canvas attached to a viewport, or it needs to be the child of another canvas item that is eventually attached to the canvas.
+ [b]3D:[/b] In 3D, all visible objects are comprised of a resource and an instance. A resource can be a mesh, a particle system, a light, or any other 3D object. In order to be visible resources must be attached to an instance using [method instance_set_base]. The instance must also be attached to the scenario using [method instance_set_scenario] in order to be visible. RenderingServer methods that don't have a prefix are usually 3D-specific (but not always).
+ [b]2D:[/b] In 2D, all visible objects are some form of canvas item. In order to be visible, a canvas item needs to be the child of a canvas attached to a viewport, or it needs to be the child of another canvas item that is eventually attached to the canvas. 2D-specific RenderingServer methods generally start with [code]canvas_*[/code].
[b]Headless mode:[/b] Starting the engine with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] disables all rendering and window management functions. Most functions from [RenderingServer] will return dummy values in this case.
</description>
<tutorials>
@@ -32,8 +31,8 @@
<return type="RID" />
<description>
Creates a camera attributes object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_attributes_[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [CameraAttributes] resource.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [CameraAttributes].
</description>
</method>
<method name="camera_attributes_set_auto_exposure">
@@ -99,9 +98,9 @@
<method name="camera_create">
<return type="RID" />
<description>
- Creates a camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [Camera3D] node.
+ Creates a 3D camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [Camera3D].
</description>
</method>
<method name="camera_set_camera_attributes">
@@ -179,7 +178,7 @@
<return type="RID" />
<description>
Creates a canvas and returns the assigned [RID]. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
Canvas has no [Resource] or [Node] equivalent.
</description>
</method>
@@ -260,6 +259,16 @@
See also [method CanvasItem.draw_msdf_texture_rect_region].
</description>
</method>
+ <method name="canvas_item_add_multiline">
+ <return type="void" />
+ <param index="0" name="item" type="RID" />
+ <param index="1" name="points" type="PackedVector2Array" />
+ <param index="2" name="colors" type="PackedColorArray" />
+ <param index="3" name="width" type="float" default="-1.0" />
+ <description>
+ Draws a 2D multiline on the [CanvasItem] pointed to by the [param item] [RID]. See also [method CanvasItem.draw_multiline] and [method CanvasItem.draw_multiline_colors].
+ </description>
+ </method>
<method name="canvas_item_add_multimesh">
<return type="void" />
<param index="0" name="item" type="RID" />
@@ -302,7 +311,7 @@
<param index="3" name="uvs" type="PackedVector2Array" default="PackedVector2Array()" />
<param index="4" name="texture" type="RID" />
<description>
- Draws a 2D polygon on the [CanvasItem] pointed to by the [param item] [RID]. See also [method CanvasItem.draw_polygon].
+ Draws a 2D polygon on the [CanvasItem] pointed to by the [param item] [RID]. If you need more flexibility (such as being able to use bones), use [method canvas_item_add_triangle_array] instead. See also [method CanvasItem.draw_polygon].
</description>
</method>
<method name="canvas_item_add_polyline">
@@ -313,7 +322,7 @@
<param index="3" name="width" type="float" default="-1.0" />
<param index="4" name="antialiased" type="bool" default="false" />
<description>
- Draws a 2D polyline on the [CanvasItem] pointed to by the [param item] [RID]. See also [method CanvasItem.draw_polyline].
+ Draws a 2D polyline on the [CanvasItem] pointed to by the [param item] [RID]. See also [method CanvasItem.draw_polyline] and [method CanvasItem.draw_polyline_colors].
</description>
</method>
<method name="canvas_item_add_primitive">
@@ -381,6 +390,8 @@
<param index="7" name="texture" type="RID" />
<param index="8" name="count" type="int" default="-1" />
<description>
+ Draws a triangle array on the [CanvasItem] pointed to by the [param item] [RID]. This is internally used by [Line2D] and [StyleBoxFlat] for rendering. [method canvas_item_add_triangle_array] is highly flexible, but more complex to use than [method canvas_item_add_polygon].
+ [b]Note:[/b] [param count] is unused and can be left unspecified.
</description>
</method>
<method name="canvas_item_clear">
@@ -393,9 +404,9 @@
<method name="canvas_item_create">
<return type="RID" />
<description>
- Creates a canvas item and returns the assigned [RID]. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_item_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [CanvasItem] node.
+ Creates a new CanvasItem instance and returns its [RID]. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_item_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [CanvasItem].
</description>
</method>
<method name="canvas_item_set_canvas_group_mode">
@@ -407,6 +418,8 @@
<param index="4" name="fit_margin" type="float" default="0.0" />
<param index="5" name="blur_mipmaps" type="bool" default="false" />
<description>
+ Sets the canvas group mode used during 2D rendering for the canvas item specified by the [param item] RID. For faster but more limited clipping, use [method canvas_item_set_clip] instead.
+ [b]Note:[/b] The equivalent node functionality is found in [CanvasGroup] and [member CanvasItem.clip_children].
</description>
</method>
<method name="canvas_item_set_clip">
@@ -414,6 +427,8 @@
<param index="0" name="item" type="RID" />
<param index="1" name="clip" type="bool" />
<description>
+ If [param clip] is [code]true[/code], makes the canvas item specified by the [param item] RID not draw anything outside of its rect's coordinates. This clipping is fast, but works only with axis-aligned rectangles. This means that rotation is ignored by the clipping rectangle. For more advanced clipping shapes, use [method canvas_item_set_canvas_group_mode] instead.
+ [b]Note:[/b] The equivalent node functionality is found in [member Label.clip_text], [RichTextLabel] (always enabled) and more.
</description>
</method>
<method name="canvas_item_set_copy_to_backbuffer">
@@ -431,6 +446,7 @@
<param index="1" name="use_custom_rect" type="bool" />
<param index="2" name="rect" type="Rect2" default="Rect2(0, 0, 0, 0)" />
<description>
+ If [param use_custom_rect] is [code]true[/code], sets the custom visibility rectangle (used for culling) to [param rect] for the canvas item specified by [param item]. Setting a custom visibility rect can reduce CPU load when drawing lots of 2D instances. If [param use_custom_rect] is [code]false[/code], automatically computes a visibility rectangle based on the canvas item's draw commands.
</description>
</method>
<method name="canvas_item_set_default_texture_filter">
@@ -438,6 +454,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="filter" type="int" enum="RenderingServer.CanvasItemTextureFilter" />
<description>
+ Sets the default texture filter mode for the canvas item specified by the [param item] RID. Equivalent to [member CanvasItem.texture_filter].
</description>
</method>
<method name="canvas_item_set_default_texture_repeat">
@@ -445,6 +462,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="repeat" type="int" enum="RenderingServer.CanvasItemTextureRepeat" />
<description>
+ Sets the default texture repeat mode for the canvas item specified by the [param item] RID. Equivalent to [member CanvasItem.texture_repeat].
</description>
</method>
<method name="canvas_item_set_distance_field_mode">
@@ -452,6 +470,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ If [param enabled] is [code]true[/code], enables multichannel signed distance field rendering mode for the canvas item specified by the [param item] RID. This is meant to be used for font rendering, or with specially generated images using [url=https://github.com/Chlumsky/msdfgen]msdfgen[/url].
</description>
</method>
<method name="canvas_item_set_draw_behind_parent">
@@ -459,6 +478,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ If [param enabled] is [code]true[/code], draws the canvas item specified by the [param item] RID behind its parent. Equivalent to [member CanvasItem.show_behind_parent].
</description>
</method>
<method name="canvas_item_set_draw_index">
@@ -474,6 +494,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="mask" type="int" />
<description>
+ Sets the light [param mask] for the canvas item specified by the [param item] RID. Equivalent to [member CanvasItem.light_mask].
</description>
</method>
<method name="canvas_item_set_material">
@@ -481,7 +502,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="material" type="RID" />
<description>
- Sets a new material to the [CanvasItem].
+ Sets a new [param material] to the canvas item specified by the [param item] RID. Equivalent to [member CanvasItem.material].
</description>
</method>
<method name="canvas_item_set_modulate">
@@ -489,7 +510,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="color" type="Color" />
<description>
- Sets a color modulation to the [CanvasItem]. This also affects child canvas items.
+ Multiplies the color of the canvas item specified by the [param item] RID, while affecting its children. See also [method canvas_item_set_self_modulate]. Equivalent to [member CanvasItem.modulate].
</description>
</method>
<method name="canvas_item_set_parent">
@@ -505,7 +526,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="color" type="Color" />
<description>
- Sets a color self-modulation to the [CanvasItem]. It does not affect the child canvas items.
+ Multiplies the color of the canvas item specified by the [param item] RID, without affecting its children. See also [method canvas_item_set_modulate]. Equivalent to [member CanvasItem.self_modulate].
</description>
</method>
<method name="canvas_item_set_sort_children_by_y">
@@ -513,7 +534,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- Enables or disables Y-sorting of a [CanvasItem].
+ If [param enabled] is [code]true[/code], child nodes with the lowest Y position are drawn before those with a higher Y position. Y-sorting only affects children that inherit from the canvas item specified by the [param item] RID, not the canvas item itself. Equivalent to [member CanvasItem.y_sort_enabled].
</description>
</method>
<method name="canvas_item_set_transform">
@@ -521,7 +542,7 @@
<param index="0" name="item" type="RID" />
<param index="1" name="transform" type="Transform2D" />
<description>
- Sets the transform of the [CanvasItem]. It affects where and how the item will be drawn. Child canvas items' transforms are multiplied by their parent's transform.
+ Sets the [param transform] of the canvas item specified by the [param item] RID. This affects where and how the item will be drawn. Child canvas items' transforms are multiplied by their parent's transform. Equivalent to [member Node2D.transform].
</description>
</method>
<method name="canvas_item_set_use_parent_material">
@@ -588,7 +609,8 @@
<return type="RID" />
<description>
Creates a canvas light and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_light_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [Light2D].
</description>
</method>
<method name="canvas_light_occluder_attach_to_canvas">
@@ -603,7 +625,8 @@
<return type="RID" />
<description>
Creates a light occluder and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_light_occluder_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [LightOccluder2D].
</description>
</method>
<method name="canvas_light_occluder_set_as_sdf_collision">
@@ -795,7 +818,8 @@
<return type="RID" />
<description>
Creates a new light occluder polygon and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_occluder_polygon_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [OccluderPolygon2D].
</description>
</method>
<method name="canvas_occluder_polygon_set_cull_mode">
@@ -842,14 +866,15 @@
<return type="void" />
<param index="0" name="size" type="int" />
<description>
+ Sets the [member ProjectSettings.rendering/2d/shadow_atlas/size] to use for [Light2D] shadow rendering (in pixels). The value is rounded up to the nearest power of 2.
</description>
</method>
<method name="canvas_texture_create">
<return type="RID" />
<description>
Creates a canvas texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_texture_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [CanvasTexture] resource.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method. See also [method texture_2d_create].
+ [b]Note:[/b] The equivalent resource is [CanvasTexture] and is only meant to be used in 2D rendering, not 3D.
</description>
</method>
<method name="canvas_texture_set_channel">
@@ -858,6 +883,7 @@
<param index="1" name="channel" type="int" enum="RenderingServer.CanvasTextureChannel" />
<param index="2" name="texture" type="RID" />
<description>
+ Sets the [param channel]'s [param texture] for the canvas texture specified by the [param canvas_texture] RID. Equivalent to [member CanvasTexture.diffuse_texture], [member CanvasTexture.normal_texture] and [member CanvasTexture.specular_texture].
</description>
</method>
<method name="canvas_texture_set_shading_parameters">
@@ -866,6 +892,7 @@
<param index="1" name="base_color" type="Color" />
<param index="2" name="shininess" type="float" />
<description>
+ Sets the [param base_color] and [param shininess] to use for the canvas texture specified by the [param canvas_texture] RID. Equivalent to [member CanvasTexture.specular_color] and [member CanvasTexture.specular_shininess].
</description>
</method>
<method name="canvas_texture_set_texture_filter">
@@ -873,7 +900,7 @@
<param index="0" name="canvas_texture" type="RID" />
<param index="1" name="filter" type="int" enum="RenderingServer.CanvasItemTextureFilter" />
<description>
- Sets the texture filter used by the [CanvasTexture].
+ Sets the texture [param filter] mode to use for the canvas texture specified by the [param canvas_texture] RID.
</description>
</method>
<method name="canvas_texture_set_texture_repeat">
@@ -881,7 +908,7 @@
<param index="0" name="canvas_texture" type="RID" />
<param index="1" name="repeat" type="int" enum="RenderingServer.CanvasItemTextureRepeat" />
<description>
- Sets the texture repeat used by the [CanvasTexture].
+ Sets the texture [param repeat] mode to use for the canvas texture specified by the [param canvas_texture] RID.
</description>
</method>
<method name="create_local_rendering_device" qualifiers="const">
@@ -895,8 +922,9 @@
<return type="RID" />
<description>
Creates a decal and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]decal_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [Decal] node.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ To place in a scene, attach this decal to an instance using [method instance_set_base] using the returned RID.
+ [b]Note:[/b] The equivalent node is [Decal].
</description>
</method>
<method name="decal_set_albedo_mix">
@@ -904,6 +932,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="albedo_mix" type="float" />
<description>
+ Sets the [param albedo_mix] in the decal specified by the [param decal] RID. Equivalent to [member Decal.albedo_mix].
</description>
</method>
<method name="decal_set_cull_mask">
@@ -911,6 +940,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="mask" type="int" />
<description>
+ Sets the cull [param mask] in the decal specified by the [param decal] RID. Equivalent to [member Decal.cull_mask].
</description>
</method>
<method name="decal_set_distance_fade">
@@ -920,6 +950,7 @@
<param index="2" name="begin" type="float" />
<param index="3" name="length" type="float" />
<description>
+ Sets the distance fade parameters in the decal specified by the [param decal] RID. Equivalent to [member Decal.distance_fade_enabled], [member Decal.distance_fade_begin] and [member Decal.distance_fade_length].
</description>
</method>
<method name="decal_set_emission_energy">
@@ -927,6 +958,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="energy" type="float" />
<description>
+ Sets the emission [param energy] in the decal specified by the [param decal] RID. Equivalent to [member Decal.emission_energy].
</description>
</method>
<method name="decal_set_fade">
@@ -935,6 +967,7 @@
<param index="1" name="above" type="float" />
<param index="2" name="below" type="float" />
<description>
+ Sets the upper fade ([param above]) and lower fade ([param below]) in the decal specified by the [param decal] RID. Equivalent to [member Decal.upper_fade] and [member Decal.lower_fade].
</description>
</method>
<method name="decal_set_modulate">
@@ -942,7 +975,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="color" type="Color" />
<description>
- Sets the color modulation of the [Decal].
+ Sets the color multiplier in the decal specified by the [param decal] RID to [param color]. Equivalent to [member Decal.modulate].
</description>
</method>
<method name="decal_set_normal_fade">
@@ -950,6 +983,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="fade" type="float" />
<description>
+ Sets the normal [param fade] in the decal specified by the [param decal] RID. Equivalent to [member Decal.normal_fade].
</description>
</method>
<method name="decal_set_size">
@@ -957,7 +991,7 @@
<param index="0" name="decal" type="RID" />
<param index="1" name="size" type="Vector3" />
<description>
- Sets the size of the [Decal].
+ Sets the [param size] of the decal specified by the [param decal] RID. Equivalent to [member Decal.size].
</description>
</method>
<method name="decal_set_texture">
@@ -966,22 +1000,23 @@
<param index="1" name="type" type="int" enum="RenderingServer.DecalTexture" />
<param index="2" name="texture" type="RID" />
<description>
- Sets the texture of the [Decal].
+ Sets the [param texture] in the given texture [param type] slot for the specified decal. Equivalent to [method Decal.set_texture].
</description>
</method>
<method name="decals_set_filter">
<return type="void" />
<param index="0" name="filter" type="int" enum="RenderingServer.DecalFilter" />
<description>
+ Sets the texture [param filter] mode to use when rendering decals. This parameter is global and cannot be set on a per-decal basis.
</description>
</method>
<method name="directional_light_create">
<return type="RID" />
<description>
Creates a directional light and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID can be used in most [code]light_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this directional light to an instance using [method instance_set_base] using the returned RID.
- This is the internal equivalent of the [DirectionalLight3D] node.
+ [b]Note:[/b] The equivalent node is [DirectionalLight3D].
</description>
</method>
<method name="directional_shadow_atlas_set_size">
@@ -989,12 +1024,14 @@
<param index="0" name="size" type="int" />
<param index="1" name="is_16bits" type="bool" />
<description>
+ Sets the [param size] of the directional light shadows in 3D. See also [member ProjectSettings.rendering/lights_and_shadows/directional_shadow/size]. This parameter is global and cannot be set on a per-viewport basis.
</description>
</method>
<method name="directional_soft_shadow_filter_set_quality">
<return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.ShadowQuality" />
<description>
+ Sets the filter [param quality] for directional light shadows in 3D. See also [member ProjectSettings.rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality]. This parameter is global and cannot be set on a per-viewport basis.
</description>
</method>
<method name="environment_bake_panorama">
@@ -1003,20 +1040,24 @@
<param index="1" name="bake_irradiance" type="bool" />
<param index="2" name="size" type="Vector2i" />
<description>
+ Generates and returns an [Image] containing the radiance map for the specified [param environment] RID's sky. This supports built-in sky material and custom sky shaders. If [param bake_irradiance] is [code]true[/code], the irradiance map is saved instead of the radiance map. The radiance map is used to render reflected light, while the irradiance map is used to render ambient light. See also [method sky_bake_panorama].
+ [b]Note:[/b] The image is saved in linear color space without any tonemapping performed, which means it will look too dark if viewed directly in an image editor.
+ [b]Note:[/b] [param size] should be a 2:1 aspect ratio for the generated panorama to have square pixels. For radiance maps, there is no point in using a height greater than [member Sky.radiance_size], as it won't increase detail. Irradiance maps only contain low-frequency data, so there is usually no point in going past a size of 128×64 pixels when saving an irradiance map.
</description>
</method>
<method name="environment_create">
<return type="RID" />
<description>
Creates an environment and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]environment_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [Environment] resource.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [Environment].
</description>
</method>
<method name="environment_glow_set_use_bicubic_upscale">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
+ If [param enable] is [code]true[/code], enables bicubic upscaling for glow which improves quality at the cost of performance. Equivalent to [member ProjectSettings.rendering/environment/glow/upscale_mode].
</description>
</method>
<method name="environment_set_adjustment">
@@ -1029,7 +1070,7 @@
<param index="5" name="use_1d_color_correction" type="bool" />
<param index="6" name="color_correction" type="RID" />
<description>
- Sets the values to be used with the "Adjustment" post-process effect. See [Environment] for more details.
+ Sets the values to be used with the "adjustments" post-process effect. See [Environment] for more details.
</description>
</method>
<method name="environment_set_ambient_light">
@@ -1041,6 +1082,7 @@
<param index="4" name="sky_contibution" type="float" default="0.0" />
<param index="5" name="reflection_source" type="int" enum="RenderingServer.EnvironmentReflectionSource" default="0" />
<description>
+ Sets the values to be used for ambient light rendering. See [Environment] for more details.
</description>
</method>
<method name="environment_set_background">
@@ -1048,7 +1090,7 @@
<param index="0" name="env" type="RID" />
<param index="1" name="bg" type="int" enum="RenderingServer.EnvironmentBG" />
<description>
- Sets the [i]BGMode[/i] of the environment. Equivalent to [member Environment.background_mode].
+ Sets the environment's background mode. Equivalent to [member Environment.background_mode].
</description>
</method>
<method name="environment_set_bg_color">
@@ -1056,7 +1098,7 @@
<param index="0" name="env" type="RID" />
<param index="1" name="color" type="Color" />
<description>
- Color displayed for clear areas of the scene (if using Custom color or Color+Sky background modes).
+ Color displayed for clear areas of the scene. Only effective if using the [constant ENV_BG_COLOR] background mode.
</description>
</method>
<method name="environment_set_bg_energy">
@@ -1089,6 +1131,7 @@
<param index="8" name="aerial_perspective" type="float" />
<param index="9" name="sky_affect" type="float" />
<description>
+ Configures fog for the specified environment RID. See [code]fog_*[/code] properties in [Environment] for more information.
</description>
</method>
<method name="environment_set_glow">
@@ -1107,6 +1150,7 @@
<param index="11" name="glow_map_strength" type="float" />
<param index="12" name="glow_map" type="RID" />
<description>
+ Configures glow for the specified environment RID. See [code]glow_*[/code] properties in [Environment] for more information.
</description>
</method>
<method name="environment_set_sdfgi">
@@ -1123,24 +1167,28 @@
<param index="9" name="normal_bias" type="float" />
<param index="10" name="probe_bias" type="float" />
<description>
+ Configures signed distance field global illumination for the specified environment RID. See [code]sdfgi_*[/code] properties in [Environment] for more information.
</description>
</method>
<method name="environment_set_sdfgi_frames_to_converge">
<return type="void" />
<param index="0" name="frames" type="int" enum="RenderingServer.EnvironmentSDFGIFramesToConverge" />
<description>
+ Sets the number of frames to use for converging signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_converge].
</description>
</method>
<method name="environment_set_sdfgi_frames_to_update_light">
<return type="void" />
<param index="0" name="frames" type="int" enum="RenderingServer.EnvironmentSDFGIFramesToUpdateLight" />
<description>
+ Sets the update speed for dynamic lights' indirect lighting when computing signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights].
</description>
</method>
<method name="environment_set_sdfgi_ray_count">
<return type="void" />
<param index="0" name="ray_count" type="int" enum="RenderingServer.EnvironmentSDFGIRayCount" />
<description>
+ Sets the number of rays to throw per frame when computing signed distance field global illumination. Equivalent to [member ProjectSettings.rendering/global_illumination/sdfgi/probe_ray_count].
</description>
</method>
<method name="environment_set_sky">
@@ -1216,7 +1264,7 @@
<param index="4" name="fade_out" type="float" />
<param index="5" name="depth_tolerance" type="float" />
<description>
- Sets the variables to be used with the "screen space reflections" post-process effect. See [Environment] for more details.
+ Sets the variables to be used with the screen-space reflections (SSR) post-process effect. See [Environment] for more details.
</description>
</method>
<method name="environment_set_ssr_roughness_quality">
@@ -1252,6 +1300,7 @@
<param index="12" name="ambient_inject" type="float" />
<param index="13" name="sky_affect" type="float" />
<description>
+ Sets the variables to be used with the volumetric fog post-process effect. See [Environment] for more details.
</description>
</method>
<method name="environment_set_volumetric_fog_filter_active">
@@ -1272,9 +1321,9 @@
<method name="fog_volume_create">
<return type="RID" />
<description>
- Creates a fog volume and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]fog_volume_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [FogVolume] node.
+ Creates a new fog volume and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]fog_volume_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [FogVolume].
</description>
</method>
<method name="fog_volume_set_material">
@@ -1306,29 +1355,32 @@
<param index="0" name="swap_buffers" type="bool" default="true" />
<param index="1" name="frame_step" type="float" default="0.0" />
<description>
+ Forces redrawing of all viewports at once.
</description>
</method>
<method name="force_sync">
<return type="void" />
<description>
+ Forces a synchronization between the CPU and GPU, which may be required in certain cases. Only call this when needed, as CPU-GPU synchronization has a performance cost.
</description>
</method>
<method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
- Tries to free an object in the RenderingServer.
+ Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RendeeringServer directly.
</description>
</method>
<method name="get_default_clear_color">
<return type="Color" />
<description>
- Returns the default clear color which is used when a specific clear color has not been selected.
+ Returns the default clear color which is used when a specific clear color has not been selected. See also [method set_default_clear_color].
</description>
</method>
<method name="get_frame_setup_time_cpu" qualifiers="const">
<return type="float" />
<description>
+ Returns the time taken to setup rendering on the CPU in milliseconds. This value is shared across all viewports and does [i]not[/i] require [method viewport_set_measure_render_time] to be enabled on a viewport to be queried. See also [method viewport_get_measured_render_time_cpu].
</description>
</method>
<method name="get_rendering_device" qualifiers="const">
@@ -1342,6 +1394,16 @@
<return type="int" />
<param index="0" name="info" type="int" enum="RenderingServer.RenderingInfo" />
<description>
+ Returns a statistic about the rendering engine which can be used for performance profiling. See [enum RenderingServer.RenderingInfo] for a list of values that can be queried. See also [method viewport_get_render_info], which returns information specific to a viewport.
+ [b]Note:[/b] Only 3D rendering is currently taken into account by some of these values, such as the number of draw calls.
+ [b]Note:[/b] Rendering information is not available until at least 2 frames have been rendered by the engine. If rendering information is not available, [method get_rendering_info] returns [code]0[/code]. To print rendering information in [code]_ready()[/code] successfully, use the following:
+ [codeblock]
+ func _ready():
+ for _i in 2:
+ await get_tree().process_frame
+
+ print(RenderingServer.get_rendering_info(RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME))
+ [/codeblock]
</description>
</method>
<method name="get_shader_parameter_list" qualifiers="const">
@@ -1354,19 +1416,25 @@
<method name="get_test_cube">
<return type="RID" />
<description>
- Returns the ID of the test cube. Creates one if none exists.
+ Returns the RID of the test cube. This mesh will be created and returned on the first call to [method get_test_cube], then it will be cached for subsequent calls. See also [method make_sphere_mesh].
</description>
</method>
<method name="get_test_texture">
<return type="RID" />
<description>
- Returns the ID of the test texture. Creates one if none exists.
+ Returns the RID of a 256×256 texture with a testing pattern on it (in [constant Image.FORMAT_RGB8] format). This texture will be created and returned on the first call to [method get_test_texture], then it will be cached for subsequent calls. See also [method get_white_texture].
+ Example of getting the test texture and applying it to a [Sprite2D] node:
+ [codeblock]
+ var texture_rid = RenderingServer.get_test_texture()
+ var texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(texture_rid))
+ $Sprite2D.texture = texture
+ [/codeblock]
</description>
</method>
<method name="get_video_adapter_api_version" qualifiers="const">
<return type="String" />
<description>
- Returns the version of the graphics video adapter [i]currently in use[/i] (e.g. "1.2.189" for Vulkan, "3.3.0 NVIDIA 510.60.02" for OpenGL). This version may be different from the actual latest version supported by the hardware, as Godot may not always request the latest version.
+ Returns the version of the graphics video adapter [i]currently in use[/i] (e.g. "1.2.189" for Vulkan, "3.3.0 NVIDIA 510.60.02" for OpenGL). This version may be different from the actual latest version supported by the hardware, as Godot may not always request the latest version. See also [method OS.get_video_adapter_driver_info].
[b]Note:[/b] When running a headless or server binary, this function returns an empty string.
</description>
</method>
@@ -1394,14 +1462,20 @@
<method name="get_white_texture">
<return type="RID" />
<description>
- Returns the ID of a white texture. Creates one if none exists.
+ Returns the ID of a 4×4 white texture (in [constant Image.FORMAT_RGB8] format). This texture will be created and returned on the first call to [method get_white_texture], then it will be cached for subsequent calls. See also [method get_test_texture].
+ Example of getting the white texture and applying it to a [Sprite2D] node:
+ [codeblock]
+ var texture_rid = RenderingServer.get_white_texture()
+ var texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(texture_rid))
+ $Sprite2D.texture = texture
+ [/codeblock]
</description>
</method>
<method name="gi_set_use_half_resolution">
<return type="void" />
<param index="0" name="half_resolution" type="bool" />
<description>
- If [param half_resolution] is [code]true[/code], renders [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) buffers at halved resolution (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or SDFGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. See also [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution].
+ If [param half_resolution] is [code]true[/code], renders [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) buffers at halved resolution on each axis (e.g. 960×540 when the viewport size is 1920×1080). This improves performance significantly when VoxelGI or SDFGI is enabled, at the cost of artifacts that may be visible on polygon edges. The loss in quality becomes less noticeable as the viewport resolution increases. [LightmapGI] rendering is not affected by this setting. Equivalent to [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution].
</description>
</method>
<method name="global_shader_parameter_add">
@@ -1410,29 +1484,38 @@
<param index="1" name="type" type="int" enum="RenderingServer.GlobalShaderParameterType" />
<param index="2" name="default_value" type="Variant" />
<description>
+ Creates a new global shader uniform.
+ [b]Note:[/b] Global shader parameter names are case-sensitive.
</description>
</method>
<method name="global_shader_parameter_get" qualifiers="const">
<return type="Variant" />
<param index="0" name="name" type="StringName" />
<description>
+ Returns the value of the global shader uniform specified by [param name].
+ [b]Note:[/b] [method global_shader_parameter_get] has a large performance penalty as the rendering thread needs to synchronize with the calling thread, which is slow. Do not use this method during gameplay to avoid stuttering. If you need to read values in a script after setting them, consider creating an autoload where you store the values you need to query at the same time you're setting them as global parameters.
</description>
</method>
<method name="global_shader_parameter_get_list" qualifiers="const">
- <return type="PackedStringArray" />
+ <return type="StringName[]" />
<description>
+ Returns the list of global shader uniform names.
+ [b]Note:[/b] [method global_shader_parameter_get] has a large performance penalty as the rendering thread needs to synchronize with the calling thread, which is slow. Do not use this method during gameplay to avoid stuttering. If you need to read values in a script after setting them, consider creating an autoload where you store the values you need to query at the same time you're setting them as global parameters.
</description>
</method>
<method name="global_shader_parameter_get_type" qualifiers="const">
<return type="int" enum="RenderingServer.GlobalShaderParameterType" />
<param index="0" name="name" type="StringName" />
<description>
+ Returns the type associated to the global shader uniform specified by [param name].
+ [b]Note:[/b] [method global_shader_parameter_get] has a large performance penalty as the rendering thread needs to synchronize with the calling thread, which is slow. Do not use this method during gameplay to avoid stuttering. If you need to read values in a script after setting them, consider creating an autoload where you store the values you need to query at the same time you're setting them as global parameters.
</description>
</method>
<method name="global_shader_parameter_remove">
<return type="void" />
<param index="0" name="name" type="StringName" />
<description>
+ Removes the global shader uniform specified by [param name].
</description>
</method>
<method name="global_shader_parameter_set">
@@ -1440,6 +1523,7 @@
<param index="0" name="name" type="StringName" />
<param index="1" name="value" type="Variant" />
<description>
+ Sets the global shader uniform [param name] to [param value].
</description>
</method>
<method name="global_shader_parameter_set_override">
@@ -1447,6 +1531,7 @@
<param index="0" name="name" type="StringName" />
<param index="1" name="value" type="Variant" />
<description>
+ Overrides the global shader uniform [param name] with [param value]. Equivalent to the [ShaderGlobalsOverride] node.
</description>
</method>
<method name="has_changed" qualifiers="const">
@@ -1489,9 +1574,9 @@
<return type="RID" />
<description>
Creates a visual instance and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]instance_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- An instance is a way of placing a 3D object in the scenario. Objects like particles, meshes, and reflection probes need to be associated with an instance to be visible in the scenario using [method instance_set_base].
- This is the internal equivalent of the [VisualInstance3D] node.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ An instance is a way of placing a 3D object in the scenario. Objects like particles, meshes, reflection probes and decals need to be associated with an instance to be visible in the scenario using [method instance_set_base].
+ [b]Note:[/b] The equivalent node is [VisualInstance3D].
</description>
</method>
<method name="instance_create2">
@@ -1500,7 +1585,7 @@
<param index="1" name="scenario" type="RID" />
<description>
Creates a visual instance, adds it to the RenderingServer, and sets both base and scenario. It can be accessed with the RID that is returned. This RID will be used in all [code]instance_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method. This is a shorthand for using [method instance_create] and setting the base and scenario manually.
</description>
</method>
<method name="instance_geometry_get_shader_parameter" qualifiers="const">
@@ -1508,6 +1593,8 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
+ Returns the value of the per-instance shader uniform from the specified 3D geometry instance. Equivalent to [method GeometryInstance3D.get_instance_shader_parameter].
+ [b]Note:[/b] Per-instance shader parameter names are case-sensitive.
</description>
</method>
<method name="instance_geometry_get_shader_parameter_default_value" qualifiers="const">
@@ -1515,12 +1602,14 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
+ Returns the default value of the per-instance shader uniform from the specified 3D geometry instance. Equivalent to [method GeometryInstance3D.get_instance_shader_parameter].
</description>
</method>
<method name="instance_geometry_get_shader_parameter_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="instance" type="RID" />
<description>
+ Returns a dictionary of per-instance shader uniform names of the per-instance shader uniform from the specified 3D geometry instance. The returned dictionary is in PropertyInfo format, with the keys [code]name[/code], [code]class_name[/code], [code]type[/code], [code]hint[/code], [code]hint_string[/code] and [code]usage[/code]. Equivalent to [method GeometryInstance3D.get_instance_shader_parameter].
</description>
</method>
<method name="instance_geometry_set_cast_shadows_setting">
@@ -1547,6 +1636,7 @@
<param index="2" name="lightmap_uv_scale" type="Rect2" />
<param index="3" name="lightmap_slice" type="int" />
<description>
+ Sets the lightmap GI instance to use for the specified 3D geometry instance. The lightmap UV scale for the specified instance (equivalent to [member GeometryInstance3D.gi_lightmap_scale]) and lightmap atlas slice must also be specified.
</description>
</method>
<method name="instance_geometry_set_lod_bias">
@@ -1554,6 +1644,7 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="lod_bias" type="float" />
<description>
+ Sets the level of detail bias to use when rendering the specified 3D geometry instance. Higher values result in higher detail from further away. Equivalent to [member GeometryInstance3D.lod_bias].
</description>
</method>
<method name="instance_geometry_set_material_overlay">
@@ -1578,6 +1669,7 @@
<param index="1" name="parameter" type="StringName" />
<param index="2" name="value" type="Variant" />
<description>
+ Sets the per-instance shader uniform on the specified 3D geometry instance. Equivalent to [method GeometryInstance3D.set_instance_shader_parameter].
</description>
</method>
<method name="instance_geometry_set_transparency">
@@ -1608,7 +1700,7 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="base" type="RID" />
<description>
- Sets the base of the instance. A base can be any of the 3D objects that are created in the RenderingServer that can be displayed. For example, any of the light types, mesh, multimesh, immediate geometry, particle system, reflection probe, lightmap, and the GI probe are all types that can be set as the base of an instance in order to be displayed in the scenario.
+ Sets the base of the instance. A base can be any of the 3D objects that are created in the RenderingServer that can be displayed. For example, any of the light types, mesh, multimesh, particle system, reflection probe, decal, lightmap, voxel GI and visibility notifiers are all types that can be set as the base of an instance in order to be displayed in the scenario.
</description>
</method>
<method name="instance_set_blend_shape_weight">
@@ -1641,6 +1733,7 @@
<param index="0" name="instance" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ If [code]true[/code], ignores both frustum and occlusion culling on the specified 3D geometry instance. This is not the same as [member GeometryInstance3D.ignore_occlusion_culling], which only ignores occlusion culling and leaves frustum culling intact.
</description>
</method>
<method name="instance_set_layer_mask">
@@ -1706,7 +1799,7 @@
<param index="0" name="aabb" type="AABB" />
<param index="1" name="scenario" type="RID" />
<description>
- Returns an array of object IDs intersecting with the provided AABB. Only visual 3D nodes are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
+ Returns an array of object IDs intersecting with the provided AABB. Only 3D nodes that inherit from [VisualInstance3D] are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
[b]Warning:[/b] This function is primarily intended for editor usage. For in-game use cases, prefer physics collision.
</description>
</method>
@@ -1715,7 +1808,7 @@
<param index="0" name="convex" type="Plane[]" />
<param index="1" name="scenario" type="RID" />
<description>
- Returns an array of object IDs intersecting with the provided convex shape. Only visual 3D nodes are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
+ Returns an array of object IDs intersecting with the provided convex shape. Only 3D nodes that inherit from [VisualInstance3D] are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
[b]Warning:[/b] This function is primarily intended for editor usage. For in-game use cases, prefer physics collision.
</description>
</method>
@@ -1725,7 +1818,7 @@
<param index="1" name="to" type="Vector3" />
<param index="2" name="scenario" type="RID" />
<description>
- Returns an array of object IDs intersecting with the provided 3D ray. Only visual 3D nodes are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
+ Returns an array of object IDs intersecting with the provided 3D ray. Only 3D nodes that inherit from [VisualInstance3D] are considered, such as [MeshInstance3D] or [DirectionalLight3D]. Use [method @GlobalScope.instance_from_id] to obtain the actual nodes. A scenario RID must be provided, which is available in the [World3D] you want to query. This forces an update for all resources queued to update.
[b]Warning:[/b] This function is primarily intended for editor usage. For in-game use cases, prefer physics collision.
</description>
</method>
@@ -1765,6 +1858,7 @@
<return type="void" />
<param index="0" name="filter" type="int" enum="RenderingServer.LightProjectorFilter" />
<description>
+ Sets the texture filter mode to use when rendering light projectors. This parameter is global and cannot be set on a per-light basis.
</description>
</method>
<method name="light_set_bake_mode">
@@ -1772,6 +1866,7 @@
<param index="0" name="light" type="RID" />
<param index="1" name="bake_mode" type="int" enum="RenderingServer.LightBakeMode" />
<description>
+ Sets the bake mode to use for the specified 3D light. Equivalent to [member Light3D.light_bake_mode].
</description>
</method>
<method name="light_set_color">
@@ -1787,7 +1882,7 @@
<param index="0" name="light" type="RID" />
<param index="1" name="mask" type="int" />
<description>
- Sets the cull mask for this Light3D. Lights only affect objects in the selected layers. Equivalent to [member Light3D.light_cull_mask].
+ Sets the cull mask for this 3D light. Lights only affect objects in the selected layers. Equivalent to [member Light3D.light_cull_mask].
</description>
</method>
<method name="light_set_distance_fade">
@@ -1798,7 +1893,7 @@
<param index="3" name="shadow" type="float" />
<param index="4" name="length" type="float" />
<description>
- Sets the distance fade for this Light3D. This acts as a form of level of detail (LOD) and can be used to improve performance. Equivalent to [member Light3D.distance_fade_enabled], [member Light3D.distance_fade_begin], [member Light3D.distance_fade_shadow], and [member Light3D.distance_fade_length].
+ Sets the distance fade for this 3D light. This acts as a form of level of detail (LOD) and can be used to improve performance. Equivalent to [member Light3D.distance_fade_enabled], [member Light3D.distance_fade_begin], [member Light3D.distance_fade_shadow], and [member Light3D.distance_fade_length].
</description>
</method>
<method name="light_set_max_sdfgi_cascade">
@@ -1806,6 +1901,7 @@
<param index="0" name="light" type="RID" />
<param index="1" name="cascade" type="int" />
<description>
+ Sets the maximum SDFGI cascade in which the 3D light's indirect lighting is rendered. Higher values allow the light to be rendered in SDFGI further away from the camera.
</description>
</method>
<method name="light_set_negative">
@@ -1813,7 +1909,7 @@
<param index="0" name="light" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
- If [code]true[/code], light will subtract light instead of adding light. Equivalent to [member Light3D.light_negative].
+ If [code]true[/code], the 3D light will subtract light instead of adding light. Equivalent to [member Light3D.light_negative].
</description>
</method>
<method name="light_set_param">
@@ -1822,7 +1918,7 @@
<param index="1" name="param" type="int" enum="RenderingServer.LightParam" />
<param index="2" name="value" type="float" />
<description>
- Sets the specified light parameter. See [enum LightParam] for options. Equivalent to [method Light3D.set_param].
+ Sets the specified 3D light parameter. See [enum LightParam] for options. Equivalent to [method Light3D.set_param].
</description>
</method>
<method name="light_set_projector">
@@ -1830,7 +1926,7 @@
<param index="0" name="light" type="RID" />
<param index="1" name="texture" type="RID" />
<description>
- Not implemented in Godot 3.x.
+ Sets the projector texture to use for the specified 3D light. Equivalent to [member Light3D.light_projector].
</description>
</method>
<method name="light_set_reverse_cull_face_mode">
@@ -1852,9 +1948,9 @@
<method name="lightmap_create">
<return type="RID" />
<description>
- Creates a lightmap GI and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]lightmap_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [LightmapGI] node.
+ Creates a new lightmap global illumination instance and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]lightmap_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [LightmapGI].
</description>
</method>
<method name="lightmap_get_probe_capture_bsp_tree" qualifiers="const">
@@ -1925,6 +2021,7 @@
<param index="1" name="light" type="RID" />
<param index="2" name="uses_sh" type="bool" />
<description>
+ Set the textures on the given [param lightmap] GI instance to the texture array pointed to by the [param light] RID. If the lightmap texture was baked with [member LightmapGI.directional] set to [code]true[/code], then [param uses_sh] must also be [code]true[/code].
</description>
</method>
<method name="make_sphere_mesh">
@@ -1933,15 +2030,15 @@
<param index="1" name="longitudes" type="int" />
<param index="2" name="radius" type="float" />
<description>
- Returns a mesh of a sphere with the given number of horizontal and vertical subdivisions.
+ Returns a mesh of a sphere with the given number of horizontal subdivisions, vertical subdivisions and radius. See also [method get_test_cube].
</description>
</method>
<method name="material_create">
<return type="RID" />
<description>
Creates an empty material and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]material_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
- This is the internal equivalent of the [Material] resource.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [Material].
</description>
</method>
<method name="material_get_param" qualifiers="const">
@@ -2014,9 +2111,9 @@
<return type="RID" />
<description>
Creates a new mesh and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]mesh_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this mesh to an instance using [method instance_set_base] using the returned RID.
- This is the internal equivalent of the [Mesh] resource.
+ [b]Note:[/b] The equivalent resource is [Mesh].
</description>
</method>
<method name="mesh_create_from_surfaces">
@@ -2187,9 +2284,9 @@
<return type="RID" />
<description>
Creates a new multimesh on the RenderingServer and returns an [RID] handle. This RID will be used in all [code]multimesh_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this multimesh to an instance using [method instance_set_base] using the returned RID.
- This is the internal equivalent of the [MultiMesh] resource.
+ [b]Note:[/b] The equivalent resource is [MultiMesh].
</description>
</method>
<method name="multimesh_get_aabb" qualifiers="const">
@@ -2203,6 +2300,8 @@
<return type="PackedFloat32Array" />
<param index="0" name="multimesh" type="RID" />
<description>
+ Returns the MultiMesh data (such as instance transforms, colors, etc). See [method multimesh_set_buffer] for a description of the returned data.
+ [b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible.
</description>
</method>
<method name="multimesh_get_instance_count" qualifiers="const">
@@ -2299,6 +2398,20 @@
<param index="0" name="multimesh" type="RID" />
<param index="1" name="buffer" type="PackedFloat32Array" />
<description>
+ Set the entire data to use for drawing the [param multimesh] at once to [param buffer] (such as instance transforms and colors). [param buffer]'s size must match the number of instances multiplied by the per-instance data size (which depends on the enabled MultiMesh fields). Otherwise, an error message is printed and nothing is rendered. See also [method multimesh_get_buffer].
+ The per-instance data size and expected data order is:
+ [codeblock]
+ 2D:
+ - Position: 8 floats (8 floats for Transform2D)
+ - Position + Vertex color: 12 floats (8 floats for Transform2D, 4 floats for Color)
+ - Position + Custom data: 12 floats (8 floats for Transform2D, 4 floats of custom data)
+ - Position + Vertex color + Custom data: 16 floats (8 floats for Transform2D, 4 floats for Color, 4 floats of custom data)
+ 3D:
+ - Position: 12 floats (12 floats for Transform3D)
+ - Position + Vertex color: 16 floats (12 floats for Transform3D, 4 floats for Color)
+ - Position + Custom data: 16 floats (12 floats for Transform3D, 4 floats of custom data)
+ - Position + Vertex color + Custom data: 20 floats (12 floats for Transform3D, 4 floats for Color, 4 floats of custom data)
+ [/codeblock]
</description>
</method>
<method name="multimesh_set_mesh">
@@ -2320,6 +2433,9 @@
<method name="occluder_create">
<return type="RID" />
<description>
+ Creates an occluder instance and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]occluder_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [Occluder3D] (not to be confused with the [OccluderInstance3D] node).
</description>
</method>
<method name="occluder_set_mesh">
@@ -2328,25 +2444,30 @@
<param index="1" name="vertices" type="PackedVector3Array" />
<param index="2" name="indices" type="PackedInt32Array" />
<description>
+ Sets the mesh data for the given occluder RID, which controls the shape of the occlusion culling that will be performed.
</description>
</method>
<method name="omni_light_create">
<return type="RID" />
<description>
Creates a new omni light and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID can be used in most [code]light_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this omni light to an instance using [method instance_set_base] using the returned RID.
+ [b]Note:[/b] The equivalent node is [OmniLight3D].
</description>
</method>
<method name="particles_collision_create">
<return type="RID" />
<description>
+ Creates a new 3D GPU particle collision or attractor and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID can be used in most [code]particles_collision_*[/code] RenderingServer functions.
+ [b]Note:[/b] The equivalent nodes are [GPUParticlesCollision3D] and [GPUParticlesAttractor3D].
</description>
</method>
<method name="particles_collision_height_field_update">
<return type="void" />
<param index="0" name="particles_collision" type="RID" />
<description>
+ Requests an update for the 3D GPU particle collision heightfield. This may be automatically called by the 3D GPU particle collision heightfield depending on its [member GPUParticlesCollisionHeightField3D.update_mode].
</description>
</method>
<method name="particles_collision_set_attractor_attenuation">
@@ -2354,6 +2475,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="curve" type="float" />
<description>
+ Sets the attenuation [param curve] for the 3D GPU particles attractor specified by the [param particles_collision] RID. Only used for attractors, not colliders. Equivalent to [member GPUParticlesAttractor3D.attenuation].
</description>
</method>
<method name="particles_collision_set_attractor_directionality">
@@ -2361,6 +2483,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="amount" type="float" />
<description>
+ Sets the directionality [param amount] for the 3D GPU particles attractor specified by the [param particles_collision] RID. Only used for attractors, not colliders. Equivalent to [member GPUParticlesAttractor3D.directionality].
</description>
</method>
<method name="particles_collision_set_attractor_strength">
@@ -2368,6 +2491,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="strength" type="float" />
<description>
+ Sets the [param strength] for the 3D GPU particles attractor specified by the [param particles_collision] RID. Only used for attractors, not colliders. Equivalent to [member GPUParticlesAttractor3D.strength].
</description>
</method>
<method name="particles_collision_set_box_extents">
@@ -2375,6 +2499,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="extents" type="Vector3" />
<description>
+ Sets the [param extents] for the 3D GPU particles collision by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionBox3D.size], [member GPUParticlesCollisionSDF3D.size], [member GPUParticlesCollisionHeightField3D.size], [member GPUParticlesAttractorBox3D.size] or [member GPUParticlesAttractorVectorField3D.size] depending on the [param particles_collision] type.
</description>
</method>
<method name="particles_collision_set_collision_type">
@@ -2382,6 +2507,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="type" type="int" enum="RenderingServer.ParticlesCollisionType" />
<description>
+ Sets the collision or attractor shape [param type] for the 3D GPU particles collision or attractor specified by the [param particles_collision] RID.
</description>
</method>
<method name="particles_collision_set_cull_mask">
@@ -2389,6 +2515,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="mask" type="int" />
<description>
+ Sets the cull [param mask] for the 3D GPU particles collision or attractor specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollision3D.cull_mask] or [member GPUParticlesAttractor3D.cull_mask] depending on the [param particles_collision] type.
</description>
</method>
<method name="particles_collision_set_field_texture">
@@ -2396,6 +2523,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="texture" type="RID" />
<description>
+ Sets the signed distance field [param texture] for the 3D GPU particles collision specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionSDF3D.texture] or [member GPUParticlesAttractorVectorField3D.texture] depending on the [param particles_collision] type.
</description>
</method>
<method name="particles_collision_set_height_field_resolution">
@@ -2403,6 +2531,7 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="resolution" type="int" enum="RenderingServer.ParticlesCollisionHeightfieldResolution" />
<description>
+ Sets the heightmap [param resolution] for the 3D GPU particles heightfield collision specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionHeightField3D.resolution].
</description>
</method>
<method name="particles_collision_set_sphere_radius">
@@ -2410,14 +2539,17 @@
<param index="0" name="particles_collision" type="RID" />
<param index="1" name="radius" type="float" />
<description>
+ Sets the [param radius] for the 3D GPU particles sphere collision or attractor specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionSphere3D.radius] or [member GPUParticlesAttractorSphere3D.radius] depending on the [param particles_collision] type.
</description>
</method>
<method name="particles_create">
<return type="RID" />
<description>
- Creates a particle system and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]particles_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Creates a GPU-based particle system and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]particles_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach these particles to an instance using [method instance_set_base] using the returned RID.
+ [b]Note:[/b] The equivalent nodes are [GPUParticles2D] and [GPUParticles3D].
+ [b]Note:[/b] All [code]particles_*[/code] methods only apply to GPU-based particles, not CPU-based particles. [CPUParticles2D] and [CPUParticles3D] do not have equivalent RenderingServer functions available, as these use [MultiMeshInstance2D] and [MultiMeshInstance3D] under the hood (see [code]multimesh_*[/code] methods).
</description>
</method>
<method name="particles_emit">
@@ -2429,6 +2561,7 @@
<param index="4" name="custom" type="Color" />
<param index="5" name="emit_flags" type="int" />
<description>
+ Manually emits particles from the [param particles] instance.
</description>
</method>
<method name="particles_get_current_aabb">
@@ -2574,6 +2707,7 @@
<param index="0" name="particles" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.ParticlesMode" />
<description>
+ Sets whether the GPU particles specified by the [param particles] RID should be rendered in 2D or 3D according to [param mode].
</description>
</method>
<method name="particles_set_one_shot">
@@ -2637,6 +2771,7 @@
<param index="1" name="enable" type="bool" />
<param index="2" name="length_sec" type="float" />
<description>
+ If [param enable] is [code]true[/code], enables trails for the [param particles] with the specified [param length_sec] in seconds. Equivalent to [member GPUParticles3D.trail_enabled] and [member GPUParticles3D.trail_lifetime].
</description>
</method>
<method name="particles_set_transform_align">
@@ -2658,14 +2793,16 @@
<return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.ShadowQuality" />
<description>
+ Sets the filter quality for omni and spot light shadows in 3D. See also [member ProjectSettings.rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality]. This parameter is global and cannot be set on a per-viewport basis.
</description>
</method>
<method name="reflection_probe_create">
<return type="RID" />
<description>
Creates a reflection probe and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]reflection_probe_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this reflection probe to an instance using [method instance_set_base] using the returned RID.
+ [b]Note:[/b] The equivalent node is [ReflectionProbe].
</description>
</method>
<method name="reflection_probe_set_ambient_color">
@@ -2673,6 +2810,7 @@
<param index="0" name="probe" type="RID" />
<param index="1" name="color" type="Color" />
<description>
+ Sets the reflection probe's custom ambient light color. Equivalent to [member ReflectionProbe.ambient_color].
</description>
</method>
<method name="reflection_probe_set_ambient_energy">
@@ -2680,6 +2818,7 @@
<param index="0" name="probe" type="RID" />
<param index="1" name="energy" type="float" />
<description>
+ Sets the reflection probe's custom ambient light energy. Equivalent to [member ReflectionProbe.ambient_color_energy].
</description>
</method>
<method name="reflection_probe_set_ambient_mode">
@@ -2687,6 +2826,7 @@
<param index="0" name="probe" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.ReflectionProbeAmbientMode" />
<description>
+ Sets the reflection probe's ambient light mode. Equivalent to [member ReflectionProbe.ambient_mode].
</description>
</method>
<method name="reflection_probe_set_as_interior">
@@ -2742,6 +2882,7 @@
<param index="0" name="probe" type="RID" />
<param index="1" name="pixels" type="float" />
<description>
+ Sets the mesh level of detail to use in the reflection probe rendering. Higher values will use less detailed versions of meshes that have LOD variations generated, which can improve performance. Equivalent to [member ReflectionProbe.mesh_lod_threshold].
</description>
</method>
<method name="reflection_probe_set_origin_offset">
@@ -2757,6 +2898,7 @@
<param index="0" name="probe" type="RID" />
<param index="1" name="resolution" type="int" />
<description>
+ Sets the resolution to use when rendering the specified reflection probe. The [param resolution] is specified for each cubemap face: for instance, specifying [code]512[/code] will allocate 6 faces of 512×512 each (plus mipmaps for roughness levels).
</description>
</method>
<method name="reflection_probe_set_size">
@@ -2786,7 +2928,7 @@
<return type="RID" />
<description>
Creates a scenario and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]scenario_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
The scenario is the 3D world that all the visual instances exist in.
</description>
</method>
@@ -2795,6 +2937,7 @@
<param index="0" name="scenario" type="RID" />
<param index="1" name="effects" type="RID" />
<description>
+ Sets the camera attributes ([param effects]) that will be used with this scenario. See also [CameraAttributes].
</description>
</method>
<method name="scenario_set_environment">
@@ -2802,7 +2945,7 @@
<param index="0" name="scenario" type="RID" />
<param index="1" name="environment" type="RID" />
<description>
- Sets the environment that will be used with this scenario.
+ Sets the environment that will be used with this scenario. See also [Environment].
</description>
</method>
<method name="scenario_set_fallback_environment">
@@ -2819,6 +2962,7 @@
<param index="1" name="amount" type="float" />
<param index="2" name="limit" type="float" />
<description>
+ Sets the screen-space roughness limiter parameters, such as whether it should be enabled and its thresholds. Equivalent to [member ProjectSettings.rendering/anti_aliasing/screen_space_roughness_limiter/enabled], [member ProjectSettings.rendering/anti_aliasing/screen_space_roughness_limiter/amount] and [member ProjectSettings.rendering/anti_aliasing/screen_space_roughness_limiter/limit].
</description>
</method>
<method name="set_boot_image">
@@ -2835,28 +2979,29 @@
<return type="void" />
<param index="0" name="generate" type="bool" />
<description>
- If [code]true[/code], the engine will generate wireframes for use with the wireframe debug mode.
+ This method is currently unimplemented and does nothing if called with [param generate] set to [code]true[/code].
</description>
</method>
<method name="set_default_clear_color">
<return type="void" />
<param index="0" name="color" type="Color" />
<description>
- Sets the default clear color which is used when a specific clear color has not been selected.
+ Sets the default clear color which is used when a specific clear color has not been selected. See also [method get_default_clear_color].
</description>
</method>
<method name="shader_create">
<return type="RID" />
<description>
Creates an empty shader and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]shader_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [Shader].
</description>
</method>
<method name="shader_get_code" qualifiers="const">
<return type="String" />
<param index="0" name="shader" type="RID" />
<description>
- Returns a shader's code.
+ Returns a shader's source code as a string.
</description>
</method>
<method name="shader_get_default_texture_parameter" qualifiers="const">
@@ -2874,6 +3019,7 @@
<param index="0" name="shader" type="RID" />
<param index="1" name="name" type="StringName" />
<description>
+ Returns the default value for the specified shader uniform. This is usually the value written in the shader source code.
</description>
</method>
<method name="shader_set_code">
@@ -2881,6 +3027,7 @@
<param index="0" name="shader" type="RID" />
<param index="1" name="code" type="String" />
<description>
+ Sets the shader's source code (which triggers recompilation after being changed).
</description>
</method>
<method name="shader_set_default_texture_parameter">
@@ -2899,6 +3046,7 @@
<param index="0" name="shader" type="RID" />
<param index="1" name="path" type="String" />
<description>
+ Sets the path hint for the specified shader. This should generally match the [Shader] resource's [member Resource.resource_path].
</description>
</method>
<method name="skeleton_allocate_data">
@@ -2947,7 +3095,7 @@
<return type="RID" />
<description>
Creates a skeleton and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]skeleton_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
</description>
</method>
<method name="skeleton_get_bone_count" qualifiers="const">
@@ -2971,13 +3119,16 @@
<param index="2" name="bake_irradiance" type="bool" />
<param index="3" name="size" type="Vector2i" />
<description>
+ Generates and returns an [Image] containing the radiance map for the specified [param sky] RID. This supports built-in sky material and custom sky shaders. If [param bake_irradiance] is [code]true[/code], the irradiance map is saved instead of the radiance map. The radiance map is used to render reflected light, while the irradiance map is used to render ambient light. See also [method environment_bake_panorama].
+ [b]Note:[/b] The image is saved in linear color space without any tonemapping performed, which means it will look too dark if viewed directly in an image editor. [param energy] values above [code]1.0[/code] can be used to brighten the resulting image.
+ [b]Note:[/b] [param size] should be a 2:1 aspect ratio for the generated panorama to have square pixels. For radiance maps, there is no point in using a height greater than [member Sky.radiance_size], as it won't increase detail. Irradiance maps only contain low-frequency data, so there is usually no point in going past a size of 128×64 pixels when saving an irradiance map.
</description>
</method>
<method name="sky_create">
<return type="RID" />
<description>
Creates an empty sky and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]sky_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
</description>
</method>
<method name="sky_set_material">
@@ -2985,7 +3136,7 @@
<param index="0" name="sky" type="RID" />
<param index="1" name="material" type="RID" />
<description>
- Sets the material that the sky uses to render the background and reflection maps.
+ Sets the material that the sky uses to render the background, ambient and reflection maps.
</description>
</method>
<method name="sky_set_mode">
@@ -2993,6 +3144,7 @@
<param index="0" name="sky" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.SkyMode" />
<description>
+ Sets the process [param mode] of the sky specified by the [param sky] RID. Equivalent to [member Sky.process_mode].
</description>
</method>
<method name="sky_set_radiance_size">
@@ -3000,13 +3152,14 @@
<param index="0" name="sky" type="RID" />
<param index="1" name="radiance_size" type="int" />
<description>
+ Sets the [param radiance_size] of the sky specified by the [param sky] RID (in pixels). Equivalent to [member Sky.radiance_size].
</description>
</method>
<method name="spot_light_create">
<return type="RID" />
<description>
Creates a spot light and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID can be used in most [code]light_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
To place in a scene, attach this spot light to an instance using [method instance_set_base] using the returned RID.
</description>
</method>
@@ -3014,6 +3167,7 @@
<return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.SubSurfaceScatteringQuality" />
<description>
+ Sets [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_quality] to use when rendering materials that have subsurface scattering enabled.
</description>
</method>
<method name="sub_surface_scattering_set_scale">
@@ -3021,18 +3175,30 @@
<param index="0" name="scale" type="float" />
<param index="1" name="depth_scale" type="float" />
<description>
+ Sets the [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_scale] and [member ProjectSettings.rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale] to use when rendering materials that have subsurface scattering enabled.
</description>
</method>
<method name="texture_2d_create">
<return type="RID" />
<param index="0" name="image" type="Image" />
<description>
+ Creates a 2-dimensional texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [Texture2D].
+ [b]Note:[/b] Not to be confused with [method RenderingDevice.texture_create], which creates the graphics API's own texture type as opposed to the Godot-specific [Texture2D] resource.
</description>
</method>
<method name="texture_2d_get" qualifiers="const">
<return type="Image" />
<param index="0" name="texture" type="RID" />
<description>
+ Returns an [Image] instance from the given [param texture] [RID].
+ Example of getting the test texture from [method get_test_texture] and applying it to a [Sprite2D] node:
+ [codeblock]
+ var texture_rid = RenderingServer.get_test_texture()
+ var texture = ImageTexture.create_from_image(RenderingServer.texture_2d_get(texture_rid))
+ $Sprite2D.texture = texture
+ [/codeblock]
</description>
</method>
<method name="texture_2d_layer_get" qualifiers="const">
@@ -3040,6 +3206,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="layer" type="int" />
<description>
+ Returns an [Image] instance from the given [param texture] [RID] and [param layer].
</description>
</method>
<method name="texture_2d_layered_create">
@@ -3047,17 +3214,25 @@
<param index="0" name="layers" type="Image[]" />
<param index="1" name="layered_type" type="int" enum="RenderingServer.TextureLayeredType" />
<description>
+ Creates a 2-dimensional layered texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_layered_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [TextureLayered].
</description>
</method>
<method name="texture_2d_layered_placeholder_create">
<return type="RID" />
<param index="0" name="layered_type" type="int" enum="RenderingServer.TextureLayeredType" />
<description>
+ Creates a placeholder for a 2-dimensional layered texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_layered_*[/code] RenderingServer functions, although it does nothing when used. See also [method texture_2d_placeholder_create].
+ [b]Note:[/b] The equivalent resource is [PlaceholderTextureLayered].
</description>
</method>
<method name="texture_2d_placeholder_create">
<return type="RID" />
<description>
+ Creates a placeholder for a 2-dimensional layered texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_2d_layered_*[/code] RenderingServer functions, although it does nothing when used. See also [method texture_2d_layered_placeholder_create]
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [PlaceholderTexture2D].
</description>
</method>
<method name="texture_2d_update">
@@ -3066,6 +3241,8 @@
<param index="1" name="image" type="Image" />
<param index="2" name="layer" type="int" />
<description>
+ Updates the texture specified by the [param texture] [RID] with the data in [param image]. A [param layer] must also be specified, which should be [code]0[/code] when updating a single-layer texture ([Texture2D]).
+ [b]Note:[/b] The [param image] must have the same width, height and format as the current [param texture] data. Otherwise, an error will be printed and the original texture won't be modified. If you need to use different width, height or format, use [method texture_replace] instead.
</description>
</method>
<method name="texture_3d_create">
@@ -3077,17 +3254,22 @@
<param index="4" name="mipmaps" type="bool" />
<param index="5" name="data" type="Image[]" />
<description>
+ [b]Note:[/b] The equivalent resource is [Texture3D].
</description>
</method>
<method name="texture_3d_get" qualifiers="const">
<return type="Image[]" />
<param index="0" name="texture" type="RID" />
<description>
+ Returns 3D texture data as an array of [Image]s for the specified texture [RID].
</description>
</method>
<method name="texture_3d_placeholder_create">
<return type="RID" />
<description>
+ Creates a placeholder for a 3-dimensional texture and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]texture_3d_*[/code] RenderingServer functions, although it does nothing when used.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent resource is [PlaceholderTexture3D].
</description>
</method>
<method name="texture_3d_update">
@@ -3095,6 +3277,17 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="data" type="Image[]" />
<description>
+ Updates the texture specified by the [param texture] [RID]'s data with the data in [param data]. All the texture's layers must be replaced at once.
+ [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_native_handle" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="texture" type="RID" />
+ <param index="1" name="srgb" type="bool" default="false" />
+ <description>
+ Returns the internal graphics handle for this texture object. For use when communicating with 3rd party APIs mostly with GDExternal.
+ [b]Note:[/b] This functions returns a [code]uint64_t[/code] which internally maps to a [code]GLuint[/code] (OpenGL) or [code]VkImage[/code] (Vulkan).
</description>
</method>
<method name="texture_get_path" qualifiers="const">
@@ -3115,6 +3308,7 @@
<return type="RID" />
<param index="0" name="base" type="RID" />
<description>
+ [i]Deprecated.[/i] As ProxyTexture was removed in Godot 4, this method does nothing when called and always returns a null [RID].
</description>
</method>
<method name="texture_proxy_update">
@@ -3122,6 +3316,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="proxy_to" type="RID" />
<description>
+ [i]Deprecated.[/i] ProxyTexture was removed in Godot 4, so this method cannot be used anymore.
</description>
</method>
<method name="texture_replace">
@@ -3129,6 +3324,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="by_texture" type="RID" />
<description>
+ Replaces [param texture]'s texture data by the texture specified by the [param by_texture] RID, without changing [param texture]'s RID.
</description>
</method>
<method name="texture_set_force_redraw_if_visible">
@@ -3192,19 +3388,25 @@
<return type="RID" />
<description>
Creates an empty viewport and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]viewport_*[/code] RenderingServer functions.
- Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [Viewport].
</description>
</method>
<method name="viewport_get_measured_render_time_cpu" qualifiers="const">
<return type="float" />
<param index="0" name="viewport" type="RID" />
<description>
+ Returns the CPU time taken to render the last frame in milliseconds. This [i]only[/i] includes time spent in rendering-related operations; scripts' [code]_process[/code] functions and other engine subsystems are not included in this readout. To get a complete readout of CPU time spent to render the scene, sum the render times of all viewports that are drawn every frame plus [method get_frame_setup_time_cpu]. Unlike [method Engine.get_frames_per_second], this method will accurately reflect CPU utilization even if framerate is capped via V-Sync or [member Engine.max_fps]. See also [method viewport_get_measured_render_time_gpu].
+ [b]Note:[/b] Requires measurements to be enabled on the specified [param viewport] using [method viewport_set_measure_render_time]. Otherwise, this method returns [code]0.0[/code].
</description>
</method>
<method name="viewport_get_measured_render_time_gpu" qualifiers="const">
<return type="float" />
<param index="0" name="viewport" type="RID" />
<description>
+ Returns the GPU time taken to render the last frame in milliseconds. To get a complete readout of GPU time spent to render the scene, sum the render times of all viewports that are drawn every frame. Unlike [method Engine.get_frames_per_second], this method accurately reflects GPU utilization even if framerate is capped via V-Sync or [member Engine.max_fps]. See also [method viewport_get_measured_render_time_gpu].
+ [b]Note:[/b] Requires measurements to be enabled on the specified [param viewport] using [method viewport_set_measure_render_time]. Otherwise, this method returns [code]0.0[/code].
+ [b]Note:[/b] When GPU utilization is low enough during a certain period of time, GPUs will decrease their power state (which in turn decreases core and memory clock speeds). This can cause the reported GPU time to increase if GPU utilization is kept low enough by a framerate cap (compared to what it would be at the GPU's highest power state). Keep this in mind when benchmarking using [method viewport_get_measured_render_time_gpu]. This behavior can be overridden in the graphics driver settings at the cost of higher power usage.
</description>
</method>
<method name="viewport_get_render_info">
@@ -3213,6 +3415,27 @@
<param index="1" name="type" type="int" enum="RenderingServer.ViewportRenderInfoType" />
<param index="2" name="info" type="int" enum="RenderingServer.ViewportRenderInfo" />
<description>
+ Returns a statistic about the rendering engine which can be used for performance profiling. This is separated into render pass [param type]s, each of them having the same [param info]s you can query (different passes will return different values). See [enum RenderingServer.ViewportRenderInfoType] for a list of render pass types and [enum RenderingServer.ViewportRenderInfo] for a list of information that can be queried.
+ See also [method get_rendering_info], which returns global information across all viewports.
+ [b]Note:[/b] Viewport rendering information is not available until at least 2 frames have been rendered by the engine. If rendering information is not available, [method viewport_get_render_info] returns [code]0[/code]. To print rendering information in [code]_ready()[/code] successfully, use the following:
+ [codeblock]
+ func _ready():
+ for _i in 2:
+ await get_tree().process_frame
+
+ print(
+ RenderingServer.viewport_get_render_info(get_viewport().get_viewport_rid(),
+ RenderingServer.VIEWPORT_RENDER_INFO_TYPE_VISIBLE,
+ RenderingServer.VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME)
+ )
+ [/codeblock]
+ </description>
+ </method>
+ <method name="viewport_get_render_target" qualifiers="const">
+ <return type="RID" />
+ <param index="0" name="viewport" type="RID" />
+ <description>
+ Returns the render target for the viewport.
</description>
</method>
<method name="viewport_get_texture" qualifiers="const">
@@ -3287,6 +3510,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="filter" type="int" enum="RenderingServer.CanvasItemTextureFilter" />
<description>
+ Sets the default texture filtering mode for the specified [param viewport] RID. See [enum CanvasItemTextureFilter] for options.
</description>
</method>
<method name="viewport_set_default_canvas_item_texture_repeat">
@@ -3294,6 +3518,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="repeat" type="int" enum="RenderingServer.CanvasItemTextureRepeat" />
<description>
+ Sets the default texture repeat mode for the specified [param viewport] RID. See [enum CanvasItemTextureRepeat] for options.
</description>
</method>
<method name="viewport_set_disable_2d">
@@ -3301,7 +3526,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="disable" type="bool" />
<description>
- If [code]true[/code], the viewport's canvas is not rendered.
+ If [code]true[/code], the viewport's canvas (i.e. 2D and GUI elements) is not rendered.
</description>
</method>
<method name="viewport_set_disable_3d">
@@ -3309,6 +3534,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="disable" type="bool" />
<description>
+ If [code]true[/code], the viewport's 3D elements are not rendered.
</description>
</method>
<method name="viewport_set_environment_mode">
@@ -3340,6 +3566,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
+ Sets the measurement for the given [param viewport] RID (obtained using [method Viewport.get_viewport_rid]). Once enabled, [method viewport_get_measured_render_time_cpu] and [method viewport_get_measured_render_time_gpu] will return values greater than [code]0.0[/code] when queried with the given [param viewport].
</description>
</method>
<method name="viewport_set_msaa_2d">
@@ -3347,7 +3574,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="msaa" type="int" enum="RenderingServer.ViewportMSAA" />
<description>
- Sets the multisample anti-aliasing mode for 2D/Canvas. See [enum ViewportMSAA] for options.
+ Sets the multisample anti-aliasing mode for 2D/Canvas on the specified [param viewport] RID. See [enum ViewportMSAA] for options.
</description>
</method>
<method name="viewport_set_msaa_3d">
@@ -3355,19 +3582,21 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="msaa" type="int" enum="RenderingServer.ViewportMSAA" />
<description>
- Sets the multisample anti-aliasing mode for 3D. See [enum ViewportMSAA] for options.
+ Sets the multisample anti-aliasing mode for 3D on the specified [param viewport] RID. See [enum ViewportMSAA] for options.
</description>
</method>
<method name="viewport_set_occlusion_culling_build_quality">
<return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.ViewportOcclusionCullingBuildQuality" />
<description>
+ Sets the [member ProjectSettings.rendering/occlusion_culling/bvh_build_quality] to use for occlusion culling. This parameter is global and cannot be set on a per-viewport basis.
</description>
</method>
<method name="viewport_set_occlusion_rays_per_thread">
<return type="void" />
<param index="0" name="rays_per_thread" type="int" />
<description>
+ Sets the [member ProjectSettings.rendering/occlusion_culling/occlusion_rays_per_thread] to use for occlusion culling. This parameter is global and cannot be set on a per-viewport basis.
</description>
</method>
<method name="viewport_set_parent_viewport">
@@ -3375,7 +3604,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="parent_viewport" type="RID" />
<description>
- Sets the viewport's parent to another viewport.
+ Sets the viewport's parent to the viewport specified by the [param parent_viewport] RID.
</description>
</method>
<method name="viewport_set_positional_shadow_atlas_quadrant_subdivision">
@@ -3384,7 +3613,7 @@
<param index="1" name="quadrant" type="int" />
<param index="2" name="subdivision" type="int" />
<description>
- Sets the shadow atlas quadrant's subdivision.
+ Sets the number of subdivisions to use in the specified shadow atlas [param quadrant] for omni and spot shadows. See also [method Viewport.set_positional_shadow_atlas_quadrant_subdiv].
</description>
</method>
<method name="viewport_set_positional_shadow_atlas_size">
@@ -3393,8 +3622,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="use_16_bits" type="bool" default="false" />
<description>
- Sets the size of the shadow atlas's images (used for omni and spot lights). The value will be rounded up to the nearest power of 2.
- [b]Note:[/b] If this is set to [code]0[/code], no shadows will be visible at all (including directional shadows).
+ Sets the [param size] of the shadow atlas's images (used for omni and spot lights) on the viewport specified by the [param viewport] RID. The value is rounded up to the nearest power of 2. If [code]use_16_bits[/code] is [code]true[/code], use 16 bits for the omni/spot shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
+ [b]Note:[/b] If this is set to [code]0[/code], no positional shadows will be visible at all. This can improve performance significantly on low-end systems by reducing both the CPU and GPU load (as fewer draw calls are needed to draw the scene without shadows).
</description>
</method>
<method name="viewport_set_render_direct_to_screen">
@@ -3410,7 +3639,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="scaling_3d_mode" type="int" enum="RenderingServer.ViewportScaling3DMode" />
<description>
- Sets scaling 3d mode. Bilinear scaling renders at different resolution to either undersample or supersample the viewport. FidelityFX Super Resolution 1.0, abbreviated to FSR, is an upscaling technology that produces high quality images at fast framerates by using a spatially aware upscaling algorithm. FSR is slightly more expensive than bilinear, but it produces significantly higher image quality. FSR should be used where possible.
+ Sets the 3D resolution scaling mode. Bilinear scaling renders at different resolution to either undersample or supersample the viewport. FidelityFX Super Resolution 1.0, abbreviated to FSR, is an upscaling technology that produces high quality images at fast framerates by using a spatially aware upscaling algorithm. FSR is slightly more expensive than bilinear, but it produces significantly higher image quality. FSR should be used where possible.
</description>
</method>
<method name="viewport_set_scaling_3d_scale">
@@ -3427,8 +3656,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="scenario" type="RID" />
<description>
- Sets a viewport's scenario.
- The scenario contains information about environment information, reflection atlas etc.
+ Sets a viewport's scenario. The scenario contains information about environment information, reflection atlas, etc.
</description>
</method>
<method name="viewport_set_screen_space_aa">
@@ -3436,6 +3664,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.ViewportScreenSpaceAA" />
<description>
+ Sets the viewport's screen-space antialiasing mode.
</description>
</method>
<method name="viewport_set_sdf_oversize_and_scale">
@@ -3444,6 +3673,7 @@
<param index="1" name="oversize" type="int" enum="RenderingServer.ViewportSDFOversize" />
<param index="2" name="scale" type="int" enum="RenderingServer.ViewportSDFScale" />
<description>
+ Sets the viewport's 2D signed distance field [member ProjectSettings.rendering/2d/sdf/oversize] and [member ProjectSettings.rendering/2d/sdf/scale]. This is used when sampling the signed distance field in [CanvasItem] shaders as well as [GPUParticles2D] collision. This is [i]not[/i] used by SDFGI in 3D rendering.
</description>
</method>
<method name="viewport_set_size">
@@ -3452,7 +3682,7 @@
<param index="1" name="width" type="int" />
<param index="2" name="height" type="int" />
<description>
- Sets the viewport's width and height.
+ Sets the viewport's width and height in pixels.
</description>
</method>
<method name="viewport_set_snap_2d_transforms_to_pixel">
@@ -3460,6 +3690,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ If [code]true[/code], canvas item transforms (i.e. origin position) are snapped to the nearest pixel when rendering. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled. Equivalent to [member ProjectSettings.rendering/2d/snap/snap_2d_transforms_to_pixel].
</description>
</method>
<method name="viewport_set_snap_2d_vertices_to_pixel">
@@ -3467,6 +3698,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ If [code]true[/code], canvas item vertices (i.e. polygon points) are snapped to the nearest pixel when rendering. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled. Equivalent to [member ProjectSettings.rendering/2d/snap/snap_2d_vertices_to_pixel].
</description>
</method>
<method name="viewport_set_texture_mipmap_bias">
@@ -3499,6 +3731,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
+ If [code]true[/code], enables debanding on the specified viewport. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding].
</description>
</method>
<method name="viewport_set_use_occlusion_culling">
@@ -3506,6 +3739,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
+ If [code]true[/code], enables occlusion culling on the specified viewport. Equivalent to [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling].
</description>
</method>
<method name="viewport_set_use_taa">
@@ -3513,7 +3747,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
- If [code]true[/code], use Temporal Anti-Aliasing.
+ If [code]true[/code], use Temporal Anti-Aliasing. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_taa].
</description>
</method>
<method name="viewport_set_use_xr">
@@ -3529,7 +3763,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="mode" type="int" enum="RenderingServer.ViewportVRSMode" />
<description>
- Sets the Variable Rate Shading (VRS) mode for the viewport. Note, if hardware does not support VRS this property is ignored.
+ Sets the Variable Rate Shading (VRS) mode for the viewport. If the GPU does not support VRS, this property is ignored. Equivalent to [member ProjectSettings.rendering/vrs/mode].
</description>
</method>
<method name="viewport_set_vrs_texture">
@@ -3537,12 +3771,16 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="texture" type="RID" />
<description>
- Texture to use when the VRS mode is set to [constant RenderingServer.VIEWPORT_VRS_TEXTURE].
+ The texture to use when the VRS mode is set to [constant RenderingServer.VIEWPORT_VRS_TEXTURE]. Equivalent to [member ProjectSettings.rendering/vrs/texture].
</description>
</method>
<method name="visibility_notifier_create">
<return type="RID" />
<description>
+ Creates a new 3D visibility notifier object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]visibility_notifier_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ To place in a scene, attach this mesh to an instance using [method instance_set_base] using the returned RID.
+ [b]Note:[/b] The equivalent node is [VisibleOnScreenNotifier3D].
</description>
</method>
<method name="visibility_notifier_set_aabb">
@@ -3576,6 +3814,9 @@
<method name="voxel_gi_create">
<return type="RID" />
<description>
+ Creates a new voxel-based global illumination object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]voxel_gi_*[/code] RenderingServer functions.
+ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method.
+ [b]Note:[/b] The equivalent node is [VoxelGI].
</description>
</method>
<method name="voxel_gi_get_data_cells" qualifiers="const">
@@ -3627,6 +3868,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="bias" type="float" />
<description>
+ Sets the [member VoxelGIData.bias] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_dynamic_range">
@@ -3634,6 +3876,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="range" type="float" />
<description>
+ Sets the [member VoxelGIData.dynamic_range] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_energy">
@@ -3641,6 +3884,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="energy" type="float" />
<description>
+ Sets the [member VoxelGIData.energy] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_interior">
@@ -3648,6 +3892,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
+ Sets the [member VoxelGIData.interior] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_normal_bias">
@@ -3655,6 +3900,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="bias" type="float" />
<description>
+ Sets the [member VoxelGIData.normal_bias] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_propagation">
@@ -3662,12 +3908,14 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="amount" type="float" />
<description>
+ Sets the [member VoxelGIData.propagation] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
<method name="voxel_gi_set_quality">
<return type="void" />
<param index="0" name="quality" type="int" enum="RenderingServer.VoxelGIQuality" />
<description>
+ Sets the [member ProjectSettings.rendering/global_illumination/voxel_gi/quality] value to use when rendering. This parameter is global and cannot be set on a per-VoxelGI basis.
</description>
</method>
<method name="voxel_gi_set_use_two_bounces">
@@ -3675,6 +3923,7 @@
<param index="0" name="voxel_gi" type="RID" />
<param index="1" name="enable" type="bool" />
<description>
+ Sets the [member VoxelGIData.use_two_bounces] value to use on the specified [param voxel_gi]'s [RID].
</description>
</method>
</methods>
@@ -3709,30 +3958,40 @@
The maximum Z-layer for canvas items.
</constant>
<constant name="MAX_GLOW_LEVELS" value="7">
- Max number of glow levels that can be used with glow post-process effect.
+ The maximum number of glow levels that can be used with the glow post-processing effect.
</constant>
<constant name="MAX_CURSORS" value="8">
- Unused enum in Godot 3.x.
+ [i]Deprecated.[/i] This constant is unused.
</constant>
<constant name="MAX_2D_DIRECTIONAL_LIGHTS" value="8">
+ The maximum number of directional lights that can be rendered at a given time in 2D.
</constant>
<constant name="TEXTURE_LAYERED_2D_ARRAY" value="0" enum="TextureLayeredType">
+ Array of 2-dimensional textures (see [Texture2DArray]).
</constant>
<constant name="TEXTURE_LAYERED_CUBEMAP" value="1" enum="TextureLayeredType">
+ Cubemap texture (see [Cubemap]).
</constant>
<constant name="TEXTURE_LAYERED_CUBEMAP_ARRAY" value="2" enum="TextureLayeredType">
+ Array of cubemap textures (see [CubemapArray]).
</constant>
<constant name="CUBEMAP_LAYER_LEFT" value="0" enum="CubeMapLayer">
+ Left face of a [Cubemap].
</constant>
<constant name="CUBEMAP_LAYER_RIGHT" value="1" enum="CubeMapLayer">
+ Right face of a [Cubemap].
</constant>
<constant name="CUBEMAP_LAYER_BOTTOM" value="2" enum="CubeMapLayer">
+ Bottom face of a [Cubemap].
</constant>
<constant name="CUBEMAP_LAYER_TOP" value="3" enum="CubeMapLayer">
+ Top face of a [Cubemap].
</constant>
<constant name="CUBEMAP_LAYER_FRONT" value="4" enum="CubeMapLayer">
+ Front face of a [Cubemap].
</constant>
<constant name="CUBEMAP_LAYER_BACK" value="5" enum="CubeMapLayer">
+ Back face of a [Cubemap].
</constant>
<constant name="SHADER_SPATIAL" value="0" enum="ShaderMode">
Shader is a 3D shader.
@@ -3741,13 +4000,13 @@
Shader is a 2D shader.
</constant>
<constant name="SHADER_PARTICLES" value="2" enum="ShaderMode">
- Shader is a particle shader.
+ Shader is a particle shader (can be used in both 2D and 3D).
</constant>
<constant name="SHADER_SKY" value="3" enum="ShaderMode">
- Shader is a sky shader.
+ Shader is a 3D sky shader.
</constant>
<constant name="SHADER_FOG" value="4" enum="ShaderMode">
- Shader is a fog shader.
+ Shader is a 3D fog shader.
</constant>
<constant name="SHADER_MAX" value="5" enum="ShaderMode">
Represents the size of the [enum ShaderMode] enum.
@@ -3759,7 +4018,7 @@
The maximum renderpriority of all materials.
</constant>
<constant name="ARRAY_VERTEX" value="0" enum="ArrayType">
- Array is a vertex array.
+ Array is a vertex position array.
</constant>
<constant name="ARRAY_NORMAL" value="1" enum="ArrayType">
Array is a normal array.
@@ -3768,21 +4027,25 @@
Array is a tangent array.
</constant>
<constant name="ARRAY_COLOR" value="3" enum="ArrayType">
- Array is a color array.
+ Array is a vertex color array.
</constant>
<constant name="ARRAY_TEX_UV" value="4" enum="ArrayType">
Array is an UV coordinates array.
</constant>
<constant name="ARRAY_TEX_UV2" value="5" enum="ArrayType">
- Array is an UV coordinates array for the second UV coordinates.
+ Array is an UV coordinates array for the second set of UV coordinates.
</constant>
<constant name="ARRAY_CUSTOM0" value="6" enum="ArrayType">
+ Array is a custom data array for the first set of custom data.
</constant>
<constant name="ARRAY_CUSTOM1" value="7" enum="ArrayType">
+ Array is a custom data array for the second set of custom data.
</constant>
<constant name="ARRAY_CUSTOM2" value="8" enum="ArrayType">
+ Array is a custom data array for the third set of custom data.
</constant>
<constant name="ARRAY_CUSTOM3" value="9" enum="ArrayType">
+ Array is a custom data array for the fourth set of custom data.
</constant>
<constant name="ARRAY_BONES" value="10" enum="ArrayType">
Array contains bone information.
@@ -3791,33 +4054,43 @@
Array is weight information.
</constant>
<constant name="ARRAY_INDEX" value="12" enum="ArrayType">
- Array is index array.
+ Array is an index array.
</constant>
<constant name="ARRAY_MAX" value="13" enum="ArrayType">
Represents the size of the [enum ArrayType] enum.
</constant>
<constant name="ARRAY_CUSTOM_COUNT" value="4">
+ The number of custom data arrays available ([constant ARRAY_CUSTOM0], [constant ARRAY_CUSTOM1], [constant ARRAY_CUSTOM2], [constant ARRAY_CUSTOM3]).
</constant>
<constant name="ARRAY_CUSTOM_RGBA8_UNORM" value="0" enum="ArrayCustomFormat">
+ Custom data array contains 8-bit-per-channel red/green/blue/alpha color data. Values are normalized, unsigned floating-point in the [code][0.0, 1.0][/code] range.
</constant>
<constant name="ARRAY_CUSTOM_RGBA8_SNORM" value="1" enum="ArrayCustomFormat">
+ Custom data array contains 8-bit-per-channel red/green/blue/alpha color data. Values are normalized, signed floating-point in the [code][-1.0, 1.0][/code] range.
</constant>
<constant name="ARRAY_CUSTOM_RG_HALF" value="2" enum="ArrayCustomFormat">
+ Custom data array contains 16-bit-per-channel red/green color data. Values are floating-point in half precision.
</constant>
<constant name="ARRAY_CUSTOM_RGBA_HALF" value="3" enum="ArrayCustomFormat">
+ Custom data array contains 16-bit-per-channel red/green/blue/alpha color data. Values are floating-point in half precision.
</constant>
<constant name="ARRAY_CUSTOM_R_FLOAT" value="4" enum="ArrayCustomFormat">
+ Custom data array contains 32-bit-per-channel red color data. Values are floating-point in single precision.
</constant>
<constant name="ARRAY_CUSTOM_RG_FLOAT" value="5" enum="ArrayCustomFormat">
+ Custom data array contains 32-bit-per-channel red/green color data. Values are floating-point in single precision.
</constant>
<constant name="ARRAY_CUSTOM_RGB_FLOAT" value="6" enum="ArrayCustomFormat">
+ Custom data array contains 32-bit-per-channel red/green/blue color data. Values are floating-point in single precision.
</constant>
<constant name="ARRAY_CUSTOM_RGBA_FLOAT" value="7" enum="ArrayCustomFormat">
+ Custom data array contains 32-bit-per-channel red/green/blue/alpha color data. Values are floating-point in single precision.
</constant>
<constant name="ARRAY_CUSTOM_MAX" value="8" enum="ArrayCustomFormat">
+ Represents the size of the [enum ArrayCustomFormat] enum.
</constant>
<constant name="ARRAY_FORMAT_VERTEX" value="1" enum="ArrayFormat" is_bitfield="true">
- Flag used to mark a vertex array.
+ Flag used to mark a vertex position array.
</constant>
<constant name="ARRAY_FORMAT_NORMAL" value="2" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a normal array.
@@ -3826,7 +4099,7 @@
Flag used to mark a tangent array.
</constant>
<constant name="ARRAY_FORMAT_COLOR" value="8" enum="ArrayFormat" is_bitfield="true">
- Flag used to mark a color array.
+ Flag used to mark a vertex color array.
</constant>
<constant name="ARRAY_FORMAT_TEX_UV" value="16" enum="ArrayFormat" is_bitfield="true">
Flag used to mark an UV coordinates array.
@@ -3835,12 +4108,16 @@
Flag used to mark an UV coordinates array for the second UV coordinates.
</constant>
<constant name="ARRAY_FORMAT_CUSTOM0" value="64" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark an array of custom per-vertex data for the first set of custom data.
</constant>
<constant name="ARRAY_FORMAT_CUSTOM1" value="128" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark an array of custom per-vertex data for the second set of custom data.
</constant>
<constant name="ARRAY_FORMAT_CUSTOM2" value="256" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark an array of custom per-vertex data for the third set of custom data.
</constant>
<constant name="ARRAY_FORMAT_CUSTOM3" value="512" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark an array of custom per-vertex data for the fourth set of custom data.
</constant>
<constant name="ARRAY_FORMAT_BONES" value="1024" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a bone information array.
@@ -3875,6 +4152,7 @@
<constant name="ARRAY_FLAG_USE_DYNAMIC_UPDATE" value="67108864" enum="ArrayFormat" is_bitfield="true">
</constant>
<constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark that the array uses 8 bone weighs instead of 4.
</constant>
<constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
</constant>
@@ -3927,13 +4205,13 @@
Linear filter for light projectors (use for non-pixel art light projectors). Anisotropic mipmaps are used for rendering, which means light projectors at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
</constant>
<constant name="LIGHT_DIRECTIONAL" value="0" enum="LightType">
- Is a directional (sun) light.
+ Directional (sun/moon) light (see [DirectionalLight3D]).
</constant>
<constant name="LIGHT_OMNI" value="1" enum="LightType">
- Is an omni light.
+ Omni light (see [OmniLight3D]).
</constant>
<constant name="LIGHT_SPOT" value="2" enum="LightType">
- Is a spot light.
+ Spot light (see [SpotLight3D]).
</constant>
<constant name="LIGHT_PARAM_ENERGY" value="0" enum="LightParam">
The light's energy multiplier.
@@ -3963,7 +4241,7 @@
The spotlight's attenuation.
</constant>
<constant name="LIGHT_PARAM_SHADOW_MAX_DISTANCE" value="9" enum="LightParam">
- Max distance that shadows will be rendered.
+ The maximum distance for shadow splits. Increasing this value will make directional shadows visible from further away, at the cost of lower overall shadow detail and performance (since more objects need to be included in the directional shadow rendering).
</constant>
<constant name="LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET" value="10" enum="LightParam">
Proportion of shadow atlas occupied by the first split.
@@ -3998,10 +4276,13 @@
Represents the size of the [enum LightParam] enum.
</constant>
<constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode">
+ Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights.
</constant>
<constant name="LIGHT_BAKE_STATIC" value="1" enum="LightBakeMode">
+ Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off.
</constant>
<constant name="LIGHT_BAKE_DYNAMIC" value="2" enum="LightBakeMode">
+ Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant LIGHT_BAKE_STATIC]. This has a greater performance cost compared to [constant LIGHT_BAKE_STATIC]. When using SDFGI, the update speed of dynamic lights is affected by [member ProjectSettings.rendering/global_illumination/sdfgi/frames_to_update_lights].
</constant>
<constant name="LIGHT_OMNI_SHADOW_DUAL_PARABOLOID" value="0" enum="LightOmniShadowMode">
Use a dual paraboloid shadow map for omni lights.
@@ -4047,6 +4328,7 @@
Highest low shadow filtering quality (slowest). When using this quality setting, [member Light3D.shadow_blur] is automatically multiplied by 2× to better make use of the high sample count. This increased blur also improves the stability of dynamic object shadows. This multiplier only applies to lights whose [member Light3D.light_size] or [member Light3D.light_angular_distance] is [code]0.0[/code]).
</constant>
<constant name="SHADOW_QUALITY_MAX" value="6" enum="ShadowQuality">
+ Represents the size of the [enum ShadowQuality] enum.
</constant>
<constant name="REFLECTION_PROBE_UPDATE_ONCE" value="0" enum="ReflectionProbeUpdateMode">
Reflection probe will update reflections once and then stop.
@@ -4055,20 +4337,28 @@
Reflection probe will update each frame. This mode is necessary to capture moving objects.
</constant>
<constant name="REFLECTION_PROBE_AMBIENT_DISABLED" value="0" enum="ReflectionProbeAmbientMode">
+ Do not apply any ambient lighting inside the reflection probe's box defined by its size.
</constant>
<constant name="REFLECTION_PROBE_AMBIENT_ENVIRONMENT" value="1" enum="ReflectionProbeAmbientMode">
+ Apply automatically-sourced environment lighting inside the reflection probe's box defined by its size.
</constant>
<constant name="REFLECTION_PROBE_AMBIENT_COLOR" value="2" enum="ReflectionProbeAmbientMode">
+ Apply custom ambient lighting inside the reflection probe's box defined by its size. See [method reflection_probe_set_ambient_color] and [method reflection_probe_set_ambient_energy].
</constant>
<constant name="DECAL_TEXTURE_ALBEDO" value="0" enum="DecalTexture">
+ Albedo texture slot in a decal ([member Decal.texture_albedo]).
</constant>
<constant name="DECAL_TEXTURE_NORMAL" value="1" enum="DecalTexture">
+ Normal map texture slot in a decal ([member Decal.texture_normal]).
</constant>
<constant name="DECAL_TEXTURE_ORM" value="2" enum="DecalTexture">
+ Occlusion/Roughness/Metallic texture slot in a decal ([member Decal.texture_orm]).
</constant>
<constant name="DECAL_TEXTURE_EMISSION" value="3" enum="DecalTexture">
+ Emission texture slot in a decal ([member Decal.texture_emission]).
</constant>
<constant name="DECAL_TEXTURE_MAX" value="4" enum="DecalTexture">
+ Represents the size of the [enum DecalTexture] enum.
</constant>
<constant name="DECAL_FILTER_NEAREST" value="0" enum="DecalFilter">
Nearest-neighbor filter for decals (use for pixel art decals). No mipmaps are used for rendering, which means decals at a distance will look sharp but grainy. This has roughly the same performance cost as using mipmaps.
@@ -4089,12 +4379,16 @@
Linear filter for decals (use for non-pixel art decals). Anisotropic mipmaps are used for rendering, which means decals at a distance will look smooth and sharp when viewed from oblique angles. This looks better compared to isotropic mipmaps, but is slower. The level of anisotropic filtering is defined by [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
</constant>
<constant name="VOXEL_GI_QUALITY_LOW" value="0" enum="VoxelGIQuality">
+ Low [VoxelGI] rendering quality using 4 cones.
</constant>
<constant name="VOXEL_GI_QUALITY_HIGH" value="1" enum="VoxelGIQuality">
+ High [VoxelGI] rendering quality using 6 cones.
</constant>
<constant name="PARTICLES_MODE_2D" value="0" enum="ParticlesMode">
+ 2D particles.
</constant>
<constant name="PARTICLES_MODE_3D" value="1" enum="ParticlesMode">
+ 3D particles.
</constant>
<constant name="PARTICLES_TRANSFORM_ALIGN_DISABLED" value="0" enum="ParticlesTransformAlign">
</constant>
@@ -4152,6 +4446,7 @@
<constant name="PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_8192" value="5" enum="ParticlesCollisionHeightfieldResolution">
</constant>
<constant name="PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX" value="6" enum="ParticlesCollisionHeightfieldResolution">
+ Represents the size of the [enum ParticlesCollisionHeightfieldResolution] enum.
</constant>
<constant name="FOG_VOLUME_SHAPE_ELLIPSOID" value="0" enum="FogVolumeShape">
[FogVolume] will be shaped like an ellipsoid (stretched sphere).
@@ -4169,6 +4464,7 @@
[FogVolume] will have no shape, will cover the whole world and will not be culled.
</constant>
<constant name="FOG_VOLUME_SHAPE_MAX" value="5" enum="FogVolumeShape">
+ Represents the size of the [enum FogVolumeShape] enum.
</constant>
<constant name="VIEWPORT_SCALING_3D_MODE_BILINEAR" value="0" enum="ViewportScaling3DMode">
Use bilinear scaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will result in undersampling while values greater than [code]1.0[/code] will result in supersampling. A value of [code]1.0[/code] disables scaling.
@@ -4177,29 +4473,31 @@
Use AMD FidelityFX Super Resolution 1.0 upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using FSR. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] disables scaling.
</constant>
<constant name="VIEWPORT_SCALING_3D_MODE_MAX" value="2" enum="ViewportScaling3DMode">
+ Represents the size of the [enum ViewportScaling3DMode] enum.
</constant>
<constant name="VIEWPORT_UPDATE_DISABLED" value="0" enum="ViewportUpdateMode">
- Do not update the viewport.
+ Do not update the viewport's render target.
</constant>
<constant name="VIEWPORT_UPDATE_ONCE" value="1" enum="ViewportUpdateMode">
- Update the viewport once then set to disabled.
+ Update the viewport's render target once, then switch to [constant VIEWPORT_UPDATE_DISABLED].
</constant>
<constant name="VIEWPORT_UPDATE_WHEN_VISIBLE" value="2" enum="ViewportUpdateMode">
- Update the viewport whenever it is visible.
+ Update the viewport's render target only when it is visible. This is the default value.
</constant>
<constant name="VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE" value="3" enum="ViewportUpdateMode">
+ Update the viewport's render target only when its parent is visible.
</constant>
<constant name="VIEWPORT_UPDATE_ALWAYS" value="4" enum="ViewportUpdateMode">
- Always update the viewport.
+ Always update the viewport's render target.
</constant>
<constant name="VIEWPORT_CLEAR_ALWAYS" value="0" enum="ViewportClearMode">
- The viewport is always cleared before drawing.
+ Always clear the viewport's render target before drawing.
</constant>
<constant name="VIEWPORT_CLEAR_NEVER" value="1" enum="ViewportClearMode">
- The viewport is never cleared before drawing.
+ Never clear the viewport's render target.
</constant>
<constant name="VIEWPORT_CLEAR_ONLY_NEXT_FRAME" value="2" enum="ViewportClearMode">
- The viewport is cleared once, then the clear mode is set to [constant VIEWPORT_CLEAR_NEVER].
+ Clear the viewport's render target on the next frame, then switch to [constant VIEWPORT_CLEAR_NEVER].
</constant>
<constant name="VIEWPORT_ENVIRONMENT_DISABLED" value="0" enum="ViewportEnvironmentMode">
Disable rendering of 3D environment over 2D canvas.
@@ -4208,28 +4506,37 @@
Enable rendering of 3D environment over 2D canvas.
</constant>
<constant name="VIEWPORT_ENVIRONMENT_INHERIT" value="2" enum="ViewportEnvironmentMode">
- Inherit enable/disable value from parent. If topmost parent is also set to inherit, then this has the same behavior as [constant VIEWPORT_ENVIRONMENT_ENABLED].
+ Inherit enable/disable value from parent. If the topmost parent is also set to [constant VIEWPORT_ENVIRONMENT_INHERIT], then this has the same behavior as [constant VIEWPORT_ENVIRONMENT_ENABLED].
</constant>
<constant name="VIEWPORT_ENVIRONMENT_MAX" value="3" enum="ViewportEnvironmentMode">
- Max value of [enum ViewportEnvironmentMode] enum.
+ Represents the size of the [enum ViewportEnvironmentMode] enum.
</constant>
<constant name="VIEWPORT_SDF_OVERSIZE_100_PERCENT" value="0" enum="ViewportSDFOversize">
+ Do not oversize the 2D signed distance field. Occluders may disappear when touching the viewport's edges, and [GPUParticles3D] collision may stop working earlier than intended. This has the lowest GPU requirements.
</constant>
<constant name="VIEWPORT_SDF_OVERSIZE_120_PERCENT" value="1" enum="ViewportSDFOversize">
+ 2D signed distance field covers 20% of the viewport's size outside the viewport on each side (top, right, bottom, left).
</constant>
<constant name="VIEWPORT_SDF_OVERSIZE_150_PERCENT" value="2" enum="ViewportSDFOversize">
+ 2D signed distance field covers 50% of the viewport's size outside the viewport on each side (top, right, bottom, left).
</constant>
<constant name="VIEWPORT_SDF_OVERSIZE_200_PERCENT" value="3" enum="ViewportSDFOversize">
+ 2D signed distance field covers 100% of the viewport's size outside the viewport on each side (top, right, bottom, left). This has the highest GPU requirements.
</constant>
<constant name="VIEWPORT_SDF_OVERSIZE_MAX" value="4" enum="ViewportSDFOversize">
+ Represents the size of the [enum ViewportSDFOversize] enum.
</constant>
<constant name="VIEWPORT_SDF_SCALE_100_PERCENT" value="0" enum="ViewportSDFScale">
+ Full resolution 2D signed distance field scale. This has the highest GPU requirements.
</constant>
<constant name="VIEWPORT_SDF_SCALE_50_PERCENT" value="1" enum="ViewportSDFScale">
+ Half resolution 2D signed distance field scale on each axis (25% of the viewport pixel count).
</constant>
<constant name="VIEWPORT_SDF_SCALE_25_PERCENT" value="2" enum="ViewportSDFScale">
+ Quarter resolution 2D signed distance field scale on each axis (6.25% of the viewport pixel count). This has the lowest GPU requirements.
</constant>
<constant name="VIEWPORT_SDF_SCALE_MAX" value="3" enum="ViewportSDFScale">
+ Represents the size of the [enum ViewportSDFScale] enum.
</constant>
<constant name="VIEWPORT_MSAA_DISABLED" value="0" enum="ViewportMSAA">
Multisample antialiasing for 3D is disabled. This is the default value, and also the fastest setting.
@@ -4244,18 +4551,25 @@
Multisample antialiasing uses 8 samples per pixel for 3D. This has a very high impact on performance. Likely unsupported on low-end and older hardware.
</constant>
<constant name="VIEWPORT_MSAA_MAX" value="4" enum="ViewportMSAA">
+ Represents the size of the [enum ViewportMSAA] enum.
</constant>
<constant name="VIEWPORT_SCREEN_SPACE_AA_DISABLED" value="0" enum="ViewportScreenSpaceAA">
+ Do not perform any antialiasing in the full screen post-process.
</constant>
<constant name="VIEWPORT_SCREEN_SPACE_AA_FXAA" value="1" enum="ViewportScreenSpaceAA">
+ Use fast approximate antialiasing. FXAA is a popular screen-space antialiasing method, which is fast but will make the image look blurry, especially at lower resolutions. It can still work relatively well at large resolutions such as 1440p and 4K.
</constant>
<constant name="VIEWPORT_SCREEN_SPACE_AA_MAX" value="2" enum="ViewportScreenSpaceAA">
+ Represents the size of the [enum ViewportScreenSpaceAA] enum.
</constant>
<constant name="VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW" value="0" enum="ViewportOcclusionCullingBuildQuality">
+ Low occlusion culling BVH build quality (as defined by Embree). Results in the lowest CPU usage, but least effective culling.
</constant>
<constant name="VIEWPORT_OCCLUSION_BUILD_QUALITY_MEDIUM" value="1" enum="ViewportOcclusionCullingBuildQuality">
+ Medium occlusion culling BVH build quality (as defined by Embree).
</constant>
<constant name="VIEWPORT_OCCLUSION_BUILD_QUALITY_HIGH" value="2" enum="ViewportOcclusionCullingBuildQuality">
+ High occlusion culling BVH build quality (as defined by Embree). Results in the highest CPU usage, but most effective culling.
</constant>
<constant name="VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME" value="0" enum="ViewportRenderInfo">
Number of objects drawn in a single frame.
@@ -4270,10 +4584,13 @@
Represents the size of the [enum ViewportRenderInfo] enum.
</constant>
<constant name="VIEWPORT_RENDER_INFO_TYPE_VISIBLE" value="0" enum="ViewportRenderInfoType">
+ Visible render pass (excluding shadows).
</constant>
<constant name="VIEWPORT_RENDER_INFO_TYPE_SHADOW" value="1" enum="ViewportRenderInfoType">
+ Shadow render pass. Objects will be rendered several times depending on the number of amounts of lights with shadows and the number of directional shadow splits.
</constant>
<constant name="VIEWPORT_RENDER_INFO_TYPE_MAX" value="2" enum="ViewportRenderInfoType">
+ Represents the size of the [enum ViewportRenderInfoType] enum.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_DISABLED" value="0" enum="ViewportDebugDraw">
Debug draw is disabled. Default setting.
@@ -4310,6 +4627,7 @@
Draws the shadow atlas that stores shadows from [DirectionalLight3D]s in the upper left quadrant of the [Viewport].
</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.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_SSAO" value="12" enum="ViewportDebugDraw">
Draws the screen space ambient occlusion texture instead of the scene so that you can clearly see how it is affecting objects. In order for this display mode to work, you must have [member Environment.ssao_enabled] set in your [WorldEnvironment].
@@ -4321,49 +4639,62 @@
Colors each PSSM split for the [DirectionalLight3D]s in the scene a different color so you can see where the splits are. In order they will be colored red, green, blue, yellow.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_DECAL_ATLAS" value="15" enum="ViewportDebugDraw">
+ Draws the decal atlas that stores decal textures from [Decal]s.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_SDFGI" value="16" enum="ViewportDebugDraw">
+ Draws SDFGI cascade data. This is the data structure that is used to bounce lighting against and create reflections.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_SDFGI_PROBES" value="17" enum="ViewportDebugDraw">
+ Draws SDFGI probe data. This is the data structure that is used to give indirect lighting dynamic objects moving within the scene.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_GI_BUFFER" value="18" enum="ViewportDebugDraw">
+ Draws the global illumination buffer ([VoxelGI] or SDFGI).
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_DISABLE_LOD" value="19" enum="ViewportDebugDraw">
+ Disable mesh LOD. All meshes are drawn with full detail, which can be used to compare performance.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS" value="20" enum="ViewportDebugDraw">
+ Draws the [OmniLight3D] cluster. Clustering determines where lights are positioned in screen-space, which allows the engine to only process these portions of the screen for lighting.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS" value="21" enum="ViewportDebugDraw">
+ Draws the [SpotLight3D] cluster. Clustering determines where lights are positioned in screen-space, which allows the engine to only process these portions of the screen for lighting.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS" value="22" enum="ViewportDebugDraw">
+ Draws the [Decal] cluster. Clustering determines where decals are positioned in screen-space, which allows the engine to only process these portions of the screen for decals.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES" value="23" enum="ViewportDebugDraw">
+ Draws the [ReflectionProbe] cluster. Clustering determines where reflection probes are positioned in screen-space, which allows the engine to only process these portions of the screen for reflection probes.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_OCCLUDERS" value="24" enum="ViewportDebugDraw">
+ Draws the occlusion culling buffer. This low-resolution occlusion culling buffer is rasterized on the CPU and is used to check whether instances are occluded by other objects.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_MOTION_VECTORS" value="25" enum="ViewportDebugDraw">
+ Draws the motion vectors buffer. This is used by temporal antialiasing to correct for motion that occurs during gameplay.
</constant>
<constant name="VIEWPORT_VRS_DISABLED" value="0" enum="ViewportVRSMode">
- VRS is disabled.
+ Variable rate shading is disabled.
</constant>
<constant name="VIEWPORT_VRS_TEXTURE" value="1" enum="ViewportVRSMode">
- VRS uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view.
+ Variable rate shading uses a texture. Note, for stereoscopic use a texture atlas with a texture for each view.
</constant>
<constant name="VIEWPORT_VRS_XR" value="2" enum="ViewportVRSMode">
- VRS texture is supplied by the primary [XRInterface].
+ Variable rate shading texture is supplied by the primary [XRInterface].
</constant>
<constant name="VIEWPORT_VRS_MAX" value="3" enum="ViewportVRSMode">
Represents the size of the [enum ViewportVRSMode] enum.
</constant>
<constant name="SKY_MODE_AUTOMATIC" value="0" enum="SkyMode">
+ Automatically selects the appropriate process mode based on your sky shader. If your shader uses [code]TIME[/code] or [code]POSITION[/code], this will use [constant SKY_MODE_REALTIME]. If your shader uses any of the [code]LIGHT_*[/code] variables or any custom uniforms, this uses [constant SKY_MODE_INCREMENTAL]. Otherwise, this defaults to [constant SKY_MODE_QUALITY].
</constant>
<constant name="SKY_MODE_QUALITY" value="1" enum="SkyMode">
- Uses high quality importance sampling to process the radiance map. In general, this results in much higher quality than [constant Sky.PROCESS_MODE_REALTIME] but takes much longer to generate. This should not be used if you plan on changing the sky at runtime. If you are finding that the reflection is not blurry enough and is showing sparkles or fireflies, try increasing [member ProjectSettings.rendering/reflections/sky_reflections/ggx_samples].
+ Uses high quality importance sampling to process the radiance map. In general, this results in much higher quality than [constant SKY_MODE_REALTIME] but takes much longer to generate. This should not be used if you plan on changing the sky at runtime. If you are finding that the reflection is not blurry enough and is showing sparkles or fireflies, try increasing [member ProjectSettings.rendering/reflections/sky_reflections/ggx_samples].
</constant>
<constant name="SKY_MODE_INCREMENTAL" value="2" enum="SkyMode">
+ Uses the same high quality importance sampling to process the radiance map as [constant SKY_MODE_QUALITY], but updates over several frames. The number of frames is determined by [member ProjectSettings.rendering/reflections/sky_reflections/roughness_layers]. Use this when you need highest quality radiance maps, but have a sky that updates slowly.
</constant>
<constant name="SKY_MODE_REALTIME" value="3" enum="SkyMode">
- Uses the fast filtering algorithm to process the radiance map. In general this results in lower quality, but substantially faster run times.
- [b]Note:[/b] The fast filtering algorithm is limited to 256x256 cubemaps, so [member Sky.radiance_size] must be set to [constant Sky.RADIANCE_SIZE_256].
+ Uses the fast filtering algorithm to process the radiance map. In general this results in lower quality, but substantially faster run times. If you need better quality, but still need to update the sky every frame, consider turning on [member ProjectSettings.rendering/reflections/sky_reflections/fast_filter_high_quality].
+ [b]Note:[/b] The fast filtering algorithm is limited to 256×256 cubemaps, so [method sky_set_radiance_size] must be set to [code]256[/code]. Otherwise, a warning is printed and the overridden radiance size is ignored.
</constant>
<constant name="ENV_BG_CLEAR_COLOR" value="0" enum="EnvironmentBG">
Use the clear color as background.
@@ -4478,60 +4809,88 @@
Highest quality screen-space indirect lighting. Uses the adaptive target setting which can be dynamically adjusted to smoothly balance performance and visual quality.
</constant>
<constant name="ENV_SDFGI_Y_SCALE_50_PERCENT" value="0" enum="EnvironmentSDFGIYScale">
+ Use 50% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be twice as short as they are wide. This allows providing increased GI detail and reduced light leaking with thin floors and ceilings. This is usually the best choice for scenes that don't feature much verticality.
</constant>
<constant name="ENV_SDFGI_Y_SCALE_75_PERCENT" value="1" enum="EnvironmentSDFGIYScale">
+ Use 75% scale for SDFGI on the Y (vertical) axis. This is a balance between the 50% and 100% SDFGI Y scales.
</constant>
<constant name="ENV_SDFGI_Y_SCALE_100_PERCENT" value="2" enum="EnvironmentSDFGIYScale">
+ Use 100% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be as tall as they are wide. This is usually the best choice for highly vertical scenes. The downside is that light leaking may become more noticeable with thin floors and ceilings.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_4" value="0" enum="EnvironmentSDFGIRayCount">
+ Throw 4 rays per frame when converging SDFGI. This has the lowest GPU requirements, but creates the most noisy result.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_8" value="1" enum="EnvironmentSDFGIRayCount">
+ Throw 8 rays per frame when converging SDFGI.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_16" value="2" enum="EnvironmentSDFGIRayCount">
+ Throw 16 rays per frame when converging SDFGI.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_32" value="3" enum="EnvironmentSDFGIRayCount">
+ Throw 32 rays per frame when converging SDFGI.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_64" value="4" enum="EnvironmentSDFGIRayCount">
+ Throw 64 rays per frame when converging SDFGI.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_96" value="5" enum="EnvironmentSDFGIRayCount">
+ Throw 96 rays per frame when converging SDFGI. This has high GPU requirements.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_128" value="6" enum="EnvironmentSDFGIRayCount">
+ Throw 128 rays per frame when converging SDFGI. This has very high GPU requirements, but creates the least noisy result.
</constant>
<constant name="ENV_SDFGI_RAY_COUNT_MAX" value="7" enum="EnvironmentSDFGIRayCount">
+ Represents the size of the [enum EnvironmentSDFGIRayCount] enum.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_5_FRAMES" value="0" enum="EnvironmentSDFGIFramesToConverge">
+ Converge SDFGI over 5 frames. This is the most responsive, but creates the most noisy result with a given ray count.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_10_FRAMES" value="1" enum="EnvironmentSDFGIFramesToConverge">
+ Configure SDFGI to fully converge over 10 frames.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_15_FRAMES" value="2" enum="EnvironmentSDFGIFramesToConverge">
+ Configure SDFGI to fully converge over 15 frames.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_20_FRAMES" value="3" enum="EnvironmentSDFGIFramesToConverge">
+ Configure SDFGI to fully converge over 20 frames.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_25_FRAMES" value="4" enum="EnvironmentSDFGIFramesToConverge">
+ Configure SDFGI to fully converge over 25 frames.
</constant>
<constant name="ENV_SDFGI_CONVERGE_IN_30_FRAMES" value="5" enum="EnvironmentSDFGIFramesToConverge">
+ Configure SDFGI to fully converge over 30 frames. This is the least responsive, but creates the least noisy result with a given ray count.
</constant>
<constant name="ENV_SDFGI_CONVERGE_MAX" value="6" enum="EnvironmentSDFGIFramesToConverge">
+ Represents the size of the [enum EnvironmentSDFGIFramesToConverge] enum.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_IN_1_FRAME" value="0" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Update indirect light from dynamic lights in SDFGI over 1 frame. This is the most responsive, but has the highest GPU requirements.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_IN_2_FRAMES" value="1" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Update indirect light from dynamic lights in SDFGI over 2 frames.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES" value="2" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Update indirect light from dynamic lights in SDFGI over 4 frames.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_IN_8_FRAMES" value="3" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Update indirect light from dynamic lights in SDFGI over 8 frames.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_IN_16_FRAMES" value="4" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Update indirect light from dynamic lights in SDFGI over 16 frames. This is the least responsive, but has the lowest GPU requirements.
</constant>
<constant name="ENV_SDFGI_UPDATE_LIGHT_MAX" value="5" enum="EnvironmentSDFGIFramesToUpdateLight">
+ Represents the size of the [enum EnvironmentSDFGIFramesToUpdateLight] enum.
</constant>
<constant name="SUB_SURFACE_SCATTERING_QUALITY_DISABLED" value="0" enum="SubSurfaceScatteringQuality">
+ Disables subsurface scattering entirely, even on materials that have [member BaseMaterial3D.subsurf_scatter_enabled] set to [code]true[/code]. This has the lowest GPU requirements.
</constant>
<constant name="SUB_SURFACE_SCATTERING_QUALITY_LOW" value="1" enum="SubSurfaceScatteringQuality">
+ Low subsurface scattering quality.
</constant>
<constant name="SUB_SURFACE_SCATTERING_QUALITY_MEDIUM" value="2" enum="SubSurfaceScatteringQuality">
+ Medium subsurface scattering quality.
</constant>
<constant name="SUB_SURFACE_SCATTERING_QUALITY_HIGH" value="3" enum="SubSurfaceScatteringQuality">
+ High subsurface scattering quality. This has the highest GPU requirements.
</constant>
<constant name="DOF_BOKEH_BOX" value="0" enum="DOFBokehShape">
Calculate the DOF blur using a box filter. The fastest option, but results in obvious lines in blur pattern.
@@ -4567,6 +4926,7 @@
The instance is a particle emitter.
</constant>
<constant name="INSTANCE_PARTICLES_COLLISION" value="4" enum="InstanceType">
+ The instance is a GPUParticles collision shape.
</constant>
<constant name="INSTANCE_LIGHT" value="5" enum="InstanceType">
The instance is a light.
@@ -4584,10 +4944,13 @@
The instance is a lightmap.
</constant>
<constant name="INSTANCE_OCCLUDER" value="10" enum="InstanceType">
+ The instance is an occlusion culling occluder.
</constant>
<constant name="INSTANCE_VISIBLITY_NOTIFIER" value="11" enum="InstanceType">
+ The instance is a visible on-screen notifier.
</constant>
<constant name="INSTANCE_FOG_VOLUME" value="12" enum="InstanceType">
+ The instance is a fog volume.
</constant>
<constant name="INSTANCE_MAX" value="13" enum="InstanceType">
Represents the size of the [enum InstanceType] enum.
@@ -4605,6 +4968,7 @@
When set, manually requests to draw geometry on next frame.
</constant>
<constant name="INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING" value="3" enum="InstanceFlags">
+ Always draw, even if the instance would be culled by occlusion culling. Does not affect view frustum culling.
</constant>
<constant name="INSTANCE_FLAG_MAX" value="4" enum="InstanceFlags">
Represents the size of the [enum InstanceFlags] enum.
@@ -4643,10 +5007,13 @@
Index of [Image] in array of [Image]s returned by [method bake_render_uv2]. Image uses [constant Image.FORMAT_RGBAH] and contains emission color in the [code].rgb[/code] channels and nothing in the [code].a[/code] channel.
</constant>
<constant name="CANVAS_TEXTURE_CHANNEL_DIFFUSE" value="0" enum="CanvasTextureChannel">
+ Diffuse canvas texture ([member CanvasTexture.diffuse_texture]).
</constant>
<constant name="CANVAS_TEXTURE_CHANNEL_NORMAL" value="1" enum="CanvasTextureChannel">
+ Normal map canvas texture ([member CanvasTexture.normal_texture]).
</constant>
<constant name="CANVAS_TEXTURE_CHANNEL_SPECULAR" value="2" enum="CanvasTextureChannel">
+ Specular map canvas texture ([member CanvasTexture.specular_texture]).
</constant>
<constant name="NINE_PATCH_STRETCH" value="0" enum="NinePatchAxisMode">
The nine patch gets stretched where needed.
@@ -4697,16 +5064,21 @@
Max value for [enum CanvasItemTextureRepeat] enum.
</constant>
<constant name="CANVAS_GROUP_MODE_DISABLED" value="0" enum="CanvasGroupMode">
+ Child draws over parent and is not clipped.
</constant>
<constant name="CANVAS_GROUP_MODE_CLIP_ONLY" value="1" enum="CanvasGroupMode">
+ Parent is used for the purposes of clipping only. Child is clipped to the parent's visible area, parent is not drawn.
</constant>
<constant name="CANVAS_GROUP_MODE_CLIP_AND_DRAW" value="2" enum="CanvasGroupMode">
+ Parent is used for clipping child, but parent is also drawn underneath child as normal before clipping child to its visible area.
</constant>
<constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="3" enum="CanvasGroupMode">
</constant>
<constant name="CANVAS_LIGHT_MODE_POINT" value="0" enum="CanvasLightMode">
+ 2D point light (see [PointLight2D]).
</constant>
<constant name="CANVAS_LIGHT_MODE_DIRECTIONAL" value="1" enum="CanvasLightMode">
+ 2D directional (sun/moon) light (see [DirectionalLight2D]).
</constant>
<constant name="CANVAS_LIGHT_BLEND_MODE_ADD" value="0" enum="CanvasLightBlendMode">
Adds light color additive to the canvas.
@@ -4739,74 +5111,109 @@
Culling of the canvas occluder is counterclockwise.
</constant>
<constant name="GLOBAL_VAR_TYPE_BOOL" value="0" enum="GlobalShaderParameterType">
+ Boolean global shader parameter ([code]global uniform bool ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_BVEC2" value="1" enum="GlobalShaderParameterType">
+ 2-dimensional boolean vector global shader parameter ([code]global uniform bvec2 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_BVEC3" value="2" enum="GlobalShaderParameterType">
+ 3-dimensional boolean vector global shader parameter ([code]global uniform bvec3 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_BVEC4" value="3" enum="GlobalShaderParameterType">
+ 4-dimensional boolean vector global shader parameter ([code]global uniform bvec4 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_INT" value="4" enum="GlobalShaderParameterType">
+ Integer global shader parameter ([code]global uniform int ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_IVEC2" value="5" enum="GlobalShaderParameterType">
+ 2-dimensional integer vector global shader parameter ([code]global uniform ivec2 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_IVEC3" value="6" enum="GlobalShaderParameterType">
+ 3-dimensional integer vector global shader parameter ([code]global uniform ivec3 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_IVEC4" value="7" enum="GlobalShaderParameterType">
+ 4-dimensional integer vector global shader parameter ([code]global uniform ivec4 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_RECT2I" value="8" enum="GlobalShaderParameterType">
+ 2-dimensional integer rectangle global shader parameter ([code]global uniform ivec4 ...[/code]). Equivalent to [constant GLOBAL_VAR_TYPE_IVEC4] in shader code, but exposed as a [Rect2i] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_UINT" value="9" enum="GlobalShaderParameterType">
+ Unsigned integer global shader parameter ([code]global uniform uint ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_UVEC2" value="10" enum="GlobalShaderParameterType">
+ 2-dimensional unsigned integer vector global shader parameter ([code]global uniform uvec2 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_UVEC3" value="11" enum="GlobalShaderParameterType">
+ 3-dimensional unsigned integer vector global shader parameter ([code]global uniform uvec3 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_UVEC4" value="12" enum="GlobalShaderParameterType">
+ 4-dimensional unsigned integer vector global shader parameter ([code]global uniform uvec4 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_FLOAT" value="13" enum="GlobalShaderParameterType">
+ Single-precision floating-point global shader parameter ([code]global uniform float ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_VEC2" value="14" enum="GlobalShaderParameterType">
+ 2-dimensional floating-point vector global shader parameter ([code]global uniform vec2 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_VEC3" value="15" enum="GlobalShaderParameterType">
+ 3-dimensional floating-point vector global shader parameter ([code]global uniform vec3 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_VEC4" value="16" enum="GlobalShaderParameterType">
+ 4-dimensional floating-point vector global shader parameter ([code]global uniform vec4 ...[/code]).
</constant>
<constant name="GLOBAL_VAR_TYPE_COLOR" value="17" enum="GlobalShaderParameterType">
+ Color global shader parameter ([code]global uniform vec4 ...[/code]). Equivalent to [constant GLOBAL_VAR_TYPE_VEC4] in shader code, but exposed as a [Color] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_RECT2" value="18" enum="GlobalShaderParameterType">
+ 2-dimensional floating-point rectangle global shader parameter ([code]global uniform vec4 ...[/code]). Equivalent to [constant GLOBAL_VAR_TYPE_VEC4] in shader code, but exposed as a [Rect2] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAT2" value="19" enum="GlobalShaderParameterType">
+ 2×2 matrix global shader parameter ([code]global uniform mat2 ...[/code]). Exposed as a [PackedInt32Array] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAT3" value="20" enum="GlobalShaderParameterType">
+ 3×3 matrix global shader parameter ([code]global uniform mat3 ...[/code]). Exposed as a [Basis] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAT4" value="21" enum="GlobalShaderParameterType">
+ 4×4 matrix global shader parameter ([code]global uniform mat4 ...[/code]). Exposed as a [Projection] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_TRANSFORM_2D" value="22" enum="GlobalShaderParameterType">
+ 2-dimensional transform global shader parameter ([code]global uniform mat2x3 ...[/code]). Exposed as a [Transform2D] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_TRANSFORM" value="23" enum="GlobalShaderParameterType">
+ 3-dimensional transform global shader parameter ([code]global uniform mat3x4 ...[/code]). Exposed as a [Transform3D] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_SAMPLER2D" value="24" enum="GlobalShaderParameterType">
+ 2D sampler global shader parameter ([code]global uniform sampler2D ...[/code]). Exposed as a [Texture2D] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_SAMPLER2DARRAY" value="25" enum="GlobalShaderParameterType">
+ 2D sampler array global shader parameter ([code]global uniform sampler2DArray ...[/code]). Exposed as a [Texture2DArray] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_SAMPLER3D" value="26" enum="GlobalShaderParameterType">
+ 3D sampler global shader parameter ([code]global uniform sampler3D ...[/code]). Exposed as a [Texture3D] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_SAMPLERCUBE" value="27" enum="GlobalShaderParameterType">
+ Cubemap sampler global shader parameter ([code]global uniform samplerCube ...[/code]). Exposed as a [Cubemap] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAX" value="28" enum="GlobalShaderParameterType">
+ Represents the size of the [enum GlobalShaderParameterType] enum.
</constant>
<constant name="RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME" value="0" enum="RenderingInfo">
+ Number of objects rendered in the current 3D scene. This varies depending on camera position and rotation.
</constant>
<constant name="RENDERING_INFO_TOTAL_PRIMITIVES_IN_FRAME" value="1" enum="RenderingInfo">
+ Number of vertices/indices rendered in the current 3D scene. This varies depending on camera position and rotation.
</constant>
<constant name="RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME" value="2" enum="RenderingInfo">
+ Number of draw calls performed to render in the current 3D scene. This varies depending on camera position and rotation.
</constant>
<constant name="RENDERING_INFO_TEXTURE_MEM_USED" value="3" enum="RenderingInfo">
+ Texture memory used (in bytes).
</constant>
<constant name="RENDERING_INFO_BUFFER_MEM_USED" value="4" enum="RenderingInfo">
+ Buffer memory used (in bytes).
</constant>
<constant name="RENDERING_INFO_VIDEO_MEM_USED" value="5" enum="RenderingInfo">
+ Video memory used (in bytes). This is always greater than the sum of [constant RENDERING_INFO_TEXTURE_MEM_USED] and [constant RENDERING_INFO_BUFFER_MEM_USED], since there is miscellaneous data not accounted for by those two metrics.
</constant>
<constant name="FEATURE_SHADERS" value="0" enum="Features">
Hardware supports shaders. This enum is currently unused in Godot 3.x.
diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml
index 97cba0dcac..f804b5a25d 100644
--- a/doc/classes/ResourceLoader.xml
+++ b/doc/classes/ResourceLoader.xml
@@ -71,6 +71,7 @@
The [param cache_mode] property defines whether and how the cache should be used or updated when loading the resource. See [enum CacheMode] for details.
Returns an empty resource if no [ResourceFormatLoader] could handle the file.
GDScript has a simplified [method @GDScript.load] built-in method which can be used in most situations, leaving the use of [ResourceLoader] for more advanced scenarios.
+ [b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code].
</description>
</method>
<method name="load_threaded_get">
diff --git a/doc/classes/RibbonTrailMesh.xml b/doc/classes/RibbonTrailMesh.xml
index db5c8d097c..c049570310 100644
--- a/doc/classes/RibbonTrailMesh.xml
+++ b/doc/classes/RibbonTrailMesh.xml
@@ -1,29 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="RibbonTrailMesh" inherits="PrimitiveMesh" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Represents a straight ribbon-shaped [PrimitiveMesh] with variable width.
</brief_description>
<description>
+ [RibbonTrailMesh] represents a straight ribbon-shaped mesh with variable width. The ribbon is composed of a number of flat or cross-shaped sections, each with the same [member section_length] and number of [member section_segments]. A [member curve] is sampled along the total length of the ribbon, meaning that the curve determines the size of the ribbon along its length.
+ This primitive mesh is usually used for particle trails.
</description>
<tutorials>
+ <link title="3D Particle trails">$DOCS_URL/tutorials/3d/particles/trails.html</link>
+ <link title="Particle systems (3D)">$DOCS_URL/tutorials/3d/particles/index.html</link>
</tutorials>
<members>
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
+ Determines the size of the ribbon along its length. The size of a particular section segment is obtained by multiplying the baseline [member size] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted.
</member>
<member name="section_length" type="float" setter="set_section_length" getter="get_section_length" default="0.2">
+ The length of a section of the ribbon.
</member>
<member name="section_segments" type="int" setter="set_section_segments" getter="get_section_segments" default="3">
+ The number of segments in a section. The [member curve] is sampled on each segment to determine its size. Higher values result in a more detailed ribbon at the cost of performance.
</member>
<member name="sections" type="int" setter="set_sections" getter="get_sections" default="5">
+ The total number of sections on the ribbon.
</member>
<member name="shape" type="int" setter="set_shape" getter="get_shape" enum="RibbonTrailMesh.Shape" default="1">
+ Determines the shape of the ribbon.
</member>
<member name="size" type="float" setter="set_size" getter="get_size" default="1.0">
+ The baseline size of the ribbon. The size of a particular section segment is obtained by multiplying this size by the value of the [member curve] at the given distance.
</member>
</members>
<constants>
<constant name="SHAPE_FLAT" value="0" enum="Shape">
+ Gives the mesh a single flat face.
</constant>
<constant name="SHAPE_CROSS" value="1" enum="Shape">
+ Gives the mesh two perpendicular flat faces, making a cross shape.
</constant>
</constants>
</class>
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 7f840ee49e..a93fdfd296 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -357,7 +357,7 @@
<method name="push_italics">
<return type="void" />
<description>
- Adds a [code][font][/code] tag with a italics font to the tag stack. This is the same as adding a [code][i][/code] tag if not currently in a [code][b][/code] tag.
+ Adds a [code][font][/code] tag with an italics font to the tag stack. This is the same as adding an [code][i][/code] tag if not currently in a [code][b][/code] tag.
</description>
</method>
<method name="push_list">
diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml
index 38334b1348..24372e51e0 100644
--- a/doc/classes/RigidBody2D.xml
+++ b/doc/classes/RigidBody2D.xml
@@ -283,7 +283,7 @@
Kinematic body freeze mode. Similar to [constant FREEZE_MODE_STATIC], but collides with other bodies along its path when moved. Useful for a frozen body that needs to be animated.
</constant>
<constant name="CENTER_OF_MASS_MODE_AUTO" value="0" enum="CenterOfMassMode">
- In this mode, the body's center of mass is calculated automatically based on its shapes.
+ In this mode, the body's center of mass is calculated automatically based on its shapes. This assumes that the shapes' origins are also their center of mass.
</constant>
<constant name="CENTER_OF_MASS_MODE_CUSTOM" value="1" enum="CenterOfMassMode">
In this mode, the body's center of mass is set through [member center_of_mass]. Defaults to the body's origin position.
diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml
index f087f912bc..3bd660d8dd 100644
--- a/doc/classes/RigidBody3D.xml
+++ b/doc/classes/RigidBody3D.xml
@@ -290,7 +290,7 @@
Kinematic body freeze mode. Similar to [constant FREEZE_MODE_STATIC], but collides with other bodies along its path when moved. Useful for a frozen body that needs to be animated.
</constant>
<constant name="CENTER_OF_MASS_MODE_AUTO" value="0" enum="CenterOfMassMode">
- In this mode, the body's center of mass is calculated automatically based on its shapes.
+ In this mode, the body's center of mass is calculated automatically based on its shapes. This assumes that the shapes' origins are also their center of mass.
</constant>
<constant name="CENTER_OF_MASS_MODE_CUSTOM" value="1" enum="CenterOfMassMode">
In this mode, the body's center of mass is set through [member center_of_mass]. Defaults to the body's origin position.
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 2921117b8b..ce793deec1 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -62,9 +62,9 @@
<param index="3" name="ignore_time_scale" type="bool" default="false" />
<description>
Returns a [SceneTreeTimer] which will [signal SceneTreeTimer.timeout] after the given time in seconds elapsed in this [SceneTree].
- If [code]process_always[/code] is set to [code]false[/code], pausing the [SceneTree] will also pause the timer.
- If [code]process_in_physics[/code] is set to [code]true[/code], will update the [SceneTreeTimer] during the physics frame instead of the process frame (fixed framerate processing).
- If [code]ignore_time_scale[/code] is set to [code]true[/code], will ignore [member Engine.time_scale] and update the [SceneTreeTimer] with the actual frame delta.
+ If [param process_always] is set to [code]false[/code], pausing the [SceneTree] will also pause the timer.
+ If [param process_in_physics] is set to [code]true[/code], will update the [SceneTreeTimer] during the physics frame instead of the process frame (fixed framerate processing).
+ If [param ignore_time_scale] is set to [code]true[/code], will ignore [member Engine.time_scale] and update the [SceneTreeTimer] with the actual frame delta.
Commonly used to create a one-shot delay timer as in the following example:
[codeblocks]
[gdscript]
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 3c66478cf5..cfc4ca6b4f 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -52,10 +52,26 @@
Deadzone for touch scrolling. Lower deadzone makes the scrolling more sensitive.
</member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll" default="0">
- The current horizontal scroll value.
+ The current horizontal scroll value.
+ [b]Note:[/b] If you are setting this value in the [method Node._ready] function or earlier, it needs to be wrapped with [method Object.set_deferred], since scroll bar's [member Range.max_value] is not initialized yet.
+ [codeblock]
+ func _ready():
+ set_deferred("scroll_horizontal", 600)
+ [/codeblock]
+ </member>
+ <member name="scroll_horizontal_custom_step" type="float" setter="set_horizontal_custom_step" getter="get_horizontal_custom_step" default="-1.0">
+ Overrides the [member ScrollBar.custom_step] used when clicking the internal scroll bar's horizontal increment and decrement buttons or when using arrow keys when the [ScrollBar] is focused.
</member>
<member name="scroll_vertical" type="int" setter="set_v_scroll" getter="get_v_scroll" default="0">
The current vertical scroll value.
+ [b]Note:[/b] Setting it early needs to be deferred, just like in [member scroll_horizontal].
+ [codeblock]
+ func _ready():
+ set_deferred("scroll_vertical", 600)
+ [/codeblock]
+ </member>
+ <member name="scroll_vertical_custom_step" type="float" setter="set_vertical_custom_step" getter="get_vertical_custom_step" default="-1.0">
+ Overrides the [member ScrollBar.custom_step] used when clicking the internal scroll bar's vertical increment and decrement buttons or when using arrow keys when the [ScrollBar] is focused.
</member>
<member name="vertical_scroll_mode" type="int" setter="set_vertical_scroll_mode" getter="get_vertical_scroll_mode" enum="ScrollContainer.ScrollMode" default="1">
Controls whether vertical scrollbar can be used and when it should be visible. See [enum ScrollMode] for options.
diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml
index c95917b7bb..32eb69fe8c 100644
--- a/doc/classes/Semaphore.xml
+++ b/doc/classes/Semaphore.xml
@@ -5,6 +5,10 @@
</brief_description>
<description>
A synchronization semaphore which can be used to synchronize multiple [Thread]s. Initialized to zero on creation. Be careful to avoid deadlocks. For a binary version, see [Mutex].
+ [b]Warning:[/b]
+ To guarantee that the operating system is able to perform proper cleanup (no crashes, no deadlocks), these conditions must be met:
+ - By the time a [Semaphore]'s reference count reaches zero and therefore it is destroyed, no threads must be waiting on it.
+ - By the time a [Thread]'s reference count reaches zero and therefore it is destroyed, it must not be waiting on any semaphore.
</description>
<tutorials>
<link title="Using multiple threads">$DOCS_URL/tutorials/performance/using_multiple_threads.html</link>
diff --git a/doc/classes/ShaderGlobalsOverride.xml b/doc/classes/ShaderGlobalsOverride.xml
index 0a13e9485a..bfa3b9fbcd 100644
--- a/doc/classes/ShaderGlobalsOverride.xml
+++ b/doc/classes/ShaderGlobalsOverride.xml
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderGlobalsOverride" inherits="Node" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Overrides global shader parameters' values in a specific scene.
</brief_description>
<description>
+ Similar to how a [WorldEnvironment] node can be used to override the environment while a specific scene is loaded, [ShaderGlobalsOverride] can be used to override global shader parameters temporarily. Once the node is removed, the project-wide values for the global shader parameters are restored. See the [RenderingServer] [code]global_shader_parameter_*[/code] methods for more information.
+ [b]Note:[/b] Only one [ShaderGlobalsOverride] can be used per scene. If there is more than one [ShaderGlobalsOverride] node in the scene tree, only the first node (in tree order) will be taken into account.
+ [b]Note:[/b] All [ShaderGlobalsOverride] nodes are made part of a [code]"shader_overrides_group"[/code] group when they are added to the scene tree. The currently active [ShaderGlobalsOverride] node also has a [code]"shader_overrides_group_active"[/code] group added to it. You can use this to check which [ShaderGlobalsOverride] node is currently active.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml
index e986eb7a9c..e4b157c7ca 100644
--- a/doc/classes/Skeleton2D.xml
+++ b/doc/classes/Skeleton2D.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Skeleton2D" inherits="Node2D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Skeleton for 2D characters and animated objects.
+ The parent of a hierarchy of [Bone2D]s, used to create a 2D skeletal animation.
</brief_description>
<description>
- Skeleton2D parents a hierarchy of [Bone2D] objects. It is a requirement of [Bone2D]. Skeleton2D holds a reference to the rest pose of its children and acts as a single point of access to its bones.
- To setup different types of inverse kinematics for the given Skeleton2D, a [SkeletonModificationStack2D] should be created. They can be applied by creating the desired number of modifications, which can be done by increasing [member SkeletonModificationStack2D.modification_count].
+ [Skeleton2D] parents a hierarchy of [Bone2D] nodes. It holds a reference to each [Bone2D]'s rest pose and acts as a single point of access to its bones.
+ To set up different types of inverse kinematics for the given Skeleton2D, a [SkeletonModificationStack2D] should be created. The inverse kinematics be applied by increasing [member SkeletonModificationStack2D.modification_count] and creating the desired number of modifications.
</description>
<tutorials>
<link title="2D skeletons">$DOCS_URL/tutorials/animation/2d_skeletons.html</link>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index d8875dfa0e..8f40c63104 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Skeleton3D" inherits="Node3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Skeleton for characters and animated objects.
+ A node containing a bone hierarchy, used to create a 3D skeletal animation.
</brief_description>
<description>
- Skeleton3D provides a hierarchical interface for managing bones, including pose, rest and animation (see [Animation]). It can also use ragdoll physics.
+ [Skeleton3D] provides an interface for managing a hierarchy of bones, including pose, rest and animation (see [Animation]). It can also use ragdoll physics.
The overall transform of a bone with respect to the skeleton is determined by the following hierarchical order: rest pose, custom pose and pose.
Note that "global pose" below refers to the overall transform of the bone with respect to skeleton, so it not the actual global/world transform of the bone.
To setup different types of inverse kinematics, consider using [SkeletonIK3D], or add a custom IK implementation in [method Node._process] as a child node.
diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml
index 58cf70df6a..6de86c3b10 100644
--- a/doc/classes/SkeletonIK3D.xml
+++ b/doc/classes/SkeletonIK3D.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonIK3D" inherits="Node" is_deprecated="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- SkeletonIK3D is used to place the end bone of a [Skeleton3D] bone chain at a certain point in 3D by rotating all bones in the chain accordingly.
+ A node used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position.
</brief_description>
<description>
- SkeletonIK3D is used to place the end bone of a [Skeleton3D] bone chain at a certain point in 3D by rotating all bones in the chain accordingly. A typical scenario for IK in games is to place a characters feet on the ground or a characters hands on a currently hold object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [code]interpolation[/code] property.
+ SkeletonIK3D is used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. A typical scenario for IK in games is to place a character's feet on the ground or a character's hands on a currently held object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied, this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [member interpolation] property.
[codeblock]
# Apply IK effect automatically on every new frame (not the current)
skeleton_ik_node.start()
@@ -45,7 +45,7 @@
<return type="void" />
<param index="0" name="one_time" type="bool" default="false" />
<description>
- Starts applying IK effects on each frame to the [Skeleton3D] bones but will only take effect starting on the next frame. If [code]one_time[/code] is [code]true[/code], this will take effect immediately but also reset on the next frame.
+ Starts applying IK effects on each frame to the [Skeleton3D] bones but will only take effect starting on the next frame. If [param one_time] is [code]true[/code], this will take effect immediately but also reset on the next frame.
</description>
</method>
<method name="stop">
diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml
index 88062eb4da..f5fa3f8bb9 100644
--- a/doc/classes/SkeletonModification2D.xml
+++ b/doc/classes/SkeletonModification2D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonModification2D" inherits="Resource" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A resource that operates on [Bone2D] nodes in a [Skeleton2D].
+ Base class for resources that operate on [Bone2D]s in a [Skeleton2D].
</brief_description>
<description>
This resource provides an interface that can be expanded so code that operates on [Bone2D] nodes in a [Skeleton2D] can be mixed and matched together to create complex interactions.
@@ -38,7 +38,7 @@
<param index="2" name="max" type="float" />
<param index="3" name="invert" type="bool" />
<description>
- Takes a angle and clamps it so it is within the passed-in [param min] and [param max] range. [param invert] will inversely clamp the angle, clamping it to the range outside of the given bounds.
+ Takes an angle and clamps it so it is within the passed-in [param min] and [param max] range. [param invert] will inversely clamp the angle, clamping it to the range outside of the given bounds.
</description>
</method>
<method name="get_editor_draw_gizmo" qualifiers="const">
diff --git a/doc/classes/SkeletonModification2DTwoBoneIK.xml b/doc/classes/SkeletonModification2DTwoBoneIK.xml
index 9a8b9bd8f5..254a8df046 100644
--- a/doc/classes/SkeletonModification2DTwoBoneIK.xml
+++ b/doc/classes/SkeletonModification2DTwoBoneIK.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonModification2DTwoBoneIK" inherits="SkeletonModification2D" is_experimental="true" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A modification that rotates two bones using the law of cosigns to reach the target.
+ A modification that rotates two bones using the law of cosines to reach the target.
</brief_description>
<description>
- This [SkeletonModification2D] uses an algorithm typically called TwoBoneIK. This algorithm works by leveraging the law of cosigns and the lengths of the bones to figure out what rotation the bones currently have, and what rotation they need to make a complete triangle, where the first bone, the second bone, and the target form the three vertices of the triangle. Because the algorithm works by making a triangle, it can only operate on two bones.
+ This [SkeletonModification2D] uses an algorithm typically called TwoBoneIK. This algorithm works by leveraging the law of cosines and the lengths of the bones to figure out what rotation the bones currently have, and what rotation they need to make a complete triangle, where the first bone, the second bone, and the target form the three vertices of the triangle. Because the algorithm works by making a triangle, it can only operate on two bones.
TwoBoneIK is great for arms, legs, and really any joints that can be represented by just two bones that bend to reach a target. This solver is more lightweight than [SkeletonModification2DFABRIK], but gives similar, natural looking results.
</description>
<tutorials>
diff --git a/doc/classes/SkeletonProfile.xml b/doc/classes/SkeletonProfile.xml
index d6a38e9ee6..1796dabcfd 100644
--- a/doc/classes/SkeletonProfile.xml
+++ b/doc/classes/SkeletonProfile.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonProfile" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Profile of a virtual skeleton used as a target for retargeting.
+ Base class for a profile of a virtual skeleton used as a target for retargeting.
</brief_description>
<description>
This resource is used in [EditorScenePostImport]. Some parameters are referring to bones in [Skeleton3D], [Skin], [Animation], and some other nodes are rewritten based on the parameters of [SkeletonProfile].
diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml
index e9f4acd168..4adf4737a1 100644
--- a/doc/classes/Sky.xml
+++ b/doc/classes/Sky.xml
@@ -57,7 +57,7 @@
</constant>
<constant name="PROCESS_MODE_REALTIME" value="3" enum="ProcessMode">
Uses the fast filtering algorithm to process the radiance map. In general this results in lower quality, but substantially faster run times. If you need better quality, but still need to update the sky every frame, consider turning on [member ProjectSettings.rendering/reflections/sky_reflections/fast_filter_high_quality].
- [b]Note:[/b] The fast filtering algorithm is limited to 256x256 cubemaps, so [member radiance_size] must be set to [constant RADIANCE_SIZE_256].
+ [b]Note:[/b] The fast filtering algorithm is limited to 256×256 cubemaps, so [member radiance_size] must be set to [constant RADIANCE_SIZE_256]. Otherwise, a warning is printed and the overridden radiance size is ignored.
</constant>
</constants>
</class>
diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml
index f6935779d6..bbf51114bd 100644
--- a/doc/classes/SoftBody3D.xml
+++ b/doc/classes/SoftBody3D.xml
@@ -69,7 +69,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [param layer_number] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_layer], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
@@ -77,7 +77,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [param layer_number] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_mask], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_point_pinned">
diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml
index e93b95617c..c293f4ba5f 100644
--- a/doc/classes/StreamPeer.xml
+++ b/doc/classes/StreamPeer.xml
@@ -10,6 +10,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_8">
+ <return type="int" />
+ <description>
+ Gets a signed byte from the stream.
+ </description>
+ </method>
<method name="get_16">
<return type="int" />
<description>
@@ -28,12 +34,6 @@
Gets a signed 64-bit value from the stream.
</description>
</method>
- <method name="get_8">
- <return type="int" />
- <description>
- Gets a signed byte from the stream.
- </description>
- </method>
<method name="get_available_bytes" qualifiers="const">
<return type="int" />
<description>
@@ -73,6 +73,12 @@
Gets an ASCII string with byte-length [param bytes] from the stream. If [param bytes] is negative (default) the length will be read from the stream using the reverse process of [method put_string].
</description>
</method>
+ <method name="get_u8">
+ <return type="int" />
+ <description>
+ Gets an unsigned byte from the stream.
+ </description>
+ </method>
<method name="get_u16">
<return type="int" />
<description>
@@ -91,12 +97,6 @@
Gets an unsigned 64-bit value from the stream.
</description>
</method>
- <method name="get_u8">
- <return type="int" />
- <description>
- Gets an unsigned byte from the stream.
- </description>
- </method>
<method name="get_utf8_string">
<return type="String" />
<param index="0" name="bytes" type="int" default="-1" />
@@ -113,6 +113,13 @@
[b]Warning:[/b] Deserialized objects can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats such as remote code execution.
</description>
</method>
+ <method name="put_8">
+ <return type="void" />
+ <param index="0" name="value" type="int" />
+ <description>
+ Puts a signed byte into the stream.
+ </description>
+ </method>
<method name="put_16">
<return type="void" />
<param index="0" name="value" type="int" />
@@ -134,13 +141,6 @@
Puts a signed 64-bit value into the stream.
</description>
</method>
- <method name="put_8">
- <return type="void" />
- <param index="0" name="value" type="int" />
- <description>
- Puts a signed byte into the stream.
- </description>
- </method>
<method name="put_data">
<return type="int" enum="Error" />
<param index="0" name="data" type="PackedByteArray" />
@@ -185,6 +185,13 @@
[/codeblocks]
</description>
</method>
+ <method name="put_u8">
+ <return type="void" />
+ <param index="0" name="value" type="int" />
+ <description>
+ Puts an unsigned byte into the stream.
+ </description>
+ </method>
<method name="put_u16">
<return type="void" />
<param index="0" name="value" type="int" />
@@ -206,13 +213,6 @@
Puts an unsigned 64-bit value into the stream.
</description>
</method>
- <method name="put_u8">
- <return type="void" />
- <param index="0" name="value" type="int" />
- <description>
- Puts an unsigned byte into the stream.
- </description>
- </method>
<method name="put_utf8_string">
<return type="void" />
<param index="0" name="value" type="String" />
diff --git a/doc/classes/StreamPeerTLS.xml b/doc/classes/StreamPeerTLS.xml
index aa132f5e86..8cf0b646a5 100644
--- a/doc/classes/StreamPeerTLS.xml
+++ b/doc/classes/StreamPeerTLS.xml
@@ -4,7 +4,7 @@
TLS stream peer.
</brief_description>
<description>
- TLS stream peer. This object can be used to connect to an TLS server or accept a single TLS client connection.
+ TLS stream peer. This object can be used to connect to a TLS server or accept a single TLS client connection.
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index fd50b308c3..09ded0f4ce 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -111,7 +111,7 @@
<description>
Performs a case-sensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" and "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order.
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to] and [method naturalnocasecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to], [method naturalcasecmp_to], and [method naturalnocasecmp_to].
</description>
</method>
<method name="chr" qualifiers="static">
@@ -175,6 +175,14 @@
Returns [code]true[/code] if the string ends with the given [param text]. See also [method begins_with].
</description>
</method>
+ <method name="erase" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="position" type="int" />
+ <param index="1" name="chars" type="int" default="1" />
+ <description>
+ Returns a string with [param chars] characters erased starting from [param position]. If [param chars] goes beyond the string's length given the specified [param position], fewer characters will be erased from the returned string. Returns an empty string if either [code]position[/code] or [code]chars[/code] is negative. Returns the original string unmodified if [param chars] is [code]0[/code].
+ </description>
+ </method>
<method name="find" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
@@ -570,6 +578,16 @@
Returns the [url=https://en.wikipedia.org/wiki/MD5]MD5 hash[/url] of the string as another [String].
</description>
</method>
+ <method name="naturalcasecmp_to" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="to" type="String" />
+ <description>
+ Performs a [b]case-sensitive[/b], [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order.
+ When used for sorting, natural order comparison orders sequences of numbers by the combined value of each digit as is often expected, instead of the single digit's value. A sorted sequence of numbered strings will be [code]["1", "2", "3", ...][/code], not [code]["1", "10", "2", "3", ...][/code].
+ With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method naturalnocasecmp_to], [method nocasecmp_to], and [method casecmp_to].
+ </description>
+ </method>
<method name="naturalnocasecmp_to" qualifiers="const">
<return type="int" />
<param index="0" name="to" type="String" />
@@ -577,7 +595,7 @@
Performs a [b]case-insensitive[/b], [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters are converted to uppercase for the comparison.
When used for sorting, natural order comparison orders sequences of numbers by the combined value of each digit as is often expected, instead of the single digit's value. A sorted sequence of numbered strings will be [code]["1", "2", "3", ...][/code], not [code]["1", "10", "2", "3", ...][/code].
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to] and [method casecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method naturalcasecmp_to], [method nocasecmp_to], and [method casecmp_to].
</description>
</method>
<method name="nocasecmp_to" qualifiers="const">
@@ -586,7 +604,7 @@
<description>
Performs a [b]case-insensitive[/b] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters are converted to uppercase for the comparison.
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method casecmp_to] and [method naturalnocasecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method casecmp_to], [method naturalcasecmp_to], and [method naturalnocasecmp_to].
</description>
</method>
<method name="num" qualifiers="static">
@@ -946,22 +964,22 @@
Returns the string converted to uppercase.
</description>
</method>
- <method name="to_utf16_buffer" qualifiers="const">
+ <method name="to_utf8_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-16]UTF-16[/url] encoded [PackedByteArray].
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-8]UTF-8[/url] encoded [PackedByteArray]. This method is slightly slower than [method to_ascii_buffer], but supports all UTF-8 characters. For most cases, prefer using this method.
</description>
</method>
- <method name="to_utf32_buffer" qualifiers="const">
+ <method name="to_utf16_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-32]UTF-32[/url] encoded [PackedByteArray].
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-16]UTF-16[/url] encoded [PackedByteArray].
</description>
</method>
- <method name="to_utf8_buffer" qualifiers="const">
+ <method name="to_utf32_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-8]UTF-8[/url] encoded [PackedByteArray]. This method is slightly slower than [method to_ascii_buffer], but supports all UTF-8 characters. For most cases, prefer using this method.
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-32]UTF-32[/url] encoded [PackedByteArray].
</description>
</method>
<method name="to_wchar_buffer" qualifiers="const">
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index 5b630a092e..7ffe1d8c32 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -105,7 +105,7 @@
<description>
Performs a case-sensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" and "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order.
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to] and [method naturalnocasecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to], [method naturalcasecmp_to], and [method naturalnocasecmp_to].
</description>
</method>
<method name="contains" qualifiers="const">
@@ -158,6 +158,14 @@
Returns [code]true[/code] if the string ends with the given [param text]. See also [method begins_with].
</description>
</method>
+ <method name="erase" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="position" type="int" />
+ <param index="1" name="chars" type="int" default="1" />
+ <description>
+ Returns a string with [param chars] characters erased starting from [param position]. If [param chars] goes beyond the string's length given the specified [param position], fewer characters will be erased from the returned string. Returns an empty string if either [code]position[/code] or [code]chars[/code] is negative. Returns the original string unmodified if [param chars] is [code]0[/code].
+ </description>
+ </method>
<method name="find" qualifiers="const">
<return type="int" />
<param index="0" name="what" type="String" />
@@ -545,6 +553,16 @@
Returns the [url=https://en.wikipedia.org/wiki/MD5]MD5 hash[/url] of the string as another [String].
</description>
</method>
+ <method name="naturalcasecmp_to" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="to" type="String" />
+ <description>
+ Performs a [b]case-sensitive[/b], [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order.
+ When used for sorting, natural order comparison orders sequences of numbers by the combined value of each digit as is often expected, instead of the single digit's value. A sorted sequence of numbered strings will be [code]["1", "2", "3", ...][/code], not [code]["1", "10", "2", "3", ...][/code].
+ With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method naturalnocasecmp_to], [method nocasecmp_to], and [method casecmp_to].
+ </description>
+ </method>
<method name="naturalnocasecmp_to" qualifiers="const">
<return type="int" />
<param index="0" name="to" type="String" />
@@ -552,7 +570,7 @@
Performs a [b]case-insensitive[/b], [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters are converted to uppercase for the comparison.
When used for sorting, natural order comparison orders sequences of numbers by the combined value of each digit as is often expected, instead of the single digit's value. A sorted sequence of numbered strings will be [code]["1", "2", "3", ...][/code], not [code]["1", "10", "2", "3", ...][/code].
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to] and [method casecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method naturalcasecmp_to], [method nocasecmp_to], and [method casecmp_to].
</description>
</method>
<method name="nocasecmp_to" qualifiers="const">
@@ -561,7 +579,7 @@
<description>
Performs a [b]case-insensitive[/b] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "Less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters are converted to uppercase for the comparison.
With different string lengths, returns [code]1[/code] if this string is longer than the [param to] string, or [code]-1[/code] if shorter. Note that the length of empty strings is [i]always[/i] [code]0[/code].
- To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method casecmp_to] and [method naturalnocasecmp_to].
+ To get a [bool] result from a string comparison, use the [code]==[/code] operator instead. See also [method casecmp_to], [method naturalcasecmp_to], and [method naturalnocasecmp_to].
</description>
</method>
<method name="pad_decimals" qualifiers="const">
@@ -853,22 +871,22 @@
Returns the string converted to uppercase.
</description>
</method>
- <method name="to_utf16_buffer" qualifiers="const">
+ <method name="to_utf8_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-16]UTF-16[/url] encoded [PackedByteArray].
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-8]UTF-8[/url] encoded [PackedByteArray]. This method is slightly slower than [method to_ascii_buffer], but supports all UTF-8 characters. For most cases, prefer using this method.
</description>
</method>
- <method name="to_utf32_buffer" qualifiers="const">
+ <method name="to_utf16_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-32]UTF-32[/url] encoded [PackedByteArray].
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-16]UTF-16[/url] encoded [PackedByteArray].
</description>
</method>
- <method name="to_utf8_buffer" qualifiers="const">
+ <method name="to_utf32_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
- Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-8]UTF-8[/url] encoded [PackedByteArray]. This method is slightly slower than [method to_ascii_buffer], but supports all UTF-8 characters. For most cases, prefer using this method.
+ Converts the string to a [url=https://en.wikipedia.org/wiki/UTF-32]UTF-32[/url] encoded [PackedByteArray].
</description>
</method>
<method name="to_wchar_buffer" qualifiers="const">
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index a919bbdc6e..04452431c3 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -45,7 +45,7 @@
<param index="2" name="colors" type="PackedColorArray" default="PackedColorArray()" />
<param index="3" name="uv2s" type="PackedVector2Array" default="PackedVector2Array()" />
<param index="4" name="normals" type="PackedVector3Array" default="PackedVector3Array()" />
- <param index="5" name="tangents" type="Array" default="[]" />
+ <param index="5" name="tangents" type="Plane[]" default="[]" />
<description>
Inserts a triangle fan made of array data into [Mesh] being constructed.
Requires the primitive type be set to [constant Mesh.PRIMITIVE_TRIANGLES].
diff --git a/doc/classes/SyntaxHighlighter.xml b/doc/classes/SyntaxHighlighter.xml
index 79f4944bae..34063adbb0 100644
--- a/doc/classes/SyntaxHighlighter.xml
+++ b/doc/classes/SyntaxHighlighter.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
Base syntax highlighter resource all syntax highlighters extend from, provides syntax highlighting data to [TextEdit].
- The associated [TextEdit] node will call into the [SyntaxHighlighter] on a as needed basis.
+ The associated [TextEdit] node will call into the [SyntaxHighlighter] on an as-needed basis.
[b]Note:[/b] Each Syntax highlighter instance should not be shared across multiple [TextEdit] nodes.
</description>
<tutorials>
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index a1552982ae..bf66d27ac6 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -867,7 +867,7 @@
<description>
Moves the caret to the specified [param line] index.
If [param adjust_viewport] is [code]true[/code], the viewport will center at the caret position after the move occurs.
- If [param can_be_hidden] is [code]true[/code], the specified [code]line[/code] can be hidden.
+ If [param can_be_hidden] is [code]true[/code], the specified [param line] can be hidden.
[b]Note:[/b] If supporting multiple carets this will not check for any overlap. See [method merge_overlapping_carets].
</description>
</method>
@@ -1061,7 +1061,7 @@
<return type="void" />
<param index="0" name="action" type="int" enum="TextEdit.EditAction" />
<description>
- Starts an action, will end the current action if [code]action[/code] is different.
+ Starts an action, will end the current action if [param action] is different.
An action will also end after a call to [method end_action], after [member ProjectSettings.gui/timers/text_edit_idle_detect_sec] is triggered or a new undoable step outside the [method start_action] and [method end_action] calls.
</description>
</method>
diff --git a/doc/classes/TextServerDummy.xml b/doc/classes/TextServerDummy.xml
index 4770a641dc..4e72b3c5f8 100644
--- a/doc/classes/TextServerDummy.xml
+++ b/doc/classes/TextServerDummy.xml
@@ -1,8 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TextServerDummy" inherits="TextServerExtension" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A dummy text server that can't render text or manage fonts.
</brief_description>
<description>
+ A dummy [TextServer] interface that doesn't do anything. Useful for freeing up memory when rendering text is not needed, as text servers are resource-intensive. It can also be used for performance comparisons in complex GUIs to check the impact of text rendering.
+ A dummy text server is always available at the start of a project. Here's how to access it:
+ [codeblock]
+ var dummy_text_server = TextServerManager.find_interface("Dummy")
+ if dummy_text_server != null:
+ TextServerManager.set_primary_interface(dummy_text_server)
+ # If the other text servers are unneeded, they can be removed:
+ for i in TextServerManager.get_interface_count():
+ var text_server = TextServerManager.get_interface(i)
+ if text_server != dummy_text_server:
+ TextServerManager.remove_interface(text_server)
+ [/codeblock]
+ The command line argument [code]--text-driver Dummy[/code] (case-sensitive) can be used to force the "Dummy" [TextServer] on any project.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml
index 2b4ef65f0c..fcb803b15a 100644
--- a/doc/classes/Thread.xml
+++ b/doc/classes/Thread.xml
@@ -6,6 +6,11 @@
<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 guarantee that the operating system is able to perform proper cleanup (no crashes, no deadlocks), these conditions must be met by the time a [Thread]'s reference count reaches zero and therefore it is destroyed:
+ - It must not have any [Mutex] objects locked.
+ - It must not be waiting on any [Semaphore] objects.
+ - [method wait_to_finish] should have been called on it.
</description>
<tutorials>
<link title="Using multiple threads">$DOCS_URL/tutorials/performance/using_multiple_threads.html</link>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index ba9fc9a6f9..d4ba3ebb1f 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -310,7 +310,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.
- [b]Note:[/b] To work correctly, [code]set_cells_terrain_connect[/code] requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
+ [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>
<method name="set_cells_terrain_path">
@@ -323,7 +323,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.
- [b]Note:[/b] To work correctly, [code]set_cells_terrain_path[/code] requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
+ [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>
<method name="set_layer_enabled">
@@ -341,7 +341,7 @@
<param index="1" name="modulate" type="Color" />
<description>
Sets a layer's color. It will be multiplied by tile's color and TileMap's modulate.
- If [code]layer[/code] is negative, the layers are accessed from the last one.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_layer_name">
@@ -350,7 +350,7 @@
<param index="1" name="name" type="String" />
<description>
Sets a layer's name. This is mostly useful in the editor.
- If [code]layer[/code] is negative, the layers are accessed from the last one.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_layer_y_sort_enabled">
@@ -360,7 +360,7 @@
<description>
Enables or disables a layer's Y-sorting. If a layer is Y-sorted, the layer will behave as a CanvasItem node where each of its tile gets Y-sorted.
Y-sorted layers should usually be on different Z-index values than not Y-sorted layers, otherwise, each of those layer will be Y-sorted as whole with the Y-sorted one. This is usually an undesired behavior.
- If [code]layer[/code] is negative, the layers are accessed from the last one.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_layer_y_sort_origin">
@@ -370,7 +370,7 @@
<description>
Sets a layer's Y-sort origin value. This Y-sort origin value is added to each tile's Y-sort origin value.
This allows, for example, to fake a different height level on each layer. This can be useful for top-down view games.
- If [code]layer[/code] is negative, the layers are accessed from the last one.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_layer_z_index">
@@ -379,7 +379,7 @@
<param index="1" name="z_index" type="int" />
<description>
Sets a layers Z-index value. This Z-index is added to each tile's Z-index value.
- If [code]layer[/code] is negative, the layers are accessed from the last one.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_navigation_map">
@@ -408,7 +408,7 @@
</member>
<member name="collision_animatable" type="bool" setter="set_collision_animatable" getter="is_collision_animatable" default="false">
If enabled, the TileMap will see its collisions synced to the physics tick and change its collision type from static to kinematic. This is required to create TileMap-based moving platform.
- [b]Note:[/b] Enabling [code]collision_animatable[/code] may have a small performance impact, only do it if the TileMap is moving and has colliding tiles.
+ [b]Note:[/b] Enabling [member collision_animatable] may have a small performance impact, only do it if the TileMap is moving and has colliding tiles.
</member>
<member name="collision_visibility_mode" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0">
Show or hide the TileMap's collision shapes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show collision debug settings.
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index be6b15fb0d..f80151b722 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -8,7 +8,7 @@
Tiles can either be from a [TileSetAtlasSource], that render tiles out of a texture with support for physics, navigation, etc... or from a [TileSetScenesCollectionSource] which exposes scene-based tiles.
Tiles are referenced by using three IDs: their source ID, their atlas coordinates ID and their alternative tile ID.
A TileSet can be configured so that its tiles expose more or less properties. To do so, the TileSet resources uses property layers, that you can add or remove depending on your needs.
- For example, adding a physics layer allows giving collision shapes to your tiles. Each layer having dedicated properties (physics layer an mask), you may add several TileSet physics layers for each type of collision you need.
+ For example, adding a physics layer allows giving collision shapes to your tiles. Each layer having dedicated properties (physics layer and mask), you may add several TileSet physics layers for each type of collision you need.
See the functions to add new layers for more information.
</description>
<tutorials>
diff --git a/doc/classes/TileSetScenesCollectionSource.xml b/doc/classes/TileSetScenesCollectionSource.xml
index 2bb4146798..e1a3b3e788 100644
--- a/doc/classes/TileSetScenesCollectionSource.xml
+++ b/doc/classes/TileSetScenesCollectionSource.xml
@@ -79,7 +79,7 @@
<param index="0" name="id" type="int" />
<param index="1" name="new_id" type="int" />
<description>
- Changes a scene tile's ID from [param id] to [param new_id]. This will fail if there is already a tile with a ID equal to [param new_id].
+ Changes a scene tile's ID from [param id] to [param new_id]. This will fail if there is already a tile with an ID equal to [param new_id].
</description>
</method>
<method name="set_scene_tile_scene">
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index 1205f1627c..2f76f0d27a 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
Counts down a specified interval and emits a signal on reaching 0. Can be set to repeat or "one-shot" mode.
+ [b]Note:[/b] Timers are affected by [member Engine.time_scale], a higher scale means quicker timeouts, and vice versa.
[b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer].
</description>
<tutorials>
@@ -52,7 +53,7 @@
</member>
<member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time" default="1.0">
The wait time in seconds.
- [b]Note:[/b] Timers can only emit once per rendered frame at most (or once per physics frame if [member process_callback] is [constant TIMER_PROCESS_PHYSICS]). This means very low wait times (lower than 0.05 seconds) will behave in significantly different ways depending on the rendered framerate. For very low wait times, it is recommended to use a process loop in a script instead of using a Timer node.
+ [b]Note:[/b] Timers can only emit once per rendered frame at most (or once per physics frame if [member process_callback] is [constant TIMER_PROCESS_PHYSICS]). This means very low wait times (lower than 0.05 seconds) will behave in significantly different ways depending on the rendered framerate. For very low wait times, it is recommended to use a process loop in a script instead of using a Timer node. Timers are affected by [member Engine.time_scale], a higher scale means quicker timeouts, and vice versa.
</member>
</members>
<signals>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 2a3cba3a44..afe0088a26 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -60,8 +60,11 @@
</method>
<method name="edit_selected">
<return type="bool" />
+ <param index="0" name="force_edit" type="bool" default="false" />
<description>
- Edits the selected tree item as if it was clicked. The item must be set editable with [method TreeItem.set_editable]. Returns [code]true[/code] if the item could be edited. Fails if no item is selected.
+ Edits the selected tree item as if it was clicked.
+ Either the item must be set editable with [method TreeItem.set_editable] or [param force_edit] must be [code]true[/code].
+ Returns [code]true[/code] if the item could be edited. Fails if no item is selected.
</description>
</method>
<method name="ensure_cursor_is_visible">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 49b4622aed..6b6851daf1 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -226,11 +226,19 @@
Returns the next sibling TreeItem in the tree or a null object if there is none.
</description>
</method>
+ <method name="get_next_in_tree">
+ <return type="TreeItem" />
+ <param index="0" name="wrap" type="bool" default="false" />
+ <description>
+ Returns the next TreeItem in the tree (in the context of a depth-first search) or a [code]null[/code] object if there is none.
+ If [param wrap] is enabled, the method will wrap around to the first element in the tree when called on the last element, otherwise it returns [code]null[/code].
+ </description>
+ </method>
<method name="get_next_visible">
<return type="TreeItem" />
<param index="0" name="wrap" type="bool" default="false" />
<description>
- Returns the next visible sibling TreeItem in the tree or a null object if there is none.
+ Returns the next visible TreeItem in the tree (in the context of a depth-first search) or a [code]null[/code] object if there is none.
If [param wrap] is enabled, the method will wrap around to the first visible element in the tree when called on the last visible element, otherwise it returns [code]null[/code].
</description>
</method>
@@ -246,11 +254,19 @@
Returns the previous sibling TreeItem in the tree or a null object if there is none.
</description>
</method>
+ <method name="get_prev_in_tree">
+ <return type="TreeItem" />
+ <param index="0" name="wrap" type="bool" default="false" />
+ <description>
+ Returns the previous TreeItem in the tree (in the context of a depth-first search) or a [code]null[/code] object if there is none.
+ If [param wrap] is enabled, the method will wrap around to the last element in the tree when called on the first visible element, otherwise it returns [code]null[/code].
+ </description>
+ </method>
<method name="get_prev_visible">
<return type="TreeItem" />
<param index="0" name="wrap" type="bool" default="false" />
<description>
- Returns the previous visible sibling TreeItem in the tree or a null object if there is none.
+ Returns the previous visible sibling TreeItem in the tree (in the context of a depth-first search) or a [code]null[/code] object if there is none.
If [param wrap] is enabled, the method will wrap around to the last visible element in the tree when called on the first visible element, otherwise it returns [code]null[/code].
</description>
</method>
@@ -350,6 +366,13 @@
<description>
</description>
</method>
+ <method name="is_edit_multiline" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="column" type="int" />
+ <description>
+ Returns [code]true[/code] if the given [param column] is multiline editable.
+ </description>
+ </method>
<method name="is_editable">
<return type="bool" />
<param index="0" name="column" type="int" />
@@ -456,7 +479,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="checked" type="bool" />
<description>
- If [code]true[/code], the given [param column] is checked. Clears column's indeterminate status.
+ If [param checked] is [code]true[/code], the given [param column] is checked. Clears column's indeterminate status.
</description>
</method>
<method name="set_collapsed_recursive">
@@ -516,12 +539,21 @@
Sets custom font size used to draw text in the given [param column].
</description>
</method>
+ <method name="set_edit_multiline">
+ <return type="void" />
+ <param index="0" name="column" type="int" />
+ <param index="1" name="multiline" type="bool" />
+ <description>
+ If [param multiline] is [code]true[/code], the given [param column] is multiline editable.
+ [b]Note:[/b] This option only affects the type of control ([LineEdit] or [TextEdit]) that appears when editing the column. You can set multiline values with [method set_text] even if the column is not multiline editable.
+ </description>
+ </method>
<method name="set_editable">
<return type="void" />
<param index="0" name="column" type="int" />
<param index="1" name="enabled" type="bool" />
<description>
- If [code]true[/code], the given [param column] is editable.
+ If [param enabled] is [code]true[/code], the given [param column] is editable.
</description>
</method>
<method name="set_expand_right">
@@ -529,7 +561,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="enable" type="bool" />
<description>
- If [code]true[/code], the given [param column] is expanded to the right.
+ If [param enable] is [code]true[/code], the given [param column] is expanded to the right.
</description>
</method>
<method name="set_icon">
@@ -569,7 +601,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="indeterminate" type="bool" />
<description>
- If [code]true[/code], the given [param column] is marked [param indeterminate].
+ If [param indeterminate] is [code]true[/code], the given [param column] is marked indeterminate.
[b]Note:[/b] If set [code]true[/code] from [code]false[/code], then column is cleared of checked status.
</description>
</method>
@@ -614,7 +646,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="selectable" type="bool" />
<description>
- If [code]true[/code], the given column is selectable.
+ If [param selectable] is [code]true[/code], the given [param column] is selectable.
</description>
</method>
<method name="set_structured_text_bidi_override">
diff --git a/doc/classes/TubeTrailMesh.xml b/doc/classes/TubeTrailMesh.xml
index aa238af782..e6d446bd60 100644
--- a/doc/classes/TubeTrailMesh.xml
+++ b/doc/classes/TubeTrailMesh.xml
@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TubeTrailMesh" inherits="PrimitiveMesh" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Represents a straight tube-shaped [PrimitiveMesh] with variable width.
</brief_description>
<description>
+ [TubeTrailMesh] represents a straight tube-shaped mesh with variable width. The tube is composed of a number of cylindrical sections, each with the same [member section_length] and number of [member section_rings]. A [member curve] is sampled along the total length of the tube, meaning that the curve determines the radius of the tube along its length.
+ This primitive mesh is usually used for particle trails.
</description>
<tutorials>
+ <link title="3D Particle trails">$DOCS_URL/tutorials/3d/particles/trails.html</link>
+ <link title="Particle systems (3D)">$DOCS_URL/tutorials/3d/particles/index.html</link>
</tutorials>
<members>
<member name="cap_bottom" type="bool" setter="set_cap_bottom" getter="is_cap_bottom" default="true">
@@ -14,16 +19,22 @@
If [code]true[/code], generates a cap at the top of the tube. This can be set to [code]false[/code] to speed up generation and rendering when the cap is never seen by the camera.
</member>
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
+ Determines the radius of the tube along its length. The radius of a particular section ring is obtained by multiplying the baseline [member radius] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted.
</member>
<member name="radial_steps" type="int" setter="set_radial_steps" getter="get_radial_steps" default="8">
+ The number of sides on the tube. For example, a value of [code]5[/code] means the tube will be pentagonal. Higher values result in a more detailed tube at the cost of performance.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
+ The baseline radius of the tube. The radius of a particular section ring is obtained by multiplying this radius by the value of the [member curve] at the given distance.
</member>
<member name="section_length" type="float" setter="set_section_length" getter="get_section_length" default="0.2">
+ The length of a section of the tube.
</member>
<member name="section_rings" type="int" setter="set_section_rings" getter="get_section_rings" default="3">
+ The number of rings in a section. The [member curve] is sampled on each ring to determine its radius. Higher values result in a more detailed tube at the cost of performance.
</member>
<member name="sections" type="int" setter="set_sections" getter="get_sections" default="5">
+ The total number of sections on the tube.
</member>
</members>
</class>
diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml
index 4a203d2443..b1906ccef2 100644
--- a/doc/classes/VSlider.xml
+++ b/doc/classes/VSlider.xml
@@ -14,6 +14,9 @@
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="1" />
</members>
<theme_items>
+ <theme_item name="center_grabber" data_type="constant" type="int" default="0">
+ Boolean constant. If [code]1[/code], the grabber texture size will be ignored and it will fit within slider's bounds based only on its center position.
+ </theme_item>
<theme_item name="grabber_offset" data_type="constant" type="int" default="0">
Horizontal offset of the grabber.
</theme_item>
diff --git a/doc/classes/VideoStreamPlayback.xml b/doc/classes/VideoStreamPlayback.xml
index a81d011e9d..be3b0d4950 100644
--- a/doc/classes/VideoStreamPlayback.xml
+++ b/doc/classes/VideoStreamPlayback.xml
@@ -61,21 +61,21 @@
<return type="void" />
<param index="0" name="time" type="float" />
<description>
- Seeks to [code]time[/code] seconds. Called in response to the [member VideoStreamPlayer.stream_position] setter.
+ Seeks to [param time] seconds. Called in response to the [member VideoStreamPlayer.stream_position] setter.
</description>
</method>
<method name="_set_audio_track" qualifiers="virtual">
<return type="void" />
<param index="0" name="idx" type="int" />
<description>
- Select the audio track [code]idx[/code]. Called when playback starts, and in response to the [member VideoStreamPlayer.audio_track] setter.
+ Select the audio track [param idx]. Called when playback starts, and in response to the [member VideoStreamPlayer.audio_track] setter.
</description>
</method>
<method name="_set_paused" qualifiers="virtual">
<return type="void" />
<param index="0" name="paused" type="bool" />
<description>
- Set the paused status of video playback. [method _is_paused] must return [code]paused[/code]. Called in response to the [member VideoStreamPlayer.paused] setter.
+ Set the paused status of video playback. [method _is_paused] must return [param paused]. Called in response to the [member VideoStreamPlayer.paused] setter.
</description>
</method>
<method name="_stop" qualifiers="virtual">
@@ -88,7 +88,7 @@
<return type="void" />
<param index="0" name="delta" type="float" />
<description>
- Ticks video playback for [code]delta[/code] seconds. Called every frame as long as [method _is_paused] and [method _is_playing] return true.
+ Ticks video playback for [param delta] seconds. Called every frame as long as [method _is_paused] and [method _is_playing] return true.
</description>
</method>
<method name="mix_audio">
@@ -97,7 +97,7 @@
<param index="1" name="buffer" type="PackedFloat32Array" default="PackedFloat32Array()" />
<param index="2" name="offset" type="int" default="0" />
<description>
- Render [code]num_frames[/code] audio frames (of [method _get_channels] floats each) from [code]buffer[/code], starting from index [code]offset[/code] in the array. Returns the number of audio frames rendered, or -1 on error.
+ Render [param num_frames] audio frames (of [method _get_channels] floats each) from [param buffer], starting from index [param offset] in the array. Returns the number of audio frames rendered, or -1 on error.
</description>
</method>
</methods>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 7c131202c8..7ff96bb8fd 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -159,7 +159,11 @@
Calling this method will propagate calls to child nodes for following methods in the given order:
- [method Node._input]
- [method Control._gui_input] for [Control] nodes
+ - [method Node._shortcut_input]
+ - [method Node._unhandled_input]
+ - [method Node._unhandled_key_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>
</method>
<method name="push_text_input">
@@ -197,6 +201,7 @@
<return type="void" />
<description>
Stops the input from propagating further down the [SceneTree].
+ [b]Note:[/b] This does not affect the methods in [Input], only the way events are propagated.
</description>
</method>
<method name="set_positional_shadow_atlas_quadrant_subdiv">
@@ -284,6 +289,7 @@
[b]Note:[/b] This setting is disabled by default because of its potential expensive computational cost.
</member>
<member name="positional_shadow_atlas_16_bits" type="bool" setter="set_positional_shadow_atlas_16_bits" getter="get_positional_shadow_atlas_16_bits" default="true">
+ Use 16 bits for the omni/spot shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices.
</member>
<member name="positional_shadow_atlas_quad_0" type="int" setter="set_positional_shadow_atlas_quadrant_subdiv" getter="get_positional_shadow_atlas_quadrant_subdiv" enum="Viewport.PositionalShadowAtlasQuadrantSubdiv" default="2">
The subdivision amount of the first quadrant on the shadow atlas.
@@ -298,8 +304,8 @@
The subdivision amount of the fourth quadrant on the shadow atlas.
</member>
<member name="positional_shadow_atlas_size" type="int" setter="set_positional_shadow_atlas_size" getter="get_positional_shadow_atlas_size" default="2048">
- The shadow atlas' resolution (used for omni and spot lights). The value will be rounded up to the nearest power of 2.
- [b]Note:[/b] If this is set to [code]0[/code], no shadows will be visible at all (including directional shadows).
+ The shadow atlas' resolution (used for omni and spot lights). The value is rounded up to the nearest power of 2.
+ [b]Note:[/b] If this is set to [code]0[/code], no positional shadows will be visible at all. This can improve performance significantly on low-end systems by reducing both the CPU and GPU load (as fewer draw calls are needed to draw the scene without shadows).
</member>
<member name="scaling_3d_mode" type="int" setter="set_scaling_3d_mode" getter="get_scaling_3d_mode" enum="Viewport.Scaling3DMode" default="0">
Sets scaling 3d mode. Bilinear scaling renders at different resolution to either undersample or supersample the viewport. FidelityFX Super Resolution 1.0, abbreviated to FSR, is an upscaling technology that produces high quality images at fast framerates by using a spatially aware upscaling algorithm. FSR is slightly more expensive than bilinear, but it produces significantly higher image quality. FSR should be used where possible.
diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml
index 861de6c1ed..2f62199813 100644
--- a/doc/classes/ViewportTexture.xml
+++ b/doc/classes/ViewportTexture.xml
@@ -6,7 +6,7 @@
<description>
Displays the content of a [Viewport] node as a dynamic [Texture2D]. This can be used to mix controls, 2D, and 3D elements in the same scene.
To create a ViewportTexture in code, use the [method Viewport.get_texture] method on the target viewport.
- [b]Note:[/b] When local to scene, this texture uses [method Resource.setup_local_to_scene] to set the proxy texture and flags in the local viewport.
+ [b]Note:[/b] When local to scene, this texture uses [method Resource.setup_local_to_scene] to set the proxy texture and flags in the local viewport. Local to scene viewport textures will return incorrect data until the scene root is ready (see [signal Node.ready]).
</description>
<tutorials>
<link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link>
@@ -17,6 +17,7 @@
<members>
<member name="viewport_path" type="NodePath" setter="set_viewport_path_in_scene" getter="get_viewport_path_in_scene" default="NodePath(&quot;&quot;)">
The path to the [Viewport] node to display. This is relative to the scene root, not to the node which uses the texture.
+ [b]Note:[/b] In the editor, it is automatically updated when the target viewport's node path changes due to renaming or moving the viewport or its ancestors. At runtime, it may not be able to automatically update due to the inability to determine the scene root.
</member>
</members>
</class>
diff --git a/doc/classes/VisualInstance3D.xml b/doc/classes/VisualInstance3D.xml
index c563c0e014..6e7042431b 100644
--- a/doc/classes/VisualInstance3D.xml
+++ b/doc/classes/VisualInstance3D.xml
@@ -36,7 +36,7 @@
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member layers] is enabled, given a [code]layer_number[/code] between 1 and 20.
+ Returns whether or not the specified layer of the [member layers] is enabled, given a [param layer_number] between 1 and 20.
</description>
</method>
<method name="set_base">
diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml
index d72139d483..a019eac910 100644
--- a/doc/classes/VoxelGI.xml
+++ b/doc/classes/VoxelGI.xml
@@ -20,7 +20,7 @@
<param index="0" name="from_node" type="Node" default="null" />
<param index="1" name="create_visual_debug" type="bool" default="false" />
<description>
- Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_STATIC] and [Light3D]s marked with either [constant Light3D.BAKE_STATIC] or [constant Light3D.BAKE_DYNAMIC]. If [code]create_visual_debug[/code] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring.
+ Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_STATIC] and [Light3D]s marked with either [constant Light3D.BAKE_STATIC] or [constant Light3D.BAKE_DYNAMIC]. If [param create_visual_debug] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring.
[b]Note:[/b] [method bake] works from the editor and in exported projects. This makes it suitable for procedurally generated or user-built levels. Baking a [VoxelGI] node generally takes from 5 to 20 seconds in most scenes. Reducing [member subdiv] can speed up baking.
[b]Note:[/b] [GeometryInstance3D]s and [Light3D]s must be fully ready before [method bake] is called. If you are procedurally creating those and some meshes or lights are missing from your baked [VoxelGI], use [code]call_deferred("bake")[/code] instead of calling [method bake] directly.
</description>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 87fef49aac..336d73290f 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -348,16 +348,14 @@
<return type="void" />
<param index="0" name="rect" type="Rect2i" default="Rect2i(0, 0, 0, 0)" />
<description>
- Shows the [Window] and makes it transient (see [member transient]). If [param rect] is provided, it will be set as the [Window]'s size.
- Fails if called on the main window.
+ Shows the [Window] and makes it transient (see [member transient]). If [param rect] is provided, it will be set as the [Window]'s size. Fails if called on the main window.
</description>
</method>
<method name="popup_centered">
<return type="void" />
<param index="0" name="minsize" type="Vector2i" default="Vector2i(0, 0)" />
<description>
- Popups the [Window] at the center of the current screen, with optionally given minimum size.
- If the [Window] is embedded, it will be centered in the parent [Viewport] instead.
+ Popups the [Window] at the center of the current screen, with optionally given minimum size. If the [Window] is embedded, it will be centered in the parent [Viewport] instead.
[b]Note:[/b] Calling it with the default value of [param minsize] is equivalent to calling it with [member size].
</description>
</method>
@@ -366,8 +364,7 @@
<param index="0" name="minsize" type="Vector2i" default="Vector2i(0, 0)" />
<param index="1" name="fallback_ratio" type="float" default="0.75" />
<description>
- Popups the [Window] centered inside its parent [Window].
- [code]fallback_ratio[/code] determines the maximum size of the [Window], in relation to its parent.
+ Popups the [Window] centered inside its parent [Window]. [param fallback_ratio] determines the maximum size of the [Window], in relation to its parent.
[b]Note:[/b] Calling it with the default value of [param minsize] is equivalent to calling it with [member size].
</description>
</method>
@@ -382,8 +379,7 @@
<return type="void" />
<param index="0" name="parent_rect" type="Rect2i" />
<description>
- Popups the [Window] with a position shifted by parent [Window]'s position.
- If the [Window] is embedded, has the same effect as [method popup].
+ Popups the [Window] with a position shifted by parent [Window]'s position. If the [Window] is embedded, has the same effect as [method popup].
</description>
</method>
<method name="remove_theme_color_override">
@@ -567,6 +563,7 @@
</member>
<member name="position" type="Vector2i" setter="set_position" getter="get_position" default="Vector2i(0, 0)">
The window's position in pixels.
+ If [member ProjectSettings.display/window/subwindows/embed_subwindows] is [code]false[/code], the position is in absolute screen coordinates. This typically applies to editor plugins. If the setting is [code]false[/code], the window's position is in the coordinates of its parent [Viewport].
</member>
<member name="size" type="Vector2i" setter="set_size" getter="get_size" default="Vector2i(100, 100)">
The window's size in pixels.
@@ -714,7 +711,7 @@
Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
</constant>
<constant name="FLAG_RESIZE_DISABLED" value="0" enum="Flags">
- The window can't be resizing by dragging its resize grip. It's still possible to resize the window using [member size]. This flag is ignored for full screen windows. Set with [member unresizable].
+ The window can't be resized by dragging its resize grip. It's still possible to resize the window using [member size]. This flag is ignored for full screen windows. Set with [member unresizable].
</constant>
<constant name="FLAG_BORDERLESS" value="1" enum="Flags">
The window do not have native title bar and other decorations. This flag is ignored for full-screen windows. Set with [member borderless].
diff --git a/doc/classes/WorkerThreadPool.xml b/doc/classes/WorkerThreadPool.xml
index e323cdcb61..ace5f95506 100644
--- a/doc/classes/WorkerThreadPool.xml
+++ b/doc/classes/WorkerThreadPool.xml
@@ -1,10 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WorkerThreadPool" inherits="Object" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A singleton that allocates some [Thread]s on startup, used to offload tasks to these threads.
</brief_description>
<description>
+ The [WorkerThreadPool] singleton allocates a set of [Thread]s (called worker threads) on project startup and provides methods for offloading tasks to them. This can be used for simple multithreading without having to create [Thread]s.
+ Tasks hold the [Callable] to be run by the threads. [WorkerThreadPool] can be used to create regular tasks, which will be taken by one worker thread, or group tasks, which can be distributed between multiple worker threads. Group tasks execute the [Callable] multiple times, which makes them useful for iterating over a lot of elements, such as the enemies in an arena.
+ Here's a sample on how to offload an expensive function to worker threads:
+ [codeblocks]
+ [gdscript]
+ var enemies = [] # An array to be filled with enemies.
+
+ func process_enemy_ai(enemy_index):
+ var processed_enemy = enemies[enemy_index]
+ # Expensive logic...
+
+ func _process(delta):
+ var task_id = WorkerThreadPool.add_group_task(process_enemy_ai, enemies.size())
+ # Other code...
+ WorkerThreadPool.wait_for_group_task_completion(task_id)
+ # Other code that depends on the enemy AI already being processed.
+ [/gdscript]
+ [csharp]
+ private List&lt;Node&gt; _enemies = new List&lt;Node&gt;(); // A list to be filled with enemies.
+
+ private void ProcessEnemyAI(int enemyIndex)
+ {
+ Node processedEnemy = _enemies[enemyIndex];
+ // Expensive logic here.
+ }
+
+ public override void _Process(double delta)
+ {
+ long taskId = WorkerThreadPool.AddGroupTask(Callable.From&lt;int&gt;(ProcessEnemyAI), _enemies.Count);
+ // Other code...
+ WorkerThreadPool.WaitForGroupTaskCompletion(taskId);
+ // Other code that depends on the enemy AI already being processed.
+ }
+ [/csharp]
+ [/codeblocks]
+ The above code relies on the number of elements in the [code]enemies[/code] array remaining constant during the multithreaded part.
+ [b]Note:[/b] Using this singleton could affect performance negatively if the task being distributed between threads is not computationally expensive.
</description>
<tutorials>
+ <link title="Using multiple threads">$DOCS_URL/tutorials/performance/using_multiple_threads.html</link>
+ <link title="Thread-safe APIs">$DOCS_URL/tutorials/performance/thread_safe_apis.html</link>
</tutorials>
<methods>
<method name="add_group_task">
@@ -15,6 +55,9 @@
<param index="3" name="high_priority" type="bool" default="false" />
<param index="4" name="description" type="String" default="&quot;&quot;" />
<description>
+ Adds [param action] as a group task to be executed by the worker threads. The [Callable] will be called a number of times based on [param elements], with the first thread calling it with the value [code]0[/code] as a parameter, and each consecutive execution incrementing this value by 1 until it reaches [code]element - 1[/code].
+ The number of threads the task is distributed to is defined by [param tasks_needed], where the default value [code]-1[/code] means it is distributed to all worker threads. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging.
+ Returns a group task ID that can be used by other methods.
</description>
</method>
<method name="add_task">
@@ -23,36 +66,44 @@
<param index="1" name="high_priority" type="bool" default="false" />
<param index="2" name="description" type="String" default="&quot;&quot;" />
<description>
+ Adds [param action] as a task to be executed by a worker thread. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging.
+ Returns a task ID that can be used by other methods.
</description>
</method>
<method name="get_group_processed_element_count" qualifiers="const">
<return type="int" />
<param index="0" name="group_id" type="int" />
<description>
+ Returns how many times the [Callable] of the group task with the given ID has already been executed by the worker threads.
+ [b]Note:[/b] If a thread has started executing the [Callable] but is yet to finish, it won't be counted.
</description>
</method>
<method name="is_group_task_completed" qualifiers="const">
<return type="bool" />
<param index="0" name="group_id" type="int" />
<description>
+ Returns [code]true[/code] if the group task with the given ID is completed.
</description>
</method>
<method name="is_task_completed" qualifiers="const">
<return type="bool" />
<param index="0" name="task_id" type="int" />
<description>
+ Returns [code]true[/code] if the task with the given ID is completed.
</description>
</method>
<method name="wait_for_group_task_completion">
<return type="void" />
<param index="0" name="group_id" type="int" />
<description>
+ Pauses the thread that calls this method until the group task with the given ID is completed.
</description>
</method>
<method name="wait_for_task_completion">
<return type="void" />
<param index="0" name="task_id" type="int" />
<description>
+ Pauses the thread that calls this method until the task with the given ID is completed.
</description>
</method>
</methods>
diff --git a/doc/classes/X509Certificate.xml b/doc/classes/X509Certificate.xml
index 6123d35d1f..05a9d522f0 100644
--- a/doc/classes/X509Certificate.xml
+++ b/doc/classes/X509Certificate.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
The X509Certificate class represents an X509 certificate. Certificates can be loaded and saved like any other [Resource].
- They can be used as the server certificate in [method StreamPeerTLS.accept_stream] (along with the proper [CryptoKey]), and to specify the only certificate that should be accepted when connecting to an TLS server via [method StreamPeerTLS.connect_to_stream].
+ They can be used as the server certificate in [method StreamPeerTLS.accept_stream] (along with the proper [CryptoKey]), and to specify the only certificate that should be accepted when connecting to a TLS server via [method StreamPeerTLS.connect_to_stream].
</description>
<tutorials>
</tutorials>
@@ -17,6 +17,13 @@
Loads a certificate from [param path] ("*.crt" file).
</description>
</method>
+ <method name="load_from_string">
+ <return type="int" enum="Error" />
+ <param index="0" name="string" type="String" />
+ <description>
+ Loads a certificate from the given [param string].
+ </description>
+ </method>
<method name="save">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
@@ -24,5 +31,11 @@
Saves a certificate to the given [param path] (should be a "*.crt" file).
</description>
</method>
+ <method name="save_to_string">
+ <return type="String" />
+ <description>
+ Returns a string representation of the certificate, or an empty string if the certificate is invalid.
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index 95022399a3..2594fa8cbd 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -1678,6 +1678,26 @@ def format_text_block(
inside_code_tag = cmd
escape_pre = True
+ valid_context = isinstance(context, (MethodDef, SignalDef, AnnotationDef))
+ if valid_context:
+ endcode_pos = text.find("[/code]", endq_pos + 1)
+ if endcode_pos == -1:
+ print_error(
+ f"{state.current_class}.xml: Tag depth mismatch for [code]: no closing [/code] in {context_name}.",
+ state,
+ )
+ break
+
+ inside_code_text = text[endq_pos + 1 : endcode_pos]
+ context_params: List[ParameterDef] = context.parameters # type: ignore
+ for param_def in context_params:
+ if param_def.name == inside_code_text:
+ print_warning(
+ f'{state.current_class}.xml: Potential error inside of a code tag, found a string "{inside_code_text}" that matches one of the parameters in {context_name}.',
+ state,
+ )
+ break
+
# Cross-references to items in this or other class documentation pages.
elif is_in_tagset(cmd, RESERVED_CROSSLINK_TAGS):
link_type: str = ""
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 6095fef035..966137920a 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -44,7 +44,8 @@ extern int initialize_pulse(int verbose);
#endif
Error AudioDriverALSA::init_output_device() {
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
+
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
@@ -338,7 +339,9 @@ void AudioDriverALSA::finish_output_device() {
void AudioDriverALSA::finish() {
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
finish_output_device();
}
diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp
index 81472fe70c..6b35987f70 100644
--- a/drivers/alsamidi/midi_driver_alsamidi.cpp
+++ b/drivers/alsamidi/midi_driver_alsamidi.cpp
@@ -207,7 +207,9 @@ Error MIDIDriverALSAMidi::open() {
void MIDIDriverALSAMidi::close() {
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
for (int i = 0; i < connected_inputs.size(); i++) {
snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr;
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 2c959bb07b..4011727433 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -116,7 +116,7 @@ Error AudioDriverCoreAudio::init() {
break;
}
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
@@ -405,7 +405,7 @@ Error AudioDriverCoreAudio::init_input_device() {
break;
}
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub
index 2204c486c6..91240ce3e3 100644
--- a/drivers/gl_context/SCsub
+++ b/drivers/gl_context/SCsub
@@ -10,7 +10,11 @@ if env["platform"] in ["haiku", "macos", "windows", "linuxbsd"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env.Prepend(CPPPATH=[thirdparty_dir])
+ # Treat glad headers as system headers to avoid raising warnings. Not supported on MSVC.
+ if not env.msvc:
+ env.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
+ else:
+ env.Prepend(CPPPATH=[thirdparty_dir])
env.Append(CPPDEFINES=["GLAD_ENABLED"])
env.Append(CPPDEFINES=["GLES_OVER_GL"])
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 7cbce428cb..6748e93383 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -34,6 +34,7 @@
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
@@ -257,6 +258,36 @@ RasterizerGLES3::RasterizerGLES3() {
#endif // GLES_OVER_GL
#endif // CAN_DEBUG
+ {
+ String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
+ if (shader_cache_dir.is_empty()) {
+ shader_cache_dir = "user://";
+ }
+ Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
+ if (da.is_null()) {
+ ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
+ } else {
+ Error err = da->change_dir("shader_cache");
+ if (err != OK) {
+ err = da->make_dir("shader_cache");
+ }
+ if (err != OK) {
+ ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
+ } else {
+ shader_cache_dir = shader_cache_dir.path_join("shader_cache");
+
+ bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled");
+ if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) {
+ shader_cache_dir = String(); //disable only if not editor
+ }
+
+ if (!shader_cache_dir.is_empty()) {
+ ShaderGLES3::set_shader_cache_dir(shader_cache_dir);
+ }
+ }
+ }
+ }
+
// OpenGL needs to be initialized before initializing the Rasterizers
config = memnew(GLES3::Config);
utilities = memnew(GLES3::Utilities);
@@ -300,12 +331,13 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
}
GLuint read_fbo = 0;
+ glGenFramebuffers(1, &read_fbo);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
+
if (rt->view_count > 1) {
- glGenFramebuffers(1, &read_fbo);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer);
} else {
- glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 10f42bf22b..ded5793e8f 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -242,6 +242,41 @@ static void _display_error_with_code(const String &p_error, const String &p_code
ERR_PRINT(p_error);
}
+void ShaderGLES3::_get_uniform_locations(Version::Specialization &spec, Version *p_version) {
+ glUseProgram(spec.id);
+
+ spec.uniform_location.resize(uniform_count);
+ for (int i = 0; i < uniform_count; i++) {
+ spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]);
+ }
+
+ for (int i = 0; i < texunit_pair_count; i++) {
+ GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name);
+ if (loc >= 0) {
+ if (texunit_pairs[i].index < 0) {
+ glUniform1i(loc, max_image_units + texunit_pairs[i].index);
+ } else {
+ glUniform1i(loc, texunit_pairs[i].index);
+ }
+ }
+ }
+
+ for (int i = 0; i < ubo_count; i++) {
+ GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name);
+ if (loc >= 0) {
+ glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index);
+ }
+ }
+ // textures
+ for (int i = 0; i < p_version->texture_uniforms.size(); i++) {
+ String native_uniform_name = _mkid(p_version->texture_uniforms[i]);
+ GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data());
+ glUniform1i(location, i + base_texture_index);
+ }
+
+ glUseProgram(0);
+}
+
void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) {
spec.id = glCreateProgram();
spec.ok = false;
@@ -402,40 +437,8 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
ERR_FAIL();
}
- // get uniform locations
-
- glUseProgram(spec.id);
-
- spec.uniform_location.resize(uniform_count);
- for (int i = 0; i < uniform_count; i++) {
- spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]);
- }
-
- for (int i = 0; i < texunit_pair_count; i++) {
- GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name);
- if (loc >= 0) {
- if (texunit_pairs[i].index < 0) {
- glUniform1i(loc, max_image_units + texunit_pairs[i].index);
- } else {
- glUniform1i(loc, texunit_pairs[i].index);
- }
- }
- }
-
- for (int i = 0; i < ubo_count; i++) {
- GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name);
- if (loc >= 0) {
- glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index);
- }
- }
- // textures
- for (int i = 0; i < p_version->texture_uniforms.size(); i++) {
- String native_uniform_name = _mkid(p_version->texture_uniforms[i]);
- GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data());
- glUniform1i(location, i + base_texture_index);
- }
+ _get_uniform_locations(spec, p_version);
- glUseProgram(0);
spec.ok = true;
}
@@ -504,11 +507,20 @@ String ShaderGLES3::_version_get_sha1(Version *p_version) const {
return hash_build.as_string().sha1_text();
}
-//static const char *shader_file_header = "GLSC";
-//static const uint32_t cache_file_version = 2;
+#ifndef WEB_ENABLED // not supported in webgl
+static const char *shader_file_header = "GLSC";
+static const uint32_t cache_file_version = 3;
+#endif
bool ShaderGLES3::_load_from_cache(Version *p_version) {
-#if 0
+#ifdef WEB_ENABLED // not supported in webgl
+ return false;
+#else
+#ifdef GLES_OVER_GL
+ if (glProgramBinary == NULL) { // ARB_get_program_binary extension not available
+ return false;
+ }
+#endif
String sha1 = _version_get_sha1(p_version);
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
@@ -517,7 +529,7 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) {
return false;
}
- char header[5] = { 0, 0, 0, 0, 0 };
+ char header[5] = {};
f->get_buffer((uint8_t *)header, 4);
ERR_FAIL_COND_V(header != String(shader_file_header), false);
@@ -526,70 +538,98 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) {
return false; // wrong version
}
- uint32_t variant_count = f->get_32();
+ int cache_variant_count = static_cast<int>(f->get_32());
+ ERR_FAIL_COND_V_MSG(cache_variant_count != this->variant_count, false, "shader cache variant count mismatch, expected " + itos(this->variant_count) + " got " + itos(cache_variant_count)); //should not happen but check
+
+ LocalVector<OAHashMap<uint64_t, Version::Specialization>> variants;
+ for (int i = 0; i < cache_variant_count; i++) {
+ uint32_t cache_specialization_count = f->get_32();
+ OAHashMap<uint64_t, Version::Specialization> variant;
+ for (uint32_t j = 0; j < cache_specialization_count; j++) {
+ uint64_t specialization_key = f->get_64();
+ uint32_t variant_size = f->get_32();
+ if (variant_size == 0) {
+ continue;
+ }
+ uint32_t variant_format = f->get_32();
+ Vector<uint8_t> variant_bytes;
+ variant_bytes.resize(variant_size);
+
+ uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
- ERR_FAIL_COND_V(variant_count != (uint32_t)variant_count, false); //should not happen but check
+ ERR_FAIL_COND_V(br != variant_size, false);
- for (uint32_t i = 0; i < variant_count; i++) {
- uint32_t variant_size = f->get_32();
- ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false);
- if (!variants_enabled[i]) {
- continue;
- }
- Vector<uint8_t> variant_bytes;
- variant_bytes.resize(variant_size);
+ Version::Specialization specialization;
- uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
+ specialization.id = glCreateProgram();
+ glProgramBinary(specialization.id, variant_format, variant_bytes.ptr(), variant_bytes.size());
- ERR_FAIL_COND_V(br != variant_size, false);
+ _get_uniform_locations(specialization, p_version);
- p_version->variant_data[i] = variant_bytes;
- }
+ specialization.ok = true;
- for (uint32_t i = 0; i < variant_count; i++) {
- if (!variants_enabled[i]) {
- MutexLock lock(variant_set_mutex);
- p_version->variants[i] = RID();
- continue;
- }
- RID shader = GLES3::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]);
- if (shader.is_null()) {
- for (uint32_t j = 0; j < i; j++) {
- GLES3::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;
+ variant.insert(specialization_key, specialization);
}
+ variants.push_back(variant);
}
+ p_version->variants = variants;
- memdelete_arr(p_version->variant_data); //clear stages
- p_version->variant_data = nullptr;
- p_version->valid = true;
return true;
-#endif
- return false;
+#endif // WEB_ENABLED
}
void ShaderGLES3::_save_to_cache(Version *p_version) {
-#if 0
+#ifdef WEB_ENABLED // not supported in webgl
+ return;
+#else
+#ifdef GLES_OVER_GL
+ if (glGetProgramBinary == NULL) { // ARB_get_program_binary extension not available
+ return;
+ }
+#endif
String sha1 = _version_get_sha1(p_version);
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
- Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE);
+ Error error;
+ Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &error);
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_count;
- f->store_32(variant_count); //variant count
+ f->store_32(cache_file_version);
+ f->store_32(variant_count);
+
+ for (int i = 0; i < variant_count; i++) {
+ int cache_specialization_count = p_version->variants[i].get_num_elements();
+ f->store_32(cache_specialization_count);
+
+ for (OAHashMap<uint64_t, ShaderGLES3::Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) {
+ const uint64_t specialization_key = *it.key;
+ f->store_64(specialization_key);
- 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());
+ const Version::Specialization *specialization = it.value;
+ if (specialization == nullptr) {
+ f->store_32(0);
+ continue;
+ }
+ GLint program_size = 0;
+ glGetProgramiv(specialization->id, GL_PROGRAM_BINARY_LENGTH, &program_size);
+ if (program_size == 0) {
+ f->store_32(0);
+ continue;
+ }
+ PackedByteArray compiled_program;
+ compiled_program.resize(program_size);
+ GLenum binary_format = 0;
+ glGetProgramBinary(specialization->id, program_size, nullptr, &binary_format, compiled_program.ptrw());
+ if (program_size != compiled_program.size()) {
+ f->store_32(0);
+ continue;
+ }
+ f->store_32(program_size);
+ f->store_32(binary_format);
+ f->store_buffer(compiled_program.ptr(), compiled_program.size());
+ }
}
-#endif
+#endif // WEB_ENABLED
}
void ShaderGLES3::_clear_version(Version *p_version) {
@@ -613,6 +653,9 @@ 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)) {
+ return;
+ }
p_version->variants.reserve(variant_count);
for (int i = 0; i < variant_count; i++) {
OAHashMap<uint64_t, Version::Specialization> variant;
@@ -621,6 +664,7 @@ 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);
}
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 Vector<StringName> &p_texture_uniforms, bool p_initialize) {
diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h
index 0b2ecbaca6..565434bb36 100644
--- a/drivers/gles3/shader_gles3.h
+++ b/drivers/gles3/shader_gles3.h
@@ -98,7 +98,6 @@ private:
GLuint frag_id;
LocalVector<GLint> uniform_location;
LocalVector<GLint> texture_uniform_locations;
- HashMap<StringName, GLint> custom_uniform_locations;
bool build_queued = false;
bool ok = false;
Specialization() {
@@ -113,6 +112,7 @@ private:
Mutex variant_set_mutex;
+ void _get_uniform_locations(Version::Specialization &spec, Version *p_version);
void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization);
void _clear_version(Version *p_version);
@@ -209,6 +209,7 @@ protected:
_compile_specialization(s, p_variant, version, p_specialization);
version->variants[p_variant].insert(p_specialization, s);
spec = version->variants[p_variant].lookup_ptr(p_specialization);
+ _save_to_cache(version);
}
} else if (spec->build_queued) {
// Still queued, wait
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index ac5d0faccf..37976bb9a0 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -771,7 +771,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
alpha = min(alpha, clamp(1.0 - attenuation, 0.0, 1.0));
#endif
-#endif // LIGHT_CODE_USED
+#endif // USE_LIGHT_SHADER_CODE
}
float get_omni_spot_attenuation(float distance, float inv_range, float decay) {
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 026f7467a8..8da5e657cd 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -76,6 +76,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0;
light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05;
+ light.param[RS::LIGHT_PARAM_INTENSITY] = p_type == RS::LIGHT_DIRECTIONAL ? 100000.0 : 1000.0;
light_owner.initialize_rid(p_light, light);
}
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 60a8a41abe..c7b2a715be 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1651,7 +1651,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
- actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz";
+ actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * model_matrix)[3].xyz";
actions.renames["VIEW_INDEX"] = "ViewIndex";
actions.renames["VIEW_MONO_LEFT"] = "uint(0)";
@@ -1660,6 +1660,7 @@ MaterialStorage::MaterialStorage() {
//for light
actions.renames["VIEW"] = "view";
+ actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
actions.renames["LIGHT_COLOR"] = "light_color";
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
actions.renames["LIGHT"] = "light";
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index d147cee54f..3fd54e9180 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1140,6 +1140,13 @@ RID TextureStorage::texture_get_rd_texture(RID p_texture, bool p_srgb) const {
return RID();
}
+uint64_t TextureStorage::texture_get_native_handle(RID p_texture, bool p_srgb) const {
+ const Texture *texture = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!texture, 0);
+
+ return texture->tex_id;
+}
+
void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) {
Texture *texture = texture_owner.get_or_null(p_texture);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 85d2ae0314..c6bdd39dbd 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -537,6 +537,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) 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);
void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0);
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 9ae74a8906..ab94d8911f 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -222,8 +222,8 @@ Error AudioDriverPulseAudio::init_output_device() {
break;
}
- int latency = GLOBAL_GET("audio/driver/output_latency");
- buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
+ int tmp_latency = GLOBAL_GET("audio/driver/output_latency");
+ buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
@@ -305,7 +305,7 @@ Error AudioDriverPulseAudio::init() {
active.clear();
exit_thread.clear();
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
pa_ml = pa_mainloop_new();
ERR_FAIL_COND_V(pa_ml == nullptr, ERR_CANT_OPEN);
@@ -676,7 +676,9 @@ void AudioDriverPulseAudio::finish() {
}
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
finish_output_device();
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 3c2f36c7c0..e9affe41af 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -301,11 +301,11 @@ bool FileAccessUnix::file_exists(const String &p_path) {
uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
String file = fix_path(p_file);
- struct stat flags = {};
- int err = stat(file.utf8().get_data(), &flags);
+ struct stat status = {};
+ int err = stat(file.utf8().get_data(), &status);
if (!err) {
- return flags.st_mtime;
+ return status.st_mtime;
} else {
print_verbose("Failed to get modified time for: " + p_file + "");
return 0;
@@ -314,11 +314,11 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) {
String file = fix_path(p_file);
- struct stat flags = {};
- int err = stat(file.utf8().get_data(), &flags);
+ struct stat status = {};
+ int err = stat(file.utf8().get_data(), &status);
if (!err) {
- return flags.st_mode & 0x7FF; //only permissions
+ return status.st_mode & 0x7FF; //only permissions
} else {
ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + ".");
}
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index d03324527e..69d9baf910 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -2462,10 +2462,10 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co
}
ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE,
- "Texture can't be updated while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Texture can't be updated 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture.");
ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER,
- "Texture requires the TEXTURE_USAGE_CAN_UPDATE_BIT in order to be updatable.");
+ "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable.");
uint32_t layer_count = texture->layers;
if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) {
@@ -2738,9 +2738,9 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t
ERR_FAIL_COND_V(!tex, Vector<uint8_t>());
ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(),
- "Texture can't be retrieved while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Texture can't be retrieved 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to retrieve this texture.");
ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(),
- "Texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved.");
+ "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved.");
uint32_t layer_count = tex->layers;
if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) {
@@ -2881,6 +2881,15 @@ Size2i RenderingDeviceVulkan::texture_size(RID p_texture) {
return Size2i(tex->width, tex->height);
}
+uint64_t RenderingDeviceVulkan::texture_native_handle(RID p_texture) {
+ _THREAD_SAFE_METHOD_
+
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!tex, 0);
+
+ return (uint64_t)tex->image;
+}
+
Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier) {
_THREAD_SAFE_METHOD_
@@ -2888,9 +2897,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
- "Source texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Source 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture.");
ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER,
- "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved.");
+ "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved.");
uint32_t src_layer_count = src_tex->layers;
uint32_t src_width, src_height, src_depth;
@@ -2909,9 +2918,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER,
- "Destination texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Destination 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture.");
ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER,
- "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be retrieved.");
+ "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved.");
uint32_t dst_layer_count = dst_tex->layers;
uint32_t dst_width, dst_height, dst_depth;
@@ -3083,9 +3092,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
- "Source texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Source 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture.");
ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER,
- "Source texture requires the TEXTURE_USAGE_CAN_COPY_FROM_BIT in order to be retrieved.");
+ "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved.");
ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)");
ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled.");
@@ -3094,9 +3103,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID
ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER,
- "Destination texture can't be copied while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Destination 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to copy this texture.");
ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER,
- "Destination texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be retrieved.");
+ "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved.");
ERR_FAIL_COND_V_MSG(dst_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Destination texture must be 2D (or a slice of a 3D/Cube texture).");
ERR_FAIL_COND_V_MSG(dst_tex->samples != TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Destination texture must not be multisampled.");
@@ -3254,13 +3263,13 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color,
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
- "Source texture can't be cleared while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");
+ "Source 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 `RenderingDevice.FINAL_ACTION_CONTINUE`) to clear this texture.");
ERR_FAIL_COND_V(p_layers == 0, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_mipmaps == 0, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER,
- "Source texture requires the TEXTURE_USAGE_CAN_COPY_TO_BIT in order to be cleared.");
+ "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be cleared.");
uint32_t src_layer_count = src_tex->layers;
if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) {
@@ -4305,6 +4314,18 @@ RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) {
return id;
}
+bool RenderingDeviceVulkan::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const {
+ ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false);
+
+ _THREAD_SAFE_METHOD_
+
+ // Validate that this image is supported for the intended filtering.
+ VkFormatProperties properties;
+ vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties);
+
+ return p_sampler_filter == RD::SAMPLER_FILTER_NEAREST || (p_sampler_filter == RD::SAMPLER_FILTER_LINEAR && (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT));
+}
+
/**********************/
/**** VERTEX ARRAY ****/
/**********************/
@@ -4313,10 +4334,6 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
- ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
uint32_t usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
if (p_use_as_storage) {
@@ -4454,10 +4471,6 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo
RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) {
_THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
ERR_FAIL_COND_V(p_index_count == 0, RID());
@@ -5140,10 +5153,6 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
- ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
Buffer buffer;
Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0);
@@ -5163,10 +5172,6 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve
RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) {
_THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
@@ -5189,10 +5194,6 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve
RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) {
_THREAD_SAFE_METHOD_
- ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(),
- "Creating buffers with data is forbidden during creation of a draw list");
uint32_t element_size = get_format_vertex_size(p_format);
ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers");
@@ -8923,6 +8924,11 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
}
}
+ for (int i = 0; i < frame_count; i++) {
+ //Reset all queries in a query pool before doing any operations with them.
+ vkCmdResetQueryPool(frames[0].setup_command_buffer, frames[i].timestamp_pool, 0, max_timestamp_query_elements);
+ }
+
staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb");
staging_buffer_block_size = MAX(4u, staging_buffer_block_size);
staging_buffer_block_size *= 1024; // Kb -> bytes.
@@ -9388,6 +9394,9 @@ bool RenderingDeviceVulkan::has_feature(const Features p_feature) const {
VulkanContext::VRSCapabilities vrs_capabilities = context->get_vrs_capabilities();
return vrs_capabilities.attachment_vrs_supported && context->get_physical_device_features().shaderStorageImageExtendedFormats;
} break;
+ case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: {
+ return true;
+ } break;
default: {
return false;
}
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index 3f01895745..4150e0a8e6 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -1057,6 +1057,7 @@ public:
virtual bool texture_is_shared(RID p_texture);
virtual bool texture_is_valid(RID p_texture);
virtual Size2i texture_size(RID p_texture);
+ virtual uint64_t texture_native_handle(RID p_texture);
virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
@@ -1084,6 +1085,7 @@ public:
/*****************/
virtual RID sampler_create(const SamplerState &p_state);
+ virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const;
/**********************/
/**** VERTEX ARRAY ****/
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 805528b8c7..eeeb6ee689 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -480,6 +480,14 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) {
}
switch (audio_output.channels) {
+ case 1: // Mono
+ case 3: // Surround 2.1
+ case 5: // Surround 5.0
+ case 7: // Surround 7.0
+ // We will downmix as required.
+ channels = audio_output.channels + 1;
+ break;
+
case 2: // Stereo
case 4: // Surround 3.1
case 6: // Surround 5.1
@@ -499,7 +507,7 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) {
input_position = 0;
input_size = 0;
- print_verbose("WASAPI: detected " + itos(channels) + " channels");
+ print_verbose("WASAPI: detected " + itos(audio_output.channels) + " channels");
print_verbose("WASAPI: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
return OK;
@@ -545,7 +553,7 @@ Error AudioDriverWASAPI::finish_input_device() {
}
Error AudioDriverWASAPI::init() {
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
target_latency_ms = GLOBAL_GET("audio/driver/output_latency");
@@ -746,6 +754,19 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
for (unsigned int i = 0; i < write_frames * ad->channels; i++) {
ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i, ad->samples_in.write[write_ofs++]);
}
+ } else if (ad->channels == ad->audio_output.channels + 1) {
+ // Pass all channels except the last two as-is, and then mix the last two
+ // together as one channel. E.g. stereo -> mono, or 3.1 -> 2.1.
+ unsigned int last_chan = ad->audio_output.channels - 1;
+ for (unsigned int i = 0; i < write_frames; i++) {
+ for (unsigned int j = 0; j < last_chan; j++) {
+ ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, ad->samples_in.write[write_ofs++]);
+ }
+ int32_t l = ad->samples_in.write[write_ofs++];
+ int32_t r = ad->samples_in.write[write_ofs++];
+ int32_t c = (int32_t)(((int64_t)l + (int64_t)r) / 2);
+ ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + last_chan, c);
+ }
} else {
for (unsigned int i = 0; i < write_frames; i++) {
for (unsigned int j = 0; j < MIN(ad->channels, ad->audio_output.channels); j++) {
@@ -930,7 +951,9 @@ void AudioDriverWASAPI::unlock() {
void AudioDriverWASAPI::finish() {
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
finish_input_device();
finish_output_device();
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 7b88bd8a95..88eb89656a 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -59,6 +59,14 @@ struct DirAccessWindowsPrivate {
WIN32_FIND_DATAW fu; //unicode version
};
+String DirAccessWindows::fix_path(String p_path) const {
+ String r_path = DirAccess::fix_path(p_path);
+ if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) {
+ r_path = "\\\\?\\" + r_path.replace("/", "\\");
+ }
+ return r_path;
+}
+
// CreateFolderAsync
Error DirAccessWindows::list_dir_begin() {
@@ -158,6 +166,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
p_dir = fix_path(p_dir);
if (p_dir.is_relative_path()) {
p_dir = current_dir.path_join(p_dir);
+ p_dir = fix_path(p_dir);
}
p_dir = p_dir.simplify_path().replace("/", "\\");
@@ -165,12 +174,6 @@ Error DirAccessWindows::make_dir(String p_dir) {
bool success;
int err;
- if (!p_dir.is_network_share_path()) {
- p_dir = "\\\\?\\" + p_dir;
- // Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path.
- // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
- }
-
success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr);
err = GetLastError();
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 9d91c22f7e..1e55917756 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -53,6 +53,9 @@ class DirAccessWindows : public DirAccess {
bool _cisdir = false;
bool _cishidden = false;
+protected:
+ virtual String fix_path(String p_path) const override;
+
public:
virtual Error list_dir_begin() override; ///< This starts dir listing
virtual String get_next() override;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index f36bbcc350..6e69743d4e 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -68,6 +68,14 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
return invalid_files.has(fname);
}
+String FileAccessWindows::fix_path(const String &p_path) const {
+ String r_path = FileAccess::fix_path(p_path);
+ if (r_path.is_absolute_path() && !r_path.is_network_share_path() && r_path.length() > MAX_PATH) {
+ r_path = "\\\\?\\" + r_path.replace("/", "\\");
+ }
+ return r_path;
+}
+
Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
if (is_path_invalid(p_path)) {
#ifdef DEBUG_ENABLED
diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h
index 453f8d3b5f..13c881e562 100644
--- a/drivers/windows/file_access_windows.h
+++ b/drivers/windows/file_access_windows.h
@@ -54,6 +54,7 @@ class FileAccessWindows : public FileAccess {
static HashSet<String> invalid_files;
public:
+ virtual String fix_path(const String &p_path) const override;
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index 44ce01d4d7..22063c52d9 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -39,7 +39,8 @@ Error AudioDriverXAudio2::init() {
pcm_open = false;
samples_in = nullptr;
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
+
// FIXME: speaker_mode seems unused in the Xaudio2 driver so far
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
@@ -150,7 +151,9 @@ void AudioDriverXAudio2::unlock() {
void AudioDriverXAudio2::finish() {
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
if (source_voice) {
source_voice->Stop(0);
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 61df3a977a..b188e1faca 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -902,6 +902,9 @@ void CodeTextEditor::_line_col_changed() {
sb.append(" : ");
sb.append(itos(positional_column + 1).lpad(3));
+ sb.append(" | ");
+ sb.append(text_editor->is_indent_using_spaces() ? "Spaces" : "Tabs");
+
line_and_col_txt->set_text(sb.as_string());
if (find_replace_bar) {
@@ -1142,115 +1145,6 @@ void CodeTextEditor::insert_final_newline() {
}
}
-void CodeTextEditor::convert_indent_to_spaces() {
- int indent_size = EDITOR_GET("text_editor/behavior/indent/size");
- String indent = "";
-
- for (int i = 0; i < indent_size; i++) {
- indent += " ";
- }
-
- Vector<int> cursor_columns;
- cursor_columns.resize(text_editor->get_caret_count());
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- cursor_columns.write[c] = text_editor->get_caret_column(c);
- }
-
- bool changed_indentation = false;
- for (int i = 0; i < text_editor->get_line_count(); i++) {
- String line = text_editor->get_line(i);
-
- if (line.length() <= 0) {
- continue;
- }
-
- int j = 0;
- while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) {
- if (line[j] == '\t') {
- if (!changed_indentation) {
- text_editor->begin_complex_operation();
- changed_indentation = true;
- }
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- if (text_editor->get_caret_line(c) == i && text_editor->get_caret_column(c) > j) {
- cursor_columns.write[c] += indent_size - 1;
- }
- }
- line = line.left(j) + indent + line.substr(j + 1);
- }
- j++;
- }
- if (changed_indentation) {
- text_editor->set_line(i, line);
- }
- }
- if (changed_indentation) {
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- text_editor->set_caret_column(cursor_columns[c], c == 0, c);
- }
- text_editor->merge_overlapping_carets();
- text_editor->end_complex_operation();
- text_editor->queue_redraw();
- }
-}
-
-void CodeTextEditor::convert_indent_to_tabs() {
- int indent_size = EDITOR_GET("text_editor/behavior/indent/size");
- indent_size -= 1;
-
- Vector<int> cursor_columns;
- cursor_columns.resize(text_editor->get_caret_count());
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- cursor_columns.write[c] = text_editor->get_caret_column(c);
- }
-
- bool changed_indentation = false;
- for (int i = 0; i < text_editor->get_line_count(); i++) {
- String line = text_editor->get_line(i);
-
- if (line.length() <= 0) {
- continue;
- }
-
- int j = 0;
- int space_count = -1;
- while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) {
- if (line[j] != '\t') {
- space_count++;
-
- if (space_count == indent_size) {
- if (!changed_indentation) {
- text_editor->begin_complex_operation();
- changed_indentation = true;
- }
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- if (text_editor->get_caret_line(c) == i && text_editor->get_caret_column(c) > j) {
- cursor_columns.write[c] -= indent_size;
- }
- }
- line = line.left(j - indent_size) + "\t" + line.substr(j + 1);
- j = 0;
- space_count = -1;
- }
- } else {
- space_count = -1;
- }
- j++;
- }
- if (changed_indentation) {
- text_editor->set_line(i, line);
- }
- }
- if (changed_indentation) {
- for (int c = 0; c < text_editor->get_caret_count(); c++) {
- text_editor->set_caret_column(cursor_columns[c], c == 0, c);
- }
- text_editor->merge_overlapping_carets();
- text_editor->end_complex_operation();
- text_editor->queue_redraw();
- }
-}
-
void CodeTextEditor::convert_case(CaseStyle p_case) {
if (!text_editor->has_selection()) {
return;
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 343720637b..a83bb96771 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -222,9 +222,6 @@ public:
void trim_trailing_whitespace();
void insert_final_newline();
- void convert_indent_to_spaces();
- void convert_indent_to_tabs();
-
enum CaseStyle {
UPPER,
LOWER,
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
index fc806ded5e..e3686a0217 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
@@ -32,9 +32,8 @@
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
-#include "editor/editor_node.h"
-#include "editor/editor_run_native.h"
#include "editor/export/editor_export_platform.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/plugins/script_editor_plugin.h"
void DebugAdapterParser::_bind_methods() {
@@ -162,7 +161,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
Dictionary DebugAdapterParser::req_disconnect(const Dictionary &p_params) const {
if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->attached) {
- EditorNode::get_singleton()->run_stop();
+ EditorRunBar::get_singleton()->stop_playing();
}
return prepare_success_response(p_params);
@@ -188,7 +187,7 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const {
String platform_string = args.get("platform", "host");
if (platform_string == "host") {
- EditorNode::get_singleton()->run_play();
+ EditorRunBar::get_singleton()->play_main_scene();
} else {
int device = args.get("device", -1);
int idx = -1;
@@ -212,8 +211,8 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const {
return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN_PLATFORM);
}
- EditorNode *editor = EditorNode::get_singleton();
- Error err = platform_string == "android" ? editor->run_play_native(device * 10000 + idx) : editor->run_play_native(idx);
+ EditorRunBar *run_bar = EditorRunBar::get_singleton();
+ Error err = platform_string == "android" ? run_bar->start_native_device(device * 10000 + idx) : run_bar->start_native_device(idx);
if (err) {
if (err == ERR_INVALID_PARAMETER && platform_string == "android") {
return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE);
@@ -257,13 +256,13 @@ Dictionary DebugAdapterParser::req_restart(const Dictionary &p_params) const {
}
Dictionary DebugAdapterParser::req_terminate(const Dictionary &p_params) const {
- EditorNode::get_singleton()->run_stop();
+ EditorRunBar::get_singleton()->stop_playing();
return prepare_success_response(p_params);
}
Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const {
- EditorNode::get_singleton()->get_pause_button()->set_pressed(true);
+ EditorRunBar::get_singleton()->get_pause_button()->set_pressed(true);
EditorDebuggerNode::get_singleton()->_paused();
DebugAdapterProtocol::get_singleton()->notify_stopped_paused();
@@ -272,7 +271,7 @@ Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const {
}
Dictionary DebugAdapterParser::req_continue(const Dictionary &p_params) const {
- EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
+ EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);
EditorDebuggerNode::get_singleton()->_paused();
DebugAdapterProtocol::get_singleton()->notify_continued();
diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
index f85163cd6a..26fb73570e 100644
--- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_run_bar.h"
DebugAdapterProtocol *DebugAdapterProtocol::singleton = nullptr;
@@ -812,7 +813,7 @@ Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array
}
void DebugAdapterProtocol::on_debug_paused() {
- if (EditorNode::get_singleton()->get_pause_button()->is_pressed()) {
+ if (EditorRunBar::get_singleton()->get_pause_button()->is_pressed()) {
notify_stopped_paused();
} else {
notify_continued();
@@ -1017,8 +1018,7 @@ DebugAdapterProtocol::DebugAdapterProtocol() {
reset_ids();
- EditorNode *node = EditorNode::get_singleton();
- node->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused));
+ EditorRunBar::get_singleton()->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused));
EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton();
debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled));
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index a368cacf56..7083640b24 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
@@ -87,8 +88,7 @@ EditorDebuggerNode::EditorDebuggerNode() {
remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0);
inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
- EditorNode *editor = EditorNode::get_singleton();
- editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
+ EditorRunBar::get_singleton()->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
}
ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
@@ -260,7 +260,7 @@ void EditorDebuggerNode::stop(bool p_force) {
server->stop();
EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
- if (EditorNode::get_singleton()->is_movie_maker_enabled()) {
+ if (EditorRunBar::get_singleton()->is_movie_maker_enabled()) {
// Request attention in case the user was doing something else when movie recording is finished.
DisplayServer::get_singleton()->window_request_attention();
}
@@ -344,7 +344,7 @@ void EditorDebuggerNode::_notification(int p_what) {
}
}
- EditorNode::get_singleton()->get_pause_button()->set_disabled(false);
+ EditorRunBar::get_singleton()->get_pause_button()->set_disabled(false);
// Switch to remote tree view if so desired.
auto_switch_remote_scene_tree = (bool)EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");
if (auto_switch_remote_scene_tree) {
@@ -413,8 +413,8 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) {
}
});
if (!found) {
- EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
- EditorNode::get_singleton()->get_pause_button()->set_disabled(true);
+ EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);
+ EditorRunBar::get_singleton()->get_pause_button()->set_disabled(true);
SceneTreeDock::get_singleton()->hide_remote_tree();
SceneTreeDock::get_singleton()->hide_tab_buttons();
EditorNode::get_singleton()->notify_all_debug_sessions_exited();
@@ -509,7 +509,7 @@ void EditorDebuggerNode::_update_debug_options() {
}
void EditorDebuggerNode::_paused() {
- const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed();
+ const bool paused = EditorRunBar::get_singleton()->get_pause_button()->is_pressed();
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
if (paused && !dbg->is_breaked()) {
dbg->debug_break();
@@ -527,7 +527,7 @@ void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, String p_mes
tabs->set_current_tab(p_debugger);
}
_break_state_changed();
- EditorNode::get_singleton()->get_pause_button()->set_pressed(p_breaked);
+ EditorRunBar::get_singleton()->get_pause_button()->set_pressed(p_breaked);
emit_signal(SNAME("breaked"), p_breaked, p_can_debug);
}
diff --git a/editor/debugger/editor_file_server.cpp b/editor/debugger/editor_file_server.cpp
index ba5dd9e6e2..6056de3557 100644
--- a/editor/debugger/editor_file_server.cpp
+++ b/editor/debugger/editor_file_server.cpp
@@ -32,273 +32,230 @@
#include "../editor_settings.h"
#include "core/io/marshalls.h"
+#include "editor/editor_node.h"
+#include "editor/export/editor_export_platform.h"
+
+#define FILESYSTEM_PROTOCOL_VERSION 1
+#define PASSWORD_LENGTH 32
+#define MAX_FILE_BUFFER_SIZE 100 * 1024 * 1024 // 100mb max file buffer size (description of files to update, compressed).
+
+static void _add_file(String f, const uint64_t &p_modified_time, HashMap<String, uint64_t> &files_to_send, HashMap<String, uint64_t> &cached_files) {
+ f = f.replace_first("res://", ""); // remove res://
+ const uint64_t *cached_mt = cached_files.getptr(f);
+ if (cached_mt && *cached_mt == p_modified_time) {
+ // File is good, skip it.
+ cached_files.erase(f); // Erase to mark this file as existing. Remaining files not added to files_to_send will be considered erased here, so they need to be erased in the client too.
+ return;
+ }
+ files_to_send.insert(f, p_modified_time);
+}
-//#define DEBUG_PRINT(m_p) print_line(m_p)
-//#define DEBUG_TIME(m_what) printf("MS: %s - %lu\n", m_what, OS::get_singleton()->get_ticks_usec());
+void EditorFileServer::_scan_files_changed(EditorFileSystemDirectory *efd, const Vector<String> &p_tags, HashMap<String, uint64_t> &files_to_send, HashMap<String, uint64_t> &cached_files) {
+ for (int i = 0; i < efd->get_file_count(); i++) {
+ String f = efd->get_file_path(i);
+ if (FileAccess::exists(f + ".import")) {
+ // is imported, determine what to do
+ // Todo the modified times of remapped files should most likely be kept in EditorFileSystem to speed this up in the future.
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ Error err = cf->load(f + ".import");
+
+ ERR_CONTINUE(err != OK);
+ {
+ uint64_t mt = FileAccess::get_modified_time(f + ".import");
+ _add_file(f + ".import", mt, files_to_send, cached_files);
+ }
-#define DEBUG_PRINT(m_what)
-#define DEBUG_TIME(m_what)
+ if (!cf->has_section("remap")) {
+ continue;
+ }
-void EditorFileServer::_close_client(ClientData *cd) {
- cd->connection->disconnect_from_host();
- {
- MutexLock lock(cd->efs->wait_mutex);
- cd->efs->to_wait.insert(cd->thread);
+ List<String> remaps;
+ cf->get_section_keys("remap", &remaps);
+
+ for (const String &remap : remaps) {
+ if (remap == "path") {
+ String remapped_path = cf->get_value("remap", remap);
+ uint64_t mt = FileAccess::get_modified_time(remapped_path);
+ _add_file(remapped_path, mt, files_to_send, cached_files);
+ } else if (remap.begins_with("path.")) {
+ String feature = remap.get_slice(".", 1);
+ if (p_tags.find(feature) != -1) {
+ String remapped_path = cf->get_value("remap", remap);
+ uint64_t mt = FileAccess::get_modified_time(remapped_path);
+ _add_file(remapped_path, mt, files_to_send, cached_files);
+ }
+ }
+ }
+ } else {
+ uint64_t mt = efd->get_file_modified_time(i);
+ _add_file(f, mt, files_to_send, cached_files);
+ }
}
- while (cd->files.size()) {
- cd->files.remove(cd->files.begin());
+
+ for (int i = 0; i < efd->get_subdir_count(); i++) {
+ _scan_files_changed(efd->get_subdir(i), p_tags, files_to_send, cached_files);
}
- memdelete(cd);
}
-void EditorFileServer::_subthread_start(void *s) {
- ClientData *cd = static_cast<ClientData *>(s);
+static void _add_custom_file(const String f, HashMap<String, uint64_t> &files_to_send, HashMap<String, uint64_t> &cached_files) {
+ if (!FileAccess::exists(f)) {
+ return;
+ }
+ _add_file(f, FileAccess::get_modified_time(f), files_to_send, cached_files);
+}
- cd->connection->set_no_delay(true);
- uint8_t buf4[8];
- Error err = cd->connection->get_data(buf4, 4);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
+void EditorFileServer::poll() {
+ if (!active) {
+ return;
}
- int passlen = decode_uint32(buf4);
-
- if (passlen > 512) {
- _close_client(cd);
- ERR_FAIL_COND(passlen > 512);
- } else if (passlen > 0) {
- Vector<char> passutf8;
- passutf8.resize(passlen + 1);
- err = cd->connection->get_data((uint8_t *)passutf8.ptr(), passlen);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
- passutf8.write[passlen] = 0;
- String s2;
- s2.parse_utf8(passutf8.ptr());
- if (s2 != cd->efs->password) {
- encode_uint32(ERR_INVALID_DATA, buf4);
- cd->connection->put_data(buf4, 4);
- OS::get_singleton()->delay_usec(1000000);
- _close_client(cd);
- ERR_PRINT("CLIENT PASSWORD MISMATCH");
- ERR_FAIL();
- }
- } else {
- if (!cd->efs->password.is_empty()) {
- encode_uint32(ERR_INVALID_DATA, buf4);
- cd->connection->put_data(buf4, 4);
- OS::get_singleton()->delay_usec(1000000);
- _close_client(cd);
- ERR_PRINT("CLIENT PASSWORD MISMATCH (should be empty!)");
- ERR_FAIL();
- }
+ if (!server->is_connection_available()) {
+ return;
}
- encode_uint32(OK, buf4);
- cd->connection->put_data(buf4, 4);
+ Ref<StreamPeerTCP> tcp_peer = server->take_connection();
+ ERR_FAIL_COND(tcp_peer.is_null());
+
+ // Got a connection!
+ EditorProgress pr("updating_remote_file_system", TTR("Updating assets on target device:"), 105);
+
+ pr.step(TTR("Syncinc headers"), 0, true);
+ print_verbose("EFS: Connecting taken!");
+ char header[4];
+ Error err = tcp_peer->get_data((uint8_t *)&header, 4);
+ ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(header[0] != 'G');
+ ERR_FAIL_COND(header[1] != 'R');
+ ERR_FAIL_COND(header[2] != 'F');
+ ERR_FAIL_COND(header[3] != 'S');
+
+ uint32_t protocol_version = tcp_peer->get_u32();
+ ERR_FAIL_COND(protocol_version != FILESYSTEM_PROTOCOL_VERSION);
+
+ char cpassword[PASSWORD_LENGTH + 1];
+ err = tcp_peer->get_data((uint8_t *)cpassword, PASSWORD_LENGTH);
+ cpassword[PASSWORD_LENGTH] = 0;
+ ERR_FAIL_COND(err != OK);
+ print_verbose("EFS: Got password: " + String(cpassword));
+ ERR_FAIL_COND_MSG(password != cpassword, "Client disconnected because password mismatch.");
+
+ uint32_t tag_count = tcp_peer->get_u32();
+ print_verbose("EFS: Getting tags: " + itos(tag_count));
+
+ ERR_FAIL_COND(tcp_peer->get_status() != StreamPeerTCP::STATUS_CONNECTED);
+ Vector<String> tags;
+ for (uint32_t i = 0; i < tag_count; i++) {
+ String tag = tcp_peer->get_utf8_string();
+ print_verbose("EFS: tag #" + itos(i) + ": " + tag);
+ ERR_FAIL_COND(tcp_peer->get_status() != StreamPeerTCP::STATUS_CONNECTED);
+ tags.push_back(tag);
+ }
- while (!cd->quit) {
- //wait for ID
- err = cd->connection->get_data(buf4, 4);
- DEBUG_TIME("get_data")
+ uint32_t file_buffer_decompressed_size = tcp_peer->get_32();
+ HashMap<String, uint64_t> cached_files;
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
- int id = decode_uint32(buf4);
+ if (file_buffer_decompressed_size > 0) {
+ pr.step(TTR("Getting remote file system"), 1, true);
- //wait for command
- err = cd->connection->get_data(buf4, 4);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
- int cmd = decode_uint32(buf4);
-
- switch (cmd) {
- case FileAccessNetwork::COMMAND_FILE_EXISTS:
- case FileAccessNetwork::COMMAND_GET_MODTIME:
- case FileAccessNetwork::COMMAND_OPEN_FILE: {
- DEBUG_TIME("open_file")
- err = cd->connection->get_data(buf4, 4);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
+ // Got files cached by client.
+ uint32_t file_buffer_size = tcp_peer->get_32();
+ print_verbose("EFS: Getting file buffer: compressed - " + String::humanize_size(file_buffer_size) + " decompressed: " + String::humanize_size(file_buffer_decompressed_size));
- int namelen = decode_uint32(buf4);
- Vector<char> fileutf8;
- fileutf8.resize(namelen + 1);
- err = cd->connection->get_data((uint8_t *)fileutf8.ptr(), namelen);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
- fileutf8.write[namelen] = 0;
- String s2;
- s2.parse_utf8(fileutf8.ptr());
+ ERR_FAIL_COND(tcp_peer->get_status() != StreamPeerTCP::STATUS_CONNECTED);
+ ERR_FAIL_COND(file_buffer_size > MAX_FILE_BUFFER_SIZE);
+ LocalVector<uint8_t> file_buffer;
+ file_buffer.resize(file_buffer_size);
+ LocalVector<uint8_t> file_buffer_decompressed;
+ file_buffer_decompressed.resize(file_buffer_decompressed_size);
- if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
- print_verbose("FILE EXISTS: " + s2);
- }
- if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
- print_verbose("MOD TIME: " + s2);
- }
- if (cmd == FileAccessNetwork::COMMAND_OPEN_FILE) {
- print_verbose("OPEN: " + s2);
- }
+ err = tcp_peer->get_data(file_buffer.ptr(), file_buffer_size);
- if (!s2.begins_with("res://")) {
- _close_client(cd);
- ERR_FAIL_COND(!s2.begins_with("res://"));
- }
- ERR_CONTINUE(cd->files.has(id));
-
- if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
- encode_uint32(id, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccessNetwork::RESPONSE_FILE_EXISTS, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccess::exists(s2), buf4);
- cd->connection->put_data(buf4, 4);
- DEBUG_TIME("open_file_end")
- break;
- }
+ pr.step(TTR("Decompressing remote file system"), 2, true);
- if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
- encode_uint32(id, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccessNetwork::RESPONSE_GET_MODTIME, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint64(FileAccess::get_modified_time(s2), buf4);
- cd->connection->put_data(buf4, 8);
- DEBUG_TIME("open_file_end")
- break;
- }
-
- Ref<FileAccess> fa = FileAccess::open(s2, FileAccess::READ);
- if (fa.is_null()) {
- //not found, continue
- encode_uint32(id, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(ERR_FILE_NOT_FOUND, buf4);
- cd->connection->put_data(buf4, 4);
- DEBUG_TIME("open_file_end")
- break;
- }
+ ERR_FAIL_COND(err != OK);
+ // Decompress the text with all the files
+ Compression::decompress(file_buffer_decompressed.ptr(), file_buffer_decompressed.size(), file_buffer.ptr(), file_buffer.size(), Compression::MODE_ZSTD);
+ String files_text = String::utf8((const char *)file_buffer_decompressed.ptr(), file_buffer_decompressed.size());
+ Vector<String> files = files_text.split("\n");
+
+ print_verbose("EFS: Total cached files received: " + itos(files.size()));
+ for (int i = 0; i < files.size(); i++) {
+ if (files[i].get_slice_count("::") != 2) {
+ continue;
+ }
+ String file = files[i].get_slice("::", 0);
+ uint64_t modified_time = files[i].get_slice("::", 1).to_int();
- encode_uint32(id, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccessNetwork::RESPONSE_OPEN, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(OK, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint64(fa->get_length(), buf4);
- cd->connection->put_data(buf4, 8);
-
- cd->files[id] = fa;
- DEBUG_TIME("open_file_end")
-
- } break;
- case FileAccessNetwork::COMMAND_READ_BLOCK: {
- err = cd->connection->get_data(buf4, 8);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
+ cached_files.insert(file, modified_time);
+ }
+ } else {
+ // Client does not have any files stored.
+ }
- ERR_CONTINUE(!cd->files.has(id));
+ pr.step(TTR("Scanning for local changes"), 3, true);
- uint64_t offset = decode_uint64(buf4);
+ print_verbose("EFS: Scanning changes:");
- err = cd->connection->get_data(buf4, 4);
- if (err != OK) {
- _close_client(cd);
- ERR_FAIL_COND(err != OK);
- }
+ HashMap<String, uint64_t> files_to_send;
+ // Scan files to send.
+ _scan_files_changed(EditorFileSystem::get_singleton()->get_filesystem(), tags, files_to_send, cached_files);
+ // Add forced export files
+ Vector<String> forced_export = EditorExportPlatform::get_forced_export_files();
+ for (int i = 0; i < forced_export.size(); i++) {
+ _add_custom_file(forced_export[i], files_to_send, cached_files);
+ }
- int blocklen = decode_uint32(buf4);
- ERR_CONTINUE(blocklen > (16 * 1024 * 1024));
-
- cd->files[id]->seek(offset);
- Vector<uint8_t> buf;
- buf.resize(blocklen);
- uint32_t read = cd->files[id]->get_buffer(buf.ptrw(), blocklen);
-
- print_verbose("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen));
-
- //not found, continue
- encode_uint32(id, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint32(FileAccessNetwork::RESPONSE_DATA, buf4);
- cd->connection->put_data(buf4, 4);
- encode_uint64(offset, buf4);
- cd->connection->put_data(buf4, 8);
- encode_uint32(read, buf4);
- cd->connection->put_data(buf4, 4);
- cd->connection->put_data(buf.ptr(), read);
-
- } break;
- case FileAccessNetwork::COMMAND_CLOSE: {
- print_verbose("CLOSED");
- ERR_CONTINUE(!cd->files.has(id));
- cd->files.erase(id);
- } break;
+ _add_custom_file("res://project.godot", files_to_send, cached_files);
+ // Check which files were removed and also add them
+ for (KeyValue<String, uint64_t> K : cached_files) {
+ if (!files_to_send.has(K.key)) {
+ files_to_send.insert(K.key, 0); //0 means removed
}
}
- _close_client(cd);
-}
+ tcp_peer->put_32(files_to_send.size());
-void EditorFileServer::_thread_start(void *s) {
- EditorFileServer *self = static_cast<EditorFileServer *>(s);
- while (!self->quit) {
- if (self->cmd == CMD_ACTIVATE) {
- self->server->listen(self->port);
- self->active = true;
- self->cmd = CMD_NONE;
- } else if (self->cmd == CMD_STOP) {
- self->server->stop();
- self->active = false;
- self->cmd = CMD_NONE;
- }
+ print_verbose("EFS: Sending list of changed files.");
+ pr.step(TTR("Sending list of changed files:"), 4, true);
- if (self->active) {
- if (self->server->is_connection_available()) {
- ClientData *cd = memnew(ClientData);
- cd->connection = self->server->take_connection();
- cd->efs = self;
- cd->quit = false;
- cd->thread = memnew(Thread);
- cd->thread->start(_subthread_start, cd);
- }
- }
+ // Send list of changed files first, to ensure that if connecting breaks, the client is not found in a broken state.
+ for (KeyValue<String, uint64_t> K : files_to_send) {
+ tcp_peer->put_utf8_string(K.key);
+ tcp_peer->put_64(K.value);
+ }
+
+ print_verbose("EFS: Sending " + itos(files_to_send.size()) + " files.");
+
+ int idx = 0;
+ for (KeyValue<String, uint64_t> K : files_to_send) {
+ pr.step(TTR("Sending file: ") + K.key.get_file(), 5 + idx * 100 / files_to_send.size(), false);
+ idx++;
- self->wait_mutex.lock();
- while (self->to_wait.size()) {
- Thread *w = *self->to_wait.begin();
- self->to_wait.erase(w);
- self->wait_mutex.unlock();
- w->wait_to_finish();
- self->wait_mutex.lock();
+ if (K.value == 0 || !FileAccess::exists("res://" + K.key)) { // File was removed
+ continue;
}
- self->wait_mutex.unlock();
- OS::get_singleton()->delay_usec(100000);
+ Vector<uint8_t> array = FileAccess::_get_file_as_bytes("res://" + K.key);
+ tcp_peer->put_64(array.size());
+ tcp_peer->put_data(array.ptr(), array.size());
+ ERR_FAIL_COND(tcp_peer->get_status() != StreamPeerTCP::STATUS_CONNECTED);
}
+
+ tcp_peer->put_data((const uint8_t *)"GEND", 4); // End marker.
+
+ print_verbose("EFS: Done.");
}
void EditorFileServer::start() {
- stop();
+ if (active) {
+ stop();
+ }
port = EDITOR_GET("filesystem/file_server/port");
password = EDITOR_GET("filesystem/file_server/password");
- cmd = CMD_ACTIVATE;
+ Error err = server->listen(port);
+ ERR_FAIL_COND_MSG(err != OK, "EditorFileServer: Unable to listen on port " + itos(port));
+ active = true;
}
bool EditorFileServer::is_active() const {
@@ -306,18 +263,19 @@ bool EditorFileServer::is_active() const {
}
void EditorFileServer::stop() {
- cmd = CMD_STOP;
+ if (active) {
+ server->stop();
+ active = false;
+ }
}
EditorFileServer::EditorFileServer() {
server.instantiate();
- thread.start(_thread_start, this);
EDITOR_DEF("filesystem/file_server/port", 6010);
EDITOR_DEF("filesystem/file_server/password", "");
}
EditorFileServer::~EditorFileServer() {
- quit = true;
- thread.wait_to_finish();
+ stop();
}
diff --git a/editor/debugger/editor_file_server.h b/editor/debugger/editor_file_server.h
index ff2742e73f..4374f508ee 100644
--- a/editor/debugger/editor_file_server.h
+++ b/editor/debugger/editor_file_server.h
@@ -31,46 +31,24 @@
#ifndef EDITOR_FILE_SERVER_H
#define EDITOR_FILE_SERVER_H
-#include "core/io/file_access_network.h"
#include "core/io/packet_peer.h"
#include "core/io/tcp_server.h"
#include "core/object/class_db.h"
#include "core/os/thread.h"
+#include "editor/editor_file_system.h"
class EditorFileServer : public Object {
GDCLASS(EditorFileServer, Object);
- enum Command {
- CMD_NONE,
- CMD_ACTIVATE,
- CMD_STOP,
- };
-
- struct ClientData {
- Thread *thread = nullptr;
- Ref<StreamPeerTCP> connection;
- HashMap<int, Ref<FileAccess>> files;
- EditorFileServer *efs = nullptr;
- bool quit = false;
- };
-
Ref<TCPServer> server;
- HashSet<Thread *> to_wait;
-
- static void _close_client(ClientData *cd);
- static void _subthread_start(void *s);
-
- Mutex wait_mutex;
- Thread thread;
- static void _thread_start(void *);
- bool quit = false;
- Command cmd = CMD_NONE;
-
String password;
int port = 0;
bool active = false;
+ void _scan_files_changed(EditorFileSystemDirectory *efd, const Vector<String> &p_tags, HashMap<String, uint64_t> &files_to_send, HashMap<String, uint64_t> &cached_files);
public:
+ void poll();
+
void start();
void stop();
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index a696e1ff1f..bef34b9e73 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1704,6 +1704,14 @@ void ScriptEditorDebugger::remove_debugger_tab(Control *p_control) {
p_control->queue_free();
}
+int ScriptEditorDebugger::get_current_debugger_tab() const {
+ return tabs->get_current_tab();
+}
+
+void ScriptEditorDebugger::switch_to_debugger(int p_debugger_tab_idx) {
+ tabs->set_current_tab(p_debugger_tab_idx);
+}
+
void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) {
_put_msg(p_message, p_args);
}
@@ -1724,7 +1732,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
{ //debugger
VBoxContainer *vbc = memnew(VBoxContainer);
- vbc->set_name(TTR("Debugger"));
+ vbc->set_name(TTR("Stack Trace"));
Control *dbg = vbc;
HBoxContainer *hbc = memnew(HBoxContainer);
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index 1659bbee8d..336a113163 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -284,6 +284,8 @@ public:
void add_debugger_tab(Control *p_control);
void remove_debugger_tab(Control *p_control);
+ int get_current_debugger_tab() const;
+ void switch_to_debugger(int p_debugger_tab_idx);
void send_message(const String &p_message, const Array &p_args);
void toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data);
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 996e16be33..67a8814aa1 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -417,6 +417,7 @@ void DocTools::generate(bool p_basic_types) {
ClassDB::get_property_list(name, &own_properties, true);
}
+ // Sort is still needed here to handle inherited properties, even though it is done below, do not remove.
properties.sort();
own_properties.sort();
@@ -535,9 +536,10 @@ void DocTools::generate(bool p_basic_types) {
c.properties.push_back(prop);
}
+ c.properties.sort();
+
List<MethodInfo> method_list;
ClassDB::get_method_list(name, &method_list, true);
- method_list.sort();
for (const MethodInfo &E : method_list) {
if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) {
@@ -571,6 +573,8 @@ void DocTools::generate(bool p_basic_types) {
c.methods.push_back(method);
}
+ c.methods.sort();
+
List<MethodInfo> signal_list;
ClassDB::get_signal_list(name, &signal_list, true);
@@ -709,7 +713,6 @@ void DocTools::generate(bool p_basic_types) {
List<MethodInfo> method_list;
v.get_method_list(&method_list);
- method_list.sort();
Variant::get_constructor_list(Variant::Type(i), &method_list);
for (int j = 0; j < Variant::OP_AND; j++) { // Showing above 'and' is pretty confusing and there are a lot of variations.
@@ -832,6 +835,8 @@ void DocTools::generate(bool p_basic_types) {
}
}
+ c.methods.sort();
+
List<PropertyInfo> properties;
v.get_property_list(&properties);
for (const PropertyInfo &pi : properties) {
@@ -1372,10 +1377,7 @@ static void _write_string(Ref<FileAccess> f, int p_tablevel, const String &p_str
if (p_string.is_empty()) {
return;
}
- String tab;
- for (int i = 0; i < p_tablevel; i++) {
- tab += "\t";
- }
+ String tab = String("\t").repeat(p_tablevel);
f->store_string(tab + p_string + "\n");
}
@@ -1406,7 +1408,7 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
if (!m.return_enum.is_empty()) {
enum_text = " enum=\"" + m.return_enum + "\"";
}
- _write_string(f, 3, "<return type=\"" + m.return_type + "\"" + enum_text + " />");
+ _write_string(f, 3, "<return type=\"" + m.return_type.xml_escape(true) + "\"" + enum_text + " />");
}
if (m.errors_returned.size() > 0) {
for (int j = 0; j < m.errors_returned.size(); j++) {
@@ -1423,9 +1425,9 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
}
if (!a.default_value.is_empty()) {
- _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
+ _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape(true) + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
} else {
- _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " />");
+ _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape(true) + "\"" + enum_text + " />");
}
}
@@ -1440,7 +1442,7 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
}
}
-Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path) {
+Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path, bool p_include_xml_schema) {
for (KeyValue<String, DocData::ClassDoc> &E : class_list) {
DocData::ClassDoc &c = E.value;
@@ -1452,16 +1454,16 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
}
Error err;
- String save_file = save_path.path_join(c.name + ".xml");
+ String save_file = save_path.path_join(c.name.replace("\"", "").replace("/", "--") + ".xml");
Ref<FileAccess> f = FileAccess::open(save_file, FileAccess::WRITE, &err);
ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + ".");
_write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
- String header = "<class name=\"" + c.name + "\"";
+ String header = "<class name=\"" + c.name.xml_escape(true) + "\"";
if (!c.inherits.is_empty()) {
- header += " inherits=\"" + c.inherits + "\"";
+ header += " inherits=\"" + c.inherits.xml_escape(true) + "\"";
if (c.is_deprecated) {
header += " is_deprecated=\"true\"";
}
@@ -1470,12 +1472,15 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
}
}
header += String(" version=\"") + VERSION_BRANCH + "\"";
- // Reference the XML schema so editors can provide error checking.
- // Modules are nested deep, so change the path to reference the same schema everywhere.
- const String schema_path = save_path.find("modules/") != -1 ? "../../../doc/class.xsd" : "../class.xsd";
- header += vformat(
- R"( xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s">)",
- schema_path);
+ if (p_include_xml_schema) {
+ // Reference the XML schema so editors can provide error checking.
+ // Modules are nested deep, so change the path to reference the same schema everywhere.
+ const String schema_path = save_path.find("modules/") != -1 ? "../../../doc/class.xsd" : "../class.xsd";
+ header += vformat(
+ R"( xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s")",
+ schema_path);
+ }
+ header += ">";
_write_string(f, 0, header);
_write_string(f, 1, "<brief_description>");
@@ -1521,9 +1526,9 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
const DocData::PropertyDoc &p = c.properties[i];
if (c.properties[i].overridden) {
- _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\" overrides=\"" + p.overrides + "\"" + additional_attributes + " />");
+ _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type.xml_escape(true) + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\" overrides=\"" + p.overrides + "\"" + additional_attributes + " />");
} else {
- _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + additional_attributes + ">");
+ _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type.xml_escape(true) + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + additional_attributes + ">");
_write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape());
_write_string(f, 2, "</member>");
}
@@ -1549,12 +1554,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
if (k.is_value_valid) {
if (!k.enumeration.is_empty()) {
if (k.is_bitfield) {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">");
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
}
} else {
- _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\"" + additional_attributes + ">");
+ _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\"" + additional_attributes + ">");
}
} else {
if (!k.enumeration.is_empty()) {
diff --git a/editor/doc_tools.h b/editor/doc_tools.h
index 08efae31ef..2d4a45bda0 100644
--- a/editor/doc_tools.h
+++ b/editor/doc_tools.h
@@ -47,7 +47,7 @@ public:
bool has_doc(const String &p_class_name);
void generate(bool p_basic_types = false);
Error load_classes(const String &p_dir);
- Error save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path);
+ Error save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path, bool p_include_xml_schema = true);
Error _load(Ref<XMLParser> parser);
Error load_compressed(const uint8_t *p_data, int p_compressed_size, int p_uncompressed_size);
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 9ed8cf7c21..93788dc8b1 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -69,11 +69,15 @@ void EditorAudioBus::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ Ref<Texture2D> active_bus_texture = get_theme_icon(SNAME("BusVuActive"), SNAME("EditorIcons"));
for (int i = 0; i < CHANNELS_MAX; i++) {
- channel[i].vu_l->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons")));
- channel[i].vu_l->set_progress_texture(get_theme_icon(SNAME("BusVuFull"), SNAME("EditorIcons")));
- channel[i].vu_r->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons")));
- channel[i].vu_r->set_progress_texture(get_theme_icon(SNAME("BusVuFull"), SNAME("EditorIcons")));
+ channel[i].vu_l->set_under_texture(active_bus_texture);
+ channel[i].vu_l->set_tint_under(Color(0.75, 0.75, 0.75));
+ channel[i].vu_l->set_progress_texture(active_bus_texture);
+
+ channel[i].vu_r->set_under_texture(active_bus_texture);
+ channel[i].vu_r->set_tint_under(Color(0.75, 0.75, 0.75));
+ channel[i].vu_r->set_progress_texture(active_bus_texture);
channel[i].prev_active = true;
}
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index 1c598277dd..b44792bf0f 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -75,9 +75,15 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
r.shortcut_text = E.value.shortcut;
r.last_used = E.value.last_used;
- if (search_text.is_subsequence_ofn(r.display_name)) {
+ bool is_subsequence_of_key_name = search_text.is_subsequence_ofn(r.key_name);
+ bool is_subsequence_of_display_name = search_text.is_subsequence_ofn(r.display_name);
+
+ if (is_subsequence_of_key_name || is_subsequence_of_display_name) {
if (!search_text.is_empty()) {
- r.score = _score_path(search_text, r.display_name.to_lower());
+ float key_name_score = is_subsequence_of_key_name ? _score_path(search_text, r.key_name.to_lower()) : .0f;
+ float display_name_score = is_subsequence_of_display_name ? _score_path(search_text, r.display_name.to_lower()) : .0f;
+
+ r.score = MAX(key_name_score, display_name_score);
}
entries.push_back(r);
@@ -256,7 +262,7 @@ void EditorCommandPalette::register_shortcuts_as_command() {
ev.instantiate();
ev->set_shortcut(shortcut);
String shortcut_text = String(shortcut->get_as_text());
- add_command(command_name, E.key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text);
+ add_command(command_name, E.key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), shortcut_text);
}
unregistered_shortcuts.clear();
@@ -277,7 +283,7 @@ Ref<Shortcut> EditorCommandPalette::add_shortcut_command(const String &p_command
ev.instantiate();
ev->set_shortcut(p_shortcut);
String shortcut_text = String(p_shortcut->get_as_text());
- add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_unhandled_input), varray(ev, false), shortcut_text);
+ add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), shortcut_text);
} else {
const String key_name = String(p_key);
const String command_name = String(p_command);
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 1421db42ec..5d3037b4ec 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -300,7 +300,7 @@ void EditorData::get_editor_breakpoints(List<String> *p_breakpoints) {
}
}
-Dictionary EditorData::get_editor_states() const {
+Dictionary EditorData::get_editor_plugin_states() const {
Dictionary metadata;
for (int i = 0; i < editor_plugins.size(); i++) {
Dictionary state = editor_plugins[i]->get_state();
@@ -319,7 +319,7 @@ Dictionary EditorData::get_scene_editor_states(int p_idx) const {
return es.editor_states;
}
-void EditorData::set_editor_states(const Dictionary &p_states) {
+void EditorData::set_editor_plugin_states(const Dictionary &p_states) {
if (p_states.is_empty()) {
for (EditorPlugin *ep : editor_plugins) {
ep->clear();
@@ -622,7 +622,7 @@ void EditorData::remove_scene(int p_idx) {
}
if (!edited_scene[p_idx].path.is_empty()) {
- ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(edited_scene[p_idx].path);
+ EditorNode::get_singleton()->emit_signal("scene_closed", edited_scene[p_idx].path);
}
undo_redo_manager->discard_history(edited_scene[p_idx].history_id);
@@ -713,6 +713,16 @@ int EditorData::get_edited_scene() const {
return current_edited_scene;
}
+int EditorData::get_edited_scene_from_path(const String &p_path) const {
+ for (int i = 0; i < edited_scene.size(); i++) {
+ if (edited_scene[i].path == p_path) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
void EditorData::set_edited_scene(int p_idx) {
ERR_FAIL_INDEX(p_idx, edited_scene.size());
current_edited_scene = p_idx;
@@ -881,7 +891,7 @@ void EditorData::save_edited_scene_state(EditorSelection *p_selection, EditorSel
es.selection = p_selection->get_full_selected_node_list();
es.history_current = p_history->current_elem_idx;
es.history_stored = p_history->history;
- es.editor_states = get_editor_states();
+ es.editor_states = get_editor_plugin_states();
es.custom_state = p_custom;
}
@@ -897,7 +907,7 @@ Dictionary EditorData::restore_edited_scene_state(EditorSelection *p_selection,
for (Node *E : es.selection) {
p_selection->add_node(E);
}
- set_editor_states(es.editor_states);
+ set_editor_plugin_states(es.editor_states);
return es.custom_state;
}
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 370963074c..7ca04b5680 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -156,9 +156,9 @@ public:
void copy_object_params(Object *p_object);
void paste_object_params(Object *p_object);
- Dictionary get_editor_states() const;
+ Dictionary get_editor_plugin_states() const;
Dictionary get_scene_editor_states(int p_idx) const;
- void set_editor_states(const Dictionary &p_states);
+ void set_editor_plugin_states(const Dictionary &p_states);
void get_editor_breakpoints(List<String> *p_breakpoints);
void clear_editor_states();
void save_editor_external_data();
@@ -196,9 +196,11 @@ public:
void set_edited_scene(int p_idx);
void set_edited_scene_root(Node *p_root);
int get_edited_scene() const;
+ int get_edited_scene_from_path(const String &p_path) const;
Node *get_edited_scene_root(int p_idx = -1);
int get_edited_scene_count() const;
Vector<EditedScene> get_edited_scenes() const;
+
String get_scene_title(int p_idx, bool p_always_strip_extension = false) const;
String get_scene_path(int p_idx) const;
String get_scene_type(int p_idx) const;
@@ -211,6 +213,7 @@ public:
NodePath get_edited_scene_live_edit_root();
bool check_and_update_scene(int p_idx);
void move_edited_scene_to_index(int p_idx);
+
bool call_build();
void set_scene_as_saved(int p_idx);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 58080c08ae..0cdbddf4c4 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1256,7 +1256,9 @@ void EditorFileSystem::_notification(int p_what) {
if (scanning_changes_done) {
set_process(false);
- thread_sources.wait_to_finish();
+ if (thread_sources.is_started()) {
+ thread_sources.wait_to_finish();
+ }
bool changed = _update_scan_actions();
_update_pending_script_classes();
if (changed) {
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index b9634a7e03..aee3574398 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -32,6 +32,7 @@
#include "core/core_constants.h"
#include "core/input/input.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/version.h"
#include "doc_data_compressed.gen.h"
@@ -44,6 +45,8 @@
#define CONTRIBUTE_URL vformat("%s/contributing/documentation/updating_the_class_reference.html", VERSION_DOCS_URL)
+// 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 {
@@ -73,6 +76,55 @@ public:
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);
+ if (!ScriptServer::is_global_class(outer_class)) {
+ return false;
+ }
+
+ // ResourceLoader is used in order to have a script-agnostic way to load scripts.
+ // This forces GDScript to compile the code, which is unnecessary for docgen, but it's a good compromise right now.
+ Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(outer_class), outer_class);
+ if (script.is_valid()) {
+ Vector<DocData::ClassDoc> docs = script->get_documentation();
+ for (int j = 0; j < docs.size(); j++) {
+ const DocData::ClassDoc &doc = docs.get(j);
+ EditorHelp::get_doc_data()->add_doc(doc);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+// Removes unnecessary prefix from p_class_specifier when within the p_edited_class context
+static String _contextualize_class_specifier(const String &p_class_specifier, const String &p_edited_class) {
+ // If this is a completely different context than the current class, then keep full path
+ if (!p_class_specifier.begins_with(p_edited_class)) {
+ return p_class_specifier;
+ }
+
+ // Here equal length + begins_with from above implies p_class_specifier == p_edited_class :)
+ if (p_class_specifier.length() == p_edited_class.length()) {
+ int rfind = p_class_specifier.rfind(".");
+ if (rfind == -1) { // Single identifier
+ return p_class_specifier;
+ }
+ // Multiple specifiers: keep last one only
+ return p_class_specifier.substr(rfind + 1);
+ }
+
+ // They share a _name_ prefix but not a _class specifier_ prefix, e.g. Tree & TreeItem
+ // begins_with + lengths being different implies p_class_specifier.length() > p_edited_class.length() so this is safe
+ if (p_class_specifier[p_edited_class.length()] != '.') {
+ return p_class_specifier;
+ }
+
+ // Remove class specifier prefix
+ return p_class_specifier.substr(p_edited_class.length() + 1);
+}
+
void EditorHelp::_update_theme_item_cache() {
VBoxContainer::_update_theme_item_cache();
@@ -130,12 +182,13 @@ void EditorHelp::_class_list_select(const String &p_select) {
}
void EditorHelp::_class_desc_select(const String &p_select) {
- if (p_select.begins_with("$")) { //enum
+ if (p_select.begins_with("$")) { // enum
String select = p_select.substr(1, p_select.length());
String class_name;
- if (select.contains(".")) {
- class_name = select.get_slice(".", 0);
- select = select.get_slice(".", 1);
+ int rfind = select.rfind(".");
+ if (rfind != -1) {
+ class_name = select.substr(0, rfind);
+ select = select.substr(rfind + 1);
} else {
class_name = "@GlobalScope";
}
@@ -253,35 +306,36 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
bool is_enum_type = !p_enum.is_empty();
bool can_ref = !p_type.contains("*") || is_enum_type;
- String t = p_type;
+ String link_t = p_type; // For links in metadata
+ String display_t = link_t; // For display purposes
if (is_enum_type) {
- if (p_enum.get_slice_count(".") > 1) {
- t = p_enum.get_slice(".", 1);
- } else {
- t = p_enum.get_slice(".", 0);
- }
+ link_t = p_enum; // The link for enums is always the full enum description
+ display_t = _contextualize_class_specifier(p_enum, edited_class);
+ } else {
+ display_t = _contextualize_class_specifier(p_type, edited_class);
}
class_desc->push_color(theme_cache.type_color);
bool add_array = false;
if (can_ref) {
- if (t.ends_with("[]")) {
+ if (link_t.ends_with("[]")) {
add_array = true;
- t = t.replace("[]", "");
+ link_t = link_t.trim_suffix("[]");
+ display_t = display_t.trim_suffix("[]");
- class_desc->push_meta("#Array"); //class
+ class_desc->push_meta("#Array"); // class
class_desc->add_text("Array");
class_desc->pop();
class_desc->add_text("[");
}
if (is_enum_type) {
- class_desc->push_meta("$" + p_enum); //class
+ class_desc->push_meta("$" + link_t); // enum
} else {
- class_desc->push_meta("#" + t); //class
+ class_desc->push_meta("#" + link_t); // class
}
}
- class_desc->add_text(t);
+ class_desc->add_text(display_t);
if (can_ref) {
class_desc->pop(); // Pushed meta above.
if (add_array) {
@@ -338,7 +392,7 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
class_desc->pop();
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
- method_line[p_method.name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
+ method_line[p_method.name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description
const bool is_vararg = p_method.qualifiers.contains("vararg");
@@ -352,8 +406,8 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
_add_type(p_method.return_type, p_method.return_enum);
if (p_overview) {
- class_desc->pop(); //align
- class_desc->pop(); //cell
+ class_desc->pop(); // align
+ class_desc->pop(); // cell
class_desc->push_cell();
} else {
class_desc->add_text(" ");
@@ -368,7 +422,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
class_desc->pop();
if (p_overview && !p_method.description.strip_edges().is_empty()) {
- class_desc->pop(); //meta
+ class_desc->pop(); // meta
}
class_desc->push_color(theme_cache.symbol_color);
@@ -447,7 +501,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
}
if (p_overview) {
- class_desc->pop(); //cell
+ class_desc->pop(); // cell
}
}
@@ -488,8 +542,9 @@ void EditorHelp::_pop_code_font() {
class_desc->pop();
}
-Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
- if (!doc->class_list.has(p_class)) {
+Error EditorHelp::_goto_desc(const String &p_class) {
+ // If class doesn't have docs listed, attempt on-demand docgen
+ if (!doc->class_list.has(p_class) && !_attempt_doc_load(p_class)) {
return ERR_DOES_NOT_EXIST;
}
@@ -529,9 +584,9 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods)
if (any_previous && !m.is_empty()) {
class_desc->push_cell();
- class_desc->pop(); //cell
+ class_desc->pop(); // cell
class_desc->push_cell();
- class_desc->pop(); //cell
+ class_desc->pop(); // cell
}
String group_prefix;
@@ -549,9 +604,9 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods)
if (is_new_group && pass == 1) {
class_desc->push_cell();
- class_desc->pop(); //cell
+ class_desc->pop(); // cell
class_desc->push_cell();
- class_desc->pop(); //cell
+ class_desc->pop(); // cell
}
_add_method(m[i], true);
@@ -560,7 +615,7 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods)
any_previous = !m.is_empty();
}
- class_desc->pop(); //table
+ class_desc->pop(); // table
class_desc->pop();
_pop_code_font();
@@ -861,14 +916,15 @@ void EditorHelp::_update_doc() {
// Properties overview
HashSet<String> skip_methods;
- bool has_properties = cd.properties.size() != 0;
- if (cd.is_script_doc) {
- has_properties = false;
- for (int i = 0; i < cd.properties.size(); i++) {
- if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
- continue;
- }
- has_properties = true;
+ bool has_properties = false;
+ bool has_property_descriptions = false;
+ for (const DocData::PropertyDoc &prop : cd.properties) {
+ if (cd.is_script_doc && prop.name.begins_with("_") && prop.description.strip_edges().is_empty()) {
+ continue;
+ }
+ has_properties = true;
+ if (!prop.overridden) {
+ has_property_descriptions = true;
break;
}
}
@@ -1196,7 +1252,7 @@ void EditorHelp::_update_doc() {
_add_text(cd.signals[i].arguments[j].name);
class_desc->add_text(": ");
- _add_type(cd.signals[i].arguments[j].type);
+ _add_type(cd.signals[i].arguments[j].type, cd.signals[i].arguments[j].enumeration);
if (!cd.signals[i].arguments[j].default_value.is_empty()) {
class_desc->push_color(theme_cache.symbol_color);
class_desc->add_text(" = ");
@@ -1547,7 +1603,7 @@ void EditorHelp::_update_doc() {
}
// Property descriptions
- if (has_properties) {
+ if (has_property_descriptions) {
section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2));
_push_title_font();
class_desc->add_text(TTR("Property Descriptions"));
@@ -1767,7 +1823,6 @@ void EditorHelp::_request_help(const String &p_string) {
if (err == OK) {
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
}
- //100 palabras
}
void EditorHelp::_help_callback(const String &p_topic) {
@@ -2178,7 +2233,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control
tag_stack.push_front("font");
} else {
- p_rt->add_text("["); //ignore
+ p_rt->add_text("["); // ignore
pos = brk_pos + 1;
}
}
@@ -2310,9 +2365,9 @@ void EditorHelp::go_to_help(const String &p_help) {
_help_callback(p_help);
}
-void EditorHelp::go_to_class(const String &p_class, int p_scroll) {
+void EditorHelp::go_to_class(const String &p_class) {
_wait_for_thread();
- _goto_desc(p_class, p_scroll);
+ _goto_desc(p_class);
}
void EditorHelp::update_doc() {
@@ -2324,9 +2379,6 @@ void EditorHelp::update_doc() {
void EditorHelp::cleanup_doc() {
_wait_for_thread();
- if (doc_gen_use_threads) {
- thread.wait_to_finish();
- }
memdelete(doc);
}
@@ -2443,14 +2495,15 @@ void EditorHelpBit::_go_to_help(String p_what) {
}
void EditorHelpBit::_meta_clicked(String p_select) {
- if (p_select.begins_with("$")) { //enum
-
+ if (p_select.begins_with("$")) { // enum
String select = p_select.substr(1, p_select.length());
String class_name;
- if (select.contains(".")) {
- class_name = select.get_slice(".", 0);
+ int rfind = select.rfind(".");
+ if (rfind != -1) {
+ class_name = select.substr(0, rfind);
+ select = select.substr(rfind + 1);
} else {
- class_name = "@Global";
+ class_name = "@GlobalScope";
}
_go_to_help("class_enum:" + class_name + ":" + select);
return;
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 01e91b4593..4175ece816 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -178,7 +178,7 @@ class EditorHelp : public VBoxContainer {
void _class_desc_resized(bool p_force_update_theme);
int display_margin = 0;
- Error _goto_desc(const String &p_class, int p_vscr = -1);
+ Error _goto_desc(const String &p_class);
//void _update_history_buttons();
void _update_method_list(const Vector<DocData::MethodDoc> p_methods);
void _update_method_descriptions(const DocData::ClassDoc p_classdoc, const Vector<DocData::MethodDoc> p_methods, const String &p_method_type);
@@ -210,7 +210,7 @@ public:
static String get_cache_full_path();
void go_to_help(const String &p_help);
- void go_to_class(const String &p_class, int p_scroll = 0);
+ void go_to_class(const String &p_class);
void update_doc();
Vector<Pair<String, int>> get_sections();
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 204d918989..6aa508f40e 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -117,8 +117,11 @@ void EditorHelpSearch::_notification(int p_what) {
_update_icons();
} break;
- case NOTIFICATION_ENTER_TREE: {
+ case NOTIFICATION_READY: {
connect("confirmed", callable_mp(this, &EditorHelpSearch::_confirmed));
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
_update_icons();
} break;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 0c813f2f77..2b02818645 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3441,6 +3441,16 @@ void EditorInspector::set_property_name_style(EditorPropertyNameProcessor::Style
update_tree();
}
+void EditorInspector::set_use_settings_name_style(bool p_enable) {
+ if (use_settings_name_style == p_enable) {
+ return;
+ }
+ use_settings_name_style = p_enable;
+ if (use_settings_name_style) {
+ set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
+ }
+}
+
void EditorInspector::set_autoclear(bool p_enable) {
autoclear = p_enable;
}
@@ -3973,7 +3983,20 @@ void EditorInspector::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
_update_inspector_bg();
+ bool needs_update = false;
+
+ if (use_settings_name_style && EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
+ EditorPropertyNameProcessor::Style style = EditorPropertyNameProcessor::get_settings_style();
+ if (property_name_style != style) {
+ property_name_style = style;
+ needs_update = true;
+ }
+ }
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/inspector")) {
+ needs_update = true;
+ }
+
+ if (needs_update) {
update_tree();
}
} break;
@@ -4158,4 +4181,7 @@ EditorInspector::EditorInspector() {
ED_SHORTCUT("property_editor/copy_value", TTR("Copy Value"), KeyModifierMask::CMD_OR_CTRL | Key::C);
ED_SHORTCUT("property_editor/paste_value", TTR("Paste Value"), KeyModifierMask::CMD_OR_CTRL | Key::V);
ED_SHORTCUT("property_editor/copy_property_path", TTR("Copy Property Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
+
+ // `use_settings_name_style` is true by default, set the name style accordingly.
+ set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index b3709e93f4..e9a4eaec30 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -465,6 +465,7 @@ class EditorInspector : public ScrollContainer {
bool hide_metadata = true;
bool use_doc_hints = false;
EditorPropertyNameProcessor::Style property_name_style = EditorPropertyNameProcessor::STYLE_CAPITALIZED;
+ bool use_settings_name_style = true;
bool use_filter = false;
bool autoclear = false;
bool use_folding = false;
@@ -570,6 +571,9 @@ public:
EditorPropertyNameProcessor::Style get_property_name_style() const;
void set_property_name_style(EditorPropertyNameProcessor::Style p_style);
+ // If true, the inspector will update its property name style according to the current editor settings.
+ void set_use_settings_name_style(bool p_enable);
+
void set_autoclear(bool p_enable);
void set_show_categories(bool p_show);
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index 99803fd82d..167a5a3dba 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/filesystem_dock.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "main/main.h"
#include "scene/gui/box_container.h"
@@ -317,35 +318,35 @@ void EditorInterface::save_scene_as(const String &p_scene, bool p_with_preview)
// Scene playback.
void EditorInterface::play_main_scene() {
- EditorNode::get_singleton()->run_play();
+ EditorRunBar::get_singleton()->play_main_scene();
}
void EditorInterface::play_current_scene() {
- EditorNode::get_singleton()->run_play_current();
+ EditorRunBar::get_singleton()->play_current_scene();
}
void EditorInterface::play_custom_scene(const String &scene_path) {
- EditorNode::get_singleton()->run_play_custom(scene_path);
+ EditorRunBar::get_singleton()->play_custom_scene(scene_path);
}
void EditorInterface::stop_playing_scene() {
- EditorNode::get_singleton()->run_stop();
+ EditorRunBar::get_singleton()->stop_playing();
}
bool EditorInterface::is_playing_scene() const {
- return EditorNode::get_singleton()->is_run_playing();
+ return EditorRunBar::get_singleton()->is_playing();
}
String EditorInterface::get_playing_scene() const {
- return EditorNode::get_singleton()->get_run_playing_scene();
+ return EditorRunBar::get_singleton()->get_playing_scene();
}
void EditorInterface::set_movie_maker_enabled(bool p_enabled) {
- EditorNode::get_singleton()->set_movie_maker_enabled(p_enabled);
+ EditorRunBar::get_singleton()->set_movie_maker_enabled(p_enabled);
}
bool EditorInterface::is_movie_maker_enabled() const {
- return EditorNode::get_singleton()->is_movie_maker_enabled();
+ return EditorRunBar::get_singleton()->is_movie_maker_enabled();
}
// Base.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d6944b1ed0..2d9f9645b8 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -53,6 +53,8 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/panel_container.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/gui/tab_container.h"
@@ -65,6 +67,7 @@
#include "editor/audio_stream_preview.h"
#include "editor/debugger/editor_debugger_node.h"
+#include "editor/debugger/script_editor_debugger.h"
#include "editor/dependency_editor.h"
#include "editor/editor_about.h"
#include "editor/editor_audio_buses.h"
@@ -99,6 +102,7 @@
#include "editor/fbx_importer_manager.h"
#include "editor/filesystem_dock.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/gui/editor_toaster.h"
#include "editor/history_dock.h"
@@ -144,6 +148,7 @@
#include "editor/project_settings_editor.h"
#include "editor/register_exporters.h"
#include "editor/scene_tree_dock.h"
+#include "editor/window_wrapper.h"
#include <stdio.h>
#include <stdlib.h>
@@ -153,6 +158,8 @@ EditorNode *EditorNode::singleton = nullptr;
// The metadata key used to store and retrieve the version text to copy to the clipboard.
static const String META_TEXT_TO_COPY = "text_to_copy";
+static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
+
class AcceptDialogAutoReparent : public AcceptDialog {
GDCLASS(AcceptDialogAutoReparent, AcceptDialog);
@@ -633,6 +640,8 @@ void EditorNode::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
+ get_tree()->set_disable_node_threading(true); // No node threading while running editor.
+
Engine::get_singleton()->set_editor_hint(true);
Window *window = get_window();
@@ -759,7 +768,9 @@ void EditorNode::_notification(int p_what) {
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size");
+ EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/increase_scrollbar_touch_area") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles");
if (theme_changed) {
theme = create_custom_theme(theme_base->get_theme());
@@ -805,20 +816,6 @@ void EditorNode::_notification(int p_what) {
_build_icon_type_cache();
- if (write_movie_button->is_pressed()) {
- launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
- write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
- } else {
- launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
- write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
- }
-
- play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
- play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
- play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
- pause_button->set_icon(gui_base->get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
- stop_button->set_icon(gui_base->get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
-
prev_scene->set_icon(gui_base->get_theme_icon(SNAME("PrevScene"), SNAME("EditorIcons")));
distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")));
scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
@@ -1100,7 +1097,7 @@ void EditorNode::_sources_changed(bool p_exist) {
EditorResourcePreview::get_singleton()->start();
}
- _load_docks();
+ _load_editor_layout();
if (!defer_load_scene.is_empty()) {
Engine::get_singleton()->startup_benchmark_begin_measure("editor_load_scene");
@@ -1197,7 +1194,7 @@ void EditorNode::_vp_resized() {
}
void EditorNode::_titlebar_resized() {
- DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(menu_hb->get_global_position().y + menu_hb->get_size().y / 2, menu_hb->get_global_position().y + menu_hb->get_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
+ DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(title_bar->get_global_position().y + title_bar->get_size().y / 2, title_bar->get_global_position().y + title_bar->get_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
const Vector3i &margin = DisplayServer::get_singleton()->window_get_safe_title_margins(DisplayServer::MAIN_WINDOW_ID);
if (left_menu_spacer) {
int w = (gui_base->is_layout_rtl()) ? margin.y : margin.x;
@@ -1207,8 +1204,8 @@ void EditorNode::_titlebar_resized() {
int w = (gui_base->is_layout_rtl()) ? margin.x : margin.y;
right_menu_spacer->set_custom_minimum_size(Size2(w, 0));
}
- if (menu_hb) {
- menu_hb->set_custom_minimum_size(Size2(0, margin.z - menu_hb->get_global_position().y));
+ if (title_bar) {
+ title_bar->set_custom_minimum_size(Size2(0, margin.z - title_bar->get_global_position().y));
}
}
@@ -1469,39 +1466,28 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
}
}
-void EditorNode::_get_scene_metadata(const String &p_file) {
+void EditorNode::_load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file) {
Node *scene = editor_data.get_edited_scene_root();
if (!scene) {
return;
}
- String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg");
-
- Ref<ConfigFile> cf;
- cf.instantiate();
-
- Error err = cf->load(path);
- if (err != OK || !cf->has_section("editor_states")) {
- // Must not exist.
- return;
- }
-
List<String> esl;
- cf->get_section_keys("editor_states", &esl);
+ p_config_file->get_section_keys("editor_states", &esl);
Dictionary md;
for (const String &E : esl) {
- Variant st = cf->get_value("editor_states", E);
+ Variant st = p_config_file->get_value("editor_states", E);
if (st.get_type() != Variant::NIL) {
md[E] = st;
}
}
- editor_data.set_editor_states(md);
+ editor_data.set_editor_plugin_states(md);
}
-void EditorNode::_set_scene_metadata(const String &p_file, int p_idx) {
+void EditorNode::_save_editor_states(const String &p_file, int p_idx) {
Node *scene = editor_data.get_edited_scene_root(p_idx);
if (!scene) {
@@ -1514,20 +1500,27 @@ void EditorNode::_set_scene_metadata(const String &p_file, int p_idx) {
cf.instantiate();
Dictionary md;
-
if (p_idx < 0 || editor_data.get_edited_scene() == p_idx) {
- md = editor_data.get_editor_states();
+ md = editor_data.get_editor_plugin_states();
} else {
md = editor_data.get_scene_editor_states(p_idx);
}
List<Variant> keys;
md.get_key_list(&keys);
-
for (const Variant &E : keys) {
cf->set_value("editor_states", E, md[E]);
}
+ // Save the currently selected nodes.
+
+ List<Node *> selection = editor_selection->get_full_selected_node_list();
+ TypedArray<NodePath> selection_paths;
+ for (Node *selected_node : selection) {
+ selection_paths.push_back(selected_node->get_path());
+ }
+ cf->set_value("editor_states", "selected_nodes", selection_paths);
+
Error err = cf->save(path);
ERR_FAIL_COND_MSG(err != OK, "Cannot save config file to '" + path + "'.");
}
@@ -1817,9 +1810,9 @@ void EditorNode::_save_scene(String p_file, int idx) {
editor_data.apply_changes_in_editors();
List<Ref<AnimatedValuesBackup>> anim_backups;
_reset_animation_players(scene, &anim_backups);
- _save_default_environment();
+ save_default_environment();
- _set_scene_metadata(p_file, idx);
+ _save_editor_states(p_file, idx);
Ref<PackedScene> sdata;
@@ -1879,7 +1872,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
}
void EditorNode::save_all_scenes() {
- _menu_option_confirm(RUN_STOP, true);
+ project_run_bar->stop_playing();
_save_all_scenes();
}
@@ -1893,11 +1886,33 @@ void EditorNode::save_scene_list(Vector<String> p_scene_filenames) {
}
}
+void EditorNode::save_before_run() {
+ current_menu_option = FILE_SAVE_AND_RUN;
+ _menu_option_confirm(FILE_SAVE_AS_SCENE, true);
+ file->set_title(TTR("Save scene before running..."));
+}
+
+void EditorNode::try_autosave() {
+ if (!bool(EDITOR_GET("run/auto_save/save_before_running"))) {
+ return;
+ }
+
+ if (unsaved_cache) {
+ Node *scene = editor_data.get_edited_scene_root();
+
+ if (scene && !scene->get_scene_file_path().is_empty()) { // Only autosave if there is a scene and if it has a path.
+ _save_scene_with_preview(scene->get_scene_file_path());
+ }
+ }
+ _menu_option(FILE_SAVE_ALL_SCENES);
+ editor_data.save_editor_external_data();
+}
+
void EditorNode::restart_editor() {
exiting = true;
- if (editor_run.get_status() != EditorRun::STATUS_STOP) {
- editor_run.stop();
+ if (project_run_bar->is_playing()) {
+ project_run_bar->stop_playing();
}
String to_reopen;
@@ -1945,7 +1960,7 @@ void EditorNode::_save_all_scenes() {
if (!all_saved) {
show_warning(TTR("Could not save one or more scenes!"), TTR("Save All Scenes"));
}
- _save_default_environment();
+ save_default_environment();
}
void EditorNode::_mark_unsaved_scenes() {
@@ -1985,11 +2000,7 @@ void EditorNode::_dialog_action(String p_file) {
ProjectSettings::get_singleton()->save();
// TODO: Would be nice to show the project manager opened with the highlighted field.
- if ((bool)pick_main_scene->get_meta("from_native", false)) {
- run_native->resume_run_native();
- } else {
- _run(false, ""); // Automatically run the project.
- }
+ project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
} break;
case FILE_CLOSE:
case SCENE_TAB_CLOSE:
@@ -2010,10 +2021,10 @@ void EditorNode::_dialog_action(String p_file) {
return;
}
- _save_default_environment();
+ save_default_environment();
_save_scene_with_preview(p_file, scene_idx);
_add_to_recent_scenes(p_file);
- save_layout();
+ save_editor_layout_delayed();
if (scene_idx != -1) {
_discard_changes();
@@ -2024,9 +2035,9 @@ void EditorNode::_dialog_action(String p_file) {
case FILE_SAVE_AND_RUN: {
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
- _save_default_environment();
+ save_default_environment();
_save_scene_with_preview(p_file);
- _run(false, p_file);
+ project_run_bar->play_custom_scene(p_file);
}
} break;
@@ -2035,13 +2046,9 @@ void EditorNode::_dialog_action(String p_file) {
ProjectSettings::get_singleton()->save();
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
- _save_default_environment();
+ save_default_environment();
_save_scene_with_preview(p_file);
- if ((bool)pick_main_scene->get_meta("from_native", false)) {
- run_native->resume_run_native();
- } else {
- _run(false, p_file);
- }
+ project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
}
} break;
@@ -2253,7 +2260,7 @@ void EditorNode::push_item(Object *p_object, const String &p_property, bool p_in
_edit_current();
}
-void EditorNode::_save_default_environment() {
+void EditorNode::save_default_environment() {
Ref<Environment> fallback = get_tree()->get_root()->get_world_3d()->get_fallback_environment();
if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
@@ -2499,157 +2506,6 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
InspectorDock::get_singleton()->update(current_obj);
}
-void EditorNode::_write_movie_toggled(bool p_enabled) {
- if (p_enabled) {
- launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
- write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
- } else {
- launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
- write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
- }
-}
-
-void EditorNode::_run(bool p_current, const String &p_custom) {
- if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
- play_button->set_pressed(!_playing_edited);
- play_scene_button->set_pressed(_playing_edited);
- return;
- }
-
- play_button->set_pressed(false);
- play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
- play_scene_button->set_pressed(false);
- play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
- play_custom_scene_button->set_pressed(false);
- play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
-
- String write_movie_file;
- if (write_movie_button->is_pressed()) {
- if (p_current && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->has_meta("movie_file")) {
- // If the scene file has a movie_file metadata set, use this as file. Quick workaround if you want to have multiple scenes that write to multiple movies.
- write_movie_file = get_tree()->get_edited_scene_root()->get_meta("movie_file");
- } else {
- write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
- }
- if (write_movie_file == String()) {
- show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
- return;
- }
- }
-
- String run_filename;
-
- if ((p_current && p_custom.is_empty()) || (editor_data.get_edited_scene_root() && !p_custom.is_empty() && p_custom == editor_data.get_edited_scene_root()->get_scene_file_path())) {
- Node *scene = editor_data.get_edited_scene_root();
-
- if (!scene) {
- show_accept(TTR("There is no defined scene to run."), TTR("OK"));
- return;
- }
-
- if (scene->get_scene_file_path().is_empty()) {
- current_menu_option = FILE_SAVE_AND_RUN;
- _menu_option_confirm(FILE_SAVE_AS_SCENE, true);
- file->set_title(TTR("Save scene before running..."));
- return;
- }
-
- run_filename = scene->get_scene_file_path();
- } else if (!p_custom.is_empty()) {
- run_filename = p_custom;
- }
-
- if (run_filename.is_empty()) {
- // Evidently, run the scene.
- if (!ensure_main_scene(false)) {
- return;
- }
- run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
- }
-
- if (bool(EDITOR_GET("run/auto_save/save_before_running"))) {
- if (unsaved_cache) {
- Node *scene = editor_data.get_edited_scene_root();
-
- if (scene && !scene->get_scene_file_path().is_empty()) { // Only autosave if there is a scene and if it has a path.
- _save_scene_with_preview(scene->get_scene_file_path());
- }
- }
- _menu_option(FILE_SAVE_ALL_SCENES);
- editor_data.save_editor_external_data();
- }
-
- if (!call_build()) {
- return;
- }
-
- if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
- log->clear();
- }
-
- if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
- make_bottom_panel_item_visible(log);
- }
-
- EditorDebuggerNode::get_singleton()->start();
- Error error = editor_run.run(run_filename, write_movie_file);
- if (error != OK) {
- EditorDebuggerNode::get_singleton()->stop();
- show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
- return;
- }
-
- emit_signal(SNAME("play_pressed"));
- if (p_current) {
- run_current_filename = run_filename;
- play_scene_button->set_pressed(true);
- play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
- play_scene_button->set_tooltip_text(TTR("Reload the played scene."));
- } else if (!p_custom.is_empty()) {
- run_custom_filename = p_custom;
- play_custom_scene_button->set_pressed(true);
- play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
- play_custom_scene_button->set_tooltip_text(TTR("Reload the played scene."));
- } else {
- play_button->set_pressed(true);
- play_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
- play_button->set_tooltip_text(TTR("Reload the played scene."));
- }
- stop_button->set_disabled(false);
-
- _playing_edited = p_current;
-}
-
-void EditorNode::_run_native(const Ref<EditorExportPreset> &p_preset) {
- bool autosave = EDITOR_GET("run/auto_save/save_before_running");
- if (autosave) {
- _menu_option_confirm(FILE_SAVE_ALL_SCENES, false);
- }
- if (run_native->is_deploy_debug_remote_enabled()) {
- _menu_option_confirm(RUN_STOP, true);
-
- if (!call_build()) {
- return; // Build failed.
- }
-
- EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
- emit_signal(SNAME("play_pressed"));
- editor_run.run_native_notify();
- }
-}
-
-void EditorNode::_reset_play_buttons() {
- play_button->set_pressed(false);
- play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
- play_button->set_tooltip_text(TTR("Play the project."));
- play_scene_button->set_pressed(false);
- play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
- play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
- play_custom_scene_button->set_pressed(false);
- play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
- play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
-}
-
void EditorNode::_android_build_source_selected(const String &p_file) {
export_template_manager->install_android_template_from_file(p_file);
}
@@ -2748,7 +2604,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (scene_idx != -1) {
_discard_changes();
}
- save_layout();
+ save_editor_layout_delayed();
} else {
show_save_accept(vformat(TTR("%s no longer exists! Please specify a new save location."), scene->get_scene_file_path().get_base_dir()), TTR("OK"));
}
@@ -2816,6 +2672,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
_save_all_scenes();
} break;
+ case FILE_RUN_SCENE: {
+ project_run_bar->play_current_scene();
+ } break;
+
case FILE_EXPORT_PROJECT: {
project_export->popup_export();
} break;
@@ -2920,45 +2780,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
scene_tabs->set_current_tab(cur_idx);
} break;
- case RUN_PLAY: {
- run_play();
-
- } break;
- case RUN_PLAY_CUSTOM_SCENE: {
- if (run_custom_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
- _menu_option_confirm(RUN_STOP, true);
- quick_run->popup_dialog("PackedScene", true);
- quick_run->set_title(TTR("Quick Run Scene..."));
- play_custom_scene_button->set_pressed(false);
- } else {
- String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
- run_play_custom(last_custom_scene);
- }
-
- } break;
- case RUN_STOP: {
- if (editor_run.get_status() == EditorRun::STATUS_STOP) {
- break;
- }
-
- editor_run.stop();
- run_custom_filename.clear();
- run_current_filename.clear();
- stop_button->set_disabled(true);
- _reset_play_buttons();
-
- if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
- for (int i = 0; i < bottom_panel_items.size(); i++) {
- if (bottom_panel_items[i].control == log) {
- _bottom_panel_switch(false, i);
- break;
- }
- }
- }
- EditorDebuggerNode::get_singleton()->stop();
- emit_signal(SNAME("stop_pressed"));
-
- } break;
case FILE_SHOW_IN_FILESYSTEM: {
String path = editor_data.get_scene_path(editor_data.get_edited_scene());
@@ -2967,15 +2788,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
}
} break;
- case RUN_PLAY_SCENE: {
- if (run_current_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
- run_play_current();
- } else {
- String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
- run_play_custom(last_current_scene);
- }
-
- } break;
case RUN_SETTINGS: {
project_settings_editor->popup_project_settings();
} break;
@@ -3271,7 +3083,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
void EditorNode::_exit_editor(int p_exit_code) {
exiting = true;
resource_preview->stop(); // Stop early to avoid crashes.
- _save_docks();
+ _save_editor_layout();
// Dim the editor window while it's quitting to make it clearer that it's busy.
dim_editor(true);
@@ -3299,12 +3111,12 @@ void EditorNode::_discard_changes(const String &p_str) {
_proceed_closing_scene_tabs();
} break;
case FILE_QUIT: {
- _menu_option_confirm(RUN_STOP, true);
+ project_run_bar->stop_playing();
_exit_editor(EXIT_SUCCESS);
} break;
case RUN_PROJECT_MANAGER: {
- _menu_option_confirm(RUN_STOP, true);
+ project_run_bar->stop_playing();
_exit_editor(EXIT_SUCCESS);
String exec = OS::get_singleton()->get_executable_path();
@@ -3490,6 +3302,7 @@ void EditorNode::_update_addon_config() {
if (enabled_addons.size() == 0) {
ProjectSettings::get_singleton()->set("editor_plugins/enabled", Variant());
} else {
+ enabled_addons.sort();
ProjectSettings::get_singleton()->set("editor_plugins/enabled", enabled_addons);
}
@@ -3587,14 +3400,6 @@ bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
return addon_name_to_plugin.has("res://addons/" + p_addon + "/plugin.cfg");
}
-void EditorNode::set_movie_maker_enabled(bool p_enabled) {
- write_movie_button->set_pressed(p_enabled);
-}
-
-bool EditorNode::is_movie_maker_enabled() const {
- return write_movie_button->is_pressed();
-}
-
void EditorNode::_remove_edited_scene(bool p_change_tab) {
int new_index = editor_data.get_edited_scene();
int old_index = new_index;
@@ -3948,7 +3753,14 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
new_scene->set_scene_instance_state(Ref<SceneState>());
set_edited_scene(new_scene);
- _get_scene_metadata(p_scene);
+
+ String config_file_path = EditorPaths::get_singleton()->get_project_settings_dir().path_join(p_scene.get_file() + "-editstate-" + p_scene.md5_text() + ".cfg");
+ Ref<ConfigFile> editor_state_cf;
+ editor_state_cf.instantiate();
+ Error editor_state_cf_err = editor_state_cf->load(config_file_path);
+ if (editor_state_cf_err == OK || editor_state_cf->has_section("editor_states")) {
+ _load_editor_plugin_states_from_config(editor_state_cf);
+ }
_update_title();
_update_scene_tabs();
@@ -3969,8 +3781,20 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
push_item(new_scene);
+ // Load the selected nodes.
+ if (editor_state_cf->has_section_key("editor_states", "selected_nodes")) {
+ TypedArray<NodePath> selected_node_list = editor_state_cf->get_value("editor_states", "selected_nodes", TypedArray<String>());
+
+ for (int i = 0; i < selected_node_list.size(); i++) {
+ Node *selected_node = new_scene->get_node_or_null(selected_node_list[i]);
+ if (selected_node) {
+ editor_selection->add_node(selected_node);
+ }
+ }
+ }
+
if (!restoring_scenes) {
- save_layout();
+ save_editor_layout_delayed();
}
return OK;
@@ -4263,27 +4087,38 @@ void EditorNode::_quick_opened() {
}
}
-void EditorNode::_quick_run() {
- _run(false, quick_run->get_selected());
+void EditorNode::_project_run_started() {
+ if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
+ log->clear();
+ }
+
+ if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
+ make_bottom_panel_item_visible(log);
+ }
+}
+
+void EditorNode::_project_run_stopped() {
+ if (!bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
+ return;
+ }
+
+ for (int i = 0; i < bottom_panel_items.size(); i++) {
+ if (bottom_panel_items[i].control == log) {
+ _bottom_panel_switch(false, i);
+ break;
+ }
+ }
}
void EditorNode::notify_all_debug_sessions_exited() {
- _menu_option_confirm(RUN_STOP, false);
- stop_button->set_pressed(false);
- editor_run.stop();
+ project_run_bar->stop_playing();
}
void EditorNode::add_io_error(const String &p_error) {
- _load_error_notify(singleton, p_error);
-}
-
-void EditorNode::_load_error_notify(void *p_ud, const String &p_text) {
- EditorNode *en = static_cast<EditorNode *>(p_ud);
- if (en && en->load_error_dialog) {
- en->load_errors->add_image(en->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
- en->load_errors->add_text(p_text + "\n");
- en->load_error_dialog->attach_and_popup_centered_ratio(0.5);
- }
+ DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id());
+ singleton->load_errors->add_image(singleton->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
+ singleton->load_errors->add_text(p_error + "\n");
+ singleton->load_error_dialog->attach_and_popup_centered_ratio(0.5);
}
bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const {
@@ -4308,13 +4143,12 @@ bool EditorNode::is_scene_in_use(const String &p_path) {
return false;
}
+OS::ProcessID EditorNode::has_child_process(OS::ProcessID p_pid) const {
+ return project_run_bar->has_child_process(p_pid);
+}
+
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
- if (has_child_process(p_pid)) {
- editor_run.stop_child_process(p_pid);
- if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
- _menu_option_confirm(RUN_STOP, false);
- }
- }
+ project_run_bar->stop_child_process(p_pid);
}
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
@@ -4652,67 +4486,66 @@ void EditorNode::_copy_warning(const String &p_str) {
DisplayServer::get_singleton()->clipboard_set(warning->get_text());
}
-void EditorNode::_dock_floating_close_request(Control *p_control) {
- // Through the MarginContainer to the Window.
- Window *window = static_cast<Window *>(p_control->get_parent()->get_parent());
- int window_slot = window->get_meta("dock_slot");
+void EditorNode::_dock_floating_close_request(WindowWrapper *p_wrapper) {
+ int dock_slot_num = p_wrapper->get_meta("dock_slot");
+ int dock_slot_index = p_wrapper->get_meta("dock_index");
- p_control->get_parent()->remove_child(p_control);
- dock_slot[window_slot]->add_child(p_control);
- dock_slot[window_slot]->move_child(p_control, MIN((int)window->get_meta("dock_index"), dock_slot[window_slot]->get_tab_count() - 1));
- dock_slot[window_slot]->set_current_tab(dock_slot[window_slot]->get_tab_idx_from_control(p_control));
- dock_slot[window_slot]->set_tab_title(dock_slot[window_slot]->get_tab_idx_from_control(p_control), TTRGET(p_control->get_name()));
+ // Give back the dock to the original owner.
+ Control *dock = p_wrapper->release_wrapped_control();
- window->queue_free();
+ dock_slot[dock_slot_num]->add_child(dock);
+ dock_slot[dock_slot_num]->move_child(dock, MIN(dock_slot_index, dock_slot[dock_slot_num]->get_tab_count()));
+ dock_slot[dock_slot_num]->set_current_tab(dock_slot_index);
- _update_dock_containers();
+ floating_docks.erase(p_wrapper);
+ p_wrapper->queue_free();
- floating_docks.erase(p_control);
+ _update_dock_containers();
_edit_current();
}
-void EditorNode::_dock_make_float() {
+void EditorNode::_dock_make_selected_float() {
Control *dock = dock_slot[dock_popup_selected_idx]->get_current_tab_control();
- ERR_FAIL_COND(!dock);
+ _dock_make_float(dock, dock_popup_selected_idx);
+
+ dock_select_popup->hide();
+ _edit_current();
+}
+
+void EditorNode::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
+ ERR_FAIL_COND(!p_dock);
Size2 borders = Size2(4, 4) * EDSCALE;
// Remember size and position before removing it from the main window.
- Size2 dock_size = dock->get_size() + borders * 2;
- Point2 dock_screen_pos = dock->get_global_position() + get_tree()->get_root()->get_position() - borders;
-
- int dock_index = dock->get_index(false);
- dock_slot[dock_popup_selected_idx]->remove_child(dock);
-
- Window *window = memnew(Window);
- window->set_title(TTRGET(dock->get_name()));
- Panel *p = memnew(Panel);
- p->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("PanelForeground"), SNAME("EditorStyles")));
- p->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- window->add_child(p);
- MarginContainer *margin = memnew(MarginContainer);
- margin->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- margin->add_theme_constant_override("margin_right", borders.width);
- margin->add_theme_constant_override("margin_top", borders.height);
- margin->add_theme_constant_override("margin_left", borders.width);
- margin->add_theme_constant_override("margin_bottom", borders.height);
- window->add_child(margin);
- dock->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- margin->add_child(dock);
- window->set_wrap_controls(true);
- window->set_size(dock_size);
- window->set_position(dock_screen_pos);
- window->set_transient(true);
- window->connect("close_requested", callable_mp(this, &EditorNode::_dock_floating_close_request).bind(dock));
- window->set_meta("dock_slot", dock_popup_selected_idx);
- window->set_meta("dock_index", dock_index);
- gui_base->add_child(window);
+ Size2 dock_size = p_dock->get_size() + borders * 2;
+ Point2 dock_screen_pos = p_dock->get_screen_position();
+
+ int dock_index = p_dock->get_index() - 1;
+ dock_slot[p_slot_index]->remove_child(p_dock);
+
+ WindowWrapper *wrapper = memnew(WindowWrapper);
+ wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), p_dock->get_name()));
+ wrapper->set_margins_enabled(true);
+
+ gui_base->add_child(wrapper);
+
+ wrapper->set_wrapped_control(p_dock);
+ wrapper->set_meta("dock_slot", p_slot_index);
+ wrapper->set_meta("dock_index", dock_index);
+ wrapper->set_meta("dock_name", p_dock->get_name().operator String());
+
+ wrapper->connect("window_close_requested", callable_mp(this, &EditorNode::_dock_floating_close_request).bind(wrapper));
dock_select_popup->hide();
+ if (p_show_window) {
+ wrapper->restore_window(Rect2i(dock_screen_pos, dock_size), get_window()->get_current_screen());
+ }
+
_update_dock_containers();
- floating_docks.push_back(dock);
+ floating_docks.push_back(wrapper);
_edit_current();
}
@@ -4789,7 +4622,7 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) {
_update_dock_containers();
_edit_current();
- _save_docks();
+ _save_editor_layout();
}
}
}
@@ -4815,7 +4648,7 @@ void EditorNode::_dock_move_left() {
dock_slot[dock_popup_selected_idx]->move_child(current_ctl, prev_ctl->get_index(false));
dock_select->queue_redraw();
_edit_current();
- _save_docks();
+ _save_editor_layout();
}
void EditorNode::_dock_move_right() {
@@ -4827,7 +4660,7 @@ void EditorNode::_dock_move_right() {
dock_slot[dock_popup_selected_idx]->move_child(next_ctl, current_ctl->get_index(false));
dock_select->queue_redraw();
_edit_current();
- _save_docks();
+ _save_editor_layout();
}
void EditorNode::_dock_select_draw() {
@@ -4916,7 +4749,7 @@ void EditorNode::_dock_select_draw() {
}
}
-void EditorNode::_save_docks() {
+void EditorNode::_save_editor_layout() {
if (waiting_for_first_scan) {
return; // Scanning, do not touch docks.
}
@@ -4926,7 +4759,8 @@ void EditorNode::_save_docks() {
config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
_save_docks_to_config(config, "docks");
- _save_open_scenes_to_config(config, "EditorNode");
+ _save_open_scenes_to_config(config);
+ _save_central_editor_layout_to_config(config);
editor_data.get_plugin_window_layout(config);
config->save(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
@@ -4952,12 +4786,41 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p
if (!names.is_empty()) {
p_layout->set_value(p_section, config_key, names);
}
+
+ int selected_tab_idx = dock_slot[i]->get_current_tab();
+ if (selected_tab_idx >= 0) {
+ p_layout->set_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx", selected_tab_idx);
+ }
}
- p_layout->set_value(p_section, "dock_filesystem_split", FileSystemDock::get_singleton()->get_split_offset());
- p_layout->set_value(p_section, "dock_filesystem_display_mode", FileSystemDock::get_singleton()->get_display_mode());
- p_layout->set_value(p_section, "dock_filesystem_file_sort", FileSystemDock::get_singleton()->get_file_sort());
- p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", FileSystemDock::get_singleton()->get_file_list_display_mode());
+ Dictionary floating_docks_dump;
+
+ for (WindowWrapper *wrapper : floating_docks) {
+ Control *dock = wrapper->get_wrapped_control();
+
+ Dictionary dock_dump;
+ dock_dump["window_rect"] = wrapper->get_window_rect();
+
+ int screen = wrapper->get_window_screen();
+ dock_dump["window_screen"] = wrapper->get_window_screen();
+ dock_dump["window_screen_rect"] = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
+
+ String name = dock->get_name();
+ floating_docks_dump[name] = dock_dump;
+
+ int dock_slot_id = wrapper->get_meta("dock_slot");
+ String config_key = "dock_" + itos(dock_slot_id + 1);
+
+ String names = p_layout->get_value(p_section, config_key, "");
+ if (names.is_empty()) {
+ names = name;
+ } else {
+ names += "," + name;
+ }
+ p_layout->set_value(p_section, config_key, names);
+ }
+
+ p_layout->set_value(p_section, "dock_floating", floating_docks_dump);
for (int i = 0; i < vsplits.size(); i++) {
if (vsplits[i]->is_visible_in_tree()) {
@@ -4968,10 +4831,21 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p
for (int i = 0; i < hsplits.size(); i++) {
p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
}
+
+ // Save FileSystemDock state.
+
+ p_layout->set_value(p_section, "dock_filesystem_split", FileSystemDock::get_singleton()->get_split_offset());
+ p_layout->set_value(p_section, "dock_filesystem_display_mode", FileSystemDock::get_singleton()->get_display_mode());
+ p_layout->set_value(p_section, "dock_filesystem_file_sort", FileSystemDock::get_singleton()->get_file_sort());
+ p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", FileSystemDock::get_singleton()->get_file_list_display_mode());
+ PackedStringArray selected_files = FileSystemDock::get_singleton()->get_selected_paths();
+ p_layout->set_value(p_section, "dock_filesystem_selected_paths", selected_files);
+ Vector<String> uncollapsed_paths = FileSystemDock::get_singleton()->get_uncollapsed_paths();
+ p_layout->set_value(p_section, "dock_filesystem_uncollapsed_paths", uncollapsed_paths);
}
-void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
- Array scenes;
+void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout) {
+ PackedStringArray scenes;
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
String path = editor_data.get_scene_path(i);
if (path.is_empty()) {
@@ -4979,18 +4853,21 @@ void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout, const Str
}
scenes.push_back(path);
}
- p_layout->set_value(p_section, "open_scenes", scenes);
+ 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());
+ p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
}
-void EditorNode::save_layout() {
- dock_drag_timer->start();
+void EditorNode::save_editor_layout_delayed() {
+ editor_layout_save_delay_timer->start();
}
void EditorNode::_dock_split_dragged(int ofs) {
- dock_drag_timer->start();
+ editor_layout_save_delay_timer->start();
}
-void EditorNode::_load_docks() {
+void EditorNode::_load_editor_layout() {
Ref<ConfigFile> config;
config.instantiate();
Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
@@ -5003,7 +4880,8 @@ void EditorNode::_load_docks() {
}
_load_docks_from_config(config, "docks");
- _load_open_scenes_from_config(config, "EditorNode");
+ _load_open_scenes_from_config(config);
+ _load_central_editor_layout_from_config(config);
editor_data.set_plugin_window_layout(config);
}
@@ -5100,7 +4978,24 @@ void EditorNode::_dock_tab_changed(int p_tab) {
}
}
+void EditorNode::_restore_floating_dock(const Dictionary &p_dock_dump, Control *p_dock, int p_slot_index) {
+ WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(p_dock);
+ if (!wrapper) {
+ _dock_make_float(p_dock, p_slot_index, false);
+ wrapper = floating_docks[floating_docks.size() - 1];
+ }
+
+ wrapper->restore_window_from_saved_position(
+ p_dock_dump.get("window_rect", Rect2i()),
+ p_dock_dump.get("window_screen", -1),
+ p_dock_dump.get("window_screen_rect", Rect2i()));
+}
+
void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
+ Dictionary floating_docks_dump = p_layout->get_value(p_section, "dock_floating", Dictionary());
+
+ bool restore_window_on_load = EDITOR_GET("interface/multi_window/restore_windows_on_load");
+
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1))) {
continue;
@@ -5110,6 +5005,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
for (int j = names.size() - 1; j >= 0; j--) {
String name = names[j];
+
// FIXME: Find it, in a horribly inefficient way.
int atidx = -1;
Control *node = nullptr;
@@ -5124,45 +5020,55 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
atidx = k;
break;
}
- if (atidx == -1) { // Well, it's not anywhere.
+
+ if (atidx == -1) {
+ // Try floating docks.
+ for (WindowWrapper *wrapper : floating_docks) {
+ if (wrapper->get_meta("dock_name") == name) {
+ if (restore_window_on_load && floating_docks_dump.has(name)) {
+ _restore_floating_dock(floating_docks_dump[name], wrapper, i);
+ return;
+ } else {
+ _dock_floating_close_request(wrapper);
+ atidx = wrapper->get_meta("dock_index");
+ }
+ }
+ }
+
+ // Well, it's not anywhere.
continue;
}
if (atidx == i) {
dock_slot[i]->move_child(node, 0);
- continue;
- }
+ } else if (atidx != -1) {
+ dock_slot[atidx]->remove_child(node);
- dock_slot[atidx]->remove_child(node);
+ if (dock_slot[atidx]->get_tab_count() == 0) {
+ dock_slot[atidx]->hide();
+ }
+ dock_slot[i]->add_child(node);
+ dock_slot[i]->move_child(node, 0);
+ dock_slot[i]->set_tab_title(0, TTRGET(node->get_name()));
+ dock_slot[i]->show();
+ }
- if (dock_slot[atidx]->get_tab_count() == 0) {
- dock_slot[atidx]->hide();
+ WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(node);
+ if (restore_window_on_load && floating_docks_dump.has(name)) {
+ _restore_floating_dock(floating_docks_dump[name], node, i);
+ } else if (wrapper) {
+ _dock_floating_close_request(wrapper);
}
- dock_slot[i]->add_child(node);
- dock_slot[i]->move_child(node, 0);
- dock_slot[i]->set_tab_title(0, TTRGET(node->get_name()));
- dock_slot[i]->show();
}
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_split")) {
- int fs_split_ofs = p_layout->get_value(p_section, "dock_filesystem_split");
- FileSystemDock::get_singleton()->set_split_offset(fs_split_ofs);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
- FileSystemDock::DisplayMode dock_filesystem_display_mode = FileSystemDock::DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
- FileSystemDock::get_singleton()->set_display_mode(dock_filesystem_display_mode);
- }
- if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) {
- FileSystemDock::FileSortOption dock_filesystem_file_sort = FileSystemDock::FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort")));
- FileSystemDock::get_singleton()->set_file_sort(dock_filesystem_file_sort);
- }
+ if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx")) {
+ continue;
+ }
- if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
- FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
- FileSystemDock::get_singleton()->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
+ int selected_tab_idx = p_layout->get_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx");
+ if (selected_tab_idx >= 0 && selected_tab_idx < dock_slot[i]->get_tab_count()) {
+ dock_slot[i]->call_deferred("set_current_tab", selected_tab_idx);
+ }
}
for (int i = 0; i < vsplits.size(); i++) {
@@ -5202,24 +5108,141 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
dock_slot[i]->set_current_tab(0);
}
}
+
+ // FileSystemDock.
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_split")) {
+ int fs_split_ofs = p_layout->get_value(p_section, "dock_filesystem_split");
+ FileSystemDock::get_singleton()->set_split_offset(fs_split_ofs);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
+ FileSystemDock::DisplayMode dock_filesystem_display_mode = FileSystemDock::DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
+ FileSystemDock::get_singleton()->set_display_mode(dock_filesystem_display_mode);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) {
+ FileSystemDock::FileSortOption dock_filesystem_file_sort = FileSystemDock::FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort")));
+ FileSystemDock::get_singleton()->set_file_sort(dock_filesystem_file_sort);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
+ FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
+ FileSystemDock::get_singleton()->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_selected_paths")) {
+ PackedStringArray dock_filesystem_selected_paths = p_layout->get_value(p_section, "dock_filesystem_selected_paths");
+ for (int i = 0; i < dock_filesystem_selected_paths.size(); i++) {
+ FileSystemDock::get_singleton()->select_file(dock_filesystem_selected_paths[i]);
+ }
+ }
+
+ // Restore collapsed state of FileSystemDock.
+ if (p_layout->has_section_key(p_section, "dock_filesystem_uncollapsed_paths")) {
+ PackedStringArray uncollapsed_tis = p_layout->get_value(p_section, "dock_filesystem_uncollapsed_paths");
+ for (int i = 0; i < uncollapsed_tis.size(); i++) {
+ TreeItem *uncollapsed_ti = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata(uncollapsed_tis[i], 0);
+ if (uncollapsed_ti) {
+ uncollapsed_ti->set_collapsed(false);
+ }
+ }
+ FileSystemDock::get_singleton()->get_tree_control()->queue_redraw();
+ }
+}
+
+void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file) {
+ // Bottom panel.
+
+ int center_split_offset = center_split->get_split_offset();
+ p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset", center_split_offset);
+
+ int selected_bottom_panel_item_idx = -1;
+ for (int i = 0; i < bottom_panel_items.size(); i++) {
+ if (bottom_panel_items[i].button->is_pressed()) {
+ selected_bottom_panel_item_idx = i;
+ break;
+ }
+ }
+ if (selected_bottom_panel_item_idx != -1) {
+ p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item", selected_bottom_panel_item_idx);
+ }
+
+ // Debugger tab.
+
+ int selected_default_debugger_tab_idx = EditorDebuggerNode::get_singleton()->get_default_debugger()->get_current_debugger_tab();
+ p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx", selected_default_debugger_tab_idx);
+
+ // Main editor (plugin).
+
+ int selected_main_editor_idx = -1;
+ for (int i = 0; i < main_editor_buttons.size(); i++) {
+ if (main_editor_buttons[i]->is_pressed()) {
+ selected_main_editor_idx = i;
+ break;
+ }
+ }
+ if (selected_main_editor_idx != -1) {
+ p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx", selected_main_editor_idx);
+ }
+}
+
+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);
+ }
+
+ 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()) {
+ _bottom_panel_switch(true, selected_bottom_panel_item_idx);
+ }
+ }
+
+ // Debugger tab.
+
+ if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx")) {
+ int selected_default_debugger_tab_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx");
+ EditorDebuggerNode::get_singleton()->get_default_debugger()->switch_to_debugger(selected_default_debugger_tab_idx);
+ }
+
+ // Main editor (plugin).
+
+ if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx")) {
+ int selected_main_editor_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx");
+ if (selected_main_editor_idx >= 0 && selected_main_editor_idx < main_editor_buttons.size()) {
+ callable_mp(this, &EditorNode::editor_select).call_deferred(selected_main_editor_idx);
+ }
+ }
}
-void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
+void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) {
if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
return;
}
- if (!p_layout->has_section(p_section) || !p_layout->has_section_key(p_section, "open_scenes")) {
+ if (!p_layout->has_section(EDITOR_NODE_CONFIG_SECTION) ||
+ !p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "open_scenes")) {
return;
}
restoring_scenes = true;
- Array scenes = p_layout->get_value(p_section, "open_scenes");
+ PackedStringArray scenes = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
for (int i = 0; i < scenes.size(); i++) {
load_scene(scenes[i]);
}
- save_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);
+ }
+
+ save_editor_layout_delayed();
restoring_scenes = false;
}
@@ -5234,10 +5257,10 @@ bool EditorNode::has_scenes_in_session() {
if (err != OK) {
return false;
}
- if (!config->has_section("EditorNode") || !config->has_section_key("EditorNode", "open_scenes")) {
+ if (!config->has_section(EDITOR_NODE_CONFIG_SECTION) || !config->has_section_key(EDITOR_NODE_CONFIG_SECTION, "open_scenes")) {
return false;
}
- Array scenes = config->get_value("EditorNode", "open_scenes");
+ Array scenes = config->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
return !scenes.is_empty();
}
@@ -5277,45 +5300,6 @@ bool EditorNode::ensure_main_scene(bool p_from_native) {
return true;
}
-Error EditorNode::run_play_native(int p_id) {
- return run_native->run_native(p_id);
-}
-
-void EditorNode::run_play() {
- _menu_option_confirm(RUN_STOP, true);
- _run(false);
-}
-
-void EditorNode::run_play_current() {
- _save_default_environment();
- _menu_option_confirm(RUN_STOP, true);
- _run(true);
-}
-
-void EditorNode::run_play_custom(const String &p_custom) {
- bool is_current = !run_current_filename.is_empty();
- _menu_option_confirm(RUN_STOP, true);
- _run(is_current, p_custom);
-}
-
-void EditorNode::run_stop() {
- _menu_option_confirm(RUN_STOP, false);
-}
-
-bool EditorNode::is_run_playing() const {
- EditorRun::Status status = editor_run.get_status();
- return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
-}
-
-String EditorNode::get_run_playing_scene() const {
- String run_filename = editor_run.get_running_scene();
- if (run_filename.is_empty() && is_run_playing()) {
- run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
- }
-
- return run_filename;
-}
-
void EditorNode::_immediate_dialog_confirmed() {
immediate_dialog_confirmed = true;
}
@@ -5402,7 +5386,7 @@ void EditorNode::_layout_menu_option(int p_id) {
} break;
case SETTINGS_LAYOUT_DEFAULT: {
_load_docks_from_config(default_layout, "docks");
- _save_docks();
+ _save_editor_layout();
} break;
default: {
Ref<ConfigFile> config;
@@ -5413,7 +5397,7 @@ void EditorNode::_layout_menu_option(int p_id) {
}
_load_docks_from_config(config, editor_layouts->get_item_text(p_id));
- _save_docks();
+ _save_editor_layout();
}
}
}
@@ -5485,7 +5469,7 @@ void EditorNode::_scene_tab_closed(int p_tab, int p_option) {
_discard_changes();
}
- save_layout();
+ save_editor_layout_delayed();
_update_scene_tabs();
}
@@ -5536,7 +5520,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
if (scene_tabs->get_hovered_tab() >= 0) {
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
- scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE);
+ scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE);
scene_tabs_context_menu->add_separator();
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), FILE_CLOSE);
@@ -5986,7 +5970,7 @@ void EditorNode::reload_scene(const String &p_path) {
if (current_tab == scene_idx) {
editor_data.apply_changes_in_editors();
- _set_scene_metadata(p_path);
+ _save_editor_states(p_path);
}
// Reload scene.
@@ -6549,14 +6533,13 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base);
- ADD_SIGNAL(MethodInfo("play_pressed"));
- ADD_SIGNAL(MethodInfo("stop_pressed"));
ADD_SIGNAL(MethodInfo("request_help_search"));
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("project_settings_changed"));
ADD_SIGNAL(MethodInfo("scene_changed"));
+ ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path")));
}
static Node *_resource_get_edited_scene() {
@@ -6742,8 +6725,8 @@ EditorNode::EditorNode() {
}
ResourceLoader::set_abort_on_missing_resources(false);
- ResourceLoader::set_error_notify_func(this, _load_error_notify);
- ResourceLoader::set_dependency_error_notify_func(this, _dependency_error_report);
+ ResourceLoader::set_error_notify_func(&EditorNode::add_io_error);
+ ResourceLoader::set_dependency_error_notify_func(&EditorNode::_dependency_error_report);
{
// Register importers at the beginning, so dialogs are created with the right extensions.
@@ -6934,8 +6917,8 @@ EditorNode::EditorNode() {
main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8);
main_vbox->add_theme_constant_override("separation", 8 * EDSCALE);
- menu_hb = memnew(EditorTitleBar);
- main_vbox->add_child(menu_hb);
+ title_bar = memnew(EditorTitleBar);
+ main_vbox->add_child(title_bar);
left_l_hsplit = memnew(HSplitContainer);
main_vbox->add_child(left_l_hsplit);
@@ -7046,13 +7029,16 @@ EditorNode::EditorNode() {
dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
dock_vb->add_child(dock_select);
- dock_float = memnew(Button);
- dock_float->set_text(TTR("Make Floating"));
- dock_float->set_focus_mode(Control::FOCUS_NONE);
- dock_float->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- dock_float->connect("pressed", callable_mp(this, &EditorNode::_dock_make_float));
+ if (!SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && 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"));
+ dock_float->set_focus_mode(Control::FOCUS_NONE);
+ dock_float->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
+ dock_float->connect("pressed", callable_mp(this, &EditorNode::_dock_make_selected_float));
- dock_vb->add_child(dock_float);
+ dock_vb->add_child(dock_float);
+ }
dock_select_popup->reset_size();
@@ -7067,11 +7053,11 @@ EditorNode::EditorNode() {
dock_slot[i]->set_use_hidden_tabs_for_min_size(true);
}
- dock_drag_timer = memnew(Timer);
- add_child(dock_drag_timer);
- dock_drag_timer->set_wait_time(0.5);
- dock_drag_timer->set_one_shot(true);
- dock_drag_timer->connect("timeout", callable_mp(this, &EditorNode::_save_docks));
+ editor_layout_save_delay_timer = memnew(Timer);
+ add_child(editor_layout_save_delay_timer);
+ editor_layout_save_delay_timer->set_wait_time(0.5);
+ editor_layout_save_delay_timer->set_one_shot(true);
+ editor_layout_save_delay_timer->connect("timeout", callable_mp(this, &EditorNode::_save_editor_layout));
top_split = memnew(VSplitContainer);
center_split->add_child(top_split);
@@ -7175,11 +7161,11 @@ EditorNode::EditorNode() {
// Add spacer to avoid other controls under window minimize/maximize/close buttons (left side).
left_menu_spacer = memnew(Control);
left_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
- menu_hb->add_child(left_menu_spacer);
+ title_bar->add_child(left_menu_spacer);
}
main_menu = memnew(MenuBar);
- menu_hb->add_child(main_menu);
+ title_bar->add_child(main_menu);
main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles")));
main_menu->set_flat(true);
@@ -7355,7 +7341,7 @@ EditorNode::EditorNode() {
HBoxContainer *left_spacer = memnew(HBoxContainer);
left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
left_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- menu_hb->add_child(left_spacer);
+ title_bar->add_child(left_spacer);
if (can_expand && global_menu) {
project_title = memnew(Label);
@@ -7370,7 +7356,7 @@ EditorNode::EditorNode() {
}
main_editor_button_hb = memnew(HBoxContainer);
- menu_hb->add_child(main_editor_button_hb);
+ title_bar->add_child(main_editor_button_hb);
// Options are added and handled by DebuggerEditorPlugin.
debug_menu = memnew(PopupMenu);
@@ -7455,104 +7441,15 @@ EditorNode::EditorNode() {
Control *right_spacer = memnew(Control);
right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
right_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- menu_hb->add_child(right_spacer);
-
- launch_pad = memnew(PanelContainer);
- launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
- menu_hb->add_child(launch_pad);
-
- HBoxContainer *launch_pad_hb = memnew(HBoxContainer);
- launch_pad->add_child(launch_pad_hb);
-
- play_button = memnew(Button);
- play_button->set_flat(true);
- launch_pad_hb->add_child(play_button);
- play_button->set_toggle_mode(true);
- play_button->set_focus_mode(Control::FOCUS_NONE);
- play_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY));
- play_button->set_tooltip_text(TTR("Run the project's default scene."));
-
- ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
- ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
- play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
-
- pause_button = memnew(Button);
- pause_button->set_flat(true);
- pause_button->set_toggle_mode(true);
- pause_button->set_icon(gui_base->get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
- pause_button->set_focus_mode(Control::FOCUS_NONE);
- pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
- pause_button->set_disabled(true);
- launch_pad_hb->add_child(pause_button);
-
- ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
- ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
- pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
-
- stop_button = memnew(Button);
- stop_button->set_flat(true);
- launch_pad_hb->add_child(stop_button);
- stop_button->set_focus_mode(Control::FOCUS_NONE);
- stop_button->set_icon(gui_base->get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
- stop_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_STOP));
- stop_button->set_tooltip_text(TTR("Stop the currently running project."));
- stop_button->set_disabled(true);
-
- ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
- ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
- stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
-
- run_native = memnew(EditorRunNative);
- launch_pad_hb->add_child(run_native);
- run_native->connect("native_run", callable_mp(this, &EditorNode::_run_native));
-
- play_scene_button = memnew(Button);
- play_scene_button->set_flat(true);
- launch_pad_hb->add_child(play_scene_button);
- play_scene_button->set_toggle_mode(true);
- play_scene_button->set_focus_mode(Control::FOCUS_NONE);
- play_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_SCENE));
- play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
-
- ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
- ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
- play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
-
- play_custom_scene_button = memnew(Button);
- play_custom_scene_button->set_flat(true);
- launch_pad_hb->add_child(play_custom_scene_button);
- play_custom_scene_button->set_toggle_mode(true);
- play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
- play_custom_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_CUSTOM_SCENE));
- play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
-
- _reset_play_buttons();
-
- ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
- ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
- play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
-
- write_movie_panel = memnew(PanelContainer);
- write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
- launch_pad_hb->add_child(write_movie_panel);
-
- write_movie_button = memnew(Button);
- write_movie_button->set_flat(true);
- write_movie_button->set_toggle_mode(true);
- write_movie_panel->add_child(write_movie_button);
- write_movie_button->set_pressed(false);
- write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
- write_movie_button->set_focus_mode(Control::FOCUS_NONE);
- write_movie_button->connect("toggled", callable_mp(this, &EditorNode::_write_movie_toggled));
- write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
-
- // This button behaves differently, so color it as such.
- write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7));
- write_movie_button->add_theme_color_override("icon_pressed_color", Color(0, 0, 0, 0.84));
- write_movie_button->add_theme_color_override("icon_hover_color", Color(1, 1, 1, 0.9));
+ title_bar->add_child(right_spacer);
+
+ project_run_bar = memnew(EditorRunBar);
+ title_bar->add_child(project_run_bar);
+ project_run_bar->connect("play_pressed", callable_mp(this, &EditorNode::_project_run_started));
+ project_run_bar->connect("stop_pressed", callable_mp(this, &EditorNode::_project_run_stopped));
HBoxContainer *right_menu_hb = memnew(HBoxContainer);
- menu_hb->add_child(right_menu_hb);
+ title_bar->add_child(right_menu_hb);
renderer = memnew(OptionButton);
renderer->set_visible(true);
@@ -7570,7 +7467,7 @@ EditorNode::EditorNode() {
// Add spacer to avoid other controls under the window minimize/maximize/close buttons (right side).
right_menu_spacer = memnew(Control);
right_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
- menu_hb->add_child(right_menu_spacer);
+ title_bar->add_child(right_menu_spacer);
}
String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method");
@@ -7641,7 +7538,7 @@ EditorNode::EditorNode() {
FileSystemDock *filesystem_dock = memnew(FileSystemDock);
filesystem_dock->connect("inherit", callable_mp(this, &EditorNode::_inherit_request));
filesystem_dock->connect("instantiate", callable_mp(this, &EditorNode::_instantiate_request));
- filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_docks));
+ filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_editor_layout));
get_project_settings()->connect_filesystem_dock_signals(filesystem_dock);
history_dock = memnew(HistoryDock);
@@ -8007,10 +7904,6 @@ EditorNode::EditorNode() {
gui_base->add_child(quick_open);
quick_open->connect("quick_open", callable_mp(this, &EditorNode::_quick_opened));
- quick_run = memnew(EditorQuickOpen);
- gui_base->add_child(quick_run);
- quick_run->connect("quick_open", callable_mp(this, &EditorNode::_quick_run));
-
_update_recent_scenes();
set_process_shortcut_input(true);
@@ -8082,14 +7975,14 @@ EditorNode::EditorNode() {
screenshot_timer->set_owner(get_owner());
// Adjust spacers to center 2D / 3D / Script buttons.
- int max_w = MAX(launch_pad->get_minimum_size().x + right_menu_hb->get_minimum_size().x, main_menu->get_minimum_size().x);
+ int max_w = MAX(project_run_bar->get_minimum_size().x + right_menu_hb->get_minimum_size().x, main_menu->get_minimum_size().x);
left_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - main_menu->get_minimum_size().x), 0));
- right_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - launch_pad->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0));
+ right_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - project_run_bar->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0));
// Extend menu bar to window title.
if (can_expand) {
DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, true, DisplayServer::MAIN_WINDOW_ID);
- menu_hb->set_can_move_window(true);
+ title_bar->set_can_move_window(true);
}
String exec = OS::get_singleton()->get_executable_path();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 831e2989f5..221637be1c 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -35,8 +35,6 @@
#include "editor/editor_data.h"
#include "editor/editor_folding.h"
#include "editor/editor_plugin.h"
-#include "editor/editor_run.h"
-#include "editor/export/editor_export.h"
typedef void (*EditorNodeInitCallback)();
typedef void (*EditorPluginInitializeCallback)();
@@ -59,6 +57,8 @@ class Node2D;
class OptionButton;
class Panel;
class PanelContainer;
+class PopupPanel;
+class RichTextLabel;
class SubViewport;
class TabBar;
class TabContainer;
@@ -92,6 +92,7 @@ class EditorQuickOpen;
class EditorPropertyResource;
class EditorResourcePreview;
class EditorResourceConversionPlugin;
+class EditorRunBar;
class EditorRunNative;
class EditorSelectionHistory;
class EditorSettingsDialog;
@@ -112,6 +113,7 @@ class ProjectSettingsEditor;
class RunSettingsDialog;
class SceneImportSettings;
class ScriptCreateDialog;
+class WindowWrapper;
class EditorNode : public Node {
GDCLASS(EditorNode, Node);
@@ -162,6 +164,7 @@ private:
FILE_SAVE_ALL_SCENES,
FILE_SAVE_AND_RUN,
FILE_SAVE_AND_RUN_MAIN_SCENE,
+ FILE_RUN_SCENE,
FILE_SHOW_IN_FILESYSTEM,
FILE_EXPORT_PROJECT,
FILE_EXPORT_MESH_LIBRARY,
@@ -188,11 +191,7 @@ private:
TOOLS_CUSTOM,
RESOURCE_SAVE,
RESOURCE_SAVE_AS,
- RUN_PLAY,
- RUN_STOP,
- RUN_PLAY_SCENE,
- RUN_PLAY_CUSTOM_SCENE,
RUN_SETTINGS,
RUN_USER_DATA_FOLDER,
RELOAD_CURRENT_PROJECT,
@@ -265,7 +264,6 @@ private:
EditorData editor_data;
EditorFolding editor_folding;
- EditorRun editor_run;
EditorSelectionHistory editor_history;
EditorCommandPalette *command_palette = nullptr;
@@ -277,9 +275,7 @@ private:
EditorPluginList *editor_plugins_force_over = nullptr;
EditorPluginList *editor_plugins_over = nullptr;
EditorQuickOpen *quick_open = nullptr;
- EditorQuickOpen *quick_run = nullptr;
EditorResourcePreview *resource_preview = nullptr;
- EditorRunNative *run_native = nullptr;
EditorSelection *editor_selection = nullptr;
EditorSettingsDialog *editor_settings_dialog = nullptr;
HistoryDock *history_dock = nullptr;
@@ -342,7 +338,8 @@ private:
Label *project_title = nullptr;
Control *left_menu_spacer = nullptr;
Control *right_menu_spacer = nullptr;
- EditorTitleBar *menu_hb = nullptr;
+ EditorTitleBar *title_bar = nullptr;
+ EditorRunBar *project_run_bar = nullptr;
VBoxContainer *main_screen_vbox = nullptr;
MenuBar *main_menu = nullptr;
PopupMenu *file_menu = nullptr;
@@ -357,15 +354,6 @@ private:
Button *search_button = nullptr;
TextureProgressBar *audio_vu = nullptr;
- PanelContainer *launch_pad = nullptr;
- Button *play_button = nullptr;
- Button *pause_button = nullptr;
- Button *stop_button = nullptr;
- Button *play_scene_button = nullptr;
- Button *play_custom_scene_button = nullptr;
- PanelContainer *write_movie_panel = nullptr;
- Button *write_movie_button = nullptr;
-
Timer *screenshot_timer = nullptr;
PluginConfigDialog *plugin_config_dialog = nullptr;
@@ -433,7 +421,7 @@ private:
Button *new_inherited_button = nullptr;
String open_import_request;
- Vector<Control *> floating_docks;
+ Vector<WindowWrapper *> floating_docks;
Button *dock_float = nullptr;
Button *dock_tab_move_left = nullptr;
@@ -442,7 +430,7 @@ private:
PopupPanel *dock_select_popup = nullptr;
Rect2 dock_select_rect[DOCK_SLOT_MAX];
TabContainer *dock_slot[DOCK_SLOT_MAX];
- Timer *dock_drag_timer = nullptr;
+ Timer *editor_layout_save_delay_timer = nullptr;
bool docks_visible = true;
int dock_popup_selected_idx = -1;
int dock_select_rect_over_idx = -1;
@@ -469,7 +457,6 @@ private:
bool scene_distraction_free = false;
bool script_distraction_free = false;
- bool _playing_edited = false;
bool changing_scene = false;
bool cmdline_export_mode = false;
bool convert_old = false;
@@ -496,9 +483,6 @@ private:
String external_file;
String open_navigate;
- String run_custom_filename;
- String run_current_filename;
-
DynamicFontImportSettings *fontdata_import_settings = nullptr;
SceneImportSettings *scene_import_settings = nullptr;
AudioStreamImportSettings *audio_stream_import_settings = nullptr;
@@ -520,12 +504,12 @@ private:
static int plugin_init_callback_count;
static Vector<EditorNodeInitCallback> _init_callbacks;
- static void _dependency_error_report(void *ud, const String &p_path, const String &p_dep, const String &p_type) {
- EditorNode *en = static_cast<EditorNode *>(ud);
- if (!en->dependency_errors.has(p_path)) {
- en->dependency_errors[p_path] = HashSet<String>();
+ static void _dependency_error_report(const String &p_path, const String &p_dep, const String &p_type) {
+ DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id());
+ if (!singleton->dependency_errors.has(p_path)) {
+ singleton->dependency_errors[p_path] = HashSet<String>();
}
- en->dependency_errors[p_path].insert(p_dep + "::" + p_type);
+ singleton->dependency_errors[p_path].insert(p_dep + "::" + p_type);
}
static Ref<Texture2D> _file_dialog_get_icon(const String &p_path);
@@ -534,7 +518,6 @@ private:
static void _editor_file_dialog_register(EditorFileDialog *p_dialog);
static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
- static void _load_error_notify(void *p_ud, const String &p_text);
static void _file_access_close_error_notify(const String &p_str);
static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich);
@@ -575,8 +558,8 @@ private:
void _node_renamed();
void _editor_select_next();
void _editor_select_prev();
- void _set_scene_metadata(const String &p_file, int p_idx = -1);
- void _get_scene_metadata(const String &p_file);
+ void _save_editor_states(const String &p_file, int p_idx = -1);
+ void _load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file);
void _update_title();
void _update_scene_tabs();
void _version_control_menu_option(int p_idx);
@@ -600,14 +583,10 @@ private:
void _instantiate_request(const Vector<String> &p_files);
void _quick_opened();
- void _quick_run();
void _open_command_palette();
- void _write_movie_toggled(bool p_enabled);
-
- void _run(bool p_current = false, const String &p_custom = "");
- void _run_native(const Ref<EditorExportPreset> &p_preset);
- void _reset_play_buttons();
+ void _project_run_started();
+ void _project_run_stopped();
void _add_to_recent_scenes(const String &p_scene);
void _update_recent_scenes();
@@ -649,8 +628,9 @@ private:
void _dock_pre_popup(int p_which);
void _dock_split_dragged(int ofs);
void _dock_popup_exit();
- void _dock_floating_close_request(Control *p_control);
- void _dock_make_float();
+ void _dock_floating_close_request(WindowWrapper *p_wrapper);
+ void _dock_make_selected_float();
+ void _dock_make_float(Control *p_control, int p_slot_index, bool p_show_window = true);
void _scene_tab_changed(int p_tab);
void _proceed_closing_scene_tabs();
bool _is_closing_editor() const;
@@ -667,15 +647,19 @@ private:
int _get_current_main_editor();
- void _save_docks();
- void _load_docks();
+ void _save_editor_layout();
+ void _load_editor_layout();
void _save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section);
+ void _restore_floating_dock(const Dictionary &p_dock_dump, Control *p_wrapper, int p_slot_index);
void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
void _update_dock_slots_visibility(bool p_keep_selected_tabs = false);
void _dock_tab_changed(int p_tab);
- void _save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section);
- void _load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section);
+ void _save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file);
+ void _load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file);
+
+ void _save_open_scenes_to_config(Ref<ConfigFile> p_layout);
+ void _load_open_scenes_from_config(Ref<ConfigFile> p_layout);
void _update_layouts_menu();
void _layout_menu_option(int p_id);
@@ -687,7 +671,6 @@ private:
void _inherit_imported(const String &p_action);
void _open_imported();
- void _save_default_environment();
void _update_update_spinner();
void _resources_changed(const Vector<String> &p_resources);
@@ -717,7 +700,6 @@ protected:
friend class FileSystemDock;
static void _bind_methods();
-
void _notification(int p_what);
int get_current_tab();
@@ -738,7 +720,7 @@ public:
static EditorData &get_editor_data() { return singleton->editor_data; }
static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
- static EditorTitleBar *get_menu_hb() { return singleton->menu_hb; }
+ static EditorTitleBar *get_title_bar() { return singleton->title_bar; }
static VSplitContainer *get_top_split() { return singleton->top_split; }
static String adjust_scene_name_casing(const String &root_name);
@@ -789,9 +771,6 @@ public:
void set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed = false);
bool is_addon_plugin_enabled(const String &p_addon) const;
- void set_movie_maker_enabled(bool p_enabled);
- bool is_movie_maker_enabled() const;
-
void edit_node(Node *p_node);
void edit_resource(const Ref<Resource> &p_resource);
@@ -874,7 +853,7 @@ public:
void notify_all_debug_sessions_exited();
- OS::ProcessID has_child_process(OS::ProcessID p_pid) const { return editor_run.has_child_process(p_pid); }
+ OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
void stop_child_process(OS::ProcessID p_pid);
Ref<Theme> get_editor_theme() const { return theme; }
@@ -907,7 +886,8 @@ public:
bool is_scene_in_use(const String &p_path);
- void save_layout();
+ void save_editor_layout_delayed();
+ void save_default_environment();
void open_export_template_manager();
@@ -918,8 +898,6 @@ public:
bool is_exiting() const { return exiting; }
- Button *get_pause_button() { return pause_button; }
-
Button *add_bottom_panel_item(String p_text, Control *p_item);
void make_bottom_panel_item_visible(Control *p_item);
void raise_bottom_panel_item(Control *p_item);
@@ -937,6 +915,8 @@ public:
void save_all_scenes();
void save_scene_list(Vector<String> p_scene_filenames);
+ void save_before_run();
+ void try_autosave();
void restart_editor();
void notify_settings_changed();
@@ -958,14 +938,6 @@ public:
Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin(const Ref<Resource> &p_for_resource);
bool ensure_main_scene(bool p_from_native);
-
- Error run_play_native(int p_id);
- void run_play();
- void run_play_current();
- void run_play_custom(const String &p_custom);
- void run_stop();
- bool is_run_playing() const;
- String get_run_playing_scene() const;
};
struct EditorProgress {
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 8ac5e77d35..9e22a0ead6 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -94,7 +94,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
switch (p_location) {
case CONTAINER_TOOLBAR: {
- EditorNode::get_menu_hb()->add_child(p_control);
+ EditorNode::get_title_bar()->add_child(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_MENU: {
@@ -147,7 +147,7 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati
switch (p_location) {
case CONTAINER_TOOLBAR: {
- EditorNode::get_menu_hb()->remove_child(p_control);
+ EditorNode::get_title_bar()->remove_child(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_MENU: {
@@ -471,7 +471,7 @@ bool EditorPlugin::build() {
}
void EditorPlugin::queue_save_layout() {
- EditorNode::get_singleton()->save_layout();
+ EditorNode::get_singleton()->save_editor_layout_delayed();
}
void EditorPlugin::make_bottom_panel_item_visible(Control *p_item) {
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index 9bcb25e9c0..1e582992d1 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/config_file.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/os/main_loop.h"
#include "editor/editor_node.h"
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 18c5d4ba51..04f10bceac 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1155,6 +1155,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
layer_group_size = 4;
layer_count = 32;
} break;
+
+ case LAYER_AVOIDANCE: {
+ basename = "layer_names/avoidance";
+ layer_group_size = 4;
+ layer_count = 32;
+ } break;
}
Vector<String> names;
@@ -4001,7 +4007,6 @@ void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
Ref<ViewportTexture> vt;
vt.instantiate();
vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
- vt->setup_local_to_scene();
emit_changed(get_edited_property(), vt);
update_property();
@@ -4284,7 +4289,8 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||
p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||
p_hint == PROPERTY_HINT_LAYERS_3D_RENDER ||
- p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
+ p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||
+ p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;
switch (p_hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER:
@@ -4305,6 +4311,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;
break;
+ case PROPERTY_HINT_LAYERS_AVOIDANCE:
+ lt = EditorPropertyLayers::LAYER_AVOIDANCE;
+ break;
default: {
} //compiler could be smarter here and realize this can't happen
}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 0d54025c7b..015c65b4c6 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -308,6 +308,7 @@ public:
LAYER_PHYSICS_3D,
LAYER_RENDER_3D,
LAYER_NAVIGATION_3D,
+ LAYER_AVOIDANCE,
};
private:
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index bb0434a1bf..6fa2ec23bb 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -203,7 +203,18 @@ void EditorPropertyArray::_property_changed(const String &p_property, Variant p_
index = p_property.get_slice("/", 1).to_int();
}
- Variant array = object->get_array().duplicate();
+ Variant array;
+ const Variant &original_array = object->get_array();
+
+ if (original_array.get_type() == Variant::ARRAY) {
+ // Needed to preserve type of TypedArrays in meta pointer properties.
+ Array temp;
+ temp.assign(original_array.duplicate());
+ array = temp;
+ } else {
+ array = original_array.duplicate();
+ }
+
array.set(index, p_value);
object->set_array(array);
emit_changed(get_edited_property(), array, "", true);
@@ -244,7 +255,7 @@ void EditorPropertyArray::update_property() {
String array_type_name = Variant::get_type_name(array_type);
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
String type_name;
- if (subtype == Variant::OBJECT && subtype_hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ if (subtype == Variant::OBJECT && (subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
type_name = subtype_hint_string;
} else {
type_name = Variant::get_type_name(subtype);
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index 4ae8b262c1..83d9a18c02 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -36,6 +36,9 @@
EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr;
EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_default_inspector_style() {
+ if (!EditorSettings::get_singleton()) {
+ return STYLE_CAPITALIZED;
+ }
const Style style = (Style)EDITOR_GET("interface/inspector/default_property_name_style").operator int();
if (style == STYLE_LOCALIZED && !is_localization_available()) {
return STYLE_CAPITALIZED;
@@ -44,6 +47,9 @@ EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_default_insp
}
EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_settings_style() {
+ if (!EditorSettings::get_singleton()) {
+ return STYLE_LOCALIZED;
+ }
const bool translate = EDITOR_GET("interface/editor/localize_settings");
return translate ? STYLE_LOCALIZED : STYLE_CAPITALIZED;
}
@@ -53,6 +59,9 @@ EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_tooltip_styl
}
bool EditorPropertyNameProcessor::is_localization_available() {
+ if (!EditorSettings::get_singleton()) {
+ return false;
+ }
const Vector<String> forbidden = String("en").split(",");
return forbidden.find(EDITOR_GET("interface/editor/editor_language")) == -1;
}
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 6eef4e5a5a..45a100a4e5 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -35,6 +35,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/message_queue.h"
+#include "core/variant/variant_utility.cpp"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
@@ -48,17 +49,17 @@ bool EditorResourcePreviewGenerator::handles(const String &p_type) const {
ERR_FAIL_V_MSG(false, "EditorResourcePreviewGenerator::_handles needs to be overridden.");
}
-Ref<Texture2D> EditorResourcePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorResourcePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Texture2D> preview;
- if (GDVIRTUAL_CALL(_generate, p_from, p_size, preview)) {
+ if (GDVIRTUAL_CALL(_generate, p_from, p_size, p_metadata, preview)) {
return preview;
}
ERR_FAIL_V_MSG(Ref<Texture2D>(), "EditorResourcePreviewGenerator::_generate needs to be overridden.");
}
-Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Texture2D> preview;
- if (GDVIRTUAL_CALL(_generate_from_path, p_path, p_size, preview)) {
+ if (GDVIRTUAL_CALL(_generate_from_path, p_path, p_size, p_metadata, preview)) {
return preview;
}
@@ -66,7 +67,7 @@ Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &
if (!res.is_valid()) {
return res;
}
- return generate(res, p_size);
+ return generate(res, p_size, p_metadata);
}
bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
@@ -83,8 +84,8 @@ bool EditorResourcePreviewGenerator::can_generate_small_preview() const {
void EditorResourcePreviewGenerator::_bind_methods() {
GDVIRTUAL_BIND(_handles, "type");
- GDVIRTUAL_BIND(_generate, "resource", "size");
- GDVIRTUAL_BIND(_generate_from_path, "path", "size");
+ GDVIRTUAL_BIND(_generate, "resource", "size", "metadata");
+ GDVIRTUAL_BIND(_generate_from_path, "path", "size", "metadata");
GDVIRTUAL_BIND(_generate_small_preview_automatically);
GDVIRTUAL_BIND(_can_generate_small_preview);
}
@@ -99,35 +100,31 @@ void EditorResourcePreview::_thread_func(void *ud) {
erp->_thread();
}
-void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) {
- String path = p_str;
+void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata) {
{
MutexLock lock(preview_mutex);
- uint32_t hash = 0;
uint64_t modified_time = 0;
- if (p_str.begins_with("ID:")) {
- hash = uint32_t(p_str.get_slicec(':', 2).to_int());
- path = "ID:" + p_str.get_slicec(':', 1);
- } else {
- modified_time = FileAccess::get_modified_time(path);
+ if (!p_path.begins_with("ID:")) {
+ modified_time = FileAccess::get_modified_time(p_path);
}
Item item;
item.order = order++;
item.preview = p_texture;
item.small_preview = p_small_texture;
- item.last_hash = hash;
+ item.last_hash = p_hash;
item.modified_time = modified_time;
+ item.preview_metadata = p_metadata;
- cache[path] = item;
+ cache[p_path] = item;
}
- MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_small_texture, p_ud);
+ MessageQueue::get_singleton()->push_call(id, p_func, p_path, p_texture, p_small_texture, p_ud);
}
-void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base) {
+void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata) {
String type;
if (p_item.resource.is_valid()) {
@@ -155,9 +152,9 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
Ref<Texture2D> generated;
if (p_item.resource.is_valid()) {
- generated = preview_generators[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size));
+ generated = preview_generators.write[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size), p_metadata);
} else {
- generated = preview_generators[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size));
+ generated = preview_generators.write[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size), p_metadata);
}
r_texture = generated;
@@ -165,10 +162,11 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
if (preview_generators[i]->can_generate_small_preview()) {
Ref<Texture2D> generated_small;
+ Dictionary d;
if (p_item.resource.is_valid()) {
- generated_small = preview_generators[i]->generate(p_item.resource, Vector2(small_thumbnail_size, small_thumbnail_size));
+ generated_small = preview_generators.write[i]->generate(p_item.resource, Vector2(small_thumbnail_size, small_thumbnail_size), d);
} else {
- generated_small = preview_generators[i]->generate_from_path(p_item.path, Vector2(small_thumbnail_size, small_thumbnail_size));
+ generated_small = preview_generators.write[i]->generate_from_path(p_item.path, Vector2(small_thumbnail_size, small_thumbnail_size), d);
}
r_small_texture = generated_small;
}
@@ -185,9 +183,9 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
}
if (!p_item.resource.is_valid()) {
- // cache the preview in case it's a resource on disk
+ // Cache the preview in case it's a resource on disk.
if (r_texture.is_valid()) {
- //wow it generated a preview... save cache
+ // Wow it generated a preview... save cache.
bool has_small_texture = r_small_texture.is_valid();
ResourceSaver::save(r_texture, cache_base + ".png");
if (has_small_texture) {
@@ -195,14 +193,16 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
}
Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE);
ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions.");
- f->store_line(itos(thumbnail_size));
- f->store_line(itos(has_small_texture));
- f->store_line(itos(FileAccess::get_modified_time(p_item.path)));
- f->store_line(FileAccess::get_md5(p_item.path));
+ _write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata);
}
}
}
+const Dictionary EditorResourcePreview::get_preview_metadata(const String &p_path) const {
+ ERR_FAIL_COND_V(!cache.has(p_path), Dictionary());
+ return cache[p_path].preview_metadata;
+}
+
void EditorResourcePreview::_iterate() {
preview_mutex.lock();
@@ -211,13 +211,8 @@ void EditorResourcePreview::_iterate() {
queue.pop_front();
if (cache.has(item.path)) {
- //already has it because someone loaded it, just let it know it's ready
- String path = item.path;
- if (item.resource.is_valid()) {
- path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below)
- }
-
- _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata);
+ // Already has it because someone loaded it, just let it know it's ready.
+ _preview_ready(item.path, cache[item.path].last_hash, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata, cache[item.path].preview_metadata);
preview_mutex.unlock();
} else {
@@ -230,28 +225,31 @@ void EditorResourcePreview::_iterate() {
thumbnail_size *= EDSCALE;
if (item.resource.is_valid()) {
- _generate_preview(texture, small_texture, item, String());
+ Dictionary preview_metadata;
+ _generate_preview(texture, small_texture, item, String(), preview_metadata);
- //adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
- _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata);
+ _preview_ready(item.path, item.resource->hash_edited_version(), texture, small_texture, item.id, item.function, item.userdata, preview_metadata);
} else {
+ Dictionary preview_metadata;
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
cache_base = temp_path.path_join("resthumb-" + cache_base);
- //does not have it, try to load a cached thumbnail
+ // Does not have it, try to load a cached thumbnail.
String file = cache_base + ".txt";
Ref<FileAccess> f = FileAccess::open(file, FileAccess::READ);
if (f.is_null()) {
- // No cache found, generate
- _generate_preview(texture, small_texture, item, cache_base);
+ // No cache found, generate.
+ _generate_preview(texture, small_texture, item, cache_base, preview_metadata);
} else {
uint64_t modtime = FileAccess::get_modified_time(item.path);
- int tsize = f->get_line().to_int();
- bool has_small_texture = f->get_line().to_int();
- uint64_t last_modtime = f->get_line().to_int();
+ int tsize;
+ bool has_small_texture;
+ uint64_t last_modtime;
+ String hash;
+ _read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata);
bool cache_valid = true;
@@ -266,7 +264,7 @@ void EditorResourcePreview::_iterate() {
if (last_md5 != md5) {
cache_valid = false;
} else {
- //update modified time
+ // Update modified time.
Ref<FileAccess> f2 = FileAccess::open(file, FileAccess::WRITE);
if (f2.is_null()) {
@@ -274,10 +272,7 @@ void EditorResourcePreview::_iterate() {
// some proper cleanup/disabling of resource preview generation.
ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions.");
} else {
- f2->store_line(itos(thumbnail_size));
- f2->store_line(itos(has_small_texture));
- f2->store_line(itos(modtime));
- f2->store_line(md5);
+ _write_preview_cache(f2, thumbnail_size, has_small_texture, modtime, md5, preview_metadata);
}
}
} else {
@@ -308,10 +303,10 @@ void EditorResourcePreview::_iterate() {
}
if (!cache_valid) {
- _generate_preview(texture, small_texture, item, cache_base);
+ _generate_preview(texture, small_texture, item, cache_base, preview_metadata);
}
}
- _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata);
+ _preview_ready(item.path, 0, texture, small_texture, item.id, item.function, item.userdata, preview_metadata);
}
}
@@ -320,6 +315,22 @@ void EditorResourcePreview::_iterate() {
}
}
+void EditorResourcePreview::_write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, String p_hash, const Dictionary &p_metadata) {
+ p_file->store_line(itos(p_thumbnail_size));
+ p_file->store_line(itos(p_has_small_texture));
+ p_file->store_line(itos(p_modified_time));
+ p_file->store_line(p_hash);
+ p_file->store_line(VariantUtilityFunctions::var_to_str(p_metadata).replace("\n", " "));
+}
+
+void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata) {
+ *r_thumbnail_size = p_file->get_line().to_int();
+ *r_has_small_texture = p_file->get_line().to_int();
+ *r_modified_time = p_file->get_line().to_int();
+ *r_hash = p_file->get_line();
+ *r_metadata = VariantUtilityFunctions::str_to_var(p_file->get_line());
+}
+
void EditorResourcePreview::_thread() {
exited.clear();
while (!exit.is_set()) {
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index aae7c5b164..96ce672d0e 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -44,15 +44,15 @@ protected:
static void _bind_methods();
GDVIRTUAL1RC(bool, _handles, String)
- GDVIRTUAL2RC(Ref<Texture2D>, _generate, Ref<Resource>, Vector2i)
- GDVIRTUAL2RC(Ref<Texture2D>, _generate_from_path, String, Vector2i)
+ GDVIRTUAL3RC(Ref<Texture2D>, _generate, Ref<Resource>, Vector2i, Dictionary)
+ GDVIRTUAL3RC(Ref<Texture2D>, _generate_from_path, String, Vector2i, Dictionary)
GDVIRTUAL0RC(bool, _generate_small_preview_automatically)
GDVIRTUAL0RC(bool, _can_generate_small_preview)
public:
virtual bool handles(const String &p_type) const;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const;
- virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const;
+ virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const;
virtual bool generate_small_preview_automatically() const;
virtual bool can_generate_small_preview() const;
@@ -84,6 +84,7 @@ class EditorResourcePreview : public Node {
struct Item {
Ref<Texture2D> preview;
Ref<Texture2D> small_preview;
+ Dictionary preview_metadata;
int order = 0;
uint32_t last_hash = 0;
uint64_t modified_time = 0;
@@ -93,13 +94,16 @@ class EditorResourcePreview : public Node {
HashMap<String, Item> cache;
- void _preview_ready(const String &p_str, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud);
- void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base);
+ void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata);
+ void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata);
static void _thread_func(void *ud);
void _thread();
void _iterate();
+ void _write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, String p_hash, const Dictionary &p_metadata);
+ void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata);
+
Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
protected:
@@ -112,6 +116,7 @@ public:
// p_preview will be null if there was an error
void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
void queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
+ const Dictionary get_preview_metadata(const String &p_path) const;
void add_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
void remove_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index cf88e42472..25c25c3f7c 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -70,6 +70,7 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
bool debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
+ bool debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
if (debug_collisions) {
args.push_back("--debug-collisions");
}
@@ -82,6 +83,10 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
args.push_back("--debug-navigation");
}
+ if (debug_avoidance) {
+ args.push_back("--debug-avoidance");
+ }
+
if (p_write_movie != "") {
args.push_back("--write-movie");
args.push_back(p_write_movie);
@@ -264,7 +269,7 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_line(vformat("Running: %s", exec));
for (const String &E : args) {
- print_line(" %s", E);
+ print_line(vformat(" %s", E));
}
}
diff --git a/editor/editor_run.h b/editor/editor_run.h
index 68c8742f79..bd6770ae3d 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -50,6 +50,7 @@ private:
public:
Status get_status() const;
String get_running_scene() const;
+
Error run(const String &p_scene, const String &p_write_movie = "");
void run_native_notify() { status = STATUS_PLAY; }
void stop();
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 815d4fab9c..beccf0f2ec 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
#include "editor/export/editor_export_platform.h"
void EditorRunNative::_notification(int p_what) {
@@ -77,7 +78,7 @@ void EditorRunNative::_notification(int p_what) {
}
}
-Error EditorRunNative::run_native(int p_id) {
+Error EditorRunNative::start_run_native(int p_id) {
if (p_id < 0) {
return OK;
}
@@ -142,7 +143,7 @@ Error EditorRunNative::run_native(int p_id) {
}
void EditorRunNative::resume_run_native() {
- run_native(resume_id);
+ start_run_native(resume_id);
}
void EditorRunNative::_bind_methods() {
@@ -155,7 +156,7 @@ bool EditorRunNative::is_deploy_debug_remote_enabled() const {
EditorRunNative::EditorRunNative() {
remote_debug = memnew(MenuButton);
- remote_debug->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::run_native));
+ remote_debug->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::start_run_native));
remote_debug->set_tooltip_text(TTR("Remote Debug"));
remote_debug->set_disabled(true);
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index 2a5431e54b..f52a455bb2 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -52,11 +52,11 @@ protected:
void _notification(int p_what);
public:
- Error run_native(int p_id);
- bool is_deploy_debug_remote_enabled() const;
-
+ Error start_run_native(int p_id);
void resume_run_native();
+ bool is_deploy_debug_remote_enabled() const;
+
EditorRunNative();
};
diff --git a/editor/editor_run_script.cpp b/editor/editor_script.cpp
index a459943656..4e8c5ad8b5 100644
--- a/editor/editor_run_script.cpp
+++ b/editor/editor_script.cpp
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* editor_run_script.cpp */
+/* editor_script.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,18 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "editor_run_script.h"
+#include "editor_script.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
void EditorScript::add_root_node(Node *p_node) {
- if (!editor) {
+ if (!EditorNode::get_singleton()) {
EditorNode::add_io_error("EditorScript::add_root_node: " + TTR("Write your logic in the _run() method."));
return;
}
- if (editor->get_edited_scene()) {
+ if (EditorNode::get_singleton()->get_edited_scene()) {
EditorNode::add_io_error("EditorScript::add_root_node: " + TTR("There is an edited scene already."));
return;
}
@@ -47,36 +47,29 @@ void EditorScript::add_root_node(Node *p_node) {
//editor->set_edited_scene(p_node);
}
-EditorInterface *EditorScript::get_editor_interface() {
- return EditorInterface::get_singleton();
-}
-
-Node *EditorScript::get_scene() {
- if (!editor) {
+Node *EditorScript::get_scene() const {
+ if (!EditorNode::get_singleton()) {
EditorNode::add_io_error("EditorScript::get_scene: " + TTR("Write your logic in the _run() method."));
return nullptr;
}
- return editor->get_edited_scene();
+ return EditorNode::get_singleton()->get_edited_scene();
+}
+
+EditorInterface *EditorScript::get_editor_interface() const {
+ return EditorInterface::get_singleton();
}
-void EditorScript::_run() {
+void EditorScript::run() {
if (!GDVIRTUAL_CALL(_run)) {
EditorNode::add_io_error(TTR("Couldn't run editor script, did you forget to override the '_run' method?"));
}
}
-void EditorScript::set_editor(EditorNode *p_editor) {
- editor = p_editor;
-}
-
void EditorScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_root_node", "node"), &EditorScript::add_root_node);
ClassDB::bind_method(D_METHOD("get_scene"), &EditorScript::get_scene);
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorScript::get_editor_interface);
- GDVIRTUAL_BIND(_run);
-}
-EditorScript::EditorScript() {
- editor = nullptr;
+ GDVIRTUAL_BIND(_run);
}
diff --git a/editor/editor_run_script.h b/editor/editor_script.h
index 8284d59110..d7c813261d 100644
--- a/editor/editor_run_script.h
+++ b/editor/editor_script.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* editor_run_script.h */
+/* editor_script.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef EDITOR_RUN_SCRIPT_H
-#define EDITOR_RUN_SCRIPT_H
+#ifndef EDITOR_SCRIPT_H
+#define EDITOR_SCRIPT_H
#include "core/object/gdvirtual.gen.inc"
#include "core/object/ref_counted.h"
@@ -41,20 +41,19 @@ class EditorNode;
class EditorScript : public RefCounted {
GDCLASS(EditorScript, RefCounted);
- EditorNode *editor = nullptr;
-
protected:
static void _bind_methods();
+
GDVIRTUAL0(_run)
public:
void add_root_node(Node *p_node);
- Node *get_scene();
- EditorInterface *get_editor_interface();
- virtual void _run();
+ Node *get_scene() const;
+ EditorInterface *get_editor_interface() const;
+
+ virtual void run();
- void set_editor(EditorNode *p_editor);
- EditorScript();
+ EditorScript() {}
};
-#endif // EDITOR_RUN_SCRIPT_H
+#endif // EDITOR_SCRIPT_H
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index 8716a75efd..92aa74dd9d 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -310,16 +310,6 @@ void SectionedInspector::_search_changed(const String &p_what) {
update_category_list();
}
-void SectionedInspector::_notification(int p_what) {
- switch (p_what) {
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
- }
- } break;
- }
-}
-
EditorInspector *SectionedInspector::get_inspector() {
return inspector;
}
@@ -353,7 +343,6 @@ SectionedInspector::SectionedInspector() :
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
right_vb->add_child(inspector, true);
inspector->set_use_doc_hints(true);
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
sections->connect("cell_selected", callable_mp(this, &SectionedInspector::_section_selected));
}
diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h
index 0594ef486f..3c9eb78571 100644
--- a/editor/editor_sectioned_inspector.h
+++ b/editor/editor_sectioned_inspector.h
@@ -58,9 +58,6 @@ class SectionedInspector : public HSplitContainer {
void _search_changed(const String &p_what);
-protected:
- void _notification(int p_what);
-
public:
void register_search_box(LineEdit *p_box);
EditorInspector *get_inspector();
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 2cdce158b5..0eb76c6011 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -454,6 +454,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Inspector
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "")
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/inspector/float_drag_speed", 5.0, "0.1,100,0.01")
// Theme
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_ENUM, "interface/theme/preset", "Default", "Default,Breeze Dark,Godot 2,Gray,Light,Solarized (Dark),Solarized (Light),Black (OLED),Custom")
@@ -476,6 +477,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true);
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "")
set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true);
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1")
// Scene tabs
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy
@@ -483,6 +485,12 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/scene_tabs/maximum_width", 350, "0,9999,1", PROPERTY_USAGE_DEFAULT)
_initial_set("interface/scene_tabs/show_script_button", false);
+ // Multi Window
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/multi_window/enable", true, "");
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/multi_window/restore_windows_on_load", true, "");
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/multi_window/maximize_window", false, "");
+ set_restart_if_changed("interface/multi_window/enable", true);
+
/* Filesystem */
// External Programs
@@ -695,7 +703,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/tiles_editor/grid_color", Color(1.0, 0.5, 0.2, 0.5));
// Polygon editor
- _initial_set("editors/polygon_editor/point_grab_radius", 8);
+ _initial_set("editors/polygon_editor/point_grab_radius", has_touchscreen_ui ? 32 : 8);
_initial_set("editors/polygon_editor/show_previous_outline", true);
// Animation
@@ -706,6 +714,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/animation/onion_layers_past_color", Color(1, 0, 0));
_initial_set("editors/animation/onion_layers_future_color", Color(0, 1, 0));
+ // Shader editor
+ _initial_set("editors/shader_editor/behavior/files/restore_shaders_on_load", true);
+
// Visual editors
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/minimap_opacity", 0.85, "0.0,1.0,0.01")
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/visual_editors/lines_curvature", 0.5, "0.0,1.0,0.01")
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index a21fb9fdfb..660a9501a2 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -31,14 +31,13 @@
#ifndef EDITOR_SETTINGS_H
#define EDITOR_SETTINGS_H
+#include "core/input/shortcut.h"
#include "core/io/config_file.h"
#include "core/io/resource.h"
#include "core/os/thread_safe.h"
#include "core/templates/rb_set.h"
class EditorPlugin;
-class InputEvent;
-class Shortcut;
class EditorSettings : public Resource {
GDCLASS(EditorSettings, Resource);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 32b6e9822a..3cdd78dc7f 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -255,6 +255,28 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float
return ImageTexture::create_from_image(img);
}
+float get_gizmo_handle_scale(const String &gizmo_handle_name = "") {
+ const float scale_gizmo_handles_for_touch = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
+ if (scale_gizmo_handles_for_touch > 1.0f) {
+ // The names of the icons that require additional scaling.
+ static HashSet<StringName> gizmo_to_scale;
+ if (gizmo_to_scale.is_empty()) {
+ gizmo_to_scale.insert("EditorHandle");
+ gizmo_to_scale.insert("EditorHandleAdd");
+ gizmo_to_scale.insert("EditorHandleDisabled");
+ gizmo_to_scale.insert("EditorCurveHandle");
+ gizmo_to_scale.insert("EditorPathSharpHandle");
+ gizmo_to_scale.insert("EditorPathSmoothHandle");
+ }
+
+ if (gizmo_to_scale.has(gizmo_handle_name)) {
+ return EDSCALE * scale_gizmo_handles_for_touch;
+ }
+ }
+
+ return EDSCALE;
+}
+
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) {
// Before we register the icons, we adjust their colors and saturation.
// Most icons follow the standard rules for color conversion to follow the editor
@@ -314,22 +336,23 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f
for (int i = 0; i < editor_icons_count; i++) {
Ref<ImageTexture> icon;
- if (accent_color_icons.has(editor_icons_names[i])) {
- icon = editor_generate_icon(i, EDSCALE, 1.0, accent_color_map);
+ const String &editor_icon_name = editor_icons_names[i];
+ if (accent_color_icons.has(editor_icon_name)) {
+ icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), 1.0, accent_color_map);
} else {
float saturation = p_icon_saturation;
- if (saturation_exceptions.has(editor_icons_names[i])) {
+ if (saturation_exceptions.has(editor_icon_name)) {
saturation = 1.0;
}
- if (conversion_exceptions.has(editor_icons_names[i])) {
- icon = editor_generate_icon(i, EDSCALE, saturation);
+ if (conversion_exceptions.has(editor_icon_name)) {
+ icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), saturation);
} else {
- icon = editor_generate_icon(i, EDSCALE, saturation, color_conversion_map);
+ icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), saturation, color_conversion_map);
}
}
- p_theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), icon);
+ p_theme->set_icon(editor_icon_name, SNAME("EditorIcons"), icon);
}
}
@@ -395,6 +418,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color base_color = EDITOR_GET("interface/theme/base_color");
float contrast = EDITOR_GET("interface/theme/contrast");
bool increase_scrollbar_touch_area = EDITOR_GET("interface/touchscreen/increase_scrollbar_touch_area");
+ const float gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
bool draw_extra_borders = EDITOR_GET("interface/theme/draw_extra_borders");
float icon_saturation = EDITOR_GET("interface/theme/icon_saturation");
float relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity");
@@ -594,6 +618,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("class_icon_size", "Editor", 16 * EDSCALE);
theme->set_constant("dark_theme", "Editor", dark_theme);
theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE);
+ theme->set_constant("gizmo_handle_scale", "Editor", gizmo_handle_scale);
// Register editor icons.
// If the settings are comparable to the old theme, then just copy them over.
@@ -609,8 +634,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const bool prev_dark_theme = (bool)p_theme->get_constant(SNAME("dark_theme"), SNAME("Editor"));
const Color prev_accent_color = p_theme->get_color(SNAME("accent_color"), SNAME("Editor"));
const float prev_icon_saturation = p_theme->get_color(SNAME("icon_saturation"), SNAME("Editor")).r;
+ const float prev_gizmo_handle_scale = (float)p_theme->get_constant(SNAME("gizmo_handle_scale"), SNAME("Editor"));
keep_old_icons = (Math::is_equal_approx(prev_scale, EDSCALE) &&
+ Math::is_equal_approx(prev_gizmo_handle_scale, gizmo_handle_scale) &&
prev_dark_theme == dark_theme &&
prev_accent_color == accent_color &&
prev_icon_saturation == icon_saturation);
@@ -806,6 +833,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Script Editor
theme->set_stylebox("ScriptEditorPanel", "EditorStyles", make_empty_stylebox(default_margin_size, 0, default_margin_size, default_margin_size));
+ theme->set_stylebox("ScriptEditorPanelFloating", "EditorStyles", make_empty_stylebox(0, 0, 0, 0));
+
theme->set_stylebox("ScriptEditor", "EditorStyles", make_empty_stylebox(0, 0, 0, 0));
// Launch Pad and Play buttons
@@ -1604,6 +1633,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2));
+ theme->set_constant("center_grabber", "HSlider", 0);
theme->set_constant("grabber_offset", "HSlider", 0);
// VSlider
@@ -1612,6 +1642,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0));
+ theme->set_constant("center_grabber", "VSlider", 0);
theme->set_constant("grabber_offset", "VSlider", 0);
// RichTextLabel
@@ -1868,6 +1899,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("sv_height", "ColorPicker", 256 * EDSCALE);
theme->set_constant("h_width", "ColorPicker", 30 * EDSCALE);
theme->set_constant("label_width", "ColorPicker", 10 * EDSCALE);
+ theme->set_constant("center_slider_grabbers", "ColorPicker", 1);
theme->set_icon("screen_picker", "ColorPicker", theme->get_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
theme->set_icon("shape_circle", "ColorPicker", theme->get_icon(SNAME("PickerShapeCircle"), SNAME("EditorIcons")));
theme->set_icon("shape_rect", "ColorPicker", theme->get_icon(SNAME("PickerShapeRectangle"), SNAME("EditorIcons")));
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 3f192342a1..c6faefc45f 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -37,7 +37,9 @@ EditorExport *EditorExport::singleton = nullptr;
void EditorExport::_save() {
Ref<ConfigFile> config;
+ Ref<ConfigFile> credentials;
config.instantiate();
+ credentials.instantiate();
for (int i = 0; i < export_presets.size(); i++) {
Ref<EditorExportPreset> preset = export_presets[i];
String section = "preset." + itos(i);
@@ -83,16 +85,21 @@ void EditorExport::_save() {
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
config->set_value(section, "encrypt_pck", preset->get_enc_pck());
config->set_value(section, "encrypt_directory", preset->get_enc_directory());
- config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
+ credentials->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
String option_section = "preset." + itos(i) + ".options";
for (const PropertyInfo &E : preset->get_properties()) {
- config->set_value(option_section, E.name, preset->get(E.name));
+ if (E.usage & PROPERTY_USAGE_SECRET) {
+ credentials->set_value(option_section, E.name, preset->get(E.name));
+ } else {
+ config->set_value(option_section, E.name, preset->get(E.name));
+ }
}
}
config->save("res://export_presets.cfg");
+ credentials->save("res://.godot/export_credentials.cfg");
}
void EditorExport::save_presets() {
@@ -202,6 +209,13 @@ void EditorExport::load_config() {
return;
}
+ Ref<ConfigFile> credentials;
+ credentials.instantiate();
+ err = credentials->load("res://.godot/export_credentials.cfg");
+ if (!(err == OK || err == ERR_FILE_NOT_FOUND)) {
+ return;
+ }
+
block_save = true;
int index = 0;
@@ -284,22 +298,30 @@ void EditorExport::load_config() {
if (config->has_section_key(section, "encryption_exclude_filters")) {
preset->set_enc_ex_filter(config->get_value(section, "encryption_exclude_filters"));
}
- if (config->has_section_key(section, "script_encryption_key")) {
- preset->set_script_encryption_key(config->get_value(section, "script_encryption_key"));
+ if (credentials->has_section_key(section, "script_encryption_key")) {
+ preset->set_script_encryption_key(credentials->get_value(section, "script_encryption_key"));
}
String option_section = "preset." + itos(index) + ".options";
List<String> options;
-
config->get_section_keys(option_section, &options);
for (const String &E : options) {
Variant value = config->get_value(option_section, E);
-
preset->set(E, value);
}
+ if (credentials->has_section(option_section)) {
+ options.clear();
+ credentials->get_section_keys(option_section, &options);
+
+ for (const String &E : options) {
+ Variant value = credentials->get_value(option_section, E);
+ preset->set(E, value);
+ }
+ }
+
add_export_preset(preset);
index++;
}
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 4e17379911..65ffa45b38 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -42,6 +42,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor_export_plugin.h"
#include "scene/resources/packed_scene.h"
@@ -817,12 +818,64 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
return save_path.is_empty() ? p_path : save_path;
}
+String EditorExportPlatform::_get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const {
+ const String from_env = OS::get_singleton()->get_environment(ENV_SCRIPT_ENCRYPTION_KEY);
+ if (!from_env.is_empty()) {
+ return from_env.to_lower();
+ }
+ return p_preset->get_script_encryption_key().to_lower();
+}
+
+Vector<String> EditorExportPlatform::get_forced_export_files() {
+ Vector<String> files;
+
+ files.push_back(ProjectSettings::get_singleton()->get_global_class_list_path());
+
+ String icon = GLOBAL_GET("application/config/icon");
+ String splash = GLOBAL_GET("application/boot_splash/image");
+ if (!icon.is_empty() && FileAccess::exists(icon)) {
+ files.push_back(icon);
+ }
+ if (!splash.is_empty() && FileAccess::exists(splash) && icon != splash) {
+ files.push_back(splash);
+ }
+ String resource_cache_file = ResourceUID::get_cache_file();
+ if (FileAccess::exists(resource_cache_file)) {
+ files.push_back(resource_cache_file);
+ }
+
+ String extension_list_config_file = GDExtension::get_extension_list_config_file();
+ if (FileAccess::exists(extension_list_config_file)) {
+ files.push_back(extension_list_config_file);
+ }
+
+ // Store text server data if it is supported.
+ if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
+ bool use_data = GLOBAL_GET("internationalization/locale/include_text_server_data");
+ if (use_data) {
+ // Try using user provided data file.
+ String ts_data = "res://" + TS->get_support_data_filename();
+ if (FileAccess::exists(ts_data)) {
+ files.push_back(ts_data);
+ } else {
+ // Use default text server data.
+ String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_icu_data");
+ ERR_FAIL_COND_V(!TS->save_support_data(icu_data_file), files);
+ files.push_back(icu_data_file);
+ // Remove the file later.
+ MessageQueue::get_singleton()->push_callable(callable_mp_static(DirAccess::remove_absolute), icu_data_file);
+ }
+ }
+ }
+
+ return files;
+}
+
Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
//figure out paths of files that will be exported
HashSet<String> paths;
Vector<String> path_remaps;
- paths.insert(ProjectSettings::get_singleton()->get_global_class_list_path());
if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) {
//find stuff
_export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths);
@@ -901,7 +954,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
// Get encryption key.
- String script_key = p_preset->get_script_encryption_key().to_lower();
+ String script_key = _get_script_encryption_key(p_preset);
key.resize(32);
if (script_key.length() == 64) {
for (int i = 0; i < 32; i++) {
@@ -1294,68 +1347,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
}
- // Store icon and splash images directly, they need to bypass the import system and be loaded as images
- String icon = GLOBAL_GET("application/config/icon");
- String splash = GLOBAL_GET("application/boot_splash/image");
- if (!icon.is_empty() && FileAccess::exists(icon)) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(icon);
- err = p_func(p_udata, icon, array, idx, total, enc_in_filters, enc_ex_filters, key);
+ Vector<String> forced_export = get_forced_export_files();
+ for (int i = 0; i < forced_export.size(); i++) {
+ Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]);
+ err = p_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key);
if (err != OK) {
return err;
}
}
- if (!splash.is_empty() && FileAccess::exists(splash) && icon != splash) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(splash);
- err = p_func(p_udata, splash, array, idx, total, enc_in_filters, enc_ex_filters, key);
- if (err != OK) {
- return err;
- }
- }
- String resource_cache_file = ResourceUID::get_cache_file();
- if (FileAccess::exists(resource_cache_file)) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(resource_cache_file);
- err = p_func(p_udata, resource_cache_file, array, idx, total, enc_in_filters, enc_ex_filters, key);
- if (err != OK) {
- return err;
- }
- }
-
- String extension_list_config_file = GDExtension::get_extension_list_config_file();
- if (FileAccess::exists(extension_list_config_file)) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(extension_list_config_file);
- err = p_func(p_udata, extension_list_config_file, array, idx, total, enc_in_filters, enc_ex_filters, key);
- if (err != OK) {
- return err;
- }
- }
-
- // Store text server data if it is supported.
- if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) {
- bool use_data = GLOBAL_GET("internationalization/locale/include_text_server_data");
- if (use_data) {
- // Try using user provided data file.
- String ts_data = "res://" + TS->get_support_data_filename();
- if (FileAccess::exists(ts_data)) {
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(ts_data);
- err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
- if (err != OK) {
- return err;
- }
- } else {
- // Use default text server data.
- String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_icu_data");
- if (!TS->save_support_data(icu_data_file)) {
- return ERR_INVALID_DATA;
- }
- Vector<uint8_t> array = FileAccess::get_file_as_bytes(icu_data_file);
- err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
- DirAccess::remove_file_or_error(icu_data_file);
- if (err != OK) {
- return err;
- }
- }
- }
- }
String config_file = "project.binary";
String engine_cfb = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp" + config_file);
@@ -1586,7 +1585,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
Ref<FileAccess> fhead = f;
if (enc_pck && enc_directory) {
- String script_key = p_preset->get_script_encryption_key().to_lower();
+ String script_key = _get_script_encryption_key(p_preset);
Vector<uint8_t> key;
key.resize(32);
if (script_key.length() == 64) {
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 21d8719902..3b9663ebdf 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -43,6 +43,8 @@ struct EditorProgress;
class EditorExportPlugin;
+const String ENV_SCRIPT_ENCRYPTION_KEY = "GODOT_SCRIPT_ENCRYPTION_KEY";
+
class EditorExportPlatform : public RefCounted {
GDCLASS(EditorExportPlatform, RefCounted);
@@ -116,6 +118,7 @@ private:
bool _is_editable_ancestor(Node *p_root, Node *p_node);
String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save);
+ String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;
protected:
struct ExportNotifier {
@@ -196,6 +199,8 @@ public:
return worst_type;
}
+ static Vector<String> get_forced_export_files();
+
virtual bool fill_log_messages(RichTextLabel *p_log, Error p_err);
virtual void get_export_options(List<ExportOption> *r_options) const = 0;
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index ac93479605..2aca19a2ad 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -302,4 +302,15 @@ String EditorExportPreset::get_script_encryption_key() const {
return script_key;
}
+Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid) const {
+ const String from_env = OS::get_singleton()->get_environment(p_env_var);
+ if (!from_env.is_empty()) {
+ if (r_valid) {
+ *r_valid = true;
+ }
+ return from_env;
+ }
+ return get(p_name, r_valid);
+}
+
EditorExportPreset::EditorExportPreset() {}
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index 003e3c05a3..194858b4e8 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -152,6 +152,8 @@ public:
void set_script_encryption_key(const String &p_key);
String get_script_encryption_key() const;
+ Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
+
const List<PropertyInfo> &get_properties() const { return properties; }
EditorExportPreset();
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 114d927c4d..8009b3038c 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -70,10 +70,6 @@ void ProjectExportDialog::_notification(int p_what) {
connect("confirmed", callable_mp(this, &ProjectExportDialog::_export_pck_zip));
_update_export_all();
} break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- parameters->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
- } break;
}
}
@@ -246,6 +242,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_filter->select(current->get_export_filter());
include_filters->set_text(current->get_include_filter());
+ include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:"));
exclude_filters->set_text(current->get_exclude_filter());
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
@@ -706,6 +703,7 @@ void ProjectExportDialog::_export_type_changed(int p_which) {
if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) {
current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP);
}
+ include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:"));
updating = true;
_fill_resource_tree();
@@ -833,14 +831,20 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem
void ProjectExportDialog::_propagate_file_export_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_inherited_export_mode) {
EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)(int)p_item->get_metadata(1);
+ bool is_inherited = false;
if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
file_export_mode = p_inherited_export_mode;
+ is_inherited = true;
}
if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
p_item->set_text(1, "");
} else {
- p_item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(file_export_mode)));
+ String text = file_mode_popup->get_item_text(file_mode_popup->get_item_index(file_export_mode));
+ if (is_inherited) {
+ text += " " + TTR("(Inherited)");
+ }
+ p_item->set_text(1, text);
}
for (int i = 0; i < p_item->get_child_count(); i++) {
@@ -1161,7 +1165,6 @@ ProjectExportDialog::ProjectExportDialog() {
sections->add_child(parameters);
parameters->set_name(TTR("Options"));
parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- parameters->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
parameters->set_use_doc_hints(true);
parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters));
EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 169e78c0f5..87ba7f53b0 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -47,6 +47,7 @@
#include "editor/gui/editor_dir_dialog.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/import_dock.h"
+#include "editor/plugins/editor_resource_tooltip_plugins.h"
#include "editor/scene_create_dialog.h"
#include "editor/scene_tree_dock.h"
#include "editor/shader_create_dialog.h"
@@ -54,10 +55,27 @@
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/progress_bar.h"
+#include "scene/gui/texture_rect.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
#include "servers/display_server.h"
+Control *FileSystemTree::make_custom_tooltip(const String &p_text) const {
+ TreeItem *item = get_item_at_position(get_local_mouse_position());
+ if (!item) {
+ return nullptr;
+ }
+ return FileSystemDock::get_singleton()->create_tooltip_for_path(item->get_metadata(0));
+}
+
+Control *FileSystemList::make_custom_tooltip(const String &p_text) const {
+ int idx = get_item_at_position(get_local_mouse_position());
+ if (idx == -1) {
+ return nullptr;
+ }
+ return FileSystemDock::get_singleton()->create_tooltip_for_path(get_item_metadata(idx));
+}
+
FileSystemDock *FileSystemDock::singleton = nullptr;
Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, String p_file_type) {
@@ -87,14 +105,14 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
subdirectory_item->set_selectable(0, true);
String lpath = p_dir->get_path();
subdirectory_item->set_metadata(0, lpath);
- if (!p_select_in_favorites && (path == lpath || ((display_mode == DISPLAY_MODE_SPLIT) && path.get_base_dir() == lpath))) {
+ if (!p_select_in_favorites && (current_path == lpath || ((display_mode == DISPLAY_MODE_SPLIT) && current_path.get_base_dir() == lpath))) {
subdirectory_item->select(0);
// Keep select an item when re-created a tree
// To prevent crashing when nothing is selected.
subdirectory_item->set_as_cursor(0);
}
- if (p_unfold_path && path.begins_with(lpath) && path != lpath) {
+ if (p_unfold_path && current_path.begins_with(lpath) && current_path != lpath) {
subdirectory_item->set_collapsed(false);
} else {
subdirectory_item->set_collapsed(uncollapsed_paths.find(lpath) < 0);
@@ -155,7 +173,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type));
String file_metadata = lpath.path_join(fi.name);
file_item->set_metadata(0, file_metadata);
- if (!p_select_in_favorites && path == file_metadata) {
+ if (!p_select_in_favorites && current_path == file_metadata) {
file_item->select(0);
file_item->set_as_cursor(0);
}
@@ -168,7 +186,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
EditorResourcePreview::get_singleton()->queue_resource_preview(file_metadata, this, "_tree_thumbnail_done", udata);
}
} else if (display_mode == DISPLAY_MODE_SPLIT) {
- if (lpath.get_base_dir() == path.get_base_dir()) {
+ if (lpath.get_base_dir() == current_path.get_base_dir()) {
subdirectory_item->select(0);
subdirectory_item->set_as_cursor(0);
}
@@ -186,8 +204,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
return parent_should_expand;
}
-Vector<String> FileSystemDock::_compute_uncollapsed_paths() {
- // Register currently collapsed paths.
+Vector<String> FileSystemDock::get_uncollapsed_paths() const {
Vector<String> uncollapsed_paths;
TreeItem *root = tree->get_root();
if (root) {
@@ -196,21 +213,24 @@ Vector<String> FileSystemDock::_compute_uncollapsed_paths() {
uncollapsed_paths.push_back(favorites_item->get_metadata(0));
}
- TreeItem *resTree = root->get_first_child()->get_next();
- if (resTree) {
- Vector<TreeItem *> needs_check;
- needs_check.push_back(resTree);
+ // BFS to find all uncollapsed paths of the resource directory.
+ TreeItem *res_subtree = root->get_first_child()->get_next();
+ if (res_subtree) {
+ List<TreeItem *> queue;
+ queue.push_back(res_subtree);
- while (needs_check.size()) {
- if (!needs_check[0]->is_collapsed()) {
- uncollapsed_paths.push_back(needs_check[0]->get_metadata(0));
- TreeItem *child = needs_check[0]->get_first_child();
- while (child) {
- needs_check.push_back(child);
- child = child->get_next();
+ while (!queue.is_empty()) {
+ TreeItem *ti = queue.back()->get();
+ queue.pop_back();
+ if (!ti->is_collapsed() && ti->get_child_count() > 0) {
+ Variant path = ti->get_metadata(0);
+ if (path) {
+ uncollapsed_paths.push_back(path);
}
}
- needs_check.remove_at(0);
+ for (int i = 0; i < ti->get_child_count(); i++) {
+ queue.push_back(ti->get_child(i));
+ }
}
}
}
@@ -286,7 +306,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
ti->set_tooltip_text(0, favorite);
ti->set_selectable(0, true);
ti->set_metadata(0, favorite);
- if (p_select_in_favorites && favorite == path) {
+ if (p_select_in_favorites && favorite == current_path) {
ti->select(0);
ti->set_as_cursor(0);
}
@@ -329,7 +349,7 @@ void FileSystemDock::_update_display_mode(bool p_force) {
toolbar2_hbc->hide();
}
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
file_list_vb->hide();
break;
@@ -338,7 +358,7 @@ void FileSystemDock::_update_display_mode(bool p_force) {
tree->set_v_size_flags(SIZE_EXPAND_FILL);
tree->ensure_cursor_is_visible();
toolbar2_hbc->hide();
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
file_list_vb->show();
_update_file_list(true);
@@ -388,7 +408,7 @@ void FileSystemDock::_notification(int p_what) {
file_list_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_list_rmb_option));
tree_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_tree_rmb_option));
- current_path->connect("text_submitted", callable_mp(this, &FileSystemDock::_navigate_to_path).bind(false));
+ current_path_line_edit->connect("text_submitted", callable_mp(this, &FileSystemDock::_navigate_to_path).bind(false));
always_show_folders = bool(EDITOR_GET("docks/filesystem/always_show_folders"));
@@ -502,14 +522,14 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
TreeItem *favorites_item = tree->get_root()->get_first_child();
if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) {
// Go to the favorites if we click in the favorites and the path has changed.
- path = "Favorites";
+ current_path = "Favorites";
} else {
- path = selected->get_metadata(0);
+ current_path = selected->get_metadata(0);
// Note: the "Favorites" item also leads to this path.
}
- // Set the current path.
- _set_current_path_text(path);
+ // Display the current path.
+ _set_current_path_line_edit_text(current_path);
_push_to_history();
// Update the file list.
@@ -523,28 +543,28 @@ Vector<String> FileSystemDock::get_selected_paths() const {
}
String FileSystemDock::get_current_path() const {
- return path;
+ return current_path;
}
String FileSystemDock::get_current_directory() const {
- if (path.ends_with("/")) {
- return path;
+ if (current_path.ends_with("/")) {
+ return current_path;
} else {
- return path.get_base_dir();
+ return current_path.get_base_dir();
}
}
-void FileSystemDock::_set_current_path_text(const String &p_path) {
+void FileSystemDock::_set_current_path_line_edit_text(const String &p_path) {
if (p_path == "Favorites") {
- current_path->set_text(TTR("Favorites"));
+ current_path_line_edit->set_text(TTR("Favorites"));
} else {
- current_path->set_text(path);
+ current_path_line_edit->set_text(current_path);
}
}
void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_favorites) {
if (p_path == "Favorites") {
- path = p_path;
+ current_path = p_path;
} else {
String target_path = p_path;
// If the path is a file, do not only go to the directory in the tree, also select the file in the file list.
@@ -553,18 +573,18 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa
}
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists(p_path)) {
- path = target_path;
+ current_path = target_path;
} else if (da->dir_exists(p_path)) {
- path = target_path + "/";
+ current_path = target_path + "/";
} else {
ERR_FAIL_MSG(vformat("Cannot navigate to '%s' as it has not been found in the file system!", p_path));
}
}
- _set_current_path_text(path);
+ _set_current_path_line_edit_text(current_path);
_push_to_history();
- _update_tree(_compute_uncollapsed_paths(), false, p_select_in_favorites, true);
+ _update_tree(get_uncollapsed_paths(), false, p_select_in_favorites, true);
if (display_mode == DISPLAY_MODE_SPLIT) {
_update_file_list(false);
files->get_v_scroll_bar()->set_value(0);
@@ -588,7 +608,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) {
}
void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) {
- if ((file_list_vb->is_visible_in_tree() || path == p_path.get_base_dir()) && p_preview.is_valid()) {
+ if ((file_list_vb->is_visible_in_tree() || current_path == p_path.get_base_dir()) && p_preview.is_valid()) {
Array uarr = p_udata;
int idx = uarr[0];
String file = uarr[1];
@@ -749,9 +769,9 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->clear();
- _set_current_path_text(path);
+ _set_current_path_line_edit_text(current_path);
- String directory = path;
+ String directory = current_path;
String file = "";
int thumbnail_size = EDITOR_GET("docks/filesystem/thumbnail_size");
@@ -793,7 +813,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Build the FileInfo list.
List<FileInfo> file_list;
- if (path == "Favorites") {
+ if (current_path == "Favorites") {
// Display the favorites.
Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites();
for (const String &favorite : favorites_list) {
@@ -842,8 +862,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
}
EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory);
if (!efd) {
- directory = path.get_base_dir();
- file = path.get_file();
+ directory = current_path.get_base_dir();
+ file = current_path.get_file();
efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory);
}
if (!efd) {
@@ -1084,7 +1104,7 @@ void FileSystemDock::_file_list_activate_file(int p_idx) {
}
void FileSystemDock::_preview_invalidated(const String &p_path) {
- if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && searched_string.length() == 0 && file_list_vb->is_visible_in_tree()) {
+ if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == current_path && searched_string.length() == 0 && file_list_vb->is_visible_in_tree()) {
for (int i = 0; i < files->get_item_count(); i++) {
if (files->get_item_metadata(i) == p_path) {
// Re-request preview.
@@ -1106,7 +1126,7 @@ void FileSystemDock::_fs_changed() {
split_box->show();
if (tree->is_visible()) {
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
}
if (file_list_vb->is_visible()) {
@@ -1146,11 +1166,11 @@ void FileSystemDock::_bw_history() {
}
void FileSystemDock::_update_history() {
- path = history[history_pos];
- _set_current_path_text(path);
+ current_path = history[history_pos];
+ _set_current_path_line_edit_text(current_path);
if (tree->is_visible()) {
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
tree->grab_focus();
tree->ensure_cursor_is_visible();
}
@@ -1164,9 +1184,9 @@ void FileSystemDock::_update_history() {
}
void FileSystemDock::_push_to_history() {
- if (history[history_pos] != path) {
+ if (history[history_pos] != current_path) {
history.resize(history_pos + 1);
- history.push_back(path);
+ history.push_back(current_path);
history_pos++;
if (history.size() > history_max_size) {
@@ -1255,7 +1275,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
for (int j = 0; j < ed->get_edited_scene_count(); j++) {
if (ed->get_scene_path(j) == file_changed_paths[i]) {
ed->get_edited_scene_root(j)->set_scene_file_path(new_item_path);
- EditorNode::get_singleton()->save_layout();
+ EditorNode::get_singleton()->save_editor_layout_delayed();
break;
}
}
@@ -1292,7 +1312,7 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin
EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.") + "\n" + old_path + "\n");
return;
}
- const_cast<FileSystemDock *>(this)->path = new_path;
+ const_cast<FileSystemDock *>(this)->current_path = new_path;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
@@ -1532,48 +1552,66 @@ void FileSystemDock::_file_removed(String p_file) {
emit_signal(SNAME("file_removed"), p_file);
// Find the closest parent directory available, in case multiple items were deleted along the same path.
- path = p_file.get_base_dir();
+ current_path = p_file.get_base_dir();
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- while (!da->dir_exists(path)) {
- path = path.get_base_dir();
+ while (!da->dir_exists(current_path)) {
+ current_path = current_path.get_base_dir();
}
- current_path->set_text(path);
+ current_path_line_edit->set_text(current_path);
}
void FileSystemDock::_folder_removed(String p_folder) {
emit_signal(SNAME("folder_removed"), p_folder);
// Find the closest parent directory available, in case multiple items were deleted along the same path.
- path = p_folder.get_base_dir();
+ current_path = p_folder.get_base_dir();
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- while (!da->dir_exists(path)) {
- path = path.get_base_dir();
+ while (!da->dir_exists(current_path)) {
+ current_path = current_path.get_base_dir();
}
- current_path->set_text(path);
- EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(path);
+ current_path_line_edit->set_text(current_path);
+ EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(current_path);
if (efd) {
efd->force_update();
}
}
void FileSystemDock::_rename_operation_confirm() {
- String new_name = rename_dialog_text->get_text().strip_edges();
+ if (!tree->is_anything_selected()) {
+ return;
+ }
+ TreeItem *s = tree->get_selected();
+ int col_index = tree->get_selected_column();
+ String new_name = s->get_text(col_index);
+ new_name = new_name.strip_edges();
+ String old_name = to_rename.is_file ? to_rename.path.get_file() : to_rename.path.left(-1).get_file();
+
+ bool rename_error = false;
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
- return;
+ rename_error = true;
} else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
- return;
+ rename_error = true;
+ } else if (new_name.begins_with(".")) {
+ EditorNode::get_singleton()->show_warning(TTR("This filename begins with a dot rendering the file invisible to the editor.\nIf you want to rename it anyway, use your operating system's file manager."));
+ rename_error = true;
} else if (to_rename.is_file && to_rename.path.get_extension() != new_name.get_extension()) {
if (!EditorFileSystem::get_singleton()->get_valid_extensions().find(new_name.get_extension())) {
EditorNode::get_singleton()->show_warning(TTR("This file extension is not recognized by the editor.\nIf you want to rename it anyway, use your operating system's file manager.\nAfter renaming to an unknown extension, the file won't be shown in the editor anymore."));
- return;
+ rename_error = true;
}
}
- String old_path = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1) : to_rename.path;
+ // Restores Tree to restore original names.
+ if (rename_error) {
+ s->set_text(col_index, old_name);
+ return;
+ }
+
+ String old_path = to_rename.path.ends_with("/") ? to_rename.path.left(-1) : to_rename.path;
String new_path = old_path.get_base_dir().path_join(new_name);
if (old_path == new_path) {
return;
@@ -1592,6 +1630,7 @@ void FileSystemDock::_rename_operation_confirm() {
if (da->file_exists(new_path) || da->dir_exists(new_path)) {
#endif
EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists."));
+ s->set_text(col_index, old_name);
return;
}
@@ -1614,8 +1653,8 @@ void FileSystemDock::_rename_operation_confirm() {
print_verbose("FileSystem: saving moved scenes.");
_save_scenes_after_move(file_renames);
- path = new_path;
- current_path->set_text(path);
+ current_path = new_path;
+ current_path_line_edit->set_text(current_path);
}
void FileSystemDock::_duplicate_operation_confirm() {
@@ -1758,8 +1797,8 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop
print_verbose("FileSystem: saving moved scenes.");
_save_scenes_after_move(file_renames);
- path = p_to_path;
- current_path->set_text(path);
+ current_path = p_to_path;
+ current_path_line_edit->set_text(current_path);
}
}
}
@@ -1840,8 +1879,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
switch (p_option) {
case FILE_SHOW_IN_EXPLORER: {
// Show the file/folder in the OS explorer.
- String fpath = path;
- if (path == "Favorites") {
+ String fpath = current_path;
+ if (current_path == "Favorites") {
fpath = p_selected[0];
}
@@ -1850,8 +1889,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_OPEN_EXTERNAL: {
- String fpath = path;
- if (path == "Favorites") {
+ String fpath = current_path;
+ if (current_path == "Favorites") {
fpath = p_selected[0];
}
@@ -1914,7 +1953,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
if (p_selected.size() == 1) {
ProjectSettings::get_singleton()->set("application/run/main_scene", p_selected[0]);
ProjectSettings::get_singleton()->save();
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
_update_file_list(true);
}
} break;
@@ -1942,7 +1981,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
}
}
EditorSettings::get_singleton()->set_favorites(favorites_list);
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
} break;
case FILE_REMOVE_FAVORITE: {
@@ -1952,8 +1991,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
favorites_list.erase(p_selected[i]);
}
EditorSettings::get_singleton()->set_favorites(favorites_list);
- _update_tree(_compute_uncollapsed_paths());
- if (path == "Favorites") {
+ _update_tree(get_uncollapsed_paths());
+ if (current_path == "Favorites") {
_update_file_list(true);
}
} break;
@@ -1990,24 +2029,21 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_RENAME: {
- // Rename the active file.
- if (!p_selected.is_empty()) {
+ if (tree->is_anything_selected() && !p_selected.is_empty()) {
+ // Set to_rename variable for callback execution.
to_rename.path = p_selected[0];
- if (to_rename.path != "res://") {
- to_rename.is_file = !to_rename.path.ends_with("/");
- if (to_rename.is_file) {
- String name = to_rename.path.get_file();
- rename_dialog->set_title(TTR("Renaming file:") + " " + name);
- rename_dialog_text->set_text(name);
- rename_dialog_text->select(0, name.rfind("."));
- } else {
- String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file();
- rename_dialog->set_title(TTR("Renaming folder:") + " " + name);
- rename_dialog_text->set_text(name);
- rename_dialog_text->select(0, name.length());
- }
- rename_dialog->popup_centered(Size2(250, 80) * EDSCALE);
- rename_dialog_text->grab_focus();
+ to_rename.is_file = !to_rename.path.ends_with("/");
+
+ // Edit node in Tree.
+ tree->grab_focus();
+ tree->edit_selected(true);
+
+ if (to_rename.is_file) {
+ String name = to_rename.path.get_file();
+ tree->set_editor_selection(0, name.rfind("."));
+ } else {
+ String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders.
+ tree->set_editor_selection(0, name.length());
}
}
} break;
@@ -2069,7 +2105,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_NEW_FOLDER: {
- String directory = path;
+ String directory = current_path;
if (!directory.ends_with("/")) {
directory = directory.get_base_dir();
}
@@ -2078,7 +2114,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_NEW_SCENE: {
- String directory = path;
+ String directory = current_path;
if (!directory.ends_with("/")) {
directory = directory.get_base_dir();
}
@@ -2087,7 +2123,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} break;
case FILE_NEW_SCRIPT: {
- String fpath = path;
+ String fpath = current_path;
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
@@ -2116,7 +2152,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
new_resource_dialog->popup_create(true);
} break;
case FILE_NEW_TEXTFILE: {
- String fpath = path;
+ String fpath = current_path;
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
@@ -2127,7 +2163,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
}
void FileSystemDock::_resource_created() {
- String fpath = path;
+ String fpath = current_path;
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
@@ -2168,7 +2204,7 @@ void FileSystemDock::_resource_created() {
void FileSystemDock::_search_changed(const String &p_text, const Control *p_from) {
if (searched_string.length() == 0) {
// Register the uncollapsed paths before they change.
- uncollapsed_paths_before_search = _compute_uncollapsed_paths();
+ uncollapsed_paths_before_search = get_uncollapsed_paths();
}
searched_string = p_text.to_lower();
@@ -2179,7 +2215,7 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from
tree_search_box->set_text(searched_string);
}
- bool unfold_path = (p_text.is_empty() && !path.is_empty());
+ bool unfold_path = (p_text.is_empty() && !current_path.is_empty());
switch (display_mode) {
case DISPLAY_MODE_TREE_ONLY: {
_update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>(), false, false, unfold_path);
@@ -2231,6 +2267,41 @@ void FileSystemDock::set_file_list_display_mode(FileListDisplayMode p_mode) {
_toggle_file_display();
}
+void FileSystemDock::add_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin) {
+ tooltip_plugins.push_back(p_plugin);
+}
+
+void FileSystemDock::remove_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin) {
+ int index = tooltip_plugins.find(p_plugin);
+ ERR_FAIL_COND_MSG(index == -1, "Can't remove plugin that wasn't registered.");
+ tooltip_plugins.remove_at(index);
+}
+
+Control *FileSystemDock::create_tooltip_for_path(const String &p_path) const {
+ if (DirAccess::exists(p_path)) {
+ // No tooltip for directory.
+ return nullptr;
+ }
+
+ const String type = ResourceLoader::get_resource_type(p_path);
+ Control *tooltip = nullptr;
+
+ for (const Ref<EditorResourceTooltipPlugin> &plugin : tooltip_plugins) {
+ if (plugin->handles(type)) {
+ tooltip = plugin->make_tooltip_for_path(p_path, EditorResourcePreview::get_singleton()->get_preview_metadata(p_path));
+ }
+
+ if (tooltip) {
+ break;
+ }
+ }
+
+ if (!tooltip) {
+ tooltip = EditorResourceTooltipPlugin::make_default_tooltip(p_path);
+ }
+ return tooltip;
+}
+
Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
bool all_favorites = true;
bool all_not_favorites = true;
@@ -2417,9 +2488,9 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
}
EditorSettings::get_singleton()->set_favorites(dirs);
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
- if (display_mode == DISPLAY_MODE_SPLIT && path == "Favorites") {
+ if (display_mode == DISPLAY_MODE_SPLIT && current_path == "Favorites") {
_update_file_list(true);
}
return;
@@ -2467,7 +2538,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
}
}
EditorSettings::get_singleton()->set_favorites(favorites_list);
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
}
}
@@ -2491,7 +2562,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori
}
String ltarget = files->get_item_metadata(pos);
- target = ltarget.ends_with("/") ? ltarget : path.get_base_dir();
+ target = ltarget.ends_with("/") ? ltarget : current_path.get_base_dir();
return;
}
@@ -2674,7 +2745,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
}
#endif
- path = fpath;
+ current_path = fpath;
}
}
@@ -2702,7 +2773,7 @@ void FileSystemDock::_tree_empty_click(const Vector2 &p_pos, MouseButton p_butto
return;
}
// Right click is pressed in the empty space of the tree.
- path = "res://";
+ current_path = "res://";
tree_popup->clear();
tree_popup->reset_size();
tree_popup->add_icon_item(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), TTR("New Folder..."), FILE_NEW_FOLDER);
@@ -2763,7 +2834,7 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton
return;
}
- path = current_path->get_text();
+ current_path = current_path_line_edit->get_text();
file_list_popup->clear();
file_list_popup->reset_size();
@@ -2791,9 +2862,9 @@ void FileSystemDock::_file_multi_selected(int p_index, bool p_selected) {
if (current == p_index) {
String fpath = files->get_item_metadata(current);
if (!fpath.ends_with("/")) {
- path = fpath;
+ current_path = fpath;
if (display_mode == DISPLAY_MODE_SPLIT) {
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
}
}
}
@@ -3076,7 +3147,7 @@ void FileSystemDock::set_file_sort(FileSortOption p_file_sort) {
file_sort = p_file_sort;
// Update everything needed.
- _update_tree(_compute_uncollapsed_paths());
+ _update_tree(get_uncollapsed_paths());
_update_file_list(true);
}
@@ -3112,6 +3183,9 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_import_dock"), &FileSystemDock::_update_import_dock);
+ ClassDB::bind_method(D_METHOD("add_resource_tooltip_plugin", "plugin"), &FileSystemDock::add_resource_tooltip_plugin);
+ ClassDB::bind_method(D_METHOD("remove_resource_tooltip_plugin", "plugin"), &FileSystemDock::remove_resource_tooltip_plugin);
+
ADD_SIGNAL(MethodInfo("inherit", PropertyInfo(Variant::STRING, "file")));
ADD_SIGNAL(MethodInfo("instantiate", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files")));
@@ -3127,7 +3201,7 @@ void FileSystemDock::_bind_methods() {
FileSystemDock::FileSystemDock() {
singleton = this;
set_name("FileSystem");
- path = "res://";
+ current_path = "res://";
// `KeyModifierMask::CMD_OR_CTRL | Key::C` conflicts with other editor shortcuts.
ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
@@ -3163,11 +3237,11 @@ FileSystemDock::FileSystemDock() {
button_hist_next->set_tooltip_text(TTR("Go to next selected folder/file."));
toolbar_hbc->add_child(button_hist_next);
- current_path = memnew(LineEdit);
- current_path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
- current_path->set_h_size_flags(SIZE_EXPAND_FILL);
- _set_current_path_text(path);
- toolbar_hbc->add_child(current_path);
+ current_path_line_edit = memnew(LineEdit);
+ current_path_line_edit->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
+ current_path_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ _set_current_path_line_edit_text(current_path);
+ toolbar_hbc->add_child(current_path_line_edit);
button_reload = memnew(Button);
button_reload->connect("pressed", callable_mp(this, &FileSystemDock::_rescan));
@@ -3209,7 +3283,7 @@ FileSystemDock::FileSystemDock() {
split_box->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(split_box);
- tree = memnew(Tree);
+ tree = memnew(FileSystemTree);
tree->set_hide_root(true);
SET_DRAG_FORWARDING_GCD(tree, FileSystemDock);
@@ -3226,6 +3300,7 @@ FileSystemDock::FileSystemDock() {
tree->connect("nothing_selected", callable_mp(this, &FileSystemDock::_tree_empty_selected));
tree->connect("gui_input", callable_mp(this, &FileSystemDock::_tree_gui_input));
tree->connect("mouse_exited", callable_mp(this, &FileSystemDock::_tree_mouse_exited));
+ tree->connect("item_edited", callable_mp(this, &FileSystemDock::_rename_operation_confirm));
file_list_vb = memnew(VBoxContainer);
file_list_vb->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -3247,7 +3322,7 @@ FileSystemDock::FileSystemDock() {
button_file_list_display_mode->set_flat(true);
path_hb->add_child(button_file_list_display_mode);
- files = memnew(ItemList);
+ files = memnew(FileSystemList);
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_select_mode(ItemList::SELECT_MULTI);
SET_DRAG_FORWARDING_GCD(files, FileSystemDock);
@@ -3287,17 +3362,6 @@ FileSystemDock::FileSystemDock() {
add_child(move_dialog);
move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_dialog_confirm));
- rename_dialog = memnew(ConfirmationDialog);
- VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
- rename_dialog->add_child(rename_dialog_vb);
-
- rename_dialog_text = memnew(LineEdit);
- rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);
- rename_dialog->set_ok_button_text(TTR("Rename"));
- add_child(rename_dialog);
- rename_dialog->register_text_enter(rename_dialog_text);
- rename_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_rename_operation_confirm));
-
overwrite_dialog = memnew(ConfirmationDialog);
add_child(overwrite_dialog);
overwrite_dialog->set_ok_button_text(TTR("Overwrite"));
@@ -3347,6 +3411,8 @@ FileSystemDock::FileSystemDock() {
display_mode = DISPLAY_MODE_TREE_ONLY;
old_display_mode = DISPLAY_MODE_TREE_ONLY;
file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS;
+
+ add_resource_tooltip_plugin(memnew(EditorTextureTooltipPlugin));
}
FileSystemDock::~FileSystemDock() {
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index e47178d294..d8d7434579 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -50,6 +50,15 @@ class ProgressBar;
class SceneCreateDialog;
class ShaderCreateDialog;
class DirectoryCreateDialog;
+class EditorResourceTooltipPlugin;
+
+class FileSystemTree : public Tree {
+ virtual Control *make_custom_tooltip(const String &p_text) const;
+};
+
+class FileSystemList : public ItemList {
+ virtual Control *make_custom_tooltip(const String &p_text) const;
+};
class FileSystemDock : public VBoxContainer {
GDCLASS(FileSystemDock, VBoxContainer);
@@ -125,7 +134,7 @@ private:
Button *button_file_list_display_mode = nullptr;
Button *button_hist_next = nullptr;
Button *button_hist_prev = nullptr;
- LineEdit *current_path = nullptr;
+ LineEdit *current_path_line_edit = nullptr;
HBoxContainer *toolbar2_hbc = nullptr;
LineEdit *tree_search_box = nullptr;
@@ -152,8 +161,6 @@ private:
DependencyRemoveDialog *remove_dialog = nullptr;
EditorDirDialog *move_dialog = nullptr;
- ConfirmationDialog *rename_dialog = nullptr;
- LineEdit *rename_dialog_text = nullptr;
ConfirmationDialog *duplicate_dialog = nullptr;
LineEdit *duplicate_dialog_text = nullptr;
DirectoryCreateDialog *make_dir_dialog = nullptr;
@@ -185,26 +192,27 @@ private:
int history_pos;
int history_max_size;
- String path;
+ String current_path;
bool initialized = false;
bool updating_tree = false;
int tree_update_id;
- Tree *tree = nullptr;
- ItemList *files = nullptr;
+ FileSystemTree *tree = nullptr;
+ FileSystemList *files = nullptr;
bool import_dock_needs_update = false;
bool holding_branch = false;
Vector<TreeItem *> tree_items_selected_on_drag_begin;
PackedInt32Array list_items_selected_on_drag_begin;
+ LocalVector<Ref<EditorResourceTooltipPlugin>> tooltip_plugins;
+
void _tree_mouse_exited();
void _reselect_items_selected_on_drag_begin(bool reset = false);
Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, String p_file_type);
bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false);
- Vector<String> _compute_uncollapsed_paths();
void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false);
void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false);
@@ -295,7 +303,7 @@ private:
void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items);
- void _set_current_path_text(const String &p_path);
+ void _set_current_path_line_edit_text(const String &p_path);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
@@ -327,6 +335,7 @@ protected:
public:
Vector<String> get_selected_paths() const;
+ Vector<String> get_uncollapsed_paths() const;
String get_current_path() const;
String get_current_directory() const;
@@ -351,6 +360,12 @@ public:
void set_file_list_display_mode(FileListDisplayMode p_mode);
FileListDisplayMode get_file_list_display_mode() { return file_list_display_mode; };
+ Tree *get_tree_control() { return tree; }
+
+ void add_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin);
+ void remove_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin);
+ Control *create_tooltip_for_path(const String &p_path) const;
+
FileSystemDock();
~FileSystemDock();
};
diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp
new file mode 100644
index 0000000000..c226c1a2d6
--- /dev/null
+++ b/editor/gui/editor_run_bar.cpp
@@ -0,0 +1,444 @@
+/**************************************************************************/
+/* editor_run_bar.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 "editor_run_bar.h"
+
+#include "core/config/project_settings.h"
+#include "editor/debugger/editor_debugger_node.h"
+#include "editor/editor_command_palette.h"
+#include "editor/editor_node.h"
+#include "editor/editor_quick_open.h"
+#include "editor/editor_run_native.h"
+#include "editor/editor_settings.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/panel_container.h"
+
+EditorRunBar *EditorRunBar::singleton = nullptr;
+
+void EditorRunBar::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_POSTINITIALIZE: {
+ _reset_play_buttons();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ _update_play_buttons();
+ pause_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
+ stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
+
+ if (is_movie_maker_enabled()) {
+ main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
+ write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
+ } else {
+ main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
+ write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
+ }
+
+ write_movie_button->set_icon(get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
+ // This button behaves differently, so color it as such.
+ write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7));
+ write_movie_button->add_theme_color_override("icon_pressed_color", Color(0, 0, 0, 0.84));
+ write_movie_button->add_theme_color_override("icon_hover_color", Color(1, 1, 1, 0.9));
+ } break;
+ }
+}
+
+void EditorRunBar::_reset_play_buttons() {
+ play_button->set_pressed(false);
+ play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ play_button->set_tooltip_text(TTR("Play the project."));
+
+ play_scene_button->set_pressed(false);
+ play_scene_button->set_icon(get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
+ play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
+
+ play_custom_scene_button->set_pressed(false);
+ play_custom_scene_button->set_icon(get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
+ play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
+}
+
+void EditorRunBar::_update_play_buttons() {
+ _reset_play_buttons();
+ if (!is_playing()) {
+ return;
+ }
+
+ Button *active_button = nullptr;
+ if (current_mode == RUN_CURRENT) {
+ active_button = play_scene_button;
+ } else if (current_mode == RUN_CUSTOM) {
+ active_button = play_custom_scene_button;
+ } else {
+ active_button = play_button;
+ }
+
+ if (active_button) {
+ active_button->set_pressed(true);
+ active_button->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
+ active_button->set_tooltip_text(TTR("Reload the played scene."));
+ }
+}
+
+void EditorRunBar::_write_movie_toggled(bool p_enabled) {
+ if (p_enabled) {
+ add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
+ write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
+ } else {
+ add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
+ write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
+ }
+}
+
+void EditorRunBar::_quick_run_selected() {
+ play_custom_scene(quick_run->get_selected());
+}
+
+void EditorRunBar::_play_custom_pressed() {
+ if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CUSTOM) {
+ stop_playing();
+
+ quick_run->popup_dialog("PackedScene", true);
+ quick_run->set_title(TTR("Quick Run Scene..."));
+ play_custom_scene_button->set_pressed(false);
+ } else {
+ // Reload if already running a custom scene.
+ String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
+ play_custom_scene(last_custom_scene);
+ }
+}
+
+void EditorRunBar::_play_current_pressed() {
+ if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CURRENT) {
+ play_current_scene();
+ } else {
+ // Reload if already running the current scene.
+ play_current_scene(true);
+ }
+}
+
+void EditorRunBar::_run_scene(const String &p_scene_path) {
+ ERR_FAIL_COND_MSG(current_mode == RUN_CUSTOM && p_scene_path.is_empty(), "Attempting to run a custom scene with an empty path.");
+
+ if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
+ return;
+ }
+
+ _reset_play_buttons();
+
+ String write_movie_file;
+ if (is_movie_maker_enabled()) {
+ if (current_mode == RUN_CURRENT) {
+ Node *scene_root = nullptr;
+ if (p_scene_path.is_empty()) {
+ scene_root = get_tree()->get_edited_scene_root();
+ } else {
+ int scene_index = EditorNode::get_editor_data().get_edited_scene_from_path(p_scene_path);
+ if (scene_index >= 0) {
+ scene_root = EditorNode::get_editor_data().get_edited_scene_root(scene_index);
+ }
+ }
+
+ if (scene_root && scene_root->has_meta("movie_file")) {
+ // If the scene file has a movie_file metadata set, use this as file.
+ // Quick workaround if you want to have multiple scenes that write to
+ // multiple movies.
+ write_movie_file = scene_root->get_meta("movie_file");
+ }
+ }
+
+ if (write_movie_file.is_empty()) {
+ write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
+ }
+
+ if (write_movie_file.is_empty()) {
+ // TODO: Provide options to directly resolve the issue with a custom dialog.
+ EditorNode::get_singleton()->show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
+ return;
+ }
+ }
+
+ String run_filename;
+ switch (current_mode) {
+ case RUN_CUSTOM: {
+ run_filename = p_scene_path;
+ run_custom_filename = run_filename;
+ } break;
+
+ case RUN_CURRENT: {
+ if (!p_scene_path.is_empty()) {
+ run_filename = p_scene_path;
+ run_current_filename = run_filename;
+ break;
+ }
+
+ Node *scene_root = get_tree()->get_edited_scene_root();
+ if (!scene_root) {
+ EditorNode::get_singleton()->show_accept(TTR("There is no defined scene to run."), TTR("OK"));
+ return;
+ }
+
+ if (scene_root->get_scene_file_path().is_empty()) {
+ EditorNode::get_singleton()->save_before_run();
+ return;
+ }
+
+ run_filename = scene_root->get_scene_file_path();
+ run_current_filename = run_filename;
+ } break;
+
+ default: {
+ if (!EditorNode::get_singleton()->ensure_main_scene(false)) {
+ return;
+ }
+
+ run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
+ } break;
+ }
+
+ EditorNode::get_singleton()->try_autosave();
+ if (!EditorNode::get_singleton()->call_build()) {
+ return;
+ }
+
+ EditorDebuggerNode::get_singleton()->start();
+ Error error = editor_run.run(run_filename, write_movie_file);
+ if (error != OK) {
+ EditorDebuggerNode::get_singleton()->stop();
+ EditorNode::get_singleton()->show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
+ return;
+ }
+
+ _update_play_buttons();
+ stop_button->set_disabled(false);
+
+ emit_signal(SNAME("play_pressed"));
+}
+
+void EditorRunBar::_run_native(const Ref<EditorExportPreset> &p_preset) {
+ EditorNode::get_singleton()->try_autosave();
+
+ if (run_native->is_deploy_debug_remote_enabled()) {
+ stop_playing();
+
+ if (!EditorNode::get_singleton()->call_build()) {
+ return; // Build failed.
+ }
+
+ EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
+ emit_signal(SNAME("play_pressed"));
+ editor_run.run_native_notify();
+ }
+}
+
+void EditorRunBar::play_main_scene(bool p_from_native) {
+ if (p_from_native) {
+ run_native->resume_run_native();
+ } else {
+ stop_playing();
+
+ current_mode = RunMode::RUN_MAIN;
+ _run_scene();
+ }
+}
+
+void EditorRunBar::play_current_scene(bool p_reload) {
+ EditorNode::get_singleton()->save_default_environment();
+ stop_playing();
+
+ current_mode = RunMode::RUN_CURRENT;
+ if (p_reload) {
+ String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
+ _run_scene(last_current_scene);
+ } else {
+ _run_scene();
+ }
+}
+
+void EditorRunBar::play_custom_scene(const String &p_custom) {
+ stop_playing();
+
+ current_mode = RunMode::RUN_CUSTOM;
+ _run_scene(p_custom);
+}
+
+void EditorRunBar::stop_playing() {
+ if (editor_run.get_status() == EditorRun::STATUS_STOP) {
+ return;
+ }
+
+ current_mode = RunMode::STOPPED;
+ editor_run.stop();
+ EditorDebuggerNode::get_singleton()->stop();
+
+ run_custom_filename.clear();
+ run_current_filename.clear();
+ stop_button->set_pressed(false);
+ stop_button->set_disabled(true);
+ _reset_play_buttons();
+
+ emit_signal(SNAME("stop_pressed"));
+}
+
+bool EditorRunBar::is_playing() const {
+ EditorRun::Status status = editor_run.get_status();
+ return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
+}
+
+String EditorRunBar::get_playing_scene() const {
+ String run_filename = editor_run.get_running_scene();
+ if (run_filename.is_empty() && is_playing()) {
+ run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
+ }
+
+ return run_filename;
+}
+
+Error EditorRunBar::start_native_device(int p_device_id) {
+ return run_native->start_run_native(p_device_id);
+}
+
+OS::ProcessID EditorRunBar::has_child_process(OS::ProcessID p_pid) const {
+ return editor_run.has_child_process(p_pid);
+}
+
+void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
+ if (!has_child_process(p_pid)) {
+ return;
+ }
+
+ editor_run.stop_child_process(p_pid);
+ if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
+ stop_playing();
+ }
+}
+
+void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
+ write_movie_button->set_pressed(p_enabled);
+}
+
+bool EditorRunBar::is_movie_maker_enabled() const {
+ return write_movie_button->is_pressed();
+}
+
+void EditorRunBar::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("play_pressed"));
+ ADD_SIGNAL(MethodInfo("stop_pressed"));
+}
+
+EditorRunBar::EditorRunBar() {
+ singleton = this;
+
+ main_panel = memnew(PanelContainer);
+ add_child(main_panel);
+
+ HBoxContainer *main_hbox = memnew(HBoxContainer);
+ main_panel->add_child(main_hbox);
+
+ play_button = memnew(Button);
+ main_hbox->add_child(play_button);
+ play_button->set_flat(true);
+ play_button->set_toggle_mode(true);
+ play_button->set_focus_mode(Control::FOCUS_NONE);
+ play_button->set_tooltip_text(TTR("Run the project's default scene."));
+ play_button->connect("pressed", callable_mp(this, &EditorRunBar::play_main_scene).bind(false));
+
+ ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
+ ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
+ play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
+
+ pause_button = memnew(Button);
+ main_hbox->add_child(pause_button);
+ pause_button->set_flat(true);
+ pause_button->set_toggle_mode(true);
+ pause_button->set_focus_mode(Control::FOCUS_NONE);
+ pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
+ pause_button->set_disabled(true);
+
+ ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
+ ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
+ pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
+
+ stop_button = memnew(Button);
+ main_hbox->add_child(stop_button);
+ stop_button->set_flat(true);
+ stop_button->set_focus_mode(Control::FOCUS_NONE);
+ stop_button->set_tooltip_text(TTR("Stop the currently running project."));
+ stop_button->set_disabled(true);
+ stop_button->connect("pressed", callable_mp(this, &EditorRunBar::stop_playing));
+
+ ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
+ ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
+ stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
+
+ run_native = memnew(EditorRunNative);
+ main_hbox->add_child(run_native);
+ run_native->connect("native_run", callable_mp(this, &EditorRunBar::_run_native));
+
+ play_scene_button = memnew(Button);
+ main_hbox->add_child(play_scene_button);
+ play_scene_button->set_flat(true);
+ play_scene_button->set_toggle_mode(true);
+ play_scene_button->set_focus_mode(Control::FOCUS_NONE);
+ play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
+ play_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_current_pressed));
+
+ ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
+ ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
+ play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
+
+ play_custom_scene_button = memnew(Button);
+ main_hbox->add_child(play_custom_scene_button);
+ play_custom_scene_button->set_flat(true);
+ play_custom_scene_button->set_toggle_mode(true);
+ play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
+ play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
+ play_custom_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_custom_pressed));
+
+ ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
+ ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
+ play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
+
+ write_movie_panel = memnew(PanelContainer);
+ main_hbox->add_child(write_movie_panel);
+
+ write_movie_button = memnew(Button);
+ write_movie_panel->add_child(write_movie_button);
+ write_movie_button->set_flat(true);
+ write_movie_button->set_toggle_mode(true);
+ write_movie_button->set_pressed(false);
+ write_movie_button->set_focus_mode(Control::FOCUS_NONE);
+ write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
+ write_movie_button->connect("toggled", callable_mp(this, &EditorRunBar::_write_movie_toggled));
+
+ quick_run = memnew(EditorQuickOpen);
+ add_child(quick_run);
+ quick_run->connect("quick_open", callable_mp(this, &EditorRunBar::_quick_run_selected));
+}
diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h
new file mode 100644
index 0000000000..b7e7db2bd6
--- /dev/null
+++ b/editor/gui/editor_run_bar.h
@@ -0,0 +1,115 @@
+/**************************************************************************/
+/* editor_run_bar.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 EDITOR_RUN_BAR_H
+#define EDITOR_RUN_BAR_H
+
+#include "editor/editor_run.h"
+#include "editor/export/editor_export.h"
+#include "scene/gui/margin_container.h"
+
+class Button;
+class EditorRunNative;
+class EditorQuickOpen;
+class PanelContainer;
+
+class EditorRunBar : public MarginContainer {
+ GDCLASS(EditorRunBar, MarginContainer);
+
+ static EditorRunBar *singleton;
+
+ enum RunMode {
+ STOPPED = 0,
+ RUN_MAIN,
+ RUN_CURRENT,
+ RUN_CUSTOM,
+ };
+
+ PanelContainer *main_panel = nullptr;
+
+ Button *play_button = nullptr;
+ Button *pause_button = nullptr;
+ Button *stop_button = nullptr;
+ Button *play_scene_button = nullptr;
+ Button *play_custom_scene_button = nullptr;
+
+ EditorRun editor_run;
+ EditorRunNative *run_native = nullptr;
+
+ PanelContainer *write_movie_panel = nullptr;
+ Button *write_movie_button = nullptr;
+
+ EditorQuickOpen *quick_run = nullptr;
+
+ RunMode current_mode = RunMode::STOPPED;
+ String run_custom_filename;
+ String run_current_filename;
+
+ void _reset_play_buttons();
+ void _update_play_buttons();
+
+ void _write_movie_toggled(bool p_enabled);
+ void _quick_run_selected();
+
+ void _play_current_pressed();
+ void _play_custom_pressed();
+
+ void _run_scene(const String &p_scene_path = "");
+ void _run_native(const Ref<EditorExportPreset> &p_preset);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ static EditorRunBar *get_singleton() { return singleton; }
+
+ void play_main_scene(bool p_from_native = false);
+ void play_current_scene(bool p_reload = false);
+ void play_custom_scene(const String &p_custom);
+
+ void stop_playing();
+ bool is_playing() const;
+ String get_playing_scene() const;
+
+ Error start_native_device(int p_device_id);
+
+ OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
+ void stop_child_process(OS::ProcessID p_pid);
+
+ void set_movie_maker_enabled(bool p_enabled);
+ bool is_movie_maker_enabled() const;
+
+ Button *get_pause_button() { return pause_button; }
+
+ EditorRunBar();
+};
+
+#endif // EDITOR_RUN_BAR_H
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index ecbf12fed8..6d02fcef1d 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -34,6 +34,7 @@
#include "core/math/expression.h"
#include "core/os/keyboard.h"
#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const {
if (grabber->is_visible()) {
@@ -103,7 +104,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
if (mm->is_shift_pressed() && grabbing_spinner) {
diff_x *= 0.1;
}
- grabbing_spinner_dist_cache += diff_x;
+ grabbing_spinner_dist_cache += diff_x * grabbing_spinner_speed;
if (!grabbing_spinner && ABS(grabbing_spinner_dist_cache) > 4 * EDSCALE) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
@@ -439,7 +440,11 @@ void EditorSpinSlider::_draw_spin_slider() {
void EditorSpinSlider::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_ENTER_TREE: {
+ grabbing_spinner_speed = EditorSettings::get_singleton()->get("interface/inspector/float_drag_speed");
+ _update_value_input_stylebox();
+ } break;
+
case NOTIFICATION_THEME_CHANGED: {
_update_value_input_stylebox();
} break;
diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h
index a4d810b18b..8c643157f1 100644
--- a/editor/gui/editor_spin_slider.h
+++ b/editor/gui/editor_spin_slider.h
@@ -60,6 +60,7 @@ class EditorSpinSlider : public Range {
bool read_only = false;
float grabbing_spinner_dist_cache = 0.0f;
+ float grabbing_spinner_speed = 0.0f;
Vector2 grabbing_spinner_mouse_pos;
double pre_grab_value = 0.0;
diff --git a/editor/icons/BusVuFull.svg b/editor/icons/BusVuActive.svg
index 0bc00971c0..0bc00971c0 100644
--- a/editor/icons/BusVuFull.svg
+++ b/editor/icons/BusVuActive.svg
diff --git a/editor/icons/BusVuEmpty.svg b/editor/icons/BusVuEmpty.svg
deleted file mode 100644
index 88a14bc3ee..0000000000
--- a/editor/icons/BusVuEmpty.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="128" viewBox="0 0 16 128" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="926.36" y2="1050.36"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><g transform="translate(0 -924.36)"><path d="m3 926.36c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill="url(#a)"/><path d="m3 2c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill-opacity=".19608" transform="translate(0 924.36)"/></g></svg>
diff --git a/editor/icons/MakeFloating.svg b/editor/icons/MakeFloating.svg
new file mode 100644
index 0000000000..57ccce38ea
--- /dev/null
+++ b/editor/icons/MakeFloating.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 4.2333333 4.2333333" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m1.2907908.14089245c-.258841 0-.46866132.20982028-.46866132.46866151v.23432634h3.28061252v-.23432634c0-.25884123-.2098291-.46866151-.4686613-.46866151zm2.1089635.23433517h.2343264v.23432634h-.2343264zm-2.57762482.70298788v1.8746284c0 .2588412.20982912.4686614.46866132.4686614h2.3432899c.258841 0 .4686613-.2098202.4686613-.4686614v-1.8746284z" stroke-width=".23433"/><path d="m12.189144-6.0533422 5.5-5.4999998-2.44-2.439h7v6.9999998l-2.439-2.439-5.5 5.5z" stroke="#000" stroke-width="1.01435" transform="matrix(.19814944 0 0 .19814944 -2.163454 4.759098)"/></g></svg>
diff --git a/editor/icons/SnapDisable.svg b/editor/icons/SnapDisable.svg
new file mode 100644
index 0000000000..9bc0a6ad94
--- /dev/null
+++ b/editor/icons/SnapDisable.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11 7a4 4 0 0 0 -4 4v2h2v-2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h2v-2a4 4 0 0 0 -4-4z" fill="#fff" fill-opacity=".68627"/><g fill="#e0e0e0"><path d="m4.1817443 1.9883112a.49892098.49907772 0 0 0 -.3478478.856731l2.1416536 2.1423264-2.1416536 2.1423263a.49892098.49907772 0 1 0 .7054536.7056752l2.1416536-2.1423263 2.1416535 2.1423263a.49892098.49907772 0 1 0 .7054536-.7056752l-2.1416536-2.1423263 2.1416536-2.1423264a.49892098.49907772 0 0 0 -.3624598-.8557329.49892098.49907772 0 0 0 -.3429739.1500975l-2.1416534 2.1423265-2.1416537-2.1423265a.49892098.49907772 0 0 0 -.3575908-.1510706z" fill-rule="evenodd" stroke-width=".498949"/><path d="m7 13v2h2v-2zm6 0v2h2v-2z"/></g></svg>
diff --git a/editor/icons/Vector4i.svg b/editor/icons/Vector4i.svg
new file mode 100644
index 0000000000..ddab1f1747
--- /dev/null
+++ b/editor/icons/Vector4i.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg"><path d="m1 4v6h2c1.6568542 0 3-1.3431458 3-3v-3h-2v3c0 .5522847-.4477153 1-1 1v-4z" fill="#de66f0"/><path d="m8.482 1.523-1.526 5.694 3.02-.015.043997 3.795h1.980003v-5.881h-2.373l.943-3.595z" fill="#eba2f6"/><path d="m13 2v2h2v-2zm0 4v4.000001h2v-4.000001z" fill="#5abbef"/></svg>
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index a8d746fde6..a499fc4feb 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -44,10 +44,10 @@
/* HELPERS */
-String Collada::Effect::get_texture_path(const String &p_source, Collada &state) const {
+String Collada::Effect::get_texture_path(const String &p_source, Collada &p_state) const {
const String &image = p_source;
- ERR_FAIL_COND_V(!state.state.image_map.has(image), "");
- return state.state.image_map[image].path;
+ ERR_FAIL_COND_V(!p_state.state.image_map.has(image), "");
+ return p_state.state.image_map[image].path;
}
Transform3D Collada::get_root_transform() const {
@@ -58,9 +58,9 @@ Transform3D Collada::get_root_transform() const {
return unit_scale_transform;
}
-void Collada::Vertex::fix_unit_scale(const Collada &state) {
+void Collada::Vertex::fix_unit_scale(const Collada &p_state) {
#ifdef COLLADA_IMPORT_SCALE_SCENE
- vertex *= state.state.unit_scale;
+ vertex *= p_state.state.unit_scale;
#endif
}
@@ -102,27 +102,27 @@ Transform3D Collada::fix_transform(const Transform3D &p_transform) {
//return state.matrix_fix * p_transform;
}
-static Transform3D _read_transform_from_array(const Vector<float> &array, int ofs = 0) {
+static Transform3D _read_transform_from_array(const Vector<float> &p_array, int p_ofs = 0) {
Transform3D tr;
// i wonder why collada matrices are transposed, given that's opposed to opengl..
- tr.basis.rows[0][0] = array[0 + ofs];
- tr.basis.rows[0][1] = array[1 + ofs];
- tr.basis.rows[0][2] = array[2 + ofs];
- tr.basis.rows[1][0] = array[4 + ofs];
- tr.basis.rows[1][1] = array[5 + ofs];
- tr.basis.rows[1][2] = array[6 + ofs];
- tr.basis.rows[2][0] = array[8 + ofs];
- tr.basis.rows[2][1] = array[9 + ofs];
- tr.basis.rows[2][2] = array[10 + ofs];
- tr.origin.x = array[3 + ofs];
- tr.origin.y = array[7 + ofs];
- tr.origin.z = array[11 + ofs];
+ tr.basis.rows[0][0] = p_array[0 + p_ofs];
+ tr.basis.rows[0][1] = p_array[1 + p_ofs];
+ tr.basis.rows[0][2] = p_array[2 + p_ofs];
+ tr.basis.rows[1][0] = p_array[4 + p_ofs];
+ tr.basis.rows[1][1] = p_array[5 + p_ofs];
+ tr.basis.rows[1][2] = p_array[6 + p_ofs];
+ tr.basis.rows[2][0] = p_array[8 + p_ofs];
+ tr.basis.rows[2][1] = p_array[9 + p_ofs];
+ tr.basis.rows[2][2] = p_array[10 + p_ofs];
+ tr.origin.x = p_array[3 + p_ofs];
+ tr.origin.y = p_array[7 + p_ofs];
+ tr.origin.z = p_array[11 + p_ofs];
return tr;
}
/* STRUCTURES */
-Transform3D Collada::Node::compute_transform(const Collada &state) const {
+Transform3D Collada::Node::compute_transform(const Collada &p_state) const {
Transform3D xform;
for (int i = 0; i < xform_list.size(); i++) {
@@ -160,7 +160,7 @@ Transform3D Collada::Node::compute_transform(const Collada &state) const {
}
#ifdef COLLADA_IMPORT_SCALE_SCENE
- xform.origin *= state.state.unit_scale;
+ xform.origin *= p_state.state.unit_scale;
#endif
return xform;
}
@@ -243,41 +243,41 @@ Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) const {
ERR_FAIL_V(Vector<float>());
}
-void Collada::_parse_asset(XMLParser &parser) {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+void Collada::_parse_asset(XMLParser &p_parser) {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "up_axis") {
- parser.read();
- if (parser.get_node_data() == "X_UP") {
+ p_parser.read();
+ if (p_parser.get_node_data() == "X_UP") {
state.up_axis = Vector3::AXIS_X;
}
- if (parser.get_node_data() == "Y_UP") {
+ if (p_parser.get_node_data() == "Y_UP") {
state.up_axis = Vector3::AXIS_Y;
}
- if (parser.get_node_data() == "Z_UP") {
+ if (p_parser.get_node_data() == "Z_UP") {
state.up_axis = Vector3::AXIS_Z;
}
- COLLADA_PRINT("up axis: " + parser.get_node_data());
+ COLLADA_PRINT("up axis: " + p_parser.get_node_data());
} else if (name == "unit") {
- state.unit_scale = parser.get_named_attribute_value("meter").to_float();
+ state.unit_scale = p_parser.get_named_attribute_value("meter").to_float();
COLLADA_PRINT("unit scale: " + rtos(state.unit_scale));
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "asset") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "asset") {
break; //end of <asset>
}
}
}
-void Collada::_parse_image(XMLParser &parser) {
- String id = parser.get_named_attribute_value("id");
+void Collada::_parse_image(XMLParser &p_parser) {
+ String id = p_parser.get_named_attribute_value("id");
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
@@ -286,19 +286,19 @@ void Collada::_parse_image(XMLParser &parser) {
if (state.version < State::Version(1, 4, 0)) {
/* <1.4 */
- String path = parser.get_named_attribute_value("source").strip_edges();
+ String path = p_parser.get_named_attribute_value("source").strip_edges();
if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().path_join(path.uri_decode()));
}
} else {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "init_from") {
- parser.read();
- String path = parser.get_node_data().strip_edges().uri_decode();
+ p_parser.read();
+ String path = p_parser.get_node_data().strip_edges().uri_decode();
if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
@@ -314,11 +314,11 @@ void Collada::_parse_image(XMLParser &parser) {
} else if (name == "data") {
ERR_PRINT("COLLADA Embedded image data not supported!");
- } else if (name == "extra" && !parser.is_empty()) {
- parser.skip_section();
+ } else if (name == "extra" && !p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "image") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "image") {
break; //end of <asset>
}
}
@@ -327,29 +327,29 @@ void Collada::_parse_image(XMLParser &parser) {
state.image_map[id] = image;
}
-void Collada::_parse_material(XMLParser &parser) {
+void Collada::_parse_material(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
Material material;
- String id = parser.get_named_attribute_value("id");
- if (parser.has_attribute("name")) {
- material.name = parser.get_named_attribute_value("name");
+ String id = p_parser.get_named_attribute_value("id");
+ if (p_parser.has_attribute("name")) {
+ material.name = p_parser.get_named_attribute_value("name");
}
if (state.version < State::Version(1, 4, 0)) {
/* <1.4 */
ERR_PRINT("Collada Materials < 1.4 are not supported (yet)");
} else {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT && parser.get_node_name() == "instance_effect") {
- material.instance_effect = _uri_to_id(parser.get_named_attribute_value("url"));
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "material") {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT && p_parser.get_node_name() == "instance_effect") {
+ material.instance_effect = _uri_to_id(p_parser.get_named_attribute_value("url"));
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "material") {
break; //end of <asset>
}
}
@@ -359,8 +359,8 @@ void Collada::_parse_material(XMLParser &parser) {
}
//! reads floats from inside of xml element until end of xml element
-Vector<float> Collada::_read_float_array(XMLParser &parser) {
- if (parser.is_empty()) {
+Vector<float> Collada::_read_float_array(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return Vector<float>();
}
@@ -371,16 +371,16 @@ Vector<float> Collada::_read_float_array(XMLParser &parser) {
splitters.push_back("\t");
Vector<float> array;
- while (parser.read() == OK) {
+ while (p_parser.read() == OK) {
// TODO: check for comments inside the element
// and ignore them.
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
// parse float data
- String str = parser.get_node_data();
+ String str = p_parser.get_node_data();
array = str.split_floats_mk(splitters, false);
//array=str.split_floats(" ",false);
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
break; // end parsing text
}
}
@@ -388,21 +388,21 @@ Vector<float> Collada::_read_float_array(XMLParser &parser) {
return array;
}
-Vector<String> Collada::_read_string_array(XMLParser &parser) {
- if (parser.is_empty()) {
+Vector<String> Collada::_read_string_array(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return Vector<String>();
}
Vector<String> array;
- while (parser.read() == OK) {
+ while (p_parser.read() == OK) {
// TODO: check for comments inside the element
// and ignore them.
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
// parse String data
- String str = parser.get_node_data();
+ String str = p_parser.get_node_data();
array = str.split_spaces();
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
break; // end parsing text
}
}
@@ -410,21 +410,21 @@ Vector<String> Collada::_read_string_array(XMLParser &parser) {
return array;
}
-Transform3D Collada::_read_transform(XMLParser &parser) {
- if (parser.is_empty()) {
+Transform3D Collada::_read_transform(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return Transform3D();
}
Vector<String> array;
- while (parser.read() == OK) {
+ while (p_parser.read() == OK) {
// TODO: check for comments inside the element
// and ignore them.
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
// parse float data
- String str = parser.get_node_data();
+ String str = p_parser.get_node_data();
array = str.split_spaces();
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
break; // end parsing text
}
}
@@ -439,332 +439,332 @@ Transform3D Collada::_read_transform(XMLParser &parser) {
return _read_transform_from_array(farr);
}
-String Collada::_read_empty_draw_type(XMLParser &parser) {
+String Collada::_read_empty_draw_type(XMLParser &p_parser) {
String empty_draw_type = "";
- if (parser.is_empty()) {
+ if (p_parser.is_empty()) {
return empty_draw_type;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
- empty_draw_type = parser.get_node_data();
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
+ empty_draw_type = p_parser.get_node_data();
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END) {
break; // end parsing text
}
}
return empty_draw_type;
}
-Variant Collada::_parse_param(XMLParser &parser) {
- if (parser.is_empty()) {
+Variant Collada::_parse_param(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return Variant();
}
- String from = parser.get_node_name();
+ String from = p_parser.get_node_name();
Variant data;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "float") {
- parser.read();
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
- data = parser.get_node_data().to_float();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "float") {
+ p_parser.read();
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
+ data = p_parser.get_node_data().to_float();
}
- } else if (parser.get_node_name() == "float2") {
- Vector<float> v2 = _read_float_array(parser);
+ } else if (p_parser.get_node_name() == "float2") {
+ Vector<float> v2 = _read_float_array(p_parser);
if (v2.size() >= 2) {
data = Vector2(v2[0], v2[1]);
}
- } else if (parser.get_node_name() == "float3") {
- Vector<float> v3 = _read_float_array(parser);
+ } else if (p_parser.get_node_name() == "float3") {
+ Vector<float> v3 = _read_float_array(p_parser);
if (v3.size() >= 3) {
data = Vector3(v3[0], v3[1], v3[2]);
}
- } else if (parser.get_node_name() == "float4") {
- Vector<float> v4 = _read_float_array(parser);
+ } else if (p_parser.get_node_name() == "float4") {
+ Vector<float> v4 = _read_float_array(p_parser);
if (v4.size() >= 4) {
data = Color(v4[0], v4[1], v4[2], v4[3]);
}
- } else if (parser.get_node_name() == "sampler2D") {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "source") {
- parser.read();
+ } else if (p_parser.get_node_name() == "sampler2D") {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "source") {
+ p_parser.read();
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
- data = parser.get_node_data();
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
+ data = p_parser.get_node_data();
}
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "sampler2D") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "sampler2D") {
break;
}
}
- } else if (parser.get_node_name() == "surface") {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "init_from") {
- parser.read();
+ } else if (p_parser.get_node_name() == "surface") {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "init_from") {
+ p_parser.read();
- if (parser.get_node_type() == XMLParser::NODE_TEXT) {
- data = parser.get_node_data();
+ if (p_parser.get_node_type() == XMLParser::NODE_TEXT) {
+ data = p_parser.get_node_data();
}
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "surface") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "surface") {
break;
}
}
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == from) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == from) {
break;
}
}
- COLLADA_PRINT("newparam ending " + parser.get_node_name());
+ COLLADA_PRINT("newparam ending " + p_parser.get_node_name());
return data;
}
-void Collada::_parse_effect_material(XMLParser &parser, Effect &effect, String &id) {
+void Collada::_parse_effect_material(XMLParser &p_parser, Effect &p_effect, String &p_id) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
// first come the tags we descend, but ignore the top-levels
- COLLADA_PRINT("node name: " + parser.get_node_name());
+ COLLADA_PRINT("node name: " + p_parser.get_node_name());
- if (!parser.is_empty() &&
- (parser.get_node_name() == "profile_COMMON" ||
- parser.get_node_name() == "technique" ||
- parser.get_node_name() == "extra")) {
- _parse_effect_material(parser, effect, id); // try again
+ if (!p_parser.is_empty() &&
+ (p_parser.get_node_name() == "profile_COMMON" ||
+ p_parser.get_node_name() == "technique" ||
+ p_parser.get_node_name() == "extra")) {
+ _parse_effect_material(p_parser, p_effect, p_id); // try again
- } else if (parser.get_node_name() == "newparam") {
- String name = parser.get_named_attribute_value("sid");
- Variant value = _parse_param(parser);
- effect.params[name] = value;
+ } else if (p_parser.get_node_name() == "newparam") {
+ String name = p_parser.get_named_attribute_value("sid");
+ Variant value = _parse_param(p_parser);
+ p_effect.params[name] = value;
COLLADA_PRINT("param: " + name + " value:" + String(value));
- } else if (parser.get_node_name() == "constant" ||
- parser.get_node_name() == "lambert" ||
- parser.get_node_name() == "phong" ||
- parser.get_node_name() == "blinn") {
- COLLADA_PRINT("shade model: " + parser.get_node_name());
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String what = parser.get_node_name();
+ } else if (p_parser.get_node_name() == "constant" ||
+ p_parser.get_node_name() == "lambert" ||
+ p_parser.get_node_name() == "phong" ||
+ p_parser.get_node_name() == "blinn") {
+ COLLADA_PRINT("shade model: " + p_parser.get_node_name());
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String what = p_parser.get_node_name();
if (what == "emission" ||
what == "diffuse" ||
what == "specular" ||
what == "reflective") {
// color or texture types
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "color") {
- Vector<float> colorarr = _read_float_array(parser);
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "color") {
+ Vector<float> colorarr = _read_float_array(p_parser);
COLLADA_PRINT("colorarr size: " + rtos(colorarr.size()));
if (colorarr.size() >= 3) {
// alpha strangely not alright? maybe it needs to be multiplied by value as a channel intensity
Color color(colorarr[0], colorarr[1], colorarr[2], 1.0);
if (what == "diffuse") {
- effect.diffuse.color = color;
+ p_effect.diffuse.color = color;
}
if (what == "specular") {
- effect.specular.color = color;
+ p_effect.specular.color = color;
}
if (what == "emission") {
- effect.emission.color = color;
+ p_effect.emission.color = color;
}
COLLADA_PRINT(what + " color: " + color);
}
- } else if (parser.get_node_name() == "texture") {
- String sampler = parser.get_named_attribute_value("texture");
- if (!effect.params.has(sampler)) {
- ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data());
+ } else if (p_parser.get_node_name() == "texture") {
+ String sampler = p_parser.get_named_attribute_value("texture");
+ if (!p_effect.params.has(sampler)) {
+ ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + p_id).utf8().get_data());
} else {
- String surface = effect.params[sampler];
+ String surface = p_effect.params[sampler];
- if (!effect.params.has(surface)) {
- ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + id).utf8().get_data());
+ if (!p_effect.params.has(surface)) {
+ ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + p_id).utf8().get_data());
} else {
- String uri = effect.params[surface];
+ String uri = p_effect.params[surface];
if (what == "diffuse") {
- effect.diffuse.texture = uri;
+ p_effect.diffuse.texture = uri;
} else if (what == "specular") {
- effect.specular.texture = uri;
+ p_effect.specular.texture = uri;
} else if (what == "emission") {
- effect.emission.texture = uri;
+ p_effect.emission.texture = uri;
} else if (what == "bump") {
- if (parser.has_attribute("bumptype") && parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
+ if (p_parser.has_attribute("bumptype") && p_parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
- effect.bump.texture = uri;
+ p_effect.bump.texture = uri;
}
COLLADA_PRINT(what + " texture: " + uri);
}
}
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == what) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == what) {
break;
}
}
} else if (what == "shininess") {
- effect.shininess = _parse_param(parser);
+ p_effect.shininess = _parse_param(p_parser);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END &&
- (parser.get_node_name() == "constant" ||
- parser.get_node_name() == "lambert" ||
- parser.get_node_name() == "phong" ||
- parser.get_node_name() == "blinn")) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END &&
+ (p_parser.get_node_name() == "constant" ||
+ p_parser.get_node_name() == "lambert" ||
+ p_parser.get_node_name() == "phong" ||
+ p_parser.get_node_name() == "blinn")) {
break;
}
}
- } else if (parser.get_node_name() == "double_sided" || parser.get_node_name() == "show_double_sided") { // colladamax / google earth
+ } else if (p_parser.get_node_name() == "double_sided" || p_parser.get_node_name() == "show_double_sided") { // colladamax / google earth
// 3DS Max / Google Earth double sided extension
- parser.read();
- effect.found_double_sided = true;
- effect.double_sided = parser.get_node_data().to_int();
- COLLADA_PRINT("double sided: " + itos(parser.get_node_data().to_int()));
- } else if (parser.get_node_name() == "unshaded") {
- parser.read();
- effect.unshaded = parser.get_node_data().to_int();
- } else if (parser.get_node_name() == "bump") {
+ p_parser.read();
+ p_effect.found_double_sided = true;
+ p_effect.double_sided = p_parser.get_node_data().to_int();
+ COLLADA_PRINT("double sided: " + itos(p_parser.get_node_data().to_int()));
+ } else if (p_parser.get_node_name() == "unshaded") {
+ p_parser.read();
+ p_effect.unshaded = p_parser.get_node_data().to_int();
+ } else if (p_parser.get_node_name() == "bump") {
// color or texture types
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "texture") {
- String sampler = parser.get_named_attribute_value("texture");
- if (!effect.params.has(sampler)) {
- ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + id).utf8().get_data());
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "texture") {
+ String sampler = p_parser.get_named_attribute_value("texture");
+ if (!p_effect.params.has(sampler)) {
+ ERR_PRINT(String("Couldn't find sampler: " + sampler + " in material:" + p_id).utf8().get_data());
} else {
- String surface = effect.params[sampler];
+ String surface = p_effect.params[sampler];
- if (!effect.params.has(surface)) {
- ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + id).utf8().get_data());
+ if (!p_effect.params.has(surface)) {
+ ERR_PRINT(String("Couldn't find surface: " + surface + " in material:" + p_id).utf8().get_data());
} else {
- String uri = effect.params[surface];
+ String uri = p_effect.params[surface];
- if (parser.has_attribute("bumptype") && parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
+ if (p_parser.has_attribute("bumptype") && p_parser.get_named_attribute_value("bumptype") != "NORMALMAP") {
WARN_PRINT("'bump' texture type is not NORMALMAP, only NORMALMAP is supported.");
}
- effect.bump.texture = uri;
+ p_effect.bump.texture = uri;
COLLADA_PRINT(" bump: " + uri);
}
}
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "bump") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "bump") {
break;
}
}
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END &&
- (parser.get_node_name() == "effect" ||
- parser.get_node_name() == "profile_COMMON" ||
- parser.get_node_name() == "technique" ||
- parser.get_node_name() == "extra")) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END &&
+ (p_parser.get_node_name() == "effect" ||
+ p_parser.get_node_name() == "profile_COMMON" ||
+ p_parser.get_node_name() == "technique" ||
+ p_parser.get_node_name() == "extra")) {
break;
}
}
}
-void Collada::_parse_effect(XMLParser &parser) {
+void Collada::_parse_effect(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
Effect effect;
- if (parser.has_attribute("name")) {
- effect.name = parser.get_named_attribute_value("name");
+ if (p_parser.has_attribute("name")) {
+ effect.name = p_parser.get_named_attribute_value("name");
}
- _parse_effect_material(parser, effect, id);
+ _parse_effect_material(p_parser, effect, id);
state.effect_map[id] = effect;
COLLADA_PRINT("Effect ID:" + id);
}
-void Collada::_parse_camera(XMLParser &parser) {
+void Collada::_parse_camera(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
state.camera_data_map[id] = CameraData();
CameraData &camera = state.camera_data_map[id];
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "perspective") {
camera.mode = CameraData::MODE_PERSPECTIVE;
} else if (name == "orthographic") {
camera.mode = CameraData::MODE_ORTHOGONAL;
} else if (name == "xfov") {
- parser.read();
- camera.perspective.x_fov = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.perspective.x_fov = p_parser.get_node_data().to_float();
} else if (name == "yfov") {
- parser.read();
- camera.perspective.y_fov = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.perspective.y_fov = p_parser.get_node_data().to_float();
} else if (name == "xmag") {
- parser.read();
- camera.orthogonal.x_mag = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.orthogonal.x_mag = p_parser.get_node_data().to_float();
} else if (name == "ymag") {
- parser.read();
- camera.orthogonal.y_mag = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.orthogonal.y_mag = p_parser.get_node_data().to_float();
} else if (name == "aspect_ratio") {
- parser.read();
- camera.aspect = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.aspect = p_parser.get_node_data().to_float();
} else if (name == "znear") {
- parser.read();
- camera.z_near = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.z_near = p_parser.get_node_data().to_float();
} else if (name == "zfar") {
- parser.read();
- camera.z_far = parser.get_node_data().to_float();
+ p_parser.read();
+ camera.z_far = p_parser.get_node_data().to_float();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "camera") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "camera") {
break; //end of <asset>
}
}
@@ -772,22 +772,22 @@ void Collada::_parse_camera(XMLParser &parser) {
COLLADA_PRINT("Camera ID:" + id);
}
-void Collada::_parse_light(XMLParser &parser) {
+void Collada::_parse_light(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
state.light_data_map[id] = LightData();
LightData &light = state.light_data_map[id];
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "ambient") {
light.mode = LightData::MODE_AMBIENT;
@@ -798,8 +798,8 @@ void Collada::_parse_light(XMLParser &parser) {
} else if (name == "spot") {
light.mode = LightData::MODE_SPOT;
} else if (name == "color") {
- parser.read();
- Vector<float> colorarr = _read_float_array(parser);
+ p_parser.read();
+ Vector<float> colorarr = _read_float_array(p_parser);
COLLADA_PRINT("colorarr size: " + rtos(colorarr.size()));
if (colorarr.size() >= 4) {
@@ -809,24 +809,24 @@ void Collada::_parse_light(XMLParser &parser) {
}
} else if (name == "constant_attenuation") {
- parser.read();
- light.constant_att = parser.get_node_data().to_float();
+ p_parser.read();
+ light.constant_att = p_parser.get_node_data().to_float();
} else if (name == "linear_attenuation") {
- parser.read();
- light.linear_att = parser.get_node_data().to_float();
+ p_parser.read();
+ light.linear_att = p_parser.get_node_data().to_float();
} else if (name == "quadratic_attenuation") {
- parser.read();
- light.quad_att = parser.get_node_data().to_float();
+ p_parser.read();
+ light.quad_att = p_parser.get_node_data().to_float();
} else if (name == "falloff_angle") {
- parser.read();
- light.spot_angle = parser.get_node_data().to_float();
+ p_parser.read();
+ light.spot_angle = p_parser.get_node_data().to_float();
} else if (name == "falloff_exponent") {
- parser.read();
- light.spot_exp = parser.get_node_data().to_float();
+ p_parser.read();
+ light.spot_exp = p_parser.get_node_data().to_float();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "light") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "light") {
break; //end of <asset>
}
}
@@ -834,10 +834,10 @@ void Collada::_parse_light(XMLParser &parser) {
COLLADA_PRINT("Light ID:" + id);
}
-void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_name) {
+void Collada::_parse_curve_geometry(XMLParser &p_parser, String p_id, String p_name) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
@@ -848,7 +848,7 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
CurveData &curvedata = state.curve_data_map[p_id];
curvedata.name = p_name;
- String closed = parser.get_named_attribute_value_safe("closed").to_lower();
+ String closed = p_parser.get_named_attribute_value_safe("closed").to_lower();
curvedata.closed = closed == "true" || closed == "1";
COLLADA_PRINT("curve name: " + p_name);
@@ -856,16 +856,16 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
String current_source;
// handles geometry node and the curve children in this loop
// read sources with arrays and accessor for each curve
- if (parser.is_empty()) {
+ if (p_parser.is_empty()) {
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "source") {
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
curvedata.sources[id] = CurveData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -873,13 +873,13 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
} else if (section == "float_array" || section == "array") {
// create a new array and read it.
if (curvedata.sources.has(current_source)) {
- curvedata.sources[current_source].array = _read_float_array(parser);
+ curvedata.sources[current_source].array = _read_float_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(curvedata.sources[current_source].array.size()) + " values.");
}
} else if (section == "Name_array") {
// create a new array and read it.
if (curvedata.sources.has(current_source)) {
- curvedata.sources[current_source].sarray = _read_string_array(parser);
+ curvedata.sources[current_source].sarray = _read_string_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(curvedata.sources[current_source].array.size()) + " values.");
}
@@ -888,38 +888,38 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
} else if (section == "accessor") { // child of source (below a technique tag)
if (curvedata.sources.has(current_source)) {
- curvedata.sources[current_source].stride = parser.get_named_attribute_value("stride").to_int();
+ curvedata.sources[current_source].stride = p_parser.get_named_attribute_value("stride").to_int();
COLLADA_PRINT("section: " + current_source + " stride " + itos(curvedata.sources[current_source].stride));
}
} else if (section == "control_vertices") {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
curvedata.control_vertices[semantic] = source;
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "spline") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "spline") {
break;
}
}
}
-void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name) {
+void Collada::_parse_mesh_geometry(XMLParser &p_parser, String p_id, String p_name) {
if (!(state.import_flags & IMPORT_FLAG_SCENE)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
}
@@ -936,16 +936,16 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
String current_source;
// handles geometry node and the mesh children in this loop
// read sources with arrays and accessor for each mesh
- if (parser.is_empty()) {
+ if (p_parser.is_empty()) {
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "source") {
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
meshdata.sources[id] = MeshData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -953,7 +953,7 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
} else if (section == "float_array" || section == "array") {
// create a new array and read it.
if (meshdata.sources.has(current_source)) {
- meshdata.sources[current_source].array = _read_float_array(parser);
+ meshdata.sources[current_source].array = _read_float_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(meshdata.sources[current_source].array.size()) + " values.");
}
} else if (section == "technique_common") {
@@ -961,19 +961,19 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
} else if (section == "accessor") { // child of source (below a technique tag)
if (meshdata.sources.has(current_source)) {
- meshdata.sources[current_source].stride = parser.get_named_attribute_value("stride").to_int();
+ meshdata.sources[current_source].stride = p_parser.get_named_attribute_value("stride").to_int();
COLLADA_PRINT("section: " + current_source + " stride " + itos(meshdata.sources[current_source].stride));
}
} else if (section == "vertices") {
MeshData::Vertices vert;
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
int last_ref = 0;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
if (semantic == "TEXCOORD") {
semantic = "TEXCOORD" + itos(last_ref++);
@@ -983,7 +983,7 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
@@ -997,23 +997,23 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
}
MeshData::Primitives prim;
- if (parser.has_attribute("material")) {
- prim.material = parser.get_named_attribute_value("material");
+ if (p_parser.has_attribute("material")) {
+ prim.material = p_parser.get_named_attribute_value("material");
}
- prim.count = parser.get_named_attribute_value("count").to_int();
+ prim.count = p_parser.get_named_attribute_value("count").to_int();
prim.vertex_size = 0;
int last_ref = 0;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
if (semantic == "TEXCOORD") {
semantic = "TEXCOORD" + itos(last_ref++);
}
- int offset = parser.get_named_attribute_value("offset").to_int();
+ int offset = p_parser.get_named_attribute_value("offset").to_int();
MeshData::Primitives::SourceRef sref;
sref.source = source;
@@ -1023,9 +1023,9 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source + " offset: " + itos(offset));
- } else if (parser.get_node_name() == "p") { //indices
+ } else if (p_parser.get_node_name() == "p") { //indices
- Vector<float> values = _read_float_array(parser);
+ Vector<float> values = _read_float_array(p_parser);
if (polygons) {
ERR_CONTINUE(prim.vertex_size == 0);
prim.polygons.push_back(values.size() / prim.vertex_size);
@@ -1041,49 +1041,49 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
COLLADA_PRINT("read " + itos(values.size()) + " index values");
- } else if (parser.get_node_name() == "vcount") { // primitive
+ } else if (p_parser.get_node_name() == "vcount") { // primitive
- Vector<float> values = _read_float_array(parser);
+ Vector<float> values = _read_float_array(p_parser);
prim.polygons = values;
COLLADA_PRINT("read " + itos(values.size()) + " polygon values");
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
meshdata.primitives.push_back(prim);
- } else if (parser.get_node_name() == "double_sided") {
- parser.read();
+ } else if (p_parser.get_node_name() == "double_sided") {
+ p_parser.read();
meshdata.found_double_sided = true;
- meshdata.double_sided = parser.get_node_data().to_int();
+ meshdata.double_sided = p_parser.get_node_data().to_int();
- } else if (parser.get_node_name() == "polygons") {
+ } else if (p_parser.get_node_name() == "polygons") {
ERR_PRINT("Primitive type \"polygons\" not supported, re-export using \"polylist\" or \"triangles\".");
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "mesh") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "mesh") {
break;
}
}
}
-void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
+void Collada::_parse_skin_controller(XMLParser &p_parser, String p_id) {
state.skin_controller_data_map[p_id] = SkinControllerData();
SkinControllerData &skindata = state.skin_controller_data_map[p_id];
- skindata.base = _uri_to_id(parser.get_named_attribute_value("source"));
+ skindata.base = _uri_to_id(p_parser.get_named_attribute_value("source"));
String current_source;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "bind_shape_matrix") {
- skindata.bind_shape = _read_transform(parser);
+ skindata.bind_shape = _read_transform(p_parser);
#ifdef COLLADA_IMPORT_SCALE_SCENE
skindata.bind_shape.origin *= state.unit_scale;
@@ -1091,7 +1091,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
COLLADA_PRINT("skeleton bind shape transform: " + skindata.bind_shape);
} else if (section == "source") {
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
skindata.sources[id] = SkinControllerData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -1099,7 +1099,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
} else if (section == "float_array" || section == "array") {
// create a new array and read it.
if (skindata.sources.has(current_source)) {
- skindata.sources[current_source].array = _read_float_array(parser);
+ skindata.sources[current_source].array = _read_float_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(skindata.sources[current_source].array.size()) + " values.");
}
} else if (section == "Name_array" || section == "IDREF_array") {
@@ -1109,7 +1109,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
skindata.use_idrefs = true;
}
if (skindata.sources.has(current_source)) {
- skindata.sources[current_source].sarray = _read_string_array(parser);
+ skindata.sources[current_source].sarray = _read_string_array(p_parser);
if (section == "IDREF_array") {
Vector<String> sa = skindata.sources[current_source].sarray;
for (int i = 0; i < sa.size(); i++) {
@@ -1124,8 +1124,8 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
if (skindata.sources.has(current_source)) {
int stride = 1;
- if (parser.has_attribute("stride")) {
- stride = parser.get_named_attribute_value("stride").to_int();
+ if (p_parser.has_attribute("stride")) {
+ stride = p_parser.get_named_attribute_value("stride").to_int();
}
skindata.sources[current_source].stride = stride;
@@ -1135,17 +1135,17 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
} else if (section == "joints") {
SkinControllerData::Joints joint;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
joint.sources[semantic] = source;
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
@@ -1155,15 +1155,15 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
} else if (section == "vertex_weights") {
SkinControllerData::Weights weights;
- weights.count = parser.get_named_attribute_value("count").to_int();
+ weights.count = p_parser.get_named_attribute_value("count").to_int();
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
- int offset = parser.get_named_attribute_value("offset").to_int();
+ int offset = p_parser.get_named_attribute_value("offset").to_int();
SkinControllerData::Weights::SourceRef sref;
sref.source = source;
@@ -1172,26 +1172,26 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source + " offset: " + itos(offset));
- } else if (parser.get_node_name() == "v") { //indices
+ } else if (p_parser.get_node_name() == "v") { //indices
- Vector<float> values = _read_float_array(parser);
+ Vector<float> values = _read_float_array(p_parser);
weights.indices = values;
COLLADA_PRINT("read " + itos(values.size()) + " index values");
- } else if (parser.get_node_name() == "vcount") { // weightsitive
+ } else if (p_parser.get_node_name() == "vcount") { // weightsitive
- Vector<float> values = _read_float_array(parser);
+ Vector<float> values = _read_float_array(p_parser);
weights.sets = values;
COLLADA_PRINT("read " + itos(values.size()) + " polygon values");
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
skindata.weights = weights;
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "skin") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "skin") {
break;
}
}
@@ -1224,20 +1224,20 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
}
}
-void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
+void Collada::_parse_morph_controller(XMLParser &p_parser, String p_id) {
state.morph_controller_data_map[p_id] = MorphControllerData();
MorphControllerData &morphdata = state.morph_controller_data_map[p_id];
- morphdata.mesh = _uri_to_id(parser.get_named_attribute_value("source"));
- morphdata.mode = parser.get_named_attribute_value("method");
+ morphdata.mesh = _uri_to_id(p_parser.get_named_attribute_value("source"));
+ morphdata.mode = p_parser.get_named_attribute_value("method");
String current_source;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "source") {
- String id = parser.get_named_attribute_value("id");
+ String id = p_parser.get_named_attribute_value("id");
morphdata.sources[id] = MorphControllerData::Source();
current_source = id;
COLLADA_PRINT("source data: " + id);
@@ -1245,13 +1245,13 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
} else if (section == "float_array" || section == "array") {
// create a new array and read it.
if (morphdata.sources.has(current_source)) {
- morphdata.sources[current_source].array = _read_float_array(parser);
+ morphdata.sources[current_source].array = _read_float_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(morphdata.sources[current_source].array.size()) + " values.");
}
} else if (section == "Name_array" || section == "IDREF_array") {
// create a new array and read it.
if (morphdata.sources.has(current_source)) {
- morphdata.sources[current_source].sarray = _read_string_array(parser);
+ morphdata.sources[current_source].sarray = _read_string_array(p_parser);
COLLADA_PRINT("section: " + current_source + " read " + itos(morphdata.sources[current_source].array.size()) + " values.");
}
} else if (section == "technique_common") {
@@ -1260,8 +1260,8 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
if (morphdata.sources.has(current_source)) {
int stride = 1;
- if (parser.has_attribute("stride")) {
- stride = parser.get_named_attribute_value("stride").to_int();
+ if (p_parser.has_attribute("stride")) {
+ stride = p_parser.get_named_attribute_value("stride").to_int();
}
morphdata.sources[current_source].stride = stride;
@@ -1269,22 +1269,22 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
}
} else if (section == "targets") {
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "input") {
- String semantic = parser.get_named_attribute_value("semantic");
- String source = _uri_to_id(parser.get_named_attribute_value("source"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "input") {
+ String semantic = p_parser.get_named_attribute_value("semantic");
+ String source = _uri_to_id(p_parser.get_named_attribute_value("source"));
morphdata.targets[semantic] = source;
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == section) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == section) {
break;
}
}
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "morph") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "morph") {
break;
}
}
@@ -1294,57 +1294,57 @@ void Collada::_parse_morph_controller(XMLParser &parser, String p_id) {
}
}
-void Collada::_parse_controller(XMLParser &parser) {
- String id = parser.get_named_attribute_value("id");
+void Collada::_parse_controller(XMLParser &p_parser) {
+ String id = p_parser.get_named_attribute_value("id");
- if (parser.is_empty()) {
+ if (p_parser.is_empty()) {
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "skin") {
- _parse_skin_controller(parser, id);
+ _parse_skin_controller(p_parser, id);
} else if (section == "morph") {
- _parse_morph_controller(parser, id);
+ _parse_morph_controller(p_parser, id);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "controller") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "controller") {
break;
}
}
}
-Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
- String type = parser.get_node_name();
+Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &p_parser) {
+ String type = p_parser.get_node_name();
NodeGeometry *geom = memnew(NodeGeometry);
geom->controller = type == "instance_controller";
- geom->source = _uri_to_id(parser.get_named_attribute_value_safe("url"));
+ geom->source = _uri_to_id(p_parser.get_named_attribute_value_safe("url"));
- if (parser.is_empty()) { //nothing else to parse...
+ if (p_parser.is_empty()) { //nothing else to parse...
return geom;
}
// try to find also many materials and skeletons!
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "instance_material") {
- String symbol = parser.get_named_attribute_value("symbol");
- String target = _uri_to_id(parser.get_named_attribute_value("target"));
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "instance_material") {
+ String symbol = p_parser.get_named_attribute_value("symbol");
+ String target = _uri_to_id(p_parser.get_named_attribute_value("target"));
NodeGeometry::Material mat;
mat.target = target;
geom->material_map[symbol] = mat;
COLLADA_PRINT("uses material: '" + target + "' on primitive'" + symbol + "'");
- } else if (parser.get_node_name() == "skeleton") {
- parser.read();
- String uri = _uri_to_id(parser.get_node_data());
+ } else if (p_parser.get_node_name() == "skeleton") {
+ p_parser.read();
+ String uri = _uri_to_id(p_parser.get_node_data());
if (!uri.is_empty()) {
geom->skeletons.push_back(uri);
}
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == type) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == type) {
break;
}
}
@@ -1368,20 +1368,20 @@ Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
return geom;
}
-Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &parser) {
+Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &p_parser) {
NodeCamera *cam = memnew(NodeCamera);
- cam->camera = _uri_to_id(parser.get_named_attribute_value_safe("url"));
+ cam->camera = _uri_to_id(p_parser.get_named_attribute_value_safe("url"));
if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness
cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5);
}
- if (parser.is_empty()) { //nothing else to parse...
+ if (p_parser.is_empty()) { //nothing else to parse...
return cam;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "instance_camera") {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "instance_camera") {
break;
}
}
@@ -1389,20 +1389,20 @@ Collada::Node *Collada::_parse_visual_instance_camera(XMLParser &parser) {
return cam;
}
-Collada::Node *Collada::_parse_visual_instance_light(XMLParser &parser) {
+Collada::Node *Collada::_parse_visual_instance_light(XMLParser &p_parser) {
NodeLight *cam = memnew(NodeLight);
- cam->light = _uri_to_id(parser.get_named_attribute_value_safe("url"));
+ cam->light = _uri_to_id(p_parser.get_named_attribute_value_safe("url"));
if (state.up_axis == Vector3::AXIS_Z) { //collada weirdness
cam->post_transform.basis.rotate(Vector3(1, 0, 0), -Math_PI * 0.5);
}
- if (parser.is_empty()) { //nothing else to parse...
+ if (p_parser.is_empty()) { //nothing else to parse...
return cam;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "instance_light") {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "instance_light") {
break;
}
}
@@ -1410,23 +1410,23 @@ Collada::Node *Collada::_parse_visual_instance_light(XMLParser &parser) {
return cam;
}
-Collada::Node *Collada::_parse_visual_node_instance_data(XMLParser &parser) {
- String instance_type = parser.get_node_name();
+Collada::Node *Collada::_parse_visual_node_instance_data(XMLParser &p_parser) {
+ String instance_type = p_parser.get_node_name();
if (instance_type == "instance_geometry" || instance_type == "instance_controller") {
- return _parse_visual_instance_geometry(parser);
+ return _parse_visual_instance_geometry(p_parser);
} else if (instance_type == "instance_camera") {
- return _parse_visual_instance_camera(parser);
+ return _parse_visual_instance_camera(p_parser);
} else if (instance_type == "instance_light") {
- return _parse_visual_instance_light(parser);
+ return _parse_visual_instance_light(p_parser);
}
- if (parser.is_empty()) { //nothing else to parse...
+ if (p_parser.is_empty()) { //nothing else to parse...
return nullptr;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == instance_type) {
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == instance_type) {
break;
}
}
@@ -1434,10 +1434,10 @@ Collada::Node *Collada::_parse_visual_node_instance_data(XMLParser &parser) {
return nullptr;
}
-Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
+Collada::Node *Collada::_parse_visual_scene_node(XMLParser &p_parser) {
String name;
- String id = parser.get_named_attribute_value_safe("id");
+ String id = p_parser.get_named_attribute_value_safe("id");
bool found_name = false;
@@ -1455,25 +1455,25 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
Node *node = nullptr;
- name = parser.has_attribute("name") ? parser.get_named_attribute_value_safe("name") : parser.get_named_attribute_value_safe("id");
+ name = p_parser.has_attribute("name") ? p_parser.get_named_attribute_value_safe("name") : p_parser.get_named_attribute_value_safe("id");
if (name.is_empty()) {
name = id;
} else {
found_name = true;
}
- if ((parser.has_attribute("type") && parser.get_named_attribute_value("type") == "JOINT") || state.idref_joints.has(name)) {
+ if ((p_parser.has_attribute("type") && p_parser.get_named_attribute_value("type") == "JOINT") || state.idref_joints.has(name)) {
// handle a bone
NodeJoint *joint = memnew(NodeJoint);
- if (parser.has_attribute("sid")) { //bones may not have sid
- joint->sid = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) { //bones may not have sid
+ joint->sid = p_parser.get_named_attribute_value("sid");
//state.bone_map[joint->sid]=joint;
} else if (state.idref_joints.has(name)) {
joint->sid = name; //kind of a cheat but..
- } else if (parser.has_attribute("name")) {
- joint->sid = parser.get_named_attribute_value_safe("name");
+ } else if (p_parser.has_attribute("name")) {
+ joint->sid = p_parser.get_named_attribute_value_safe("name");
}
if (!joint->sid.is_empty()) {
@@ -1483,42 +1483,42 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
node = joint;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "translate") {
Node::XForm xf;
- if (parser.has_attribute("sid")) {
- xf.id = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) {
+ xf.id = p_parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_TRANSLATE;
- Vector<float> xlt = _read_float_array(parser);
+ Vector<float> xlt = _read_float_array(p_parser);
xf.data = xlt;
xform_list.push_back(xf);
} else if (section == "rotate") {
Node::XForm xf;
- if (parser.has_attribute("sid")) {
- xf.id = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) {
+ xf.id = p_parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_ROTATE;
- Vector<float> rot = _read_float_array(parser);
+ Vector<float> rot = _read_float_array(p_parser);
xf.data = rot;
xform_list.push_back(xf);
} else if (section == "scale") {
Node::XForm xf;
- if (parser.has_attribute("sid")) {
- xf.id = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) {
+ xf.id = p_parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_SCALE;
- Vector<float> scale = _read_float_array(parser);
+ Vector<float> scale = _read_float_array(p_parser);
xf.data = scale;
@@ -1526,12 +1526,12 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "matrix") {
Node::XForm xf;
- if (parser.has_attribute("sid")) {
- xf.id = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) {
+ xf.id = p_parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_MATRIX;
- Vector<float> matrix = _read_float_array(parser);
+ Vector<float> matrix = _read_float_array(p_parser);
xf.data = matrix;
String mtx;
@@ -1543,25 +1543,25 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else if (section == "visibility") {
Node::XForm xf;
- if (parser.has_attribute("sid")) {
- xf.id = parser.get_named_attribute_value("sid");
+ if (p_parser.has_attribute("sid")) {
+ xf.id = p_parser.get_named_attribute_value("sid");
}
xf.op = Node::XForm::OP_VISIBILITY;
- Vector<float> visible = _read_float_array(parser);
+ Vector<float> visible = _read_float_array(p_parser);
xf.data = visible;
xform_list.push_back(xf);
} else if (section == "empty_draw_type") {
- empty_draw_type = _read_empty_draw_type(parser);
+ empty_draw_type = _read_empty_draw_type(p_parser);
} else if (section == "technique" || section == "extra") {
} else if (section != "node") {
//usually what defines the type of node
if (section.begins_with("instance_")) {
if (!node) {
- node = _parse_visual_node_instance_data(parser);
+ node = _parse_visual_node_instance_data(p_parser);
} else {
ERR_PRINT("Multiple instance_* not supported.");
@@ -1571,11 +1571,11 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
} else {
/* Found a child node!! what to do..*/
- Node *child = _parse_visual_scene_node(parser);
+ Node *child = _parse_visual_scene_node(p_parser);
children.push_back(child);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "node") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "node") {
break;
}
}
@@ -1608,29 +1608,29 @@ Collada::Node *Collada::_parse_visual_scene_node(XMLParser &parser) {
return node;
}
-void Collada::_parse_visual_scene(XMLParser &parser) {
- String id = parser.get_named_attribute_value("id");
+void Collada::_parse_visual_scene(XMLParser &p_parser) {
+ String id = p_parser.get_named_attribute_value("id");
- if (parser.is_empty()) {
+ if (p_parser.is_empty()) {
return;
}
state.visual_scene_map[id] = VisualScene();
VisualScene &vscene = state.visual_scene_map[id];
- if (parser.has_attribute("name")) {
- vscene.name = parser.get_named_attribute_value("name");
+ if (p_parser.has_attribute("name")) {
+ vscene.name = p_parser.get_named_attribute_value("name");
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String section = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String section = p_parser.get_node_name();
if (section == "node") {
- vscene.root_nodes.push_back(_parse_visual_scene_node(parser));
+ vscene.root_nodes.push_back(_parse_visual_scene_node(p_parser));
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "visual_scene") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "visual_scene") {
break;
}
}
@@ -1638,10 +1638,10 @@ void Collada::_parse_visual_scene(XMLParser &parser) {
COLLADA_PRINT("Scene ID:" + id);
}
-void Collada::_parse_animation(XMLParser &parser) {
+void Collada::_parse_animation(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_ANIMATION)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
@@ -1655,8 +1655,8 @@ void Collada::_parse_animation(XMLParser &parser) {
HashMap<String, Vector<String>> source_param_types;
String id = "";
- if (parser.has_attribute("id")) {
- id = parser.get_named_attribute_value("id");
+ if (p_parser.has_attribute("id")) {
+ id = p_parser.get_named_attribute_value("id");
}
String current_source;
@@ -1664,54 +1664,54 @@ void Collada::_parse_animation(XMLParser &parser) {
Vector<String> channel_sources;
Vector<String> channel_targets;
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "source") {
- current_source = parser.get_named_attribute_value("id");
+ current_source = p_parser.get_named_attribute_value("id");
source_param_names[current_source] = Vector<String>();
source_param_types[current_source] = Vector<String>();
} else if (name == "float_array") {
if (!current_source.is_empty()) {
- float_sources[current_source] = _read_float_array(parser);
+ float_sources[current_source] = _read_float_array(p_parser);
}
} else if (name == "Name_array") {
if (!current_source.is_empty()) {
- string_sources[current_source] = _read_string_array(parser);
+ string_sources[current_source] = _read_string_array(p_parser);
}
} else if (name == "accessor") {
- if (!current_source.is_empty() && parser.has_attribute("stride")) {
- source_strides[current_source] = parser.get_named_attribute_value("stride").to_int();
+ if (!current_source.is_empty() && p_parser.has_attribute("stride")) {
+ source_strides[current_source] = p_parser.get_named_attribute_value("stride").to_int();
}
} else if (name == "sampler") {
- current_sampler = parser.get_named_attribute_value("id");
+ current_sampler = p_parser.get_named_attribute_value("id");
samplers[current_sampler] = HashMap<String, String>();
} else if (name == "param") {
- if (parser.has_attribute("name")) {
- source_param_names[current_source].push_back(parser.get_named_attribute_value("name"));
+ if (p_parser.has_attribute("name")) {
+ source_param_names[current_source].push_back(p_parser.get_named_attribute_value("name"));
} else {
source_param_names[current_source].push_back("");
}
- if (parser.has_attribute("type")) {
- source_param_types[current_source].push_back(parser.get_named_attribute_value("type"));
+ if (p_parser.has_attribute("type")) {
+ source_param_types[current_source].push_back(p_parser.get_named_attribute_value("type"));
} else {
source_param_types[current_source].push_back("");
}
} else if (name == "input") {
if (!current_sampler.is_empty()) {
- samplers[current_sampler][parser.get_named_attribute_value("semantic")] = parser.get_named_attribute_value("source");
+ samplers[current_sampler][p_parser.get_named_attribute_value("semantic")] = p_parser.get_named_attribute_value("source");
}
} else if (name == "channel") {
- channel_sources.push_back(parser.get_named_attribute_value("source"));
- channel_targets.push_back(parser.get_named_attribute_value("target"));
+ channel_sources.push_back(p_parser.get_named_attribute_value("source"));
+ channel_targets.push_back(p_parser.get_named_attribute_value("target"));
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "animation") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "animation") {
break; //end of <asset>
}
}
@@ -1845,10 +1845,10 @@ void Collada::_parse_animation(XMLParser &parser) {
}
}
-void Collada::_parse_animation_clip(XMLParser &parser) {
+void Collada::_parse_animation_clip(XMLParser &p_parser) {
if (!(state.import_flags & IMPORT_FLAG_ANIMATION)) {
- if (!parser.is_empty()) {
- parser.skip_section();
+ if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
return;
@@ -1856,27 +1856,27 @@ void Collada::_parse_animation_clip(XMLParser &parser) {
AnimationClip clip;
- if (parser.has_attribute("name")) {
- clip.name = parser.get_named_attribute_value("name");
- } else if (parser.has_attribute("id")) {
- clip.name = parser.get_named_attribute_value("id");
+ if (p_parser.has_attribute("name")) {
+ clip.name = p_parser.get_named_attribute_value("name");
+ } else if (p_parser.has_attribute("id")) {
+ clip.name = p_parser.get_named_attribute_value("id");
}
- if (parser.has_attribute("start")) {
- clip.begin = parser.get_named_attribute_value("start").to_float();
+ if (p_parser.has_attribute("start")) {
+ clip.begin = p_parser.get_named_attribute_value("start").to_float();
}
- if (parser.has_attribute("end")) {
- clip.end = parser.get_named_attribute_value("end").to_float();
+ if (p_parser.has_attribute("end")) {
+ clip.end = p_parser.get_named_attribute_value("end").to_float();
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "instance_animation") {
- String url = _uri_to_id(parser.get_named_attribute_value("url"));
+ String url = _uri_to_id(p_parser.get_named_attribute_value("url"));
clip.tracks.push_back(url);
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "animation_clip") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "animation_clip") {
break; //end of <asset>
}
}
@@ -1884,79 +1884,79 @@ void Collada::_parse_animation_clip(XMLParser &parser) {
state.animation_clips.push_back(clip);
}
-void Collada::_parse_scene(XMLParser &parser) {
- if (parser.is_empty()) {
+void Collada::_parse_scene(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
if (name == "instance_visual_scene") {
- state.root_visual_scene = _uri_to_id(parser.get_named_attribute_value("url"));
+ state.root_visual_scene = _uri_to_id(p_parser.get_named_attribute_value("url"));
} else if (name == "instance_physics_scene") {
- state.root_physics_scene = _uri_to_id(parser.get_named_attribute_value("url"));
+ state.root_physics_scene = _uri_to_id(p_parser.get_named_attribute_value("url"));
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "scene") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "scene") {
break; //end of <asset>
}
}
}
-void Collada::_parse_library(XMLParser &parser) {
- if (parser.is_empty()) {
+void Collada::_parse_library(XMLParser &p_parser) {
+ if (p_parser.is_empty()) {
return;
}
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- String name = parser.get_node_name();
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ String name = p_parser.get_node_name();
COLLADA_PRINT("library name is: " + name);
if (name == "image") {
- _parse_image(parser);
+ _parse_image(p_parser);
} else if (name == "material") {
- _parse_material(parser);
+ _parse_material(p_parser);
} else if (name == "effect") {
- _parse_effect(parser);
+ _parse_effect(p_parser);
} else if (name == "camera") {
- _parse_camera(parser);
+ _parse_camera(p_parser);
} else if (name == "light") {
- _parse_light(parser);
+ _parse_light(p_parser);
} else if (name == "geometry") {
- String id = parser.get_named_attribute_value("id");
- String name2 = parser.get_named_attribute_value_safe("name");
- while (parser.read() == OK) {
- if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
- if (parser.get_node_name() == "mesh") {
+ String id = p_parser.get_named_attribute_value("id");
+ String name2 = p_parser.get_named_attribute_value_safe("name");
+ while (p_parser.read() == OK) {
+ if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT) {
+ if (p_parser.get_node_name() == "mesh") {
state.mesh_name_map[id] = (!name2.is_empty()) ? name2 : id;
- _parse_mesh_geometry(parser, id, name2);
- } else if (parser.get_node_name() == "spline") {
+ _parse_mesh_geometry(p_parser, id, name2);
+ } else if (p_parser.get_node_name() == "spline") {
state.mesh_name_map[id] = (!name2.is_empty()) ? name2 : id;
- _parse_curve_geometry(parser, id, name2);
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ _parse_curve_geometry(p_parser, id, name2);
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name() == "geometry") {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name() == "geometry") {
break;
}
}
} else if (name == "controller") {
- _parse_controller(parser);
+ _parse_controller(p_parser);
} else if (name == "animation") {
- _parse_animation(parser);
+ _parse_animation(p_parser);
} else if (name == "animation_clip") {
- _parse_animation_clip(parser);
+ _parse_animation_clip(p_parser);
} else if (name == "visual_scene") {
COLLADA_PRINT("visual scene");
- _parse_visual_scene(parser);
- } else if (!parser.is_empty()) {
- parser.skip_section();
+ _parse_visual_scene(p_parser);
+ } else if (!p_parser.is_empty()) {
+ p_parser.skip_section();
}
- } else if (parser.get_node_type() == XMLParser::NODE_ELEMENT_END && parser.get_node_name().begins_with("library_")) {
+ } else if (p_parser.get_node_type() == XMLParser::NODE_ELEMENT_END && p_parser.get_node_name().begins_with("library_")) {
break; //end of <asset>
}
}
diff --git a/editor/import/collada.h b/editor/import/collada.h
index 12b32f982d..7877b1e86d 100644
--- a/editor/import/collada.h
+++ b/editor/import/collada.h
@@ -69,7 +69,7 @@ public:
bool double_sided = true;
bool unshaded = false;
- String get_texture_path(const String &p_source, Collada &state) const;
+ String get_texture_path(const String &p_source, Collada &p_state) const;
Effect() {
diffuse.color = Color(1, 1, 1, 1);
@@ -266,7 +266,7 @@ public:
}
}
- void fix_unit_scale(const Collada &state);
+ void fix_unit_scale(const Collada &p_state);
bool operator<(const Vertex &p_vert) const {
if (uid == p_vert.uid) {
@@ -348,7 +348,7 @@ public:
Node *parent = nullptr;
- Transform3D compute_transform(const Collada &state) const;
+ Transform3D compute_transform(const Collada &p_state) const;
Transform3D get_global_transform() const;
Transform3D get_transform() const;
@@ -526,39 +526,39 @@ public:
private: // private stuff
HashMap<String, int> channel_map;
- void _parse_asset(XMLParser &parser);
- void _parse_image(XMLParser &parser);
- void _parse_material(XMLParser &parser);
- void _parse_effect_material(XMLParser &parser, Effect &effect, String &id);
- void _parse_effect(XMLParser &parser);
- void _parse_camera(XMLParser &parser);
- void _parse_light(XMLParser &parser);
- void _parse_animation_clip(XMLParser &parser);
-
- void _parse_mesh_geometry(XMLParser &parser, String p_id, String p_name);
- void _parse_curve_geometry(XMLParser &parser, String p_id, String p_name);
-
- void _parse_skin_controller(XMLParser &parser, String p_id);
- void _parse_morph_controller(XMLParser &parser, String p_id);
- void _parse_controller(XMLParser &parser);
-
- Node *_parse_visual_instance_geometry(XMLParser &parser);
- Node *_parse_visual_instance_camera(XMLParser &parser);
- Node *_parse_visual_instance_light(XMLParser &parser);
-
- Node *_parse_visual_node_instance_data(XMLParser &parser);
- Node *_parse_visual_scene_node(XMLParser &parser);
- void _parse_visual_scene(XMLParser &parser);
-
- void _parse_animation(XMLParser &parser);
- void _parse_scene(XMLParser &parser);
- void _parse_library(XMLParser &parser);
-
- Variant _parse_param(XMLParser &parser);
- Vector<float> _read_float_array(XMLParser &parser);
- Vector<String> _read_string_array(XMLParser &parser);
- Transform3D _read_transform(XMLParser &parser);
- String _read_empty_draw_type(XMLParser &parser);
+ void _parse_asset(XMLParser &p_parser);
+ void _parse_image(XMLParser &p_parser);
+ void _parse_material(XMLParser &p_parser);
+ void _parse_effect_material(XMLParser &p_parser, Effect &p_effect, String &p_id);
+ void _parse_effect(XMLParser &p_parser);
+ void _parse_camera(XMLParser &p_parser);
+ void _parse_light(XMLParser &p_parser);
+ void _parse_animation_clip(XMLParser &p_parser);
+
+ void _parse_mesh_geometry(XMLParser &p_parser, String p_id, String p_name);
+ void _parse_curve_geometry(XMLParser &p_parser, String p_id, String p_name);
+
+ void _parse_skin_controller(XMLParser &p_parser, String p_id);
+ void _parse_morph_controller(XMLParser &p_parser, String p_id);
+ void _parse_controller(XMLParser &p_parser);
+
+ Node *_parse_visual_instance_geometry(XMLParser &p_parser);
+ Node *_parse_visual_instance_camera(XMLParser &p_parser);
+ Node *_parse_visual_instance_light(XMLParser &p_parser);
+
+ Node *_parse_visual_node_instance_data(XMLParser &p_parser);
+ Node *_parse_visual_scene_node(XMLParser &p_parser);
+ void _parse_visual_scene(XMLParser &p_parser);
+
+ void _parse_animation(XMLParser &p_parser);
+ void _parse_scene(XMLParser &p_parser);
+ void _parse_library(XMLParser &p_parser);
+
+ Variant _parse_param(XMLParser &p_parser);
+ Vector<float> _read_float_array(XMLParser &p_parser);
+ Vector<String> _read_string_array(XMLParser &p_parser);
+ Transform3D _read_transform(XMLParser &p_parser);
+ String _read_empty_draw_type(XMLParser &p_parser);
void _joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner);
void _create_skeletons(Collada::Node **p_node, NodeSkeleton *p_skeleton = nullptr);
diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp
index 65fc79a7a5..39e4ffbc16 100644
--- a/editor/import/dynamic_font_import_settings.cpp
+++ b/editor/import/dynamic_font_import_settings.cpp
@@ -928,15 +928,6 @@ void DynamicFontImportSettings::_notification(int p_what) {
add_var->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
label_warn->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
} break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
- EditorPropertyNameProcessor::Style style = EditorPropertyNameProcessor::get_singleton()->get_settings_style();
- inspector_general->set_property_name_style(style);
- inspector_vars->set_property_name_style(style);
- inspector_text->set_property_name_style(style);
- }
- } break;
}
}
@@ -1342,7 +1333,6 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
inspector_general->set_v_size_flags(Control::SIZE_EXPAND_FILL);
inspector_general->set_custom_minimum_size(Size2(300 * EDSCALE, 250 * EDSCALE));
inspector_general->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_main_prop_changed));
- inspector_general->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
page1_hb->add_child(inspector_general);
// Page 2 layout: Configurations
@@ -1394,7 +1384,6 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
inspector_vars = memnew(EditorInspector);
inspector_vars->set_v_size_flags(Control::SIZE_EXPAND_FILL);
inspector_vars->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_variation_changed));
- inspector_vars->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
page2_side_vb->add_child(inspector_vars);
VBoxContainer *preload_pages_vb = memnew(VBoxContainer);
@@ -1470,7 +1459,6 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
inspector_text->set_v_size_flags(Control::SIZE_EXPAND_FILL);
inspector_text->set_custom_minimum_size(Size2(300 * EDSCALE, 250 * EDSCALE));
inspector_text->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_change_text_opts));
- inspector_text->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
page2_1_hb->add_child(inspector_text);
text_edit = memnew(TextEdit);
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 1ffede6502..5f714e4488 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -1782,15 +1782,8 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3
ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'.");
if (state.missing_textures.size()) {
- /*
- for(int i=0;i<state.missing_textures.size();i++) {
- EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]);
- }
- */
-
if (r_missing_deps) {
for (int i = 0; i < state.missing_textures.size(); i++) {
- //EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]);
r_missing_deps->push_back(state.missing_textures[i]);
}
}
diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp
index e105135c11..6214a2b70d 100644
--- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp
+++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp
@@ -669,7 +669,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) {
- Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx);
+ Transform3D new_rest = silhouette_diff[bone_idx] * src_skeleton->get_bone_global_rest(bone_idx);
skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[bone_idx]);
}
}
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index 89e2e36b77..adc21aaa7b 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -267,7 +267,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
vtx.z = v[3].to_float() * scale_mesh.z + offset_mesh.z;
vertices.push_back(vtx);
//vertex color
- if (v.size() == 7) {
+ if (v.size() >= 7) {
while (colors.size() < vertices.size() - 1) {
colors.push_back(Color(1.0, 1.0, 1.0));
}
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index ba48fc9029..1275e5b85a 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -106,7 +106,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri
if (err != OK) {
if (!ShaderFileEditor::singleton->is_visible_in_tree()) {
- EditorNode::get_singleton()->add_io_error(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file));
+ callable_mp_static(&EditorNode::add_io_error).bind(vformat(TTR("Error importing GLSL shader file: '%s'. Open the file in the filesystem dock in order to see the reason."), p_source_file)).call_deferred();
}
}
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index e7ba47e2a1..92d287c54f 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -1006,10 +1006,6 @@ void SceneImportSettings::_notification(int p_what) {
action_menu->add_theme_style_override("hover", get_theme_stylebox("hover", "Button"));
action_menu->add_theme_style_override("pressed", get_theme_stylebox("pressed", "Button"));
} break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
- } break;
}
}
@@ -1410,7 +1406,6 @@ SceneImportSettings::SceneImportSettings() {
inspector = memnew(EditorInspector);
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
property_split->add_child(inspector);
diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp
index 0e25468e1a..ebbea827c0 100644
--- a/editor/import_defaults_editor.cpp
+++ b/editor/import_defaults_editor.cpp
@@ -36,7 +36,6 @@
#include "editor/editor_autoload_settings.h"
#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
-#include "editor/editor_settings.h"
#include "editor/localization_editor.h"
#include "editor/shader_globals_editor.h"
#include "scene/gui/center_container.h"
@@ -82,11 +81,6 @@ protected:
void ImportDefaultsEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
- } break;
-
case NOTIFICATION_PREDELETE: {
inspector->edit(nullptr);
} break;
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index b5680f2f2c..7b8b9cd7a4 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -565,7 +565,6 @@ void ImportDock::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
imported->add_theme_style_override("normal", get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")));
- import_opts->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -643,7 +642,6 @@ ImportDock::ImportDock() {
import_opts = memnew(EditorInspector);
content->add_child(import_opts);
import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
- import_opts->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
import_opts->connect("property_edited", callable_mp(this, &ImportDock::_property_edited));
import_opts->connect("property_toggled", callable_mp(this, &ImportDock::_property_toggled));
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 2acc83cead..39ed7d869b 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -767,7 +767,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
inspector->set_use_doc_hints(true);
inspector->set_hide_script(false);
inspector->set_hide_metadata(false);
- inspector->set_property_name_style(EditorPropertyNameProcessor::get_default_inspector_style());
+ inspector->set_use_settings_name_style(false);
+ inspector->set_property_name_style(property_name_style);
inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
inspector->register_text_enter(search);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index eef1fea11b..05a024f913 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/gui/editor_toaster.h"
#include "editor/gui/editor_zoom_widget.h"
#include "editor/plugins/animation_player_editor_plugin.h"
@@ -3980,10 +3981,6 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
Array selection = editor_selection->get_selected_nodes();
if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
_reset_drag();
-
- // Clear the selection
- editor_selection->clear(); //_clear_canvas_items();
- editor_selection->add_node(p_canvas_item);
}
}
@@ -4975,8 +4972,8 @@ CanvasItemEditor::CanvasItemEditor() {
SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
- EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
- EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
+ EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
+ EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
// A fluid container for all toolbars.
HFlowContainer *main_flow = memnew(HFlowContainer);
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index 4afbb87197..22ce3e2d6e 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "canvas_item_editor_plugin.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "scene/resources/capsule_shape_2d.h"
#include "scene/resources/circle_shape_2d.h"
@@ -42,6 +43,11 @@
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/separation_ray_shape_2d.h"
#include "scene/resources/world_boundary_shape_2d.h"
+#include "scene/scene_string_names.h"
+
+CollisionShape2DEditor::CollisionShape2DEditor() {
+ grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
+}
void CollisionShape2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -129,8 +135,6 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
} else if (idx == 1) {
capsule->set_height(parameter * 2);
}
-
- canvas_item_editor->update_viewport();
}
} break;
@@ -138,9 +142,6 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
case CIRCLE_SHAPE: {
Ref<CircleShape2D> circle = node->get_shape();
circle->set_radius(p_point.length());
-
- canvas_item_editor->update_viewport();
-
} break;
case CONCAVE_POLYGON_SHAPE: {
@@ -158,19 +159,13 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
} else {
world_boundary->set_normal(p_point.normalized());
}
-
- canvas_item_editor->update_viewport();
}
-
} break;
case SEPARATION_RAY_SHAPE: {
Ref<SeparationRayShape2D> ray = node->get_shape();
ray->set_length(Math::abs(p_point.y));
-
- canvas_item_editor->update_viewport();
-
} break;
case RECTANGLE_SHAPE: {
@@ -194,8 +189,6 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
pos += (size - (Point2)original) * 0.5 * RECT_HANDLES[idx] * 0.5;
node->set_global_position(original_transform.xform(pos));
}
-
- canvas_item_editor->update_viewport();
}
} break;
@@ -209,13 +202,9 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
} else if (idx == 1) {
seg->set_b(p_point);
}
-
- canvas_item_editor->update_viewport();
}
-
} break;
}
- node->get_shape()->notify_property_list_changed();
}
void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
@@ -233,10 +222,8 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
} else if (idx == 1) {
undo_redo->add_do_method(capsule.ptr(), "set_height", capsule->get_height());
}
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(capsule.ptr(), "set_radius", values[0]);
undo_redo->add_undo_method(capsule.ptr(), "set_height", values[1]);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -244,9 +231,7 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<CircleShape2D> circle = node->get_shape();
undo_redo->add_do_method(circle.ptr(), "set_radius", circle->get_radius());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(circle.ptr(), "set_radius", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -263,14 +248,10 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
if (idx == 0) {
undo_redo->add_do_method(world_boundary.ptr(), "set_distance", world_boundary->get_distance());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(world_boundary.ptr(), "set_distance", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else {
undo_redo->add_do_method(world_boundary.ptr(), "set_normal", world_boundary->get_normal());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(world_boundary.ptr(), "set_normal", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
@@ -279,9 +260,7 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<SeparationRayShape2D> ray = node->get_shape();
undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -290,10 +269,8 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
undo_redo->add_do_method(rect.ptr(), "set_size", rect->get_size());
undo_redo->add_do_method(node, "set_global_transform", node->get_global_transform());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(rect.ptr(), "set_size", p_org);
undo_redo->add_undo_method(node, "set_global_transform", original_transform);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -301,14 +278,10 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<SegmentShape2D> seg = node->get_shape();
if (idx == 0) {
undo_redo->add_do_method(seg.ptr(), "set_a", seg->get_a());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(seg.ptr(), "set_a", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else if (idx == 1) {
undo_redo->add_do_method(seg.ptr(), "set_b", seg->get_b());
- undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(seg.ptr(), "set_b", p_org);
- undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
@@ -322,10 +295,6 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
return false;
}
- if (!node->get_shape().is_valid()) {
- return false;
- }
-
if (!node->is_visible_in_tree()) {
return false;
}
@@ -343,7 +312,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
for (int i = 0; i < handles.size(); i++) {
- if (xform.xform(handles[i]).distance_to(gpoint) < 8) {
+ if (xform.xform(handles[i]).distance_to(gpoint) < grab_threshold) {
edit_handle = i;
break;
@@ -410,38 +379,44 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
return false;
}
-void CollisionShape2DEditor::_get_current_shape_type() {
+void CollisionShape2DEditor::_shape_changed() {
+ canvas_item_editor->update_viewport();
+
+ if (current_shape.is_valid()) {
+ current_shape->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
+ current_shape = Ref<Shape2D>();
+ shape_type = -1;
+ }
+
if (!node) {
return;
}
- Ref<Shape2D> s = node->get_shape();
+ current_shape = node->get_shape();
- if (!s.is_valid()) {
+ if (current_shape.is_valid()) {
+ current_shape->connect(SceneStringNames::get_singleton()->changed, callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
+ } else {
return;
}
- if (Object::cast_to<CapsuleShape2D>(*s)) {
+ if (Object::cast_to<CapsuleShape2D>(*current_shape)) {
shape_type = CAPSULE_SHAPE;
- } else if (Object::cast_to<CircleShape2D>(*s)) {
+ } else if (Object::cast_to<CircleShape2D>(*current_shape)) {
shape_type = CIRCLE_SHAPE;
- } else if (Object::cast_to<ConcavePolygonShape2D>(*s)) {
+ } else if (Object::cast_to<ConcavePolygonShape2D>(*current_shape)) {
shape_type = CONCAVE_POLYGON_SHAPE;
- } else if (Object::cast_to<ConvexPolygonShape2D>(*s)) {
+ } else if (Object::cast_to<ConvexPolygonShape2D>(*current_shape)) {
shape_type = CONVEX_POLYGON_SHAPE;
- } else if (Object::cast_to<WorldBoundaryShape2D>(*s)) {
+ } else if (Object::cast_to<WorldBoundaryShape2D>(*current_shape)) {
shape_type = WORLD_BOUNDARY_SHAPE;
- } else if (Object::cast_to<SeparationRayShape2D>(*s)) {
+ } else if (Object::cast_to<SeparationRayShape2D>(*current_shape)) {
shape_type = SEPARATION_RAY_SHAPE;
- } else if (Object::cast_to<RectangleShape2D>(*s)) {
+ } else if (Object::cast_to<RectangleShape2D>(*current_shape)) {
shape_type = RECTANGLE_SHAPE;
- } else if (Object::cast_to<SegmentShape2D>(*s)) {
+ } else if (Object::cast_to<SegmentShape2D>(*current_shape)) {
shape_type = SEGMENT_SHAPE;
- } else {
- shape_type = -1;
}
-
- canvas_item_editor->update_viewport();
}
void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
@@ -449,16 +424,10 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
return;
}
- if (!node->get_shape().is_valid()) {
- return;
- }
-
if (!node->is_visible_in_tree()) {
return;
}
- _get_current_shape_type();
-
if (shape_type == -1) {
return;
}
@@ -472,7 +441,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
switch (shape_type) {
case CAPSULE_SHAPE: {
- Ref<CapsuleShape2D> shape = node->get_shape();
+ Ref<CapsuleShape2D> shape = current_shape;
handles.resize(2);
float radius = shape->get_radius();
@@ -487,7 +456,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
case CIRCLE_SHAPE: {
- Ref<CircleShape2D> shape = node->get_shape();
+ Ref<CircleShape2D> shape = current_shape;
handles.resize(1);
handles.write[0] = Point2(shape->get_radius(), 0);
@@ -503,7 +472,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
case WORLD_BOUNDARY_SHAPE: {
- Ref<WorldBoundaryShape2D> shape = node->get_shape();
+ Ref<WorldBoundaryShape2D> shape = current_shape;
handles.resize(2);
handles.write[0] = shape->get_normal() * shape->get_distance();
@@ -515,7 +484,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
case SEPARATION_RAY_SHAPE: {
- Ref<SeparationRayShape2D> shape = node->get_shape();
+ Ref<SeparationRayShape2D> shape = current_shape;
handles.resize(1);
handles.write[0] = Point2(0, shape->get_length());
@@ -525,7 +494,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
case RECTANGLE_SHAPE: {
- Ref<RectangleShape2D> shape = node->get_shape();
+ Ref<RectangleShape2D> shape = current_shape;
handles.resize(8);
Vector2 ext = shape->get_size() / 2;
@@ -537,7 +506,7 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
} break;
case SEGMENT_SHAPE: {
- Ref<SegmentShape2D> shape = node->get_shape();
+ Ref<SegmentShape2D> shape = current_shape;
handles.resize(2);
handles.write[0] = shape->get_a();
@@ -559,6 +528,18 @@ void CollisionShape2DEditor::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
get_tree()->disconnect("node_removed", callable_mp(this, &CollisionShape2DEditor::_node_removed));
} break;
+
+ case NOTIFICATION_PROCESS: {
+ if (node && node->get_shape() != current_shape) {
+ _shape_changed();
+ }
+ } break;
+
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor/point_grab_radius")) {
+ grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
+ }
+ } break;
}
}
@@ -569,36 +550,17 @@ void CollisionShape2DEditor::edit(Node *p_node) {
if (p_node) {
node = Object::cast_to<CollisionShape2D>(p_node);
-
- _get_current_shape_type();
-
+ set_process(true);
} else {
if (pressed) {
set_handle(edit_handle, original_point);
pressed = false;
}
-
edit_handle = -1;
- shape_type = -1;
-
node = nullptr;
+ set_process(false);
}
-
- canvas_item_editor->update_viewport();
-}
-
-void CollisionShape2DEditor::_bind_methods() {
- ClassDB::bind_method("_get_current_shape_type", &CollisionShape2DEditor::_get_current_shape_type);
-}
-
-CollisionShape2DEditor::CollisionShape2DEditor() {
- node = nullptr;
- canvas_item_editor = nullptr;
-
- edit_handle = -1;
- pressed = false;
-
- shape_type = 0;
+ _shape_changed();
}
void CollisionShape2DEditorPlugin::edit(Object *p_obj) {
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 9c37b6cf9d..d58f5d511f 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -66,24 +66,26 @@ class CollisionShape2DEditor : public Control {
Vector<Point2> handles;
- int shape_type;
- int edit_handle;
- bool pressed;
+ int shape_type = -1;
+ int edit_handle = -1;
+ bool pressed = false;
+ real_t grab_threshold = 8;
Variant original;
Transform2D original_transform;
Vector2 original_point;
Point2 last_point;
+ Ref<Shape2D> current_shape;
+
Variant get_handle_value(int idx) const;
void set_handle(int idx, Point2 &p_point);
void commit_handle(int idx, Variant &p_org);
- void _get_current_shape_type();
+ void _shape_changed();
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index a7ec4bfe33..7c87f009a6 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "scene/gui/popup_menu.h"
CurveEditor::CurveEditor() {
_selected_point = -1;
@@ -48,6 +49,7 @@ CurveEditor::CurveEditor() {
_tangents_length = 40;
_dragging = false;
_has_undo_data = false;
+ _gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
set_focus_mode(FOCUS_ALL);
set_clip_contents(true);
@@ -104,6 +106,11 @@ void CurveEditor::_notification(int p_what) {
case NOTIFICATION_DRAW: {
_draw();
} break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles")) {
+ _gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
+ }
+ } break;
}
}
@@ -396,7 +403,7 @@ int CurveEditor::get_point_at(Vector2 pos) const {
}
const Curve &curve = **_curve_ref;
- const float true_hover_radius = Math::round(_hover_radius * EDSCALE);
+ const float true_hover_radius = Math::round(_hover_radius * _gizmo_handle_scale * EDSCALE);
const float r = true_hover_radius * true_hover_radius;
for (int i = 0; i < curve.get_point_count(); ++i) {
@@ -416,14 +423,14 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const {
if (_selected_point != 0) {
Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT);
- if (control_pos.distance_to(pos) < _hover_radius) {
+ if (control_pos.distance_to(pos) < _hover_radius * _gizmo_handle_scale) {
return TANGENT_LEFT;
}
}
if (_selected_point != _curve_ref->get_point_count() - 1) {
Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT);
- if (control_pos.distance_to(pos) < _hover_radius) {
+ if (control_pos.distance_to(pos) < _hover_radius * _gizmo_handle_scale) {
return TANGENT_RIGHT;
}
}
@@ -561,7 +568,7 @@ Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const {
Vector2 point_pos = get_view_pos(_curve_ref->get_point_position(i));
Vector2 control_pos = get_view_pos(_curve_ref->get_point_position(i) + dir);
- return point_pos + Math::round(_tangents_length * EDSCALE) * (control_pos - point_pos).normalized();
+ return point_pos + Math::round(_tangents_length * _gizmo_handle_scale * EDSCALE) * (control_pos - point_pos).normalized();
}
Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const {
@@ -706,13 +713,13 @@ void CurveEditor::_draw() {
if (i != 0) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE));
- draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * EDSCALE)), tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * _gizmo_handle_scale * EDSCALE)), tangent_color);
}
if (i != curve.get_point_count() - 1) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE));
- draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * EDSCALE)), tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * _gizmo_handle_scale * EDSCALE)), tangent_color);
}
}
@@ -735,7 +742,7 @@ void CurveEditor::_draw() {
for (int i = 0; i < curve.get_point_count(); ++i) {
Vector2 pos = curve.get_point_position(i);
- draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(3 * EDSCALE)), i == _selected_point ? selected_point_color : point_color);
+ draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(3 * _gizmo_handle_scale * EDSCALE)), i == _selected_point ? selected_point_color : point_color);
// TODO Circles are prettier. Needs a fix! Or a texture
//draw_circle(pos, 2, point_color);
}
@@ -745,7 +752,7 @@ void CurveEditor::_draw() {
if (_hover_point != -1) {
const Color hover_color = line_color;
Vector2 pos = curve.get_point_position(_hover_point);
- draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(_hover_radius * EDSCALE)), hover_color, false, Math::round(EDSCALE));
+ draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(_hover_radius * _gizmo_handle_scale * EDSCALE)), hover_color, false, Math::round(EDSCALE));
}
// Help text
@@ -791,7 +798,7 @@ bool CurvePreviewGenerator::handles(const String &p_type) const {
return p_type == "Curve";
}
-Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Curve> curve_ref = p_from;
ERR_FAIL_COND_V_MSG(curve_ref.is_null(), Ref<Texture2D>(), "It's not a reference to a valid Resource object.");
Curve &curve = **curve_ref;
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index d5cc0cb66a..903f8d593e 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -36,6 +36,8 @@
#include "editor/editor_resource_preview.h"
#include "scene/resources/curve.h"
+class PopupMenu;
+
// Edits a y(x) curve
class CurveEditor : public Control {
GDCLASS(CurveEditor, Control);
@@ -115,6 +117,7 @@ private:
// Constant
float _hover_radius;
float _tangents_length;
+ float _gizmo_handle_scale = 1.0;
};
class EditorInspectorPluginCurve : public EditorInspectorPlugin {
@@ -139,7 +142,7 @@ class CurvePreviewGenerator : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
};
#endif // CURVE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
index 7863e6d19e..3068ad3f93 100644
--- a/editor/plugins/debugger_editor_plugin.cpp
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -77,6 +77,9 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
debug_menu->set_item_tooltip(-1,
TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project."));
+ debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_avoidance", TTR("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
+ debug_menu->set_item_tooltip(-1,
+ TTR("When this option is enabled, avoidance objects shapes, radius and velocities will be visible in the running project."));
debug_menu->add_separator();
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG);
debug_menu->set_item_tooltip(-1,
@@ -126,8 +129,10 @@ void DebuggerEditorPlugin::_menu_option(int p_option) {
if (ischecked) {
file_server->stop();
+ set_process(false);
} else {
file_server->start();
+ set_process(true);
}
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER), !ischecked);
@@ -166,6 +171,12 @@ void DebuggerEditorPlugin::_menu_option(int p_option) {
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
} break;
+ case RUN_DEBUG_AVOIDANCE: {
+ bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE));
+ debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked);
+ EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked);
+
+ } break;
case RUN_RELOAD_SCRIPTS: {
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS));
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked);
@@ -190,6 +201,10 @@ void DebuggerEditorPlugin::_notification(int p_what) {
case NOTIFICATION_READY: {
_update_debug_options();
} break;
+
+ case NOTIFICATION_PROCESS: {
+ file_server->poll();
+ } break;
}
}
@@ -199,6 +214,7 @@ void DebuggerEditorPlugin::_update_debug_options() {
bool check_debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
+ bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
bool check_server_keep_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "server_keep_open", false);
@@ -219,6 +235,9 @@ void DebuggerEditorPlugin::_update_debug_options() {
if (check_debug_navigation) {
_menu_option(RUN_DEBUG_NAVIGATION);
}
+ if (check_debug_avoidance) {
+ _menu_option(RUN_DEBUG_AVOIDANCE);
+ }
if (check_live_debug) {
_menu_option(RUN_LIVE_DEBUG);
}
diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h
index 6e2d2c02ee..eb8da7ca8e 100644
--- a/editor/plugins/debugger_editor_plugin.h
+++ b/editor/plugins/debugger_editor_plugin.h
@@ -51,6 +51,7 @@ private:
RUN_DEBUG_COLLISIONS,
RUN_DEBUG_PATHS,
RUN_DEBUG_NAVIGATION,
+ RUN_DEBUG_AVOIDANCE,
RUN_DEPLOY_REMOTE_DEBUG,
RUN_RELOAD_SCRIPTS,
SERVER_KEEP_OPEN,
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 523e7703c4..2b0691b36f 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -79,7 +79,7 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const {
return true;
}
-Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Image> img;
Ref<AtlasTexture> atex = p_from;
if (atex.is_valid()) {
@@ -107,6 +107,7 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from,
if (img.is_null() || img->is_empty()) {
return Ref<Texture2D>();
}
+ p_metadata["dimensions"] = img->get_size();
img->clear_mipmaps();
@@ -141,7 +142,7 @@ bool EditorImagePreviewPlugin::handles(const String &p_type) const {
return p_type == "Image";
}
-Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Image> img = p_from;
if (img.is_null() || img->is_empty()) {
@@ -185,7 +186,7 @@ bool EditorBitmapPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "BitMap");
}
-Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<BitMap> bm = p_from;
if (bm->get_size() == Size2()) {
@@ -246,11 +247,11 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "PackedScene");
}
-Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
- return generate_from_path(p_from->get_path(), p_size);
+Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
+ return generate_from_path(p_from->get_path(), p_size, p_metadata);
}
-Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
cache_base = temp_path.path_join("resthumb-" + cache_base);
@@ -298,7 +299,7 @@ bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
return true;
}
-Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Material> material = p_from;
ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
@@ -455,7 +456,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Script");
}
-Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Script> scr = p_from;
if (scr.is_null()) {
return Ref<Texture2D>();
@@ -590,7 +591,7 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "AudioStream");
}
-Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<AudioStream> stream = p_from;
ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
@@ -680,7 +681,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
}
-Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Mesh> mesh = p_from;
ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
@@ -797,7 +798,7 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Font");
}
-Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
+Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Font> sampled_font = ResourceLoader::load(p_path);
ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
@@ -846,12 +847,12 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
return ImageTexture::create_from_image(img);
}
-Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
String path = p_from->get_path();
if (!FileAccess::exists(path)) {
return Ref<Texture2D>();
}
- return generate_from_path(path, p_size);
+ return generate_from_path(path, p_size, p_metadata);
}
EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
@@ -887,7 +888,7 @@ bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const {
return true;
}
-Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size) const {
+Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
Ref<Gradient> gradient = p_from;
if (gradient.is_valid()) {
Ref<GradientTexture1D> ptex;
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index 6e4b73481c..6534f31ad8 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -42,7 +42,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
virtual bool generate_small_preview_automatically() const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorTexturePreviewPlugin();
};
@@ -53,7 +53,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
virtual bool generate_small_preview_automatically() const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorImagePreviewPlugin();
};
@@ -64,7 +64,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
virtual bool generate_small_preview_automatically() const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorBitmapPreviewPlugin();
};
@@ -74,8 +74,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
- virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
+ virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorPackedScenePreviewPlugin();
};
@@ -102,7 +102,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
virtual bool generate_small_preview_automatically() const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorMaterialPreviewPlugin();
~EditorMaterialPreviewPlugin();
@@ -113,7 +113,7 @@ class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorScriptPreviewPlugin();
};
@@ -123,7 +123,7 @@ class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorAudioStreamPreviewPlugin();
};
@@ -148,7 +148,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorMeshPreviewPlugin();
~EditorMeshPreviewPlugin();
@@ -168,8 +168,8 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
- virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
+ virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorFontPreviewPlugin();
~EditorFontPreviewPlugin();
@@ -185,7 +185,7 @@ class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator
public:
virtual bool handles(const String &p_type) const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorTileMapPatternPreviewPlugin();
~EditorTileMapPatternPreviewPlugin();
@@ -197,7 +197,7 @@ class EditorGradientPreviewPlugin : public EditorResourcePreviewGenerator {
public:
virtual bool handles(const String &p_type) const override;
virtual bool generate_small_preview_automatically() const override;
- virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size) const override;
+ virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
EditorGradientPreviewPlugin();
};
diff --git a/editor/plugins/editor_resource_tooltip_plugins.cpp b/editor/plugins/editor_resource_tooltip_plugins.cpp
new file mode 100644
index 0000000000..26371360e9
--- /dev/null
+++ b/editor/plugins/editor_resource_tooltip_plugins.cpp
@@ -0,0 +1,122 @@
+/**************************************************************************/
+/* editor_resource_tooltip_plugins.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 "editor_resource_tooltip_plugins.h"
+
+#include "editor/editor_resource_preview.h"
+#include "editor/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/control.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
+
+void EditorResourceTooltipPlugin::_thumbnail_ready(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) {
+ ObjectID trid = p_udata;
+ TextureRect *tr = Object::cast_to<TextureRect>(ObjectDB::get_instance(trid));
+
+ if (!tr) {
+ return;
+ }
+
+ tr->set_texture(p_preview);
+}
+
+void EditorResourceTooltipPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_thumbnail_ready"), &EditorResourceTooltipPlugin::_thumbnail_ready);
+
+ ClassDB::bind_static_method("EditorResourceTooltipPlugin", D_METHOD("make_default_tooltip", "path"), &EditorResourceTooltipPlugin::make_default_tooltip);
+ ClassDB::bind_method(D_METHOD("request_thumbnail", "path", "control"), &EditorResourceTooltipPlugin::request_thumbnail);
+
+ GDVIRTUAL_BIND(_handles, "type");
+ GDVIRTUAL_BIND(_make_tooltip_for_path, "path", "metadata");
+}
+
+VBoxContainer *EditorResourceTooltipPlugin::make_default_tooltip(const String &p_resource_path) {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->add_theme_constant_override("separation", -4 * EDSCALE);
+ {
+ Label *label = memnew(Label(p_resource_path.get_file()));
+ vb->add_child(label);
+ }
+
+ {
+ Ref<FileAccess> f = FileAccess::open(p_resource_path, FileAccess::READ);
+ Label *label = memnew(Label(vformat(TTR("Size: %s"), String::humanize_size(f->get_length()))));
+ vb->add_child(label);
+ }
+
+ if (ResourceLoader::exists(p_resource_path)) {
+ String type = ResourceLoader::get_resource_type(p_resource_path);
+ Label *label = memnew(Label(vformat(TTR("Type: %s"), type)));
+ vb->add_child(label);
+ }
+ return vb;
+}
+
+void EditorResourceTooltipPlugin::request_thumbnail(const String &p_path, TextureRect *p_for_control) const {
+ ERR_FAIL_NULL(p_for_control);
+ EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, const_cast<EditorResourceTooltipPlugin *>(this), "_thumbnail_ready", p_for_control->get_instance_id());
+}
+
+bool EditorResourceTooltipPlugin::handles(const String &p_resource_type) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_handles, p_resource_type, ret);
+ return ret;
+}
+
+Control *EditorResourceTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata) const {
+ Object *ret = nullptr;
+ GDVIRTUAL_CALL(_make_tooltip_for_path, p_resource_path, p_metadata, ret);
+ return Object::cast_to<Control>(ret);
+}
+
+// EditorTextureTooltipPlugin
+
+bool EditorTextureTooltipPlugin::handles(const String &p_resource_type) const {
+ return ClassDB::is_parent_class(p_resource_type, "Texture2D") || ClassDB::is_parent_class(p_resource_type, "Image");
+}
+
+Control *EditorTextureTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata) const {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ VBoxContainer *vb = EditorResourceTooltipPlugin::make_default_tooltip(p_resource_path);
+ vb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+
+ Vector2 dimensions = p_metadata.get("dimensions", Vector2());
+ Label *label = memnew(Label(vformat(TTR(U"Dimensions: %d × %d"), dimensions.x, dimensions.y)));
+ vb->add_child(label);
+
+ TextureRect *tr = memnew(TextureRect);
+ tr->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ hb->add_child(tr);
+ request_thumbnail(p_resource_path, tr);
+
+ hb->add_child(vb);
+ return hb;
+}
diff --git a/editor/plugins/editor_resource_tooltip_plugins.h b/editor/plugins/editor_resource_tooltip_plugins.h
new file mode 100644
index 0000000000..dfccbb80ed
--- /dev/null
+++ b/editor/plugins/editor_resource_tooltip_plugins.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* editor_resource_tooltip_plugins.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 EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
+#define EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
+
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
+
+class Control;
+class Texture2D;
+class TextureRect;
+class VBoxContainer;
+
+class EditorResourceTooltipPlugin : public RefCounted {
+ GDCLASS(EditorResourceTooltipPlugin, RefCounted);
+
+ void _thumbnail_ready(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata);
+
+protected:
+ static void _bind_methods();
+
+ GDVIRTUAL1RC(bool, _handles, String)
+ GDVIRTUAL2RC(Object *, _make_tooltip_for_path, String, Dictionary)
+
+public:
+ static VBoxContainer *make_default_tooltip(const String &p_resource_path);
+ void request_thumbnail(const String &p_path, TextureRect *p_for_control) const;
+
+ virtual bool handles(const String &p_resource_type) const;
+ virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata) const;
+};
+
+class EditorTextureTooltipPlugin : public EditorResourceTooltipPlugin {
+ GDCLASS(EditorTextureTooltipPlugin, EditorResourceTooltipPlugin);
+
+public:
+ virtual bool handles(const String &p_resource_type) const override;
+ virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata) const override;
+};
+
+#endif // EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 328fe9b950..404711e074 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -42,6 +42,7 @@
#include "scene/gui/color_rect.h"
#include "scene/gui/subviewport_container.h"
#include "scene/gui/texture_button.h"
+#include "scene/main/viewport.h"
#include "scene/resources/fog_material.h"
#include "scene/resources/particle_process_material.h"
#include "scene/resources/sky_material.h"
@@ -163,9 +164,19 @@ void MaterialEditor::_button_pressed(Node *p_button) {
MaterialEditor::MaterialEditor() {
// canvas item
+ vc_2d = memnew(SubViewportContainer);
+ vc_2d->set_stretch(true);
+ add_child(vc_2d);
+ vc_2d->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+
+ viewport_2d = memnew(SubViewport);
+ vc_2d->add_child(viewport_2d);
+ viewport_2d->set_disable_input(true);
+ viewport_2d->set_transparent_background(true);
+
layout_2d = memnew(HBoxContainer);
layout_2d->set_alignment(BoxContainer::ALIGNMENT_CENTER);
- add_child(layout_2d);
+ viewport_2d->add_child(layout_2d);
layout_2d->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
rect_instance = memnew(ColorRect);
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index deb1211c54..ac81bdc7c7 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -51,6 +51,8 @@ class MaterialEditor : public Control {
Vector2 rot;
+ SubViewportContainer *vc_2d = nullptr;
+ SubViewport *viewport_2d = nullptr;
HBoxContainer *layout_2d = nullptr;
ColorRect *rect_instance = nullptr;
diff --git a/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp
new file mode 100644
index 0000000000..0cbc711982
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp
@@ -0,0 +1,66 @@
+/**************************************************************************/
+/* navigation_obstacle_2d_editor_plugin.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 "navigation_obstacle_2d_editor_plugin.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
+
+Node2D *NavigationObstacle2DEditor::_get_node() const {
+ return node;
+}
+
+void NavigationObstacle2DEditor::_set_node(Node *p_polygon) {
+ node = Object::cast_to<NavigationObstacle2D>(p_polygon);
+}
+
+void NavigationObstacle2DEditor::_action_add_polygon(const Variant &p_polygon) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_method(node, "set_vertices", p_polygon);
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+}
+
+void NavigationObstacle2DEditor::_action_remove_polygon(int p_idx) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_method(node, "set_vertices", Variant(Vector<Vector2>()));
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+}
+
+void NavigationObstacle2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_method(node, "set_vertices", p_polygon);
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+}
+
+NavigationObstacle2DEditor::NavigationObstacle2DEditor() {}
+
+NavigationObstacle2DEditorPlugin::NavigationObstacle2DEditorPlugin() :
+ AbstractPolygon2DEditorPlugin(memnew(NavigationObstacle2DEditor), "NavigationObstacle2D") {
+}
diff --git a/editor/plugins/navigation_obstacle_2d_editor_plugin.h b/editor/plugins/navigation_obstacle_2d_editor_plugin.h
new file mode 100644
index 0000000000..5a2206b8df
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_2d_editor_plugin.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* navigation_obstacle_2d_editor_plugin.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 NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
+#define NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
+
+#include "editor/plugins/abstract_polygon_2d_editor.h"
+#include "scene/2d/navigation_obstacle_2d.h"
+
+class NavigationObstacle2DEditor : public AbstractPolygon2DEditor {
+ GDCLASS(NavigationObstacle2DEditor, AbstractPolygon2DEditor);
+
+ NavigationObstacle2D *node = nullptr;
+
+protected:
+ virtual Node2D *_get_node() const override;
+ virtual void _set_node(Node *p_polygon) override;
+
+ virtual void _action_add_polygon(const Variant &p_polygon) override;
+ virtual void _action_remove_polygon(int p_idx) override;
+ virtual void _action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) override;
+
+public:
+ NavigationObstacle2DEditor();
+};
+
+class NavigationObstacle2DEditorPlugin : public AbstractPolygon2DEditorPlugin {
+ GDCLASS(NavigationObstacle2DEditorPlugin, AbstractPolygon2DEditorPlugin);
+
+public:
+ NavigationObstacle2DEditorPlugin();
+};
+
+#endif // NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
new file mode 100644
index 0000000000..2e39d6f67c
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -0,0 +1,599 @@
+/**************************************************************************/
+/* navigation_obstacle_3d_editor_plugin.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 "navigation_obstacle_3d_editor_plugin.h"
+
+#include "canvas_item_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"
+#include "core/os/keyboard.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
+#include "node_3d_editor_plugin.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/gui/separator.h"
+
+void NavigationObstacle3DEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ button_create->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ button_edit->set_icon(get_theme_icon(SNAME("MovePoint"), SNAME("EditorIcons")));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditor::_node_removed));
+
+ } break;
+ }
+}
+
+void NavigationObstacle3DEditor::_node_removed(Node *p_node) {
+ if (p_node == obstacle_node) {
+ obstacle_node = nullptr;
+ if (point_lines_meshinstance->get_parent() == p_node) {
+ p_node->remove_child(point_lines_meshinstance);
+ }
+ hide();
+ }
+}
+
+void NavigationObstacle3DEditor::_menu_option(int p_option) {
+ switch (p_option) {
+ case MODE_CREATE: {
+ mode = MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+ mode = MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+ }
+}
+
+void NavigationObstacle3DEditor::_wip_close() {
+ ERR_FAIL_COND_MSG(!obstacle_node, "Edited NavigationObstacle3D is not valid.");
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Set NavigationObstacle3D Vertices"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+
+ PackedVector3Array polygon_3d_vertices;
+ Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip);
+
+ if (!triangulated_polygon_2d_indices.is_empty()) {
+ polygon_3d_vertices.resize(wip.size());
+ Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw();
+ for (int i = 0; i < wip.size(); i++) {
+ const Vector2 &vert = wip[i];
+ polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
+ }
+ }
+
+ undo_redo->add_do_method(obstacle_node, "set_vertices", polygon_3d_vertices);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ wip.clear();
+ wip_active = false;
+ mode = MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point = -1;
+ undo_redo->commit_action();
+}
+
+EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+ if (!obstacle_node) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ Transform3D gt = obstacle_node->get_global_transform();
+ Transform3D gi = gt.affine_inverse();
+ Plane p(Vector3(0.0, 1.0, 0.0), gt.origin);
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid()) {
+ Vector2 gpoint = mb->get_position();
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x, spoint.z);
+
+ //DO NOT snap here, it's confusing in 3D for adding points.
+ //Let the snap happen when the point is being moved, instead.
+ //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+
+ PackedVector2Array poly = _get_polygon();
+
+ //first check if a point is to be added (segment split)
+ real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
+
+ switch (mode) {
+ case MODE_CREATE: {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
+ if (!wip_active) {
+ wip.clear();
+ wip.push_back(cpoint);
+ wip_active = true;
+ edited_point_pos = cpoint;
+ snap_ignore = false;
+ _polygon_draw();
+ edited_point = 1;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else {
+ if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) {
+ //wip closed
+ _wip_close();
+
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else {
+ wip.push_back(cpoint);
+ edited_point = wip.size();
+ snap_ignore = false;
+ _polygon_draw();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
+ _wip_close();
+ }
+
+ } break;
+
+ case MODE_EDIT: {
+ if (mb->get_button_index() == MouseButton::LEFT) {
+ if (mb->is_pressed()) {
+ if (mb->is_ctrl_pressed()) {
+ if (poly.size() < 3) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Edit Vertices"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ poly.push_back(cpoint);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+
+ //search edges
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 points[2] = {
+ p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))),
+ p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y)))
+ };
+
+ Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points);
+ if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) {
+ continue; //not valid to reuse point
+ }
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ pre_move_edit = poly;
+ poly.insert(closest_idx + 1, cpoint);
+ edited_point = closest_idx + 1;
+ edited_point_pos = cpoint;
+ _set_polygon(poly);
+ _polygon_draw();
+ snap_ignore = true;
+
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ } else {
+ //look for points to move
+
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ pre_move_edit = poly;
+ edited_point = closest_idx;
+ edited_point_pos = poly[closest_idx];
+ _polygon_draw();
+ snap_ignore = false;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ } else {
+ snap_ignore = false;
+
+ if (edited_point != -1) {
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
+ poly.write[edited_point] = edited_point_pos;
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Edit Poly"));
+ //undo_redo->add_do_method(obj, "set_polygon", poly);
+ //undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+
+ edited_point = -1;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+ if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
+ //undo_redo->add_undo_method(obj, "set_polygon", poly);
+ poly.remove_at(closest_idx);
+ //undo_redo->add_do_method(obj, "set_polygon", poly);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+
+ } break;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+ if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
+ Vector2 gpoint = mm->get_position();
+
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x, spoint.z);
+
+ if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ snap_ignore = false;
+ }
+
+ if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) {
+ cpoint = cpoint.snapped(Vector2(
+ Node3DEditor::get_singleton()->get_translate_snap(),
+ Node3DEditor::get_singleton()->get_translate_snap()));
+ }
+ edited_point_pos = cpoint;
+
+ _polygon_draw();
+ }
+ }
+
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+}
+
+PackedVector2Array NavigationObstacle3DEditor::_get_polygon() {
+ ERR_FAIL_COND_V_MSG(!obstacle_node, PackedVector2Array(), "Edited object is not valid.");
+ return PackedVector2Array(obstacle_node->call("get_polygon"));
+}
+
+void NavigationObstacle3DEditor::_set_polygon(PackedVector2Array p_poly) {
+ ERR_FAIL_COND_MSG(!obstacle_node, "Edited object is not valid.");
+ obstacle_node->call("set_polygon", p_poly);
+}
+
+void NavigationObstacle3DEditor::_polygon_draw() {
+ if (!obstacle_node) {
+ return;
+ }
+
+ PackedVector2Array poly;
+ PackedVector3Array polygon_3d_vertices;
+
+ if (wip_active) {
+ poly = wip;
+ } else {
+ poly = _get_polygon();
+ }
+ polygon_3d_vertices.resize(poly.size());
+ Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw();
+
+ for (int i = 0; i < poly.size(); i++) {
+ const Vector2 &vert = poly[i];
+ polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
+ }
+
+ point_handle_mesh->clear_surfaces();
+ point_lines_mesh->clear_surfaces();
+ point_lines_meshinstance->set_material_override(line_material);
+ point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES);
+
+ Rect2 rect;
+
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 p, p2;
+ if (i == edited_point) {
+ p = edited_point_pos;
+ } else {
+ p = poly[i];
+ }
+
+ if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
+ p2 = edited_point_pos;
+ } else {
+ p2 = poly[(i + 1) % poly.size()];
+ }
+
+ if (i == 0) {
+ rect.position = p;
+ } else {
+ rect.expand_to(p);
+ }
+
+ Vector3 point = Vector3(p.x, 0.0, p.y);
+ Vector3 next_point = Vector3(p2.x, 0.0, p2.y);
+
+ point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
+ point_lines_mesh->surface_add_vertex(point);
+ point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
+ point_lines_mesh->surface_add_vertex(next_point);
+
+ //Color col=Color(1,0.3,0.1,0.8);
+ //vpc->draw_line(point,next_point,col,2);
+ //vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+
+ rect = rect.grow(1);
+
+ AABB r;
+ r.position.x = rect.position.x;
+ r.position.y = 0.0;
+ r.position.z = rect.position.y;
+ r.size.x = rect.size.x;
+ r.size.y = 0;
+ r.size.z = rect.size.y;
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));
+
+ point_lines_mesh->surface_end();
+
+ if (poly.size() == 0) {
+ return;
+ }
+
+ Array point_handle_mesh_array;
+ point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
+ Vector<Vector3> point_handle_mesh_vertices;
+
+ point_handle_mesh_vertices.resize(poly.size());
+ Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw();
+
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 point_2d;
+ Vector2 p2;
+
+ if (i == edited_point) {
+ point_2d = edited_point_pos;
+ } else {
+ point_2d = poly[i];
+ }
+
+ Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y);
+ point_handle_mesh_vertices_ptr[i] = point_handle_3d;
+ }
+
+ point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices;
+ point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array);
+ point_handle_mesh->surface_set_material(0, handle_material);
+}
+
+void NavigationObstacle3DEditor::edit(Node *p_node) {
+ obstacle_node = Object::cast_to<NavigationObstacle3D>(p_node);
+
+ if (obstacle_node) {
+ //Enable the pencil tool if the polygon is empty
+ if (_get_polygon().is_empty()) {
+ _menu_option(MODE_CREATE);
+ }
+ wip.clear();
+ wip_active = false;
+ edited_point = -1;
+ p_node->add_child(point_lines_meshinstance);
+ _polygon_draw();
+
+ } else {
+ obstacle_node = nullptr;
+
+ if (point_lines_meshinstance->get_parent()) {
+ point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance);
+ }
+ }
+}
+
+void NavigationObstacle3DEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_polygon_draw"), &NavigationObstacle3DEditor::_polygon_draw);
+}
+
+NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
+ obstacle_node = nullptr;
+
+ add_child(memnew(VSeparator));
+ button_create = memnew(Button);
+ button_create->set_flat(true);
+ add_child(button_create);
+ button_create->connect("pressed", callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+
+ button_edit = memnew(Button);
+ button_edit->set_flat(true);
+ add_child(button_edit);
+ button_edit->connect("pressed", callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+
+ mode = MODE_EDIT;
+ wip_active = false;
+ point_lines_meshinstance = memnew(MeshInstance3D);
+ point_lines_mesh.instantiate();
+ point_lines_meshinstance->set_mesh(point_lines_mesh);
+ point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
+
+ line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ line_material->set_albedo(Color(1, 1, 1));
+
+ handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
+ handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ Ref<Texture2D> handle = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Editor3DHandle"), SNAME("EditorIcons"));
+ handle_material->set_point_size(handle->get_width());
+ handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
+
+ point_handles_meshinstance = memnew(MeshInstance3D);
+ point_lines_meshinstance->add_child(point_handles_meshinstance);
+ point_handle_mesh.instantiate();
+ point_handles_meshinstance->set_mesh(point_handle_mesh);
+ point_handles_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
+
+ snap_ignore = false;
+}
+
+NavigationObstacle3DEditor::~NavigationObstacle3DEditor() {
+ memdelete(point_lines_meshinstance);
+}
+
+void NavigationObstacle3DEditorPlugin::edit(Object *p_object) {
+ obstacle_editor->edit(Object::cast_to<Node>(p_object));
+}
+
+bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to<NavigationObstacle3D>(p_object);
+}
+
+void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ obstacle_editor->show();
+ } else {
+ obstacle_editor->hide();
+ obstacle_editor->edit(nullptr);
+ }
+}
+
+NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() {
+ obstacle_editor = memnew(NavigationObstacle3DEditor);
+ Node3DEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor);
+
+ obstacle_editor->hide();
+}
+
+NavigationObstacle3DEditorPlugin::~NavigationObstacle3DEditorPlugin() {
+}
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
new file mode 100644
index 0000000000..1b125873d1
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
@@ -0,0 +1,117 @@
+/**************************************************************************/
+/* navigation_obstacle_3d_editor_plugin.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 NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
+#define NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "scene/3d/collision_polygon_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/gui/box_container.h"
+#include "scene/resources/immediate_mesh.h"
+
+#include "scene/3d/navigation_obstacle_3d.h"
+
+class CanvasItemEditor;
+class MenuButton;
+
+class NavigationObstacle3DEditor : public HBoxContainer {
+ GDCLASS(NavigationObstacle3DEditor, HBoxContainer);
+
+ enum Mode {
+ MODE_CREATE,
+ MODE_EDIT,
+
+ };
+
+ Mode mode;
+
+ Button *button_create = nullptr;
+ Button *button_edit = nullptr;
+
+ Ref<StandardMaterial3D> line_material;
+ Ref<StandardMaterial3D> handle_material;
+
+ Panel *panel = nullptr;
+ NavigationObstacle3D *obstacle_node = nullptr;
+ Ref<ImmediateMesh> point_lines_mesh;
+ MeshInstance3D *point_lines_meshinstance = nullptr;
+ MeshInstance3D *point_handles_meshinstance = nullptr;
+ Ref<ArrayMesh> point_handle_mesh;
+
+ MenuButton *options = nullptr;
+
+ int edited_point = 0;
+ Vector2 edited_point_pos;
+ PackedVector2Array pre_move_edit;
+ PackedVector2Array wip;
+ bool wip_active;
+ bool snap_ignore;
+
+ float prev_depth = 0.0f;
+
+ void _wip_close();
+ void _polygon_draw();
+ void _menu_option(int p_option);
+
+ PackedVector2Array _get_polygon();
+ void _set_polygon(PackedVector2Array p_poly);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+public:
+ virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ void edit(Node *p_node);
+ NavigationObstacle3DEditor();
+ ~NavigationObstacle3DEditor();
+};
+
+class NavigationObstacle3DEditorPlugin : public EditorPlugin {
+ GDCLASS(NavigationObstacle3DEditorPlugin, EditorPlugin);
+
+ NavigationObstacle3DEditor *obstacle_editor = nullptr;
+
+public:
+ virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return obstacle_editor->forward_3d_gui_input(p_camera, p_event); }
+
+ virtual String get_name() const override { return "NavigationObstacle3DEditor"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ NavigationObstacle3DEditorPlugin();
+ ~NavigationObstacle3DEditorPlugin();
+};
+
+#endif // NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 00382e1a18..00f4af47e1 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -40,6 +40,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/gui/editor_spin_slider.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/gizmos/audio_listener_3d_gizmo_plugin.h"
@@ -3298,6 +3299,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
orthogonal = false;
auto_orthogonal = false;
call_deferred(SNAME("update_transform_gizmo_view"));
+ _update_camera(0);
_update_name();
} break;
@@ -3307,8 +3309,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
orthogonal = true;
auto_orthogonal = false;
call_deferred(SNAME("update_transform_gizmo_view"));
+ _update_camera(0);
_update_name();
-
} break;
case VIEW_SWITCH_PERSPECTIVE_ORTHOGONAL: {
_menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL);
@@ -3401,7 +3403,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_DISPLAY_NORMAL:
case VIEW_DISPLAY_WIREFRAME:
case VIEW_DISPLAY_OVERDRAW:
- case VIEW_DISPLAY_SHADELESS:
+ case VIEW_DISPLAY_UNSHADED:
case VIEW_DISPLAY_LIGHTING:
case VIEW_DISPLAY_NORMAL_BUFFER:
case VIEW_DISPLAY_DEBUG_SHADOW_ATLAS:
@@ -3428,7 +3430,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
VIEW_DISPLAY_OVERDRAW,
- VIEW_DISPLAY_SHADELESS,
+ VIEW_DISPLAY_UNSHADED,
VIEW_DISPLAY_LIGHTING,
VIEW_DISPLAY_NORMAL_BUFFER,
VIEW_DISPLAY_DEBUG_SHADOW_ATLAS,
@@ -3777,15 +3779,9 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
if (p_state.has("distance")) {
cursor.distance = p_state["distance"];
}
-
- if (p_state.has("use_orthogonal")) {
- bool orth = p_state["use_orthogonal"];
-
- if (orth) {
- _menu_option(VIEW_ORTHOGONAL);
- } else {
- _menu_option(VIEW_PERSPECTIVE);
- }
+ if (p_state.has("orthogonal")) {
+ bool orth = p_state["orthogonal"];
+ _menu_option(orth ? VIEW_ORTHOGONAL : VIEW_PERSPECTIVE);
}
if (p_state.has("view_type")) {
view_type = ViewType(p_state["view_type"].operator int());
@@ -3803,8 +3799,13 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
int display = p_state["display_mode"];
int idx = view_menu->get_popup()->get_item_index(display);
- if (!view_menu->get_popup()->is_item_checked(idx)) {
+ if (idx != -1 && !view_menu->get_popup()->is_item_checked(idx)) {
_menu_option(display);
+ } else {
+ idx = display_submenu->get_item_index(display);
+ if (idx != -1 && !display_submenu->is_item_checked(idx)) {
+ _menu_option(display);
+ }
}
}
if (p_state.has("lock_rotation")) {
@@ -3863,6 +3864,7 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION);
view_menu->get_popup()->set_item_checked(idx, half_res);
+ _update_shrink();
}
if (p_state.has("cinematic_preview")) {
previewing_cinema = p_state["cinematic_preview"];
@@ -3895,19 +3897,27 @@ Dictionary Node3DEditorViewport::get_state() const {
d["y_rotation"] = cursor.y_rot;
d["distance"] = cursor.distance;
d["use_environment"] = camera->get_environment().is_valid();
- d["use_orthogonal"] = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
+ d["orthogonal"] = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
d["view_type"] = view_type;
d["auto_orthogonal"] = auto_orthogonal;
d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL));
- if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL))) {
- d["display_mode"] = VIEW_DISPLAY_NORMAL;
- } else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME))) {
- d["display_mode"] = VIEW_DISPLAY_WIREFRAME;
- } else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW))) {
- d["display_mode"] = VIEW_DISPLAY_OVERDRAW;
- } else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS))) {
- d["display_mode"] = VIEW_DISPLAY_SHADELESS;
+
+ // Find selected display mode.
+ int display_mode = VIEW_DISPLAY_NORMAL;
+ for (int i = VIEW_DISPLAY_NORMAL; i < VIEW_DISPLAY_ADVANCED; i++) {
+ if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(i))) {
+ display_mode = i;
+ break;
+ }
}
+ for (int i = VIEW_DISPLAY_ADVANCED + 1; i < VIEW_DISPLAY_MAX; i++) {
+ if (display_submenu->is_item_checked(display_submenu->get_item_index(i))) {
+ display_mode = i;
+ break;
+ }
+ }
+ d["display_mode"] = display_mode;
+
d["listener"] = viewport->is_audio_listener_3d();
d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER));
d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS));
@@ -5004,7 +5014,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_lighting", TTR("Display Lighting")), VIEW_DISPLAY_LIGHTING);
- view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_UNSHADED);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
display_submenu->set_hide_on_checkable_item_selection(false);
display_submenu->add_radio_check_item(TTR("Directional Shadow Splits"), VIEW_DISPLAY_DEBUG_PSSM_SPLITS);
@@ -5073,7 +5083,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
const int normal_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL);
const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME);
const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW);
- const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS);
+ const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_UNSHADED);
const String unsupported_tooltip = TTR("Not available when using the OpenGL renderer.");
view_menu->get_popup()->set_item_disabled(normal_idx, true);
@@ -7518,8 +7528,8 @@ void Node3DEditor::_notification(int p_what) {
SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons));
editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed));
- EditorNode::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false));
- EditorNode::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true));
+ EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false));
+ EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true));
_update_preview_environment();
@@ -8372,8 +8382,6 @@ Node3DEditor::Node3DEditor() {
snap_scale->set_select_all_on_focus(true);
snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale);
- _snap_update();
-
/* SETTINGS DIALOG */
settings_dialog = memnew(ConfirmationDialog);
@@ -8577,9 +8585,11 @@ void fragment() {
sun_color->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(sun_color->get_picker()));
sun_energy = memnew(EditorSpinSlider);
+ sun_energy->set_max(64.0);
+ sun_energy->set_min(0);
+ sun_energy->set_step(0.05);
sun_vb->add_margin_child(TTR("Sun Energy"), sun_energy);
sun_energy->connect("value_changed", callable_mp(this, &Node3DEditor::_preview_settings_changed).unbind(1));
- sun_energy->set_max(64.0);
sun_max_distance = memnew(EditorSpinSlider);
sun_vb->add_margin_child(TTR("Shadow Max Distance"), sun_max_distance);
@@ -8628,8 +8638,10 @@ void fragment() {
environ_ground_color->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(environ_ground_color->get_picker()));
environ_vb->add_margin_child(TTR("Ground Color"), environ_ground_color);
environ_energy = memnew(EditorSpinSlider);
- environ_energy->connect("value_changed", callable_mp(this, &Node3DEditor::_preview_settings_changed).unbind(1));
environ_energy->set_max(8.0);
+ environ_energy->set_min(0);
+ environ_energy->set_step(0.05);
+ environ_energy->connect("value_changed", callable_mp(this, &Node3DEditor::_preview_settings_changed).unbind(1));
environ_vb->add_margin_child(TTR("Sky Energy"), environ_energy);
HBoxContainer *fx_vb = memnew(HBoxContainer);
fx_vb->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -8689,7 +8701,7 @@ void fragment() {
_load_default_preview_settings();
_preview_settings_changed();
}
- clear(); // Make sure values are initialized.
+ clear(); // Make sure values are initialized. Will call _snap_update() for us.
}
Node3DEditor::~Node3DEditor() {
memdelete(preview_node);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index c4e1070d84..4db809b2cc 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -125,25 +125,28 @@ class Node3DEditorViewport : public Control {
VIEW_GIZMOS,
VIEW_INFORMATION,
VIEW_FRAME_TIME,
+
+ // < Keep in sync with menu.
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
VIEW_DISPLAY_OVERDRAW,
- VIEW_DISPLAY_SHADELESS,
VIEW_DISPLAY_LIGHTING,
+ VIEW_DISPLAY_UNSHADED,
VIEW_DISPLAY_ADVANCED,
+ // Advanced menu:
+ VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
VIEW_DISPLAY_NORMAL_BUFFER,
VIEW_DISPLAY_DEBUG_SHADOW_ATLAS,
VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS,
+ VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
VIEW_DISPLAY_DEBUG_VOXEL_GI_ALBEDO,
VIEW_DISPLAY_DEBUG_VOXEL_GI_LIGHTING,
VIEW_DISPLAY_DEBUG_VOXEL_GI_EMISSION,
+ VIEW_DISPLAY_DEBUG_SDFGI,
+ VIEW_DISPLAY_DEBUG_SDFGI_PROBES,
VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
VIEW_DISPLAY_DEBUG_SSAO,
VIEW_DISPLAY_DEBUG_SSIL,
- VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
- VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
- VIEW_DISPLAY_DEBUG_SDFGI,
- VIEW_DISPLAY_DEBUG_SDFGI_PROBES,
VIEW_DISPLAY_DEBUG_GI_BUFFER,
VIEW_DISPLAY_DEBUG_DISABLE_LOD,
VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS,
@@ -152,6 +155,8 @@ class Node3DEditorViewport : public Control {
VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES,
VIEW_DISPLAY_DEBUG_OCCLUDERS,
VIEW_DISPLAY_MOTION_VECTORS,
+ VIEW_DISPLAY_MAX,
+ // > Keep in sync with menu.
VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW,
@@ -403,8 +408,8 @@ private:
Camera3D *previewing = nullptr;
Camera3D *preview = nullptr;
- bool previewing_camera;
- bool previewing_cinema;
+ bool previewing_camera = false;
+ bool previewing_cinema = false;
bool _is_node_locked(const Node *p_node);
void _preview_exited_scene();
void _toggle_camera_preview(bool);
@@ -581,8 +586,8 @@ private:
bool origin_enabled = false;
RID grid[3];
RID grid_instance[3];
- bool grid_visible[3]; //currently visible
- bool grid_enable[3]; //should be always visible if true
+ bool grid_visible[3] = { false, false, false }; //currently visible
+ bool grid_enable[3] = { false, false, false }; //should be always visible if true
bool grid_enabled = false;
bool grid_init_draw = false;
Camera3D::ProjectionType grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE;
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp
index b716cee3ff..a8d3ab948f 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.cpp
+++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp
@@ -96,8 +96,9 @@ void PhysicalBone3DEditorPlugin::make_visible(bool p_visible) {
}
void PhysicalBone3DEditorPlugin::edit(Object *p_node) {
- selected = static_cast<PhysicalBone3D *>(p_node); // Trust it
- ERR_FAIL_COND(!selected);
-
- physical_bone_editor.set_selected(selected);
+ PhysicalBone3D *bone = Object::cast_to<PhysicalBone3D>(p_node);
+ if (bone) {
+ selected = bone;
+ physical_bone_editor.set_selected(selected);
+ }
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 736826c231..51550acb94 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -40,19 +40,24 @@
#include "core/version.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
+#include "editor/editor_command_palette.h"
#include "editor/editor_help_search.h"
+#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
-#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
+#include "editor/editor_script.h"
#include "editor/editor_settings.h"
#include "editor/filesystem_dock.h"
#include "editor/find_in_files.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/inspector_dock.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
#include "editor/plugins/text_shader_editor.h"
+#include "editor/window_wrapper.h"
+#include "scene/main/node.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@@ -943,11 +948,7 @@ void ScriptEditor::_resave_scripts(const String &p_str) {
se->insert_final_newline();
if (convert_indent_on_save) {
- if (use_space_indentation) {
- se->convert_indent_to_spaces();
- } else {
- se->convert_indent_to_tabs();
- }
+ se->convert_indent();
}
Ref<TextFile> text_file = scr;
@@ -1298,11 +1299,7 @@ void ScriptEditor::_menu_option(int p_option) {
current->insert_final_newline();
if (convert_indent_on_save) {
- if (use_space_indentation) {
- current->convert_indent_to_spaces();
- } else {
- current->convert_indent_to_tabs();
- }
+ current->convert_indent();
}
Ref<Resource> resource = current->get_edited_resource();
@@ -1375,9 +1372,7 @@ void ScriptEditor::_menu_option(int p_option) {
Ref<EditorScript> es = memnew(EditorScript);
es->set_script(scr);
- es->set_editor(EditorNode::get_singleton());
-
- es->_run();
+ es->run();
} break;
case FILE_CLOSE: {
if (current->is_unsaved()) {
@@ -1592,24 +1587,11 @@ void ScriptEditor::_tab_changed(int p_which) {
void ScriptEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- EditorNode::get_singleton()->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
- EditorNode::get_singleton()->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
- EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
- EditorNode::get_singleton()->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
- FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
- FileSystemDock::get_singleton()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
- script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
-
- members_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_members_overview_selected));
- help_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_help_overview_selected));
- script_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
- list_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
-
- EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
- EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
+ EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
_editor_settings_changed();
[[fallthrough]];
}
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
@@ -1643,10 +1625,25 @@ void ScriptEditor::_notification(int p_what) {
get_tree()->connect("tree_changed", callable_mp(this, &ScriptEditor::_tree_changed));
InspectorDock::get_singleton()->connect("request_help", callable_mp(this, &ScriptEditor::_help_class_open));
EditorNode::get_singleton()->connect("request_help_search", callable_mp(this, &ScriptEditor::_help_search));
+ EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ScriptEditor::_close_builtin_scripts_from_scene));
+ EditorNode::get_singleton()->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
+ EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
+ EditorNode::get_singleton()->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
+ FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
+ FileSystemDock::get_singleton()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
+ script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
+
+ members_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_members_overview_selected));
+ help_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_help_overview_selected));
+ script_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
+ list_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
+
+ EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
} break;
case NOTIFICATION_EXIT_TREE: {
- EditorNode::get_singleton()->disconnect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
+ EditorRunBar::get_singleton()->disconnect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
} break;
case NOTIFICATION_APPLICATION_FOCUS_IN: {
@@ -1677,7 +1674,7 @@ bool ScriptEditor::can_take_away_focus() const {
}
}
-void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
+void ScriptEditor::_close_builtin_scripts_from_scene(const String &p_scene) {
for (int i = 0; i < tab_container->get_tab_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
@@ -1687,7 +1684,7 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
continue;
}
- if (scr->is_built_in() && scr->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
+ if (scr->is_built_in() && scr->get_path().begins_with(p_scene)) { // Is an internal script and belongs to scene being closed.
_close_tab(i, false);
i--;
}
@@ -2328,7 +2325,6 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
if (tab_container->get_current_tab() != i) {
_go_to_tab(i);
- _update_script_names();
}
if (is_visible_in_tree()) {
se->ensure_focus();
@@ -2451,11 +2447,7 @@ void ScriptEditor::save_current_script() {
current->insert_final_newline();
if (convert_indent_on_save) {
- if (use_space_indentation) {
- current->convert_indent_to_spaces();
- } else {
- current->convert_indent_to_tabs();
- }
+ current->convert_indent();
}
Ref<Resource> resource = current->get_edited_resource();
@@ -2499,11 +2491,7 @@ void ScriptEditor::save_all_scripts() {
}
if (convert_indent_on_save) {
- if (use_space_indentation) {
- se->convert_indent_to_spaces();
- } else {
- se->convert_indent_to_tabs();
- }
+ se->convert_indent();
}
if (trim_trailing_whitespace_on_save) {
@@ -2718,7 +2706,7 @@ void ScriptEditor::_save_layout() {
return;
}
- EditorNode::get_singleton()->save_layout();
+ EditorNode::get_singleton()->save_editor_layout_delayed();
}
void ScriptEditor::_editor_settings_changed() {
@@ -2730,7 +2718,6 @@ void ScriptEditor::_editor_settings_changed() {
trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save");
convert_indent_on_save = EDITOR_GET("text_editor/behavior/files/convert_indent_on_save");
- use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");
members_overview_enabled = EDITOR_GET("text_editor/script_list/show_members_overview");
help_overview_enabled = EDITOR_GET("text_editor/help/show_help_index");
@@ -3263,12 +3250,24 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
restoring_layout = false;
_update_script_names();
+
+ if (p_layout->has_section_key("ScriptEditor", "selected_script")) {
+ String selected_script = p_layout->get_value("ScriptEditor", "selected_script");
+ // If the selected script is not in the list of open scripts, select nothing.
+ 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->get_edited_resource()->get_path() == selected_script) {
+ _go_to_tab(i);
+ break;
+ }
+ }
+ }
}
void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
Array scripts;
Array helps;
-
+ String selected_script;
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) {
@@ -3277,6 +3276,10 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
continue;
}
+ if (tab_container->get_current_tab_control() == tab_container->get_tab_control(i)) {
+ selected_script = path;
+ }
+
_save_editor_state(se);
scripts.push_back(path);
}
@@ -3289,6 +3292,7 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
}
p_layout->set_value("ScriptEditor", "open_scripts", scripts);
+ p_layout->set_value("ScriptEditor", "selected_script", selected_script);
p_layout->set_value("ScriptEditor", "open_help", helps);
p_layout->set_value("ScriptEditor", "script_split_offset", script_split->get_split_offset());
p_layout->set_value("ScriptEditor", "list_split_offset", list_split->get_split_offset());
@@ -3317,7 +3321,7 @@ void ScriptEditor::_help_class_open(const String &p_class) {
eh->set_name(p_class);
tab_container->add_child(eh);
_go_to_tab(tab_container->get_tab_count() - 1);
- eh->go_to_class(p_class, 0);
+ eh->go_to_class(p_class);
eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto));
_add_recent_script(p_class);
_sort_list_on_update = true;
@@ -3728,6 +3732,10 @@ void ScriptEditor::_on_find_in_files_modified_files(PackedStringArray paths) {
_update_modified_scripts_for_external_editor();
}
+void ScriptEditor::_window_changed(bool p_visible) {
+ make_floating->set_visible(!p_visible);
+}
+
void ScriptEditor::_filter_scripts_text_changed(const String &p_newtext) {
_update_script_names();
}
@@ -3764,7 +3772,8 @@ void ScriptEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
}
-ScriptEditor::ScriptEditor() {
+ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
+ window_wrapper = p_wrapper;
current_theme = "";
script_editor_cache.instantiate();
@@ -3990,6 +3999,16 @@ ScriptEditor::ScriptEditor() {
menu_hb->add_child(help_search);
help_search->set_tooltip_text(TTR("Search the reference documentation."));
+ if (p_wrapper->is_window_available()) {
+ make_floating = memnew(ScreenSelect);
+ make_floating->set_flat(true);
+ make_floating->set_tooltip_text(TTR("Make the script editor floating."));
+ make_floating->connect("request_open_in_screen", callable_mp(window_wrapper, &WindowWrapper::enable_window_on_screen).bind(true));
+
+ menu_hb->add_child(make_floating);
+ p_wrapper->connect("window_visibility_changed", callable_mp(this, &ScriptEditor::_window_changed));
+ }
+
menu_hb->add_child(memnew(VSeparator));
script_back = memnew(Button);
@@ -4081,7 +4100,6 @@ ScriptEditor::ScriptEditor() {
edit_pass = 0;
trim_trailing_whitespace_on_save = EDITOR_GET("text_editor/behavior/files/trim_trailing_whitespace_on_save");
convert_indent_on_save = EDITOR_GET("text_editor/behavior/files/convert_indent_on_save");
- use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");
ScriptServer::edit_request_func = _open_script_request;
@@ -4097,6 +4115,39 @@ ScriptEditor::~ScriptEditor() {
memdelete(completion_cache);
}
+void ScriptEditorPlugin::_focus_another_editor() {
+ if (window_wrapper->get_window_enabled()) {
+ ERR_FAIL_COND(last_editor.is_empty());
+ EditorInterface::get_singleton()->set_main_screen_editor(last_editor);
+ }
+}
+
+void ScriptEditorPlugin::_save_last_editor(String p_editor) {
+ if (p_editor != get_name()) {
+ last_editor = p_editor;
+ }
+}
+
+void ScriptEditorPlugin::_window_visibility_changed(bool p_visible) {
+ _focus_another_editor();
+ if (p_visible) {
+ script_editor->add_theme_style_override("panel", script_editor->get_theme_stylebox("ScriptEditorPanelFloating", "EditorStyles"));
+ } else {
+ script_editor->add_theme_style_override("panel", script_editor->get_theme_stylebox("ScriptEditorPanel", "EditorStyles"));
+ }
+}
+
+void ScriptEditorPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ connect("main_screen_changed", callable_mp(this, &ScriptEditorPlugin::_save_last_editor));
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ disconnect("main_screen_changed", callable_mp(this, &ScriptEditorPlugin::_save_last_editor));
+ } break;
+ }
+}
+
void ScriptEditorPlugin::edit(Object *p_object) {
if (Object::cast_to<Script>(p_object)) {
Script *p_script = Object::cast_to<Script>(p_object);
@@ -4137,17 +4188,20 @@ bool ScriptEditorPlugin::handles(Object *p_object) const {
void ScriptEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- script_editor->show();
+ window_wrapper->show();
script_editor->set_process(true);
script_editor->ensure_select_current();
} else {
- script_editor->hide();
- script_editor->set_process(false);
+ window_wrapper->hide();
+ if (!window_wrapper->get_window_enabled()) {
+ script_editor->set_process(false);
+ }
}
}
void ScriptEditorPlugin::selected_notify() {
script_editor->ensure_select_current();
+ _focus_another_editor();
}
void ScriptEditorPlugin::save_external_data() {
@@ -4160,10 +4214,37 @@ void ScriptEditorPlugin::apply_changes() {
void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
script_editor->set_window_layout(p_layout);
+
+ if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ScriptEditor", "window_rect")) {
+ window_wrapper->restore_window_from_saved_position(
+ p_layout->get_value("ScriptEditor", "window_rect", Rect2i()),
+ p_layout->get_value("ScriptEditor", "window_screen", -1),
+ p_layout->get_value("ScriptEditor", "window_screen_rect", Rect2i()));
+ } else {
+ window_wrapper->set_window_enabled(false);
+ }
}
void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
script_editor->get_window_layout(p_layout);
+
+ if (window_wrapper->get_window_enabled()) {
+ p_layout->set_value("ScriptEditor", "window_rect", window_wrapper->get_window_rect());
+ int screen = window_wrapper->get_window_screen();
+ p_layout->set_value("ScriptEditor", "window_screen", screen);
+ p_layout->set_value("ScriptEditor", "window_screen_rect", DisplayServer::get_singleton()->screen_get_usable_rect(screen));
+
+ } else {
+ if (p_layout->has_section_key("ScriptEditor", "window_rect")) {
+ p_layout->erase_section_key("ScriptEditor", "window_rect");
+ }
+ if (p_layout->has_section_key("ScriptEditor", "window_screen")) {
+ p_layout->erase_section_key("ScriptEditor", "window_screen");
+ }
+ if (p_layout->has_section_key("ScriptEditor", "window_screen_rect")) {
+ p_layout->erase_section_key("ScriptEditor", "window_screen_rect");
+ }
+ }
}
void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
@@ -4175,11 +4256,18 @@ void ScriptEditorPlugin::edited_scene_changed() {
}
ScriptEditorPlugin::ScriptEditorPlugin() {
- script_editor = memnew(ScriptEditor);
- EditorNode::get_singleton()->get_main_screen_control()->add_child(script_editor);
- script_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-
- script_editor->hide();
+ window_wrapper = memnew(WindowWrapper);
+ window_wrapper->set_window_title(TTR("Script Editor - Godot Engine"));
+ window_wrapper->set_margins_enabled(true);
+
+ script_editor = memnew(ScriptEditor(window_wrapper));
+ Ref<Shortcut> make_floating_shortcut = ED_SHORTCUT_AND_COMMAND("script_editor/make_floating", TTR("Make Floating"));
+ window_wrapper->set_wrapped_control(script_editor, make_floating_shortcut);
+
+ EditorNode::get_singleton()->get_main_screen_control()->add_child(window_wrapper);
+ window_wrapper->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ window_wrapper->hide();
+ window_wrapper->connect("window_visibility_changed", callable_mp(this, &ScriptEditorPlugin::_window_visibility_changed));
EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change");
ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/behavior/files/auto_reload_and_parse_scripts_on_save", true));
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 7d2f5ad22a..e879920e41 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -47,6 +47,7 @@ class TabContainer;
class TextureRect;
class Tree;
class VSplitContainer;
+class WindowWrapper;
class EditorSyntaxHighlighter : public SyntaxHighlighter {
GDCLASS(EditorSyntaxHighlighter, SyntaxHighlighter)
@@ -169,8 +170,7 @@ public:
virtual void clear_executing_line() = 0;
virtual void trim_trailing_whitespace() = 0;
virtual void insert_final_newline() = 0;
- virtual void convert_indent_to_spaces() = 0;
- virtual void convert_indent_to_tabs() = 0;
+ virtual void convert_indent() = 0;
virtual void ensure_focus() = 0;
virtual void tag_saved_version() = 0;
virtual void reload(bool p_soft) {}
@@ -237,7 +237,7 @@ class ScriptEditor : public PanelContainer {
WINDOW_NEXT,
WINDOW_PREV,
WINDOW_SORT,
- WINDOW_SELECT_BASE = 100
+ WINDOW_SELECT_BASE = 100,
};
enum {
@@ -273,6 +273,7 @@ class ScriptEditor : public PanelContainer {
Button *help_search = nullptr;
Button *site_search = nullptr;
+ Button *make_floating = nullptr;
EditorHelpSearch *help_search_dialog = nullptr;
ItemList *script_list = nullptr;
@@ -309,6 +310,8 @@ class ScriptEditor : public PanelContainer {
FindInFilesPanel *find_in_files = nullptr;
Button *find_in_files_button = nullptr;
+ WindowWrapper *window_wrapper = nullptr;
+
enum {
SCRIPT_EDITOR_FUNC_MAX = 32,
};
@@ -390,7 +393,6 @@ class ScriptEditor : public PanelContainer {
bool open_textfile_after_create = true;
bool trim_trailing_whitespace_on_save;
- bool use_space_indentation;
bool convert_indent_on_save;
void _goto_script_line2(int p_line);
@@ -481,7 +483,10 @@ class ScriptEditor : public PanelContainer {
void _start_find_in_files(bool with_replace);
void _on_find_in_files_modified_files(PackedStringArray paths);
+ void _window_changed(bool p_visible);
+
static void _open_script_request(const String &p_path);
+ void _close_builtin_scripts_from_scene(const String &p_scene);
static ScriptEditor *script_editor;
@@ -523,8 +528,6 @@ public:
void notify_script_close(const Ref<Script> &p_script);
void notify_script_changed(const Ref<Script> &p_script);
- void close_builtin_scripts_from_scene(const String &p_scene);
-
void goto_help(const String &p_desc) { _help_class_goto(p_desc); }
void update_doc(const String &p_name);
void clear_docs_from_script(const Ref<Script> &p_script);
@@ -541,7 +544,7 @@ public:
static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
- ScriptEditor();
+ ScriptEditor(WindowWrapper *p_wrapper);
~ScriptEditor();
};
@@ -549,6 +552,17 @@ class ScriptEditorPlugin : public EditorPlugin {
GDCLASS(ScriptEditorPlugin, EditorPlugin);
ScriptEditor *script_editor = nullptr;
+ WindowWrapper *window_wrapper = nullptr;
+
+ String last_editor;
+
+ void _focus_another_editor();
+
+ void _save_last_editor(String p_editor);
+ void _window_visibility_changed(bool p_visible);
+
+protected:
+ void _notification(int p_what);
public:
virtual String get_name() const override { return "Script"; }
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 837645310a..ba6c90d157 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 "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h"
void ConnectionInfoDialog::ok_pressed() {
@@ -377,12 +378,8 @@ void ScriptTextEditor::insert_final_newline() {
code_editor->insert_final_newline();
}
-void ScriptTextEditor::convert_indent_to_spaces() {
- code_editor->convert_indent_to_spaces();
-}
-
-void ScriptTextEditor::convert_indent_to_tabs() {
- code_editor->convert_indent_to_tabs();
+void ScriptTextEditor::convert_indent() {
+ code_editor->get_text_editor()->convert_indent();
}
void ScriptTextEditor::tag_saved_version() {
@@ -1281,10 +1278,12 @@ void ScriptTextEditor::_edit_option(int p_op) {
trim_trailing_whitespace();
} break;
case EDIT_CONVERT_INDENT_TO_SPACES: {
- convert_indent_to_spaces();
+ tx->set_indent_using_spaces(true);
+ convert_indent();
} break;
case EDIT_CONVERT_INDENT_TO_TABS: {
- convert_indent_to_tabs();
+ tx->set_indent_using_spaces(false);
+ convert_indent();
} break;
case EDIT_PICK_COLOR: {
color_panel->popup();
@@ -1804,20 +1803,26 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
int col = pos.x;
tx->set_move_caret_on_right_click_enabled(EDITOR_GET("text_editor/behavior/navigation/move_caret_on_right_click"));
+ int caret_clicked = -1;
if (tx->is_move_caret_on_right_click_enabled()) {
- tx->remove_secondary_carets();
if (tx->has_selection()) {
- int from_line = tx->get_selection_from_line();
- int to_line = tx->get_selection_to_line();
- int from_column = tx->get_selection_from_column();
- int to_column = tx->get_selection_to_column();
-
- if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
- // Right click is outside the selected text
- tx->deselect();
+ for (int i = 0; i < tx->get_caret_count(); i++) {
+ int from_line = tx->get_selection_from_line(i);
+ int to_line = tx->get_selection_to_line(i);
+ int from_column = tx->get_selection_from_column(i);
+ int to_column = tx->get_selection_to_column(i);
+
+ if (row >= from_line && row <= to_line && (row != from_line || col >= from_column) && (row != to_line || col <= to_column)) {
+ // Right click in one of the selected text
+ caret_clicked = i;
+ break;
+ }
}
}
- if (!tx->has_selection()) {
+ if (caret_clicked < 0) {
+ tx->deselect();
+ tx->remove_secondary_carets();
+ caret_clicked = 0;
tx->set_caret_line(row, false, false);
tx->set_caret_column(col);
}
@@ -1825,10 +1830,10 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
String word_at_pos = tx->get_word_at_pos(local_pos);
if (word_at_pos.is_empty()) {
- word_at_pos = tx->get_word_under_caret(0);
+ word_at_pos = tx->get_word_under_caret(caret_clicked);
}
if (word_at_pos.is_empty()) {
- word_at_pos = tx->get_selected_text(0);
+ word_at_pos = tx->get_selected_text(caret_clicked);
}
bool has_color = (word_at_pos == "Color");
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 1b986401c2..5e167af51a 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -222,8 +222,7 @@ public:
virtual void ensure_focus() override;
virtual void trim_trailing_whitespace() override;
virtual void insert_final_newline() override;
- virtual void convert_indent_to_spaces() override;
- virtual void convert_indent_to_tabs() override;
+ virtual void convert_indent() override;
virtual void tag_saved_version() override;
virtual void goto_line(int p_line, bool p_with_error = false) override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 49459a0ba0..e7d2d7a11f 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -30,6 +30,7 @@
#include "shader_editor_plugin.h"
+#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
@@ -38,6 +39,7 @@
#include "editor/plugins/text_shader_editor.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/shader_create_dialog.h"
+#include "editor/window_wrapper.h"
#include "scene/gui/item_list.h"
#include "scene/gui/texture_rect.h"
@@ -171,7 +173,7 @@ bool ShaderEditorPlugin::handles(Object *p_object) const {
void ShaderEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(main_split);
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(window_wrapper);
}
}
@@ -196,6 +198,88 @@ VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref<Shade
return nullptr;
}
+void ShaderEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
+ if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ShaderEditor", "window_rect")) {
+ window_wrapper->restore_window_from_saved_position(
+ p_layout->get_value("ShaderEditor", "window_rect", Rect2i()),
+ p_layout->get_value("ShaderEditor", "window_screen", -1),
+ p_layout->get_value("ShaderEditor", "window_screen_rect", Rect2i()));
+ } else {
+ window_wrapper->set_window_enabled(false);
+ }
+
+ if (!bool(EDITOR_GET("editors/shader_editor/behavior/files/restore_shaders_on_load"))) {
+ return;
+ }
+ if (!p_layout->has_section("ShaderEditor")) {
+ return;
+ }
+ if (!p_layout->has_section_key("ShaderEditor", "open_shaders") ||
+ !p_layout->has_section_key("ShaderEditor", "selected_shader")) {
+ return;
+ }
+
+ Array shaders = p_layout->get_value("ShaderEditor", "open_shaders");
+ int selected_shader_idx = 0;
+ String selected_shader = p_layout->get_value("ShaderEditor", "selected_shader");
+ for (int i = 0; i < shaders.size(); i++) {
+ String path = shaders[i];
+ Ref<Resource> res = ResourceLoader::load(path);
+ if (res.is_valid()) {
+ edit(res.ptr());
+ }
+ if (selected_shader == path) {
+ selected_shader_idx = i;
+ }
+ }
+
+ if (p_layout->has_section_key("ShaderEditor", "split_offset")) {
+ main_split->set_split_offset(p_layout->get_value("ShaderEditor", "split_offset"));
+ }
+
+ _update_shader_list();
+ _shader_selected(selected_shader_idx);
+}
+
+void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
+ if (window_wrapper->get_window_enabled()) {
+ p_layout->set_value("ShaderEditor", "window_rect", window_wrapper->get_window_rect());
+ int screen = window_wrapper->get_window_screen();
+ p_layout->set_value("ShaderEditor", "window_screen", screen);
+ p_layout->set_value("ShaderEditor", "window_screen_rect", DisplayServer::get_singleton()->screen_get_usable_rect(screen));
+
+ } else {
+ if (p_layout->has_section_key("ShaderEditor", "window_rect")) {
+ p_layout->erase_section_key("ShaderEditor", "window_rect");
+ }
+ if (p_layout->has_section_key("ShaderEditor", "window_screen")) {
+ p_layout->erase_section_key("ShaderEditor", "window_screen");
+ }
+ if (p_layout->has_section_key("ShaderEditor", "window_screen_rect")) {
+ p_layout->erase_section_key("ShaderEditor", "window_screen_rect");
+ }
+ }
+
+ Array shaders;
+ String selected_shader;
+ for (int i = 0; i < shader_tabs->get_tab_count(); i++) {
+ EditedShader edited_shader = edited_shaders[i];
+ if (edited_shader.shader_editor || edited_shader.visual_shader_editor) {
+ shaders.push_back(edited_shader.shader->get_path());
+
+ TextShaderEditor *shader_editor = Object::cast_to<TextShaderEditor>(shader_tabs->get_current_tab_control());
+ VisualShaderEditor *visual_shader_editor = Object::cast_to<VisualShaderEditor>(shader_tabs->get_current_tab_control());
+
+ if ((shader_editor && edited_shader.shader_editor == shader_editor) || (visual_shader_editor && edited_shader.visual_shader_editor == visual_shader_editor)) {
+ selected_shader = edited_shader.shader->get_path();
+ }
+ }
+ }
+ p_layout->set_value("ShaderEditor", "open_shaders", shaders);
+ p_layout->set_value("ShaderEditor", "split_offset", main_split->get_split_offset());
+ p_layout->set_value("ShaderEditor", "selected_shader", selected_shader);
+}
+
void ShaderEditorPlugin::save_external_data() {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader_editor) {
@@ -214,6 +298,10 @@ void ShaderEditorPlugin::apply_changes() {
}
void ShaderEditorPlugin::_shader_selected(int p_index) {
+ if (p_index >= (int)edited_shaders.size()) {
+ return;
+ }
+
if (edited_shaders[p_index].shader_editor) {
edited_shaders[p_index].shader_editor->validate_script();
}
@@ -236,6 +324,26 @@ void ShaderEditorPlugin::_close_shader(int p_index) {
EditorUndoRedoManager::get_singleton()->clear_history(); // To prevent undo on deleted graphs.
}
+void ShaderEditorPlugin::_close_builtin_shaders_from_scene(const String &p_scene) {
+ for (uint32_t i = 0; i < edited_shaders.size();) {
+ Ref<Shader> &shader = edited_shaders[i].shader;
+ if (shader.is_valid()) {
+ if (shader->is_built_in() && shader->get_path().begins_with(p_scene)) {
+ _close_shader(i);
+ continue;
+ }
+ }
+ Ref<ShaderInclude> &include = edited_shaders[i].shader_inc;
+ if (include.is_valid()) {
+ if (include->is_built_in() && include->get_path().begins_with(p_scene)) {
+ _close_shader(i);
+ continue;
+ }
+ }
+ i++;
+ }
+}
+
void ShaderEditorPlugin::_resource_saved(Object *obj) {
// May have been renamed on save.
for (EditedShader &edited_shader : edited_shaders) {
@@ -430,13 +538,31 @@ void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
}
+void ShaderEditorPlugin::_window_changed(bool p_visible) {
+ make_floating->set_visible(!p_visible);
+}
+
+void ShaderEditorPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ShaderEditorPlugin::_close_builtin_shaders_from_scene));
+ } break;
+ }
+}
+
ShaderEditorPlugin::ShaderEditorPlugin() {
+ window_wrapper = memnew(WindowWrapper);
+ window_wrapper->set_window_title(TTR("Shader Editor - Godot Engine"));
+ window_wrapper->set_margins_enabled(true);
+
main_split = memnew(HSplitContainer);
+ Ref<Shortcut> make_floating_shortcut = ED_SHORTCUT_AND_COMMAND("shader_editor/make_floating", TTR("Make Floating"));
+ window_wrapper->set_wrapped_control(main_split, make_floating_shortcut);
VBoxContainer *vb = memnew(VBoxContainer);
- HBoxContainer *file_hb = memnew(HBoxContainer);
- vb->add_child(file_hb);
+ HBoxContainer *menu_hb = memnew(HBoxContainer);
+ vb->add_child(menu_hb);
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
file_menu->get_popup()->add_item(TTR("New Shader"), FILE_NEW);
@@ -451,12 +577,26 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_item(TTR("Close File"), FILE_CLOSE);
file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
- file_hb->add_child(file_menu);
+ menu_hb->add_child(file_menu);
for (int i = FILE_SAVE; i < FILE_MAX; i++) {
file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true);
}
+ if (window_wrapper->is_window_available()) {
+ Control *padding = memnew(Control);
+ padding->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ menu_hb->add_child(padding);
+
+ make_floating = memnew(ScreenSelect);
+ make_floating->set_flat(true);
+ make_floating->set_tooltip_text(TTR("Make the shader editor floating."));
+ make_floating->connect("request_open_in_screen", callable_mp(window_wrapper, &WindowWrapper::enable_window_on_screen).bind(true));
+
+ menu_hb->add_child(make_floating);
+ window_wrapper->connect("window_visibility_changed", callable_mp(this, &ShaderEditorPlugin::_window_changed));
+ }
+
shader_list = memnew(ItemList);
shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vb->add_child(shader_list);
@@ -475,7 +615,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
empty.instantiate();
shader_tabs->add_theme_style_override("panel", empty);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), main_split);
+ button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), window_wrapper);
// Defer connect because Editor class is not in the binding system yet.
EditorNode::get_singleton()->call_deferred("connect", "resource_saved", callable_mp(this, &ShaderEditorPlugin::_resource_saved), CONNECT_DEFERRED);
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 408d08ade0..45b48a2f91 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -40,6 +40,7 @@ class ShaderCreateDialog;
class TabContainer;
class TextShaderEditor;
class VisualShaderEditor;
+class WindowWrapper;
class ShaderEditorPlugin : public EditorPlugin {
GDCLASS(ShaderEditorPlugin, EditorPlugin);
@@ -74,6 +75,9 @@ class ShaderEditorPlugin : public EditorPlugin {
Button *button = nullptr;
MenuButton *file_menu = nullptr;
+ WindowWrapper *window_wrapper = nullptr;
+ Button *make_floating = nullptr;
+
ShaderCreateDialog *shader_create_dialog = nullptr;
void _update_shader_list();
@@ -82,6 +86,7 @@ class ShaderEditorPlugin : public EditorPlugin {
void _menu_item_pressed(int p_index);
void _resource_saved(Object *obj);
void _close_shader(int p_index);
+ void _close_builtin_shaders_from_scene(const String &p_scene);
void _shader_created(Ref<Shader> p_shader);
void _shader_include_created(Ref<ShaderInclude> p_shader_inc);
@@ -92,6 +97,11 @@ class ShaderEditorPlugin : public EditorPlugin {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ void _window_changed(bool p_visible);
+
+protected:
+ void _notification(int p_what);
+
public:
virtual String get_name() const override { return "Shader"; }
virtual void edit(Object *p_object) override;
@@ -102,6 +112,9 @@ public:
TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader);
+ virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
+ virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
+
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 ad8d93a719..f9aa14dd09 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -124,7 +124,7 @@ void ShaderFileEditor::_update_options() {
int c = versions->get_current();
//remember current
versions->clear();
- Vector<StringName> version_list = shader_file->get_version_list();
+ TypedArray<StringName> version_list = shader_file->get_version_list();
if (c >= version_list.size()) {
c = version_list.size() - 1;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 85906ef9d3..0cfeacc9b1 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -832,8 +832,8 @@ void Skeleton3DEditor::create_editors() {
void Skeleton3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
+ create_editors();
update_joint_tree();
- update_editors();
joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
joint_tree->connect("item_mouse_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
@@ -946,8 +946,6 @@ void fragment() {
handles_mesh_instance->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
handles_mesh.instantiate();
handles_mesh_instance->set_mesh(handles_mesh);
-
- create_editors();
}
void Skeleton3DEditor::update_bone_original() {
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 174588b92e..7a76e4f989 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -288,12 +288,8 @@ void TextEditor::insert_final_newline() {
code_editor->insert_final_newline();
}
-void TextEditor::convert_indent_to_spaces() {
- code_editor->convert_indent_to_spaces();
-}
-
-void TextEditor::convert_indent_to_tabs() {
- code_editor->convert_indent_to_tabs();
+void TextEditor::convert_indent() {
+ code_editor->get_text_editor()->convert_indent();
}
void TextEditor::tag_saved_version() {
@@ -419,10 +415,12 @@ void TextEditor::_edit_option(int p_op) {
trim_trailing_whitespace();
} break;
case EDIT_CONVERT_INDENT_TO_SPACES: {
- convert_indent_to_spaces();
+ tx->set_indent_using_spaces(true);
+ convert_indent();
} break;
case EDIT_CONVERT_INDENT_TO_TABS: {
- convert_indent_to_tabs();
+ tx->set_indent_using_spaces(false);
+ convert_indent();
} break;
case EDIT_TO_UPPERCASE: {
_convert_case(CodeTextEditor::UPPER);
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index d9f5222e90..0c218e9e0c 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -131,8 +131,7 @@ public:
virtual void clear_executing_line() override;
virtual void trim_trailing_whitespace() override;
virtual void insert_final_newline() override;
- virtual void convert_indent_to_spaces() override;
- virtual void convert_indent_to_tabs() override;
+ virtual void convert_indent() override;
virtual void ensure_focus() override;
virtual void tag_saved_version() override;
virtual void update_settings() override;
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index b5854f395a..791746da96 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -1065,11 +1065,6 @@ void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position)
}
TextShaderEditor::TextShaderEditor() {
- GLOBAL_DEF("debug/shader_language/warnings/enable", true);
- GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
- for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
- GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
- }
_update_warnings(false);
shader_editor = memnew(ShaderTextEditor);
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 8327fa5762..ef805e6524 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -41,9 +41,12 @@
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "scene/gui/control.h"
+#include "scene/gui/label.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
+#include "scene/gui/spin_box.h"
#ifdef DEBUG_ENABLED
#include "servers/navigation_server_3d.h"
@@ -168,6 +171,17 @@ void GenericTilePolygonEditor::_base_control_draw() {
base_control->draw_texture_rect_region(background_texture, Rect2(-background_region.size / 2 - background_offset, region_size), background_region, background_modulate, background_transpose);
}
+ // Draw grid.
+ if (current_snap_option == SNAP_GRID) {
+ Vector2 spacing = tile_size / snap_subdivision->get_value();
+ Vector2 offset = -tile_size / 2;
+
+ for (int i = 1; i < snap_subdivision->get_value(); i++) {
+ base_control->draw_line(Vector2(spacing.x * i, 0) + offset, Vector2(spacing.x * i, tile_size.y) + offset, Color(1, 1, 1, 0.33));
+ base_control->draw_line(Vector2(0, spacing.y * i) + offset, Vector2(tile_size.x, spacing.y * i) + offset, Color(1, 1, 1, 0.33));
+ }
+ }
+
// Draw the polygons.
for (const Vector<Vector2> &polygon : polygons) {
Color color = polygon_color;
@@ -195,9 +209,7 @@ void GenericTilePolygonEditor::_base_control_draw() {
Point2 in_creation_point = xform.affine_inverse().xform(base_control->get_local_mouse_position());
float in_creation_distance = grab_threshold * 2.0;
_snap_to_tile_shape(in_creation_point, in_creation_distance, grab_threshold / editor_zoom_widget->get_zoom());
- if (button_pixel_snap->is_pressed()) {
- _snap_to_half_pixel(in_creation_point);
- }
+ _snap_point(in_creation_point);
if (drag_type == DRAG_TYPE_CREATE_POINT && !in_creation_polygon.is_empty()) {
base_control->draw_line(in_creation_polygon[in_creation_polygon.size() - 1], in_creation_point, Color(1.0, 1.0, 1.0));
@@ -443,8 +455,20 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
r_point = snapped_point;
}
-void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) {
- r_point = (r_point * 2).round() / 2.0;
+void GenericTilePolygonEditor::_snap_point(Point2 &r_point) {
+ switch (current_snap_option) {
+ case SNAP_NONE:
+ break;
+
+ case SNAP_HALF_PIXEL:
+ r_point = r_point.snapped(Vector2(0.5, 0.5));
+ break;
+
+ case SNAP_GRID: {
+ const Vector2 tile_size = tile_set->get_tile_size();
+ r_point = (r_point + tile_size / 2).snapped(tile_size / snap_subdivision->get_value()) - tile_size / 2;
+ } break;
+ }
}
void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
@@ -475,9 +499,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
Point2 point = xform.affine_inverse().xform(mm->get_position());
float distance = grab_threshold * 2.0;
_snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom());
- if (button_pixel_snap->is_pressed()) {
- _snap_to_half_pixel(point);
- }
+ _snap_point(point);
polygons[drag_polygon_index].write[drag_point_index] = point;
} else if (drag_type == DRAG_TYPE_PAN) {
panning += mm->get_position() - drag_last_pos;
@@ -592,9 +614,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
Point2 point = xform.affine_inverse().xform(mb->get_position());
float distance = grab_threshold * 2;
_snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom());
- if (button_pixel_snap->is_pressed()) {
- _snap_to_half_pixel(point);
- }
+ _snap_point(point);
in_creation_polygon.push_back(point);
}
drag_type = DRAG_TYPE_NONE;
@@ -652,6 +672,19 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
}
}
+void GenericTilePolygonEditor::_set_snap_option(int p_index) {
+ current_snap_option = p_index;
+ button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(p_index));
+ snap_subdivision->set_visible(p_index == SNAP_GRID);
+ base_control->queue_redraw();
+ _store_snap_options();
+}
+
+void GenericTilePolygonEditor::_store_snap_options() {
+ EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_snap_option", current_snap_option);
+ EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_snap_subdiv", snap_subdivision->get_value());
+}
+
void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {
use_undo_redo = p_use_undo_redo;
}
@@ -766,8 +799,11 @@ void GenericTilePolygonEditor::_notification(int p_what) {
button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
button_delete->set_icon(get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
button_center_view->set_icon(get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
- button_pixel_snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
button_advanced_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+ button_pixel_snap->get_popup()->set_item_icon(0, get_theme_icon(SNAME("SnapDisable"), SNAME("EditorIcons")));
+ button_pixel_snap->get_popup()->set_item_icon(1, get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
+ button_pixel_snap->get_popup()->set_item_icon(2, get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
+ button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(current_snap_option));
PopupMenu *p = button_advanced_menu->get_popup();
p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")));
@@ -833,12 +869,20 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
toolbar->add_child(memnew(VSeparator));
- button_pixel_snap = memnew(Button);
- button_pixel_snap->set_flat(true);
- button_pixel_snap->set_toggle_mode(true);
- button_pixel_snap->set_pressed(true);
- button_pixel_snap->set_tooltip_text(TTR("Snap to half-pixel"));
+ button_pixel_snap = memnew(MenuButton);
toolbar->add_child(button_pixel_snap);
+ button_pixel_snap->set_flat(true);
+ button_pixel_snap->set_tooltip_text(TTR("Toggle Grid Snap"));
+ button_pixel_snap->get_popup()->add_item(TTR("Disable Snap"), SNAP_NONE);
+ button_pixel_snap->get_popup()->add_item(TTR("Half-Pixel Snap"), SNAP_HALF_PIXEL);
+ button_pixel_snap->get_popup()->add_item(TTR("Grid Snap"), SNAP_GRID);
+ button_pixel_snap->get_popup()->connect("index_pressed", callable_mp(this, &GenericTilePolygonEditor::_set_snap_option));
+
+ snap_subdivision = memnew(SpinBox);
+ toolbar->add_child(snap_subdivision);
+ snap_subdivision->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ snap_subdivision->set_min(1);
+ snap_subdivision->set_max(99);
Control *root = memnew(Control);
root->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -859,6 +903,8 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
base_control->set_clip_contents(true);
base_control->set_focus_mode(Control::FOCUS_CLICK);
root->add_child(base_control);
+ snap_subdivision->connect("value_changed", callable_mp((CanvasItem *)base_control, &CanvasItem::queue_redraw).unbind(1));
+ snap_subdivision->connect("value_changed", callable_mp(this, &GenericTilePolygonEditor::_store_snap_options).unbind(1));
editor_zoom_widget = memnew(EditorZoomWidget);
editor_zoom_widget->set_position(Vector2(5, 5));
@@ -873,6 +919,9 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_center_view->set_flat(true);
button_center_view->set_disabled(true);
root->add_child(button_center_view);
+
+ snap_subdivision->set_value_no_signal(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_snap_subdiv", 4));
+ _set_snap_option(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_snap_option", SNAP_NONE));
}
void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) {
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index 1ebf30aecd..b3ecdb8cfb 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -36,10 +36,10 @@
#include "editor/editor_properties.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
-#include "scene/gui/control.h"
-#include "scene/gui/label.h"
class MenuButton;
+class SpinBox;
+class Label;
class EditorUndoRedoManager;
class TileDataEditor : public VBoxContainer {
@@ -120,9 +120,17 @@ private:
Button *button_create = nullptr;
Button *button_edit = nullptr;
Button *button_delete = nullptr;
- Button *button_pixel_snap = nullptr;
MenuButton *button_advanced_menu = nullptr;
+ enum Snap {
+ SNAP_NONE,
+ SNAP_HALF_PIXEL,
+ SNAP_GRID,
+ };
+ int current_snap_option = SNAP_HALF_PIXEL;
+ MenuButton *button_pixel_snap = nullptr;
+ SpinBox *snap_subdivision = nullptr;
+
Vector<Point2> in_creation_polygon;
Panel *panel = nullptr;
@@ -155,9 +163,11 @@ private:
void _advanced_menu_item_pressed(int p_item_pressed);
void _center_view();
void _base_control_gui_input(Ref<InputEvent> p_event);
+ void _set_snap_option(int p_index);
+ void _store_snap_options();
void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist);
- void _snap_to_half_pixel(Point2 &r_point);
+ void _snap_point(Point2 &r_point);
void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index);
void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 55b5abf983..0a8ccdba1a 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -938,6 +938,20 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
}
}
+
+ Ref<Font> font = p_overlay->get_theme_font(SNAME("font"), SNAME("Label"));
+ int font_size = p_overlay->get_theme_font_size(SNAME("font_size"), SNAME("Label"));
+ Point2 msgpos = Point2(20 * EDSCALE, p_overlay->get_size().y - 20 * EDSCALE);
+
+ String text = tile_map->local_to_map(tile_map->get_local_mouse_position());
+ if (drag_type == DRAG_TYPE_RECT) {
+ Vector2i size = tile_map->local_to_map(tile_map->get_local_mouse_position()) - tile_map->local_to_map(drag_start_mouse_pos);
+ text += vformat(" %s (%dx%d)", TTR("Drawing Rect:"), ABS(size.x) + 1, ABS(size.y) + 1);
+ }
+
+ p_overlay->draw_string(font, msgpos + Point2(1, 1), text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
+ p_overlay->draw_string(font, msgpos + Point2(-1, -1), text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
+ p_overlay->draw_string(font, msgpos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
}
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 42c296e825..a8c3b8e8d5 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -34,7 +34,6 @@
#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
-#include "editor/editor_property_name_processor.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
@@ -2415,14 +2414,6 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
}
}
} break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
- EditorPropertyNameProcessor::Style style = EditorPropertyNameProcessor::get_singleton()->get_settings_style();
- atlas_source_inspector->set_property_name_style(style);
- tile_inspector->set_property_name_style(style);
- }
- } break;
}
}
@@ -2492,7 +2483,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_inspector->edit(tile_proxy_object);
tile_inspector->set_use_folding(true);
tile_inspector->connect("property_selected", callable_mp(this, &TileSetAtlasSourceEditor::_inspector_property_selected));
- tile_inspector->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
middle_vbox_container->add_child(tile_inspector);
tile_inspector_no_tile_selected_label = memnew(Label);
@@ -2544,7 +2534,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL);
atlas_source_inspector->set_show_categories(true);
atlas_source_inspector->edit(atlas_source_proxy_object);
- atlas_source_inspector->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
middle_vbox_container->add_child(atlas_source_inspector);
// -- Right side --
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 9350467176..6908dd7c3b 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -32,7 +32,6 @@
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
-#include "editor/editor_property_name_processor.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -365,14 +364,6 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
_update_scenes_list();
_update_action_buttons();
} break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
- EditorPropertyNameProcessor::Style style = EditorPropertyNameProcessor::get_singleton()->get_settings_style();
- scenes_collection_source_inspector->set_property_name_style(style);
- tile_inspector->set_property_name_style(style);
- }
- } break;
}
}
@@ -514,7 +505,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
- scenes_collection_source_inspector->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
middle_vbox_container->add_child(scenes_collection_source_inspector);
// Tile inspector.
@@ -531,7 +521,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tile_inspector->edit(tile_proxy_object);
tile_inspector->set_use_folding(true);
- tile_inspector->set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
middle_vbox_container->add_child(tile_inspector);
// Scenes list.
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b74324ff29..754533ab31 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1209,13 +1209,19 @@ void VisualShaderEditor::clear_custom_types() {
}
}
-void VisualShaderEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend) {
+void VisualShaderEditor::add_custom_type(const String &p_name, const String &p_type, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend) {
ERR_FAIL_COND(!p_name.is_valid_identifier());
- ERR_FAIL_COND(!p_script.is_valid());
+ ERR_FAIL_COND(p_type.is_empty() && !p_script.is_valid());
for (int i = 0; i < add_options.size(); i++) {
- if (add_options[i].is_custom) {
- if (add_options[i].script == p_script) {
+ const AddOption &op = add_options[i];
+
+ if (op.is_custom) {
+ if (!p_type.is_empty()) {
+ if (op.type == p_type) {
+ return;
+ }
+ } else if (op.script == p_script) {
return;
}
}
@@ -1223,12 +1229,14 @@ void VisualShaderEditor::add_custom_type(const String &p_name, const Ref<Script>
AddOption ao;
ao.name = p_name;
+ ao.type = p_type;
ao.script = p_script;
ao.return_type = p_return_icon_type;
ao.description = p_description;
ao.category = p_category;
ao.highend = p_highend;
ao.is_custom = true;
+ ao.is_native = !p_type.is_empty();
bool begin = false;
String root = p_category.split("/")[0];
@@ -1253,50 +1261,23 @@ void VisualShaderEditor::add_custom_type(const String &p_name, const Ref<Script>
Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node) {
Dictionary dict;
dict["script"] = p_custom_node->get_script();
+ dict["name"] = p_custom_node->_get_name();
+ dict["description"] = p_custom_node->_get_description();
+ dict["return_icon_type"] = p_custom_node->_get_return_icon_type();
+ dict["highend"] = p_custom_node->_is_highend();
- String name;
- if (p_custom_node->has_method("_get_name")) {
- name = (String)p_custom_node->call("_get_name");
- } else {
- name = "Unnamed";
- }
- dict["name"] = name;
-
- String description = "";
- if (p_custom_node->has_method("_get_description")) {
- description = (String)p_custom_node->call("_get_description");
- }
- dict["description"] = description;
-
- int return_icon_type = -1;
- if (p_custom_node->has_method("_get_return_icon_type")) {
- return_icon_type = (int)p_custom_node->call("_get_return_icon_type");
- }
- dict["return_icon_type"] = return_icon_type;
-
- String category = "";
- if (p_custom_node->has_method("_get_category")) {
- category = (String)p_custom_node->call("_get_category");
- }
+ String category = p_custom_node->_get_category();
category = category.rstrip("/");
category = category.lstrip("/");
category = "Addons/" + category;
-
- String subcategory = "";
if (p_custom_node->has_method("_get_subcategory")) {
- subcategory = (String)p_custom_node->call("_get_subcategory");
- }
- if (!subcategory.is_empty()) {
- category += "/" + subcategory;
+ String subcategory = (String)p_custom_node->call("_get_subcategory");
+ if (!subcategory.is_empty()) {
+ category += "/" + subcategory;
+ }
}
dict["category"] = category;
- bool highend = false;
- if (p_custom_node->has_method("_is_highend")) {
- highend = (bool)p_custom_node->call("_is_highend");
- }
- dict["highend"] = highend;
-
return dict;
}
@@ -1333,7 +1314,7 @@ void VisualShaderEditor::_script_created(const Ref<Script> &p_script) {
ref->set_script(p_script);
Dictionary dict = get_custom_node_data(ref);
- add_custom_type(dict["name"], dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]);
+ add_custom_type(dict["name"], String(), dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]);
_update_options_menu();
}
@@ -1456,7 +1437,7 @@ void VisualShaderEditor::_update_custom_script(const Ref<Script> &p_script) {
}
if (!found_type) {
- add_custom_type(dict["name"], dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]);
+ add_custom_type(dict["name"], String(), dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]);
}
// To prevent updating options multiple times when multiple scripts are saved.
@@ -1595,29 +1576,60 @@ bool VisualShaderEditor::_is_available(int p_mode) {
void VisualShaderEditor::_update_nodes() {
clear_custom_types();
- List<StringName> class_list;
- ScriptServer::get_global_class_list(&class_list);
Dictionary added;
- for (int i = 0; i < class_list.size(); i++) {
- if (ScriptServer::get_global_class_native_base(class_list[i]) == "VisualShaderNodeCustom") {
- String script_path = ScriptServer::get_global_class_path(class_list[i]);
- Ref<Resource> res = ResourceLoader::load(script_path);
- ERR_FAIL_COND(res.is_null());
- ERR_FAIL_COND(!res->is_class("Script"));
- Ref<Script> scr = Ref<Script>(res);
-
- Ref<VisualShaderNodeCustom> ref;
- ref.instantiate();
- ref->set_script(scr);
- if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
- continue;
+
+ // Add GDScript classes.
+ {
+ List<StringName> class_list;
+ ScriptServer::get_global_class_list(&class_list);
+
+ for (int i = 0; i < class_list.size(); i++) {
+ if (ScriptServer::get_global_class_native_base(class_list[i]) == "VisualShaderNodeCustom") {
+ String script_path = ScriptServer::get_global_class_path(class_list[i]);
+ Ref<Resource> res = ResourceLoader::load(script_path);
+ ERR_CONTINUE(res.is_null());
+ ERR_CONTINUE(!res->is_class("Script"));
+ Ref<Script> scr = Ref<Script>(res);
+
+ Ref<VisualShaderNodeCustom> ref;
+ ref.instantiate();
+ ref->set_script(scr);
+ if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+ continue;
+ }
+ Dictionary dict = get_custom_node_data(ref);
+ dict["type"] = String();
+
+ String key;
+ key = String(dict["category"]) + "/" + String(dict["name"]);
+
+ added[key] = dict;
}
- Dictionary dict = get_custom_node_data(ref);
+ }
+ }
- String key;
- key = String(dict["category"]) + "/" + String(dict["name"]);
+ // Add GDExtension classes.
+ {
+ List<StringName> class_list;
+ ClassDB::get_class_list(&class_list);
+
+ for (int i = 0; i < class_list.size(); i++) {
+ if (ClassDB::get_parent_class(class_list[i]) == "VisualShaderNodeCustom") {
+ Object *instance = ClassDB::instantiate(class_list[i]);
+ Ref<VisualShaderNodeCustom> ref = Object::cast_to<VisualShaderNodeCustom>(instance);
+ ERR_CONTINUE(ref.is_null());
+ if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) {
+ continue;
+ }
+ Dictionary dict = get_custom_node_data(ref);
+ dict["type"] = class_list[i];
+ dict["script"] = Ref<Script>();
- added[key] = dict;
+ String key;
+ key = String(dict["category"]) + "/" + String(dict["name"]);
+
+ added[key] = dict;
+ }
}
}
@@ -1655,7 +1667,7 @@ void VisualShaderEditor::_update_nodes() {
const Dictionary &value = (Dictionary)added[key];
- add_custom_type(value["name"], value["script"], value["description"], value["return_icon_type"], value["category"], value["highend"]);
+ add_custom_type(value["name"], value["type"], value["script"], value["description"], value["return_icon_type"], value["category"], value["highend"]);
}
_update_options_menu();
@@ -3062,12 +3074,21 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
vsnode = Ref<VisualShaderNode>(vsn);
} else {
- ERR_FAIL_COND(add_options[p_idx].script.is_null());
- StringName base_type = add_options[p_idx].script->get_instance_base_type();
+ StringName base_type;
+ bool is_native = add_options[p_idx].is_native;
+
+ if (is_native) {
+ base_type = add_options[p_idx].type;
+ } else {
+ ERR_FAIL_COND(add_options[p_idx].script.is_null());
+ base_type = add_options[p_idx].script->get_instance_base_type();
+ }
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(base_type));
ERR_FAIL_COND(!vsn);
vsnode = Ref<VisualShaderNode>(vsn);
- vsnode->set_script(add_options[p_idx].script);
+ if (!is_native) {
+ vsnode->set_script(add_options[p_idx].script);
+ }
}
bool is_texture2d = (Object::cast_to<VisualShaderNodeTexture>(vsnode.ptr()) != nullptr);
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 21139fbddd..bdb23afa0f 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -310,6 +310,7 @@ class VisualShaderEditor : public VBoxContainer {
int func = 0;
bool highend = false;
bool is_custom = false;
+ bool is_native = false;
int temp_idx = 0;
AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String(), const String &p_description = String(), const Vector<Variant> &p_ops = Vector<Variant>(), int p_return_type = -1, int p_mode = -1, int p_func = -1, bool p_highend = false) {
@@ -527,9 +528,10 @@ public:
VisualShaderGraphPlugin *get_graph_plugin() { return graph_plugin.ptr(); }
void clear_custom_types();
- void add_custom_type(const String &p_name, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend);
+ void add_custom_type(const String &p_name, const String &p_type, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend);
Dictionary get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node);
+ void update_custom_type(const Ref<Resource> &p_resource);
virtual Size2 get_minimum_size() const override;
void edit(VisualShader *p_visual_shader);
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 1cd863fb8a..95cf9496dc 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_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/export/editor_export.h"
#include "scene/gui/check_button.h"
#include "servers/movie_writer/movie_writer.h"
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index ec323b5853..011aef5e83 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -138,7 +138,7 @@ void PropertySelector::_update_search() {
search_options->get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
search_options->get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
search_options->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")),
- search_options->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), // Vector4i, needs icon.
+ search_options->get_theme_icon(SNAME("Vector4i"), SNAME("EditorIcons")),
search_options->get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
search_options->get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
search_options->get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index 1c75f86f7c..758565b266 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -39,11 +39,13 @@
#include "editor/editor_paths.h"
#include "editor/editor_resource_picker.h"
#include "editor/editor_resource_preview.h"
-#include "editor/editor_run_script.h"
+#include "editor/editor_script.h"
#include "editor/editor_settings.h"
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/export/editor_export_platform.h"
#include "editor/export/editor_export_platform_pc.h"
+#include "editor/export/editor_export_plugin.h"
#include "editor/filesystem_dock.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_spin_slider.h"
@@ -63,6 +65,7 @@
#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
#include "editor/plugins/curve_editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/editor_resource_tooltip_plugins.h"
#include "editor/plugins/font_config_plugin.h"
#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
@@ -79,6 +82,8 @@
#include "editor/plugins/mesh_library_editor_plugin.h"
#include "editor/plugins/multimesh_editor_plugin.h"
#include "editor/plugins/navigation_link_2d_editor_plugin.h"
+#include "editor/plugins/navigation_obstacle_2d_editor_plugin.h"
+#include "editor/plugins/navigation_obstacle_3d_editor_plugin.h"
#include "editor/plugins/navigation_polygon_editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
@@ -125,6 +130,7 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorNode3DGizmo);
GDREGISTER_CLASS(EditorNode3DGizmoPlugin);
GDREGISTER_ABSTRACT_CLASS(EditorResourcePreview);
+ GDREGISTER_ABSTRACT_CLASS(EditorResourceTooltipPlugin);
GDREGISTER_CLASS(EditorResourcePreviewGenerator);
GDREGISTER_ABSTRACT_CLASS(EditorFileSystem);
GDREGISTER_CLASS(EditorFileSystemDirectory);
@@ -136,7 +142,9 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorExportPlugin);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformPC);
+
register_exporter_types();
+
GDREGISTER_CLASS(EditorResourceConversionPlugin);
GDREGISTER_CLASS(EditorSceneFormatImporter);
GDREGISTER_CLASS(EditorScenePostImportPlugin);
@@ -181,6 +189,7 @@ void register_editor_types() {
EditorPlugins::add_by_type<MeshInstance3DEditorPlugin>();
EditorPlugins::add_by_type<MeshLibraryEditorPlugin>();
EditorPlugins::add_by_type<MultiMeshEditorPlugin>();
+ EditorPlugins::add_by_type<NavigationObstacle3DEditorPlugin>();
EditorPlugins::add_by_type<OccluderInstance3DEditorPlugin>();
EditorPlugins::add_by_type<PackedSceneEditorPlugin>();
EditorPlugins::add_by_type<Path3DEditorPlugin>();
@@ -209,6 +218,7 @@ void register_editor_types() {
EditorPlugins::add_by_type<LightOccluder2DEditorPlugin>();
EditorPlugins::add_by_type<Line2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationLink2DEditorPlugin>();
+ EditorPlugins::add_by_type<NavigationObstacle2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationPolygonEditorPlugin>();
EditorPlugins::add_by_type<Path2DEditorPlugin>();
EditorPlugins::add_by_type<Polygon2DEditorPlugin>();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 968cd5ab23..9e231a41c3 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -53,6 +53,7 @@
#include "editor/plugins/script_editor_plugin.h"
#include "editor/reparent_dialog.h"
#include "editor/shader_create_dialog.h"
+#include "scene/gui/check_box.h"
#include "scene/main/window.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
@@ -275,6 +276,8 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
return;
}
+ instantiated_scene->set_unique_name_in_owner(base->is_unique_name_in_owner());
+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Replace with Branch Scene"));
@@ -850,9 +853,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- if (p_confirm_override) {
+ bool allow_ask_delete_tracks = EDITOR_GET("docks/scene_tree/ask_before_deleting_related_animation_tracks").operator bool();
+ bool has_tracks_to_delete = allow_ask_delete_tracks && _has_tracks_to_delete(edited_scene, remove_list);
+ if (p_confirm_override && !has_tracks_to_delete) {
_delete_confirm();
-
} else {
String msg;
if (remove_list.size() > 1) {
@@ -863,18 +867,30 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
msg = vformat(any_children ? TTR("Delete %d nodes and any children?") : TTR("Delete %d nodes?"), remove_list.size());
} else {
- Node *node = remove_list[0];
- if (node == editor_data->get_edited_scene_root()) {
- msg = vformat(TTR("Delete the root node \"%s\"?"), node->get_name());
- } else if (node->get_scene_file_path().is_empty() && node->get_child_count() > 0) {
- // Display this message only for non-instantiated scenes
- msg = vformat(TTR("Delete node \"%s\" and its children?"), node->get_name());
+ if (!p_confirm_override) {
+ Node *node = remove_list[0];
+ if (node == editor_data->get_edited_scene_root()) {
+ msg = vformat(TTR("Delete the root node \"%s\"?"), node->get_name());
+ } else if (node->get_scene_file_path().is_empty() && node->get_child_count() > 0) {
+ // Display this message only for non-instantiated scenes
+ msg = vformat(TTR("Delete node \"%s\" and its children?"), node->get_name());
+ } else {
+ msg = vformat(TTR("Delete node \"%s\"?"), node->get_name());
+ }
+ }
+
+ if (has_tracks_to_delete) {
+ if (!msg.is_empty()) {
+ msg += "\n";
+ }
+ msg += TTR("Some nodes are referenced by animation tracks.");
+ delete_tracks_checkbox->show();
} else {
- msg = vformat(TTR("Delete node \"%s\"?"), node->get_name());
+ delete_tracks_checkbox->hide();
}
}
- delete_dialog->set_text(msg);
+ delete_dialog_label->set_text(msg);
// Resize the dialog to its minimum size.
// This prevents the dialog from being too wide after displaying
@@ -1496,12 +1512,10 @@ void SceneTreeDock::_set_owners(Node *p_owner, const Array &p_nodes) {
void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames) {
base_path.push_back(p_node->get_name());
- if (new_base_path.size()) {
- new_base_path.push_back(p_node->get_name());
- }
NodePath new_path;
- if (new_base_path.size()) {
+ if (!new_base_path.is_empty()) {
+ new_base_path.push_back(p_node->get_name());
new_path = NodePath(new_base_path, true);
}
@@ -1512,6 +1526,43 @@ void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<Stri
}
}
+bool SceneTreeDock::_has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
+ if (ap) {
+ Node *root = ap->get_node(ap->get_root());
+ if (root && !p_to_delete.find(root)) {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+
+ for (const StringName &E : anims) {
+ Ref<Animation> anim = ap->get_animation(E);
+ if (anim.is_null()) {
+ continue;
+ }
+
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ NodePath track_np = anim->track_get_path(i);
+ Node *n = root->get_node_or_null(track_np);
+ if (n) {
+ for (const Node *F : p_to_delete) {
+ if (F == n || F->is_ancestor_of(n)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ if (_has_tracks_to_delete(p_node->get_child(i), p_to_delete)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames) {
Vector<StringName> base_path;
Node *n = p_node->get_parent();
@@ -1701,7 +1752,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (found_path) {
- if (found_path->value == NodePath()) {
+ if (found_path->value.is_empty()) {
//will be erased
int idx = 0;
@@ -2094,11 +2145,6 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
return;
}
- EditorNode::get_singleton()->hide_unused_editors(this);
-
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(p_cut ? TTR("Cut Node(s)") : TTR("Remove Node(s)"), UndoRedo::MERGE_DISABLE, remove_list.front()->get());
-
bool entire_scene = false;
for (const Node *E : remove_list) {
@@ -2108,27 +2154,34 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
}
}
+ EditorNode::get_singleton()->hide_unused_editors(this);
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(p_cut ? TTR("Cut Node(s)") : TTR("Remove Node(s)"), UndoRedo::MERGE_DISABLE, remove_list.front()->get());
+
if (entire_scene) {
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene);
undo_redo->add_undo_method(edited_scene, "set_owner", edited_scene->get_owner());
undo_redo->add_undo_method(scene_tree, "update_tree");
undo_redo->add_undo_reference(edited_scene);
-
} else {
- remove_list.sort_custom<Node::Comparator>(); //sort nodes to keep positions
- HashMap<Node *, NodePath> path_renames;
+ if (delete_tracks_checkbox->is_pressed() || p_cut) {
+ remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
+ HashMap<Node *, NodePath> path_renames;
- //delete from animation
- for (Node *n : remove_list) {
- if (!n->is_inside_tree() || !n->get_parent()) {
- continue;
+ //delete from animation
+ for (Node *n : remove_list) {
+ if (!n->is_inside_tree() || !n->get_parent()) {
+ continue;
+ }
+
+ fill_path_renames(n, nullptr, &path_renames);
}
- fill_path_renames(n, nullptr, &path_renames);
+ perform_node_renames(nullptr, &path_renames);
}
- perform_node_renames(nullptr, &path_renames);
//delete for read
for (Node *n : remove_list) {
if (!n->is_inside_tree() || !n->get_parent()) {
@@ -3774,6 +3827,16 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
add_child(delete_dialog);
delete_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_delete_confirm).bind(false));
+ VBoxContainer *vb = memnew(VBoxContainer);
+ delete_dialog->add_child(vb);
+
+ delete_dialog_label = memnew(Label);
+ vb->add_child(delete_dialog_label);
+
+ delete_tracks_checkbox = memnew(CheckBox(TTR("Delete Related Animation Tracks")));
+ delete_tracks_checkbox->set_pressed(true);
+ vb->add_child(delete_tracks_checkbox);
+
editable_instance_remove_dialog = memnew(ConfirmationDialog);
add_child(editable_instance_remove_dialog);
editable_instance_remove_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_toggle_editable_children_from_selection));
@@ -3810,6 +3873,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
+ EDITOR_DEF("docks/scene_tree/ask_before_deleting_related_animation_tracks", true);
EDITOR_DEF("_use_favorites_root_selection", false);
Resource::_update_configuration_warning = _update_configuration_warning;
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 75acb37973..e8a6686386 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -36,6 +36,7 @@
#include "scene/gui/box_container.h"
#include "scene/resources/animation.h"
+class CheckBox;
class EditorData;
class EditorSelection;
class EditorQuickOpen;
@@ -148,6 +149,8 @@ class SceneTreeDock : public VBoxContainer {
ShaderCreateDialog *shader_create_dialog = nullptr;
AcceptDialog *accept = nullptr;
ConfirmationDialog *delete_dialog = nullptr;
+ Label *delete_dialog_label = nullptr;
+ CheckBox *delete_tracks_checkbox = nullptr;
ConfirmationDialog *editable_instance_remove_dialog = nullptr;
ConfirmationDialog *placeholder_editable_instance_remove_dialog = nullptr;
@@ -213,6 +216,7 @@ class SceneTreeDock : public VBoxContainer {
void _shader_creation_closed();
void _delete_confirm(bool p_cut = false);
+ void _delete_dialog_closed();
void _toggle_editable_children_from_selection();
void _toggle_editable_children(Node *p_node);
@@ -234,6 +238,7 @@ class SceneTreeDock : public VBoxContainer {
void _update_script_button();
void _fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames);
+ bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;
void _normalize_drop(Node *&to_node, int &to_pos, int p_type);
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index f3f0e7308c..23272ab284 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -894,9 +894,9 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
if (line.begins_with("space-indent")) {
String indent_value = line.substr(17, -1).strip_edges();
if (indent_value.is_valid_int()) {
- space_indent = "";
- for (int i = 0; i < indent_value.to_int(); i++) {
- space_indent += " ";
+ int indent_size = indent_value.to_int();
+ if (indent_size >= 0) {
+ space_indent = String(" ").repeat(indent_size);
}
} else {
WARN_PRINT(vformat("Template meta-use_space_indent need to be a valid integer value. Found %s.", indent_value));
diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp
new file mode 100644
index 0000000000..21b0a1a21d
--- /dev/null
+++ b/editor/window_wrapper.cpp
@@ -0,0 +1,474 @@
+/**************************************************************************/
+/* window_wrapper.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 "window_wrapper.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/panel.h"
+#include "scene/gui/popup.h"
+#include "scene/main/window.h"
+
+// WindowWrapper
+
+// Capture all shortcut events not handled by other nodes.
+class ShortcutBin : public Node {
+ GDCLASS(ShortcutBin, Node);
+
+ virtual void _notification(int what) {
+ switch (what) {
+ case NOTIFICATION_READY:
+ set_process_shortcut_input(true);
+ break;
+ }
+ }
+
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override {
+ if (!get_window()->is_visible()) {
+ return;
+ }
+ Window *grandparent_window = get_window()->get_parent_visible_window();
+ ERR_FAIL_COND(!grandparent_window);
+
+ if (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventShortcut>(p_event.ptr())) {
+ // HACK: Propagate the window input to the editor main window to handle global shortcuts.
+ grandparent_window->push_input(p_event);
+
+ if (grandparent_window->is_input_handled()) {
+ get_viewport()->set_input_as_handled();
+ }
+ }
+ }
+};
+
+Rect2 WindowWrapper::_get_default_window_rect() const {
+ // Assume that the control rect is the desidered one for the window.
+ return wrapped_control->get_screen_rect();
+}
+
+Node *WindowWrapper::_get_wrapped_control_parent() const {
+ if (margins) {
+ return margins;
+ }
+ return window;
+}
+
+void WindowWrapper::_set_window_enabled_with_rect(bool p_visible, const Rect2 p_rect) {
+ ERR_FAIL_NULL(wrapped_control);
+
+ if (!is_window_available()) {
+ return;
+ }
+
+ if (window->is_visible() == p_visible) {
+ if (p_visible) {
+ window->grab_focus();
+ }
+ return;
+ }
+
+ Node *parent = _get_wrapped_control_parent();
+
+ if (wrapped_control->get_parent() != parent) {
+ // Move the control to the window.
+ wrapped_control->reparent(parent, false);
+
+ _set_window_rect(p_rect);
+ wrapped_control->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+
+ } else if (!p_visible) {
+ // Remove control from window.
+ wrapped_control->reparent(this, false);
+ }
+
+ window->set_visible(p_visible);
+ if (!p_visible) {
+ emit_signal("window_close_requested");
+ }
+ emit_signal("window_visibility_changed", p_visible);
+}
+
+void WindowWrapper::_set_window_rect(const Rect2 p_rect) {
+ // Set the window rect even when the window is maximized to have a good default size
+ // when the user remove the maximized mode.
+ window->set_position(p_rect.position);
+ window->set_size(p_rect.size);
+
+ if (EDITOR_GET("interface/multi_window/maximize_window")) {
+ window->set_mode(Window::MODE_MAXIMIZED);
+ }
+}
+
+void WindowWrapper::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("window_visibility_changed", PropertyInfo(Variant::BOOL, "visible")));
+ ADD_SIGNAL(MethodInfo("window_close_requested"));
+}
+
+void WindowWrapper::_notification(int p_what) {
+ if (!is_window_available()) {
+ return;
+ }
+ switch (p_what) {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (get_window_enabled() && is_visible()) {
+ // Grab the focus when WindowWrapper.set_visible(true) is called
+ // and the window is showing.
+ window->grab_focus();
+ }
+ } break;
+ case NOTIFICATION_READY: {
+ set_process_shortcut_input(true);
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ window_background->add_theme_style_override("panel", get_theme_stylebox("PanelForeground", "EditorStyles"));
+ } break;
+ }
+}
+
+void WindowWrapper::shortcut_input(const Ref<InputEvent> &p_event) {
+ if (enable_shortcut.is_valid() && enable_shortcut->matches_event(p_event)) {
+ set_window_enabled(true);
+ }
+}
+
+void WindowWrapper::set_wrapped_control(Control *p_control, const Ref<Shortcut> &p_enable_shortcut) {
+ ERR_FAIL_NULL(p_control);
+ ERR_FAIL_COND(wrapped_control);
+
+ wrapped_control = p_control;
+ enable_shortcut = p_enable_shortcut;
+ add_child(p_control);
+}
+
+Control *WindowWrapper::get_wrapped_control() const {
+ return wrapped_control;
+}
+
+Control *WindowWrapper::release_wrapped_control() {
+ set_window_enabled(false);
+ if (wrapped_control) {
+ Control *old_wrapped = wrapped_control;
+ wrapped_control->get_parent()->remove_child(wrapped_control);
+ wrapped_control = nullptr;
+
+ return old_wrapped;
+ }
+ return nullptr;
+}
+
+bool WindowWrapper::is_window_available() const {
+ return window != nullptr;
+}
+
+bool WindowWrapper::get_window_enabled() const {
+ return is_window_available() ? window->is_visible() : false;
+}
+
+void WindowWrapper::set_window_enabled(bool p_enabled) {
+ _set_window_enabled_with_rect(p_enabled, _get_default_window_rect());
+}
+
+Rect2i WindowWrapper::get_window_rect() const {
+ ERR_FAIL_COND_V(!get_window_enabled(), Rect2i());
+ return Rect2i(window->get_position(), window->get_size());
+}
+
+int WindowWrapper::get_window_screen() const {
+ ERR_FAIL_COND_V(!get_window_enabled(), -1);
+ return window->get_current_screen();
+}
+
+void WindowWrapper::restore_window(const Rect2i &p_rect, int p_screen) {
+ ERR_FAIL_COND(!is_window_available());
+ ERR_FAIL_INDEX(p_screen, DisplayServer::get_singleton()->get_screen_count());
+
+ _set_window_enabled_with_rect(true, p_rect);
+ window->set_current_screen(p_screen);
+}
+
+void WindowWrapper::restore_window_from_saved_position(const Rect2 p_window_rect, int p_screen, const Rect2 p_screen_rect) {
+ ERR_FAIL_COND(!is_window_available());
+
+ Rect2 window_rect = p_window_rect;
+ int screen = p_screen;
+ Rect2 restored_screen_rect = p_screen_rect;
+
+ if (screen < 0 || screen >= DisplayServer::get_singleton()->get_screen_count()) {
+ // Fallback to the main window screen if the saved screen is not available.
+ screen = get_window()->get_window_id();
+ }
+
+ Rect2i real_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
+
+ if (restored_screen_rect == Rect2i()) {
+ // Fallback to the target screen rect.
+ restored_screen_rect = real_screen_rect;
+ }
+
+ if (window_rect == Rect2i()) {
+ // Fallback to a standard rect.
+ window_rect = Rect2i(restored_screen_rect.position + restored_screen_rect.size / 4, restored_screen_rect.size / 2);
+ }
+
+ // Adjust the window rect size in case the resolution changes.
+ Vector2 screen_ratio = Vector2(real_screen_rect.size) / Vector2(restored_screen_rect.size);
+
+ // The screen positioning may change, so remove the original screen position.
+ window_rect.position -= restored_screen_rect.position;
+ window_rect = Rect2i(window_rect.position * screen_ratio, window_rect.size * screen_ratio);
+ window_rect.position += real_screen_rect.position;
+
+ // All good, restore the window.
+ window->set_current_screen(p_screen);
+ if (window->is_visible()) {
+ _set_window_rect(window_rect);
+ } else {
+ _set_window_enabled_with_rect(true, window_rect);
+ }
+}
+
+void WindowWrapper::enable_window_on_screen(int p_screen, bool p_auto_scale) {
+ int current_screen = Object::cast_to<Window>(get_viewport())->get_current_screen();
+ int screen = p_screen < 0 ? current_screen : p_screen;
+
+ bool auto_scale = p_auto_scale && !EDITOR_GET("interface/multi_window/maximize_window");
+
+ if (auto_scale && current_screen != screen) {
+ Rect2 control_rect = _get_default_window_rect();
+
+ Rect2i source_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(current_screen);
+ Rect2i dest_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
+
+ // Adjust the window rect size in case the resolution changes.
+ Vector2 screen_ratio = Vector2(source_screen_rect.size) / Vector2(dest_screen_rect.size);
+
+ // The screen positioning may change, so remove the original screen position.
+ control_rect.position -= source_screen_rect.position;
+ control_rect = Rect2i(control_rect.position * screen_ratio, control_rect.size * screen_ratio);
+ control_rect.position += dest_screen_rect.position;
+
+ restore_window(control_rect, p_screen);
+ } else {
+ window->set_current_screen(p_screen);
+ set_window_enabled(true);
+ }
+}
+
+void WindowWrapper::set_window_title(const String p_title) {
+ if (!is_window_available()) {
+ return;
+ }
+ window->set_title(p_title);
+}
+
+void WindowWrapper::set_margins_enabled(bool p_enabled) {
+ if (!is_window_available()) {
+ return;
+ }
+
+ if (!p_enabled && margins) {
+ margins->queue_free();
+ margins = nullptr;
+ } else if (p_enabled && !margins) {
+ Size2 borders = Size2(4, 4) * EDSCALE;
+ margins = memnew(MarginContainer);
+ margins->add_theme_constant_override("margin_right", borders.width);
+ margins->add_theme_constant_override("margin_top", borders.height);
+ margins->add_theme_constant_override("margin_left", borders.width);
+ margins->add_theme_constant_override("margin_bottom", borders.height);
+
+ window->add_child(margins);
+ margins->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ }
+}
+
+WindowWrapper::WindowWrapper() {
+ if (SceneTree::get_singleton()->get_root()->is_embedding_subwindows() || !EDITOR_GET("interface/multi_window/enable")) {
+ return;
+ }
+
+ window = memnew(Window);
+ window->set_wrap_controls(true);
+
+ add_child(window);
+ window->hide();
+
+ window->connect("close_requested", callable_mp(this, &WindowWrapper::set_window_enabled).bind(false));
+
+ ShortcutBin *capturer = memnew(ShortcutBin);
+ window->add_child(capturer);
+
+ window_background = memnew(Panel);
+ window_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ window->add_child(window_background);
+}
+
+// ScreenSelect
+
+void ScreenSelect::_build_advanced_menu() {
+ // Clear old screen list.
+ while (screen_list->get_child_count(false) > 0) {
+ Node *child = screen_list->get_child(0);
+ screen_list->remove_child(child);
+ child->queue_free();
+ }
+
+ // Populate screen list.
+ const real_t height = real_t(get_theme_font_size("font_size")) * 1.5;
+
+ int current_screen = get_window()->get_current_screen();
+ for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
+ Button *button = memnew(Button);
+
+ Size2 screen_size = Size2(DisplayServer::get_singleton()->screen_get_size(i));
+ Size2 button_size = Size2(height * (screen_size.x / screen_size.y), height);
+ button->set_custom_minimum_size(button_size);
+ screen_list->add_child(button);
+
+ button->set_text(itos(i));
+ button->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ button->set_tooltip_text(vformat(TTR("Make this panel floating in the screen %d."), i));
+
+ if (i == current_screen) {
+ Color accent_color = get_theme_color("accent_color", "Editor");
+ button->add_theme_color_override("font_color", accent_color);
+ }
+
+ button->connect("pressed", callable_mp(this, &ScreenSelect::_emit_screen_signal).bind(i));
+ button->connect("pressed", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
+ button->connect("pressed", callable_mp(static_cast<Window *>(popup), &Popup::hide));
+ }
+}
+
+void ScreenSelect::_emit_screen_signal(int p_screen_idx) {
+ emit_signal("request_open_in_screen", p_screen_idx);
+}
+
+void ScreenSelect::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("request_open_in_screen", PropertyInfo(Variant::INT, "screen")));
+}
+
+void ScreenSelect::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ connect("gui_input", callable_mp(this, &ScreenSelect::_handle_mouse_shortcut));
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("MakeFloating", "EditorIcons"));
+ popup_background->add_theme_style_override("panel", get_theme_stylebox("PanelForeground", "EditorStyles"));
+
+ const real_t popup_height = real_t(get_theme_font_size("font_size")) * 2.0;
+ popup->set_min_size(Size2(0, popup_height * 3));
+ } break;
+ }
+}
+
+void ScreenSelect::_handle_mouse_shortcut(const Ref<InputEvent> &p_event) {
+ const Ref<InputEventMouseButton> mouse_button = p_event;
+ if (mouse_button.is_valid()) {
+ if (mouse_button->is_pressed() && mouse_button->get_button_index() == MouseButton::LEFT) {
+ _emit_screen_signal(get_window()->get_current_screen());
+ accept_event();
+ }
+ }
+}
+
+void ScreenSelect::_show_popup() {
+ // Adapted from /scene/gui/menu_button.cpp::show_popup
+ if (!get_viewport()) {
+ return;
+ }
+
+ Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
+
+ popup->set_size(Size2(size.width, 0));
+ Point2 gp = get_screen_position();
+ gp.y += size.y;
+ if (is_layout_rtl()) {
+ gp.x += size.width - popup->get_size().width;
+ }
+ popup->set_position(gp);
+ popup->popup();
+}
+
+void ScreenSelect::pressed() {
+ if (popup->is_visible()) {
+ popup->hide();
+ return;
+ }
+
+ _build_advanced_menu();
+ _show_popup();
+}
+
+ScreenSelect::ScreenSelect() {
+ set_text(TTR("Make Floating"));
+ set_tooltip_text(TTR("Make this panel floating.\nRight click to open the screen selector."));
+ set_button_mask(MouseButtonMask::RIGHT);
+ set_flat(true);
+ set_toggle_mode(true);
+ set_focus_mode(FOCUS_NONE);
+ set_action_mode(ACTION_MODE_BUTTON_PRESS);
+
+ // Create the popup.
+ const Size2 borders = Size2(4, 4) * EDSCALE;
+
+ popup = memnew(Popup);
+ popup->connect("popup_hide", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
+ add_child(popup);
+
+ popup_background = memnew(Panel);
+ popup_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ popup->add_child(popup_background);
+
+ MarginContainer *popup_root = memnew(MarginContainer);
+ popup_root->add_theme_constant_override("margin_right", borders.width);
+ popup_root->add_theme_constant_override("margin_top", borders.height);
+ popup_root->add_theme_constant_override("margin_left", borders.width);
+ popup_root->add_theme_constant_override("margin_bottom", borders.height);
+ popup->add_child(popup_root);
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+ popup_root->add_child(vb);
+
+ Label *description = memnew(Label(TTR("Select Screen")));
+ description->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ vb->add_child(description);
+
+ screen_list = memnew(HBoxContainer);
+ screen_list->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+ vb->add_child(screen_list);
+
+ popup_root->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+}
diff --git a/editor/window_wrapper.h b/editor/window_wrapper.h
new file mode 100644
index 0000000000..e8fcb13c92
--- /dev/null
+++ b/editor/window_wrapper.h
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* window_wrapper.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 WINDOW_WRAPPER_H
+#define WINDOW_WRAPPER_H
+
+#include "core/math/rect2.h"
+#include "scene/gui/margin_container.h"
+#include "scene/gui/menu_button.h"
+
+class Window;
+class HBoxContainer;
+
+class WindowWrapper : public MarginContainer {
+ GDCLASS(WindowWrapper, MarginContainer);
+
+ Control *wrapped_control = nullptr;
+ MarginContainer *margins = nullptr;
+ Window *window = nullptr;
+
+ Panel *window_background = nullptr;
+
+ Ref<Shortcut> enable_shortcut;
+
+ Rect2 _get_default_window_rect() const;
+ Node *_get_wrapped_control_parent() const;
+
+ void _set_window_enabled_with_rect(bool p_visible, const Rect2 p_rect);
+ void _set_window_rect(const Rect2 p_rect);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
+
+public:
+ void set_wrapped_control(Control *p_control, const Ref<Shortcut> &p_enable_shortcut = Ref<Shortcut>());
+ Control *get_wrapped_control() const;
+ Control *release_wrapped_control();
+
+ bool is_window_available() const;
+
+ bool get_window_enabled() const;
+ void set_window_enabled(bool p_enabled);
+
+ Rect2i get_window_rect() const;
+ int get_window_screen() const;
+
+ void restore_window(const Rect2i &p_rect, int p_screen = -1);
+ void restore_window_from_saved_position(const Rect2 p_window_rect, int p_screen, const Rect2 p_screen_rect);
+ void enable_window_on_screen(int p_screen = -1, bool p_auto_scale = false);
+
+ void set_window_title(const String p_title);
+ void set_margins_enabled(bool p_enabled);
+
+ WindowWrapper();
+};
+
+class ScreenSelect : public Button {
+ GDCLASS(ScreenSelect, Button);
+
+ Popup *popup = nullptr;
+ Panel *popup_background = nullptr;
+ HBoxContainer *screen_list = nullptr;
+
+ void _build_advanced_menu();
+
+ void _emit_screen_signal(int p_screen_idx);
+ void _handle_mouse_shortcut(const Ref<InputEvent> &p_event);
+ void _show_popup();
+
+protected:
+ virtual void pressed() override;
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+public:
+ ScreenSelect();
+};
+
+#endif // WINDOW_WRAPPER_H
diff --git a/main/main.cpp b/main/main.cpp
index 5ba1eac1f3..f9f2449721 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -41,7 +41,6 @@
#include "core/input/input.h"
#include "core/input/input_map.h"
#include "core/io/dir_access.h"
-#include "core/io/file_access_network.h"
#include "core/io/file_access_pack.h"
#include "core/io/file_access_zip.h"
#include "core/io/image_loader.h"
@@ -113,6 +112,10 @@
#include "modules/mono/editor/bindings_generator.h"
#endif
+#ifdef MODULE_GDSCRIPT_ENABLED
+#include "modules/gdscript/gdscript.h"
+#endif
+
/* Static members */
// Singletons
@@ -129,7 +132,6 @@ static Time *time_singleton = nullptr;
#ifdef MINIZIP_ENABLED
static ZipArchive *zip_packed_data = nullptr;
#endif
-static FileAccessNetworkClient *file_access_network_client = nullptr;
static MessageQueue *message_queue = nullptr;
// Initialized in setup2()
@@ -167,7 +169,7 @@ static bool project_manager = false;
static bool cmdline_tool = false;
static String locale;
static bool show_help = false;
-static bool auto_quit = false;
+static uint64_t quit_after = 0;
static OS::ProcessID editor_pid = 0;
#ifdef TOOLS_ENABLED
static bool found_project = false;
@@ -180,6 +182,7 @@ static int converter_max_line_length = 100000;
HashMap<Main::CLIScope, Vector<String>> forwardable_cli_arguments;
#endif
+static bool single_threaded_scene = false;
bool use_startup_benchmark = false;
String startup_benchmark_file;
@@ -207,6 +210,7 @@ static bool use_debug_profiler = false;
static bool debug_collisions = false;
static bool debug_paths = false;
static bool debug_navigation = false;
+static bool debug_avoidance = false;
#endif
static int frame_delay = 0;
static bool disable_render_loop = false;
@@ -251,6 +255,31 @@ static String get_full_version_string() {
return String(VERSION_FULL_BUILD) + hash;
}
+#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED)
+static Vector<String> get_files_with_extension(const String &p_root, const String &p_extension) {
+ Vector<String> paths;
+
+ Ref<DirAccess> dir = DirAccess::open(p_root);
+ if (dir.is_valid()) {
+ dir->list_dir_begin();
+ String fn = dir->get_next();
+ while (!fn.is_empty()) {
+ if (!dir->current_is_hidden() && fn != "." && fn != "..") {
+ if (dir->current_is_dir()) {
+ paths.append_array(get_files_with_extension(p_root.path_join(fn), p_extension));
+ } else if (fn.get_extension() == p_extension) {
+ paths.append(p_root.path_join(fn));
+ }
+ }
+ fn = dir->get_next();
+ }
+ dir->list_dir_end();
+ }
+
+ return paths;
+}
+#endif
+
// FIXME: Could maybe be moved to have less code in main.cpp.
void initialize_physics() {
/// 3D Physics Server
@@ -357,6 +386,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n");
#endif
OS::get_singleton()->print(" --quit Quit after the first iteration.\n");
+ OS::get_singleton()->print(" --quit-after <int> Quit after the given number of iterations. Set to 0 to disable.\n");
OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n");
@@ -400,6 +430,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --write-movie <file> Writes a video to the specified path (usually with .avi or .png extension).\n");
OS::get_singleton()->print(" --fixed-fps is forced when enabled, but it can be used to change movie FPS.\n");
OS::get_singleton()->print(" --disable-vsync can speed up movie writing but makes interaction more difficult.\n");
+ OS::get_singleton()->print(" --quit-after can be used to specify the number of frames to write.\n");
OS::get_singleton()->print("\n");
@@ -425,10 +456,12 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
#endif
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
+ OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n");
#if defined(DEBUG_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
+ OS::get_singleton()->print(" --debug-avoidance Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n");
#endif
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
@@ -457,6 +490,9 @@ void Main::print_help(const char *p_binary) {
#endif // DISABLE_DEPRECATED
OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
+#ifdef MODULE_GDSCRIPT_ENABLED
+ OS::get_singleton()->print(" --gdscript-docs <path> Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n");
+#endif
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
@@ -584,8 +620,6 @@ void Main::test_cleanup() {
TextServerManager::get_singleton()->get_interface(i)->cleanup();
}
- EngineDebugger::deinitialize();
-
ResourceLoader::remove_custom_loaders();
ResourceSaver::remove_custom_savers();
@@ -597,6 +631,7 @@ void Main::test_cleanup() {
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE);
+
unregister_platform_apis();
unregister_driver_types();
unregister_scene_types();
@@ -607,8 +642,12 @@ void Main::test_cleanup() {
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
unregister_server_types();
+ EngineDebugger::deinitialize();
OS::get_singleton()->finalize();
+ if (packed_data) {
+ memdelete(packed_data);
+ }
if (translation_server) {
memdelete(translation_server);
}
@@ -624,16 +663,13 @@ void Main::test_cleanup() {
if (globals) {
memdelete(globals);
}
- if (packed_data) {
- memdelete(packed_data);
- }
if (engine) {
memdelete(engine);
}
unregister_core_driver_types();
- uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
unregister_core_extensions();
+ uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
unregister_core_types();
OS::get_singleton()->finalize_core();
@@ -1111,6 +1147,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing remote debug server uri, aborting.\n");
goto error;
}
+ } else if (I->get() == "--single-threaded-scene") {
+ single_threaded_scene = true;
} else if (I->get() == "--build-solutions") { // Build the scripting solution such C#
auto_build_solutions = true;
@@ -1222,7 +1260,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "-u" || I->get() == "--upwards") { // scan folders upwards
upwards = true;
} else if (I->get() == "--quit") { // Auto quit at the end of the first main loop iteration
- auto_quit = true;
+ quit_after = 1;
+ } else if (I->get() == "--quit-after") { // Quit after the given number of iterations
+ if (I->next()) {
+ quit_after = I->next()->get().to_int();
+ N = I->next()->next();
+ } else {
+ OS::get_singleton()->print("Missing number of iterations, aborting.\n");
+ goto error;
+ }
} else if (I->get().ends_with("project.godot")) {
String path;
String file = I->get();
@@ -1290,6 +1336,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
debug_paths = true;
} else if (I->get() == "--debug-navigation") {
debug_navigation = true;
+ } else if (I->get() == "--debug-avoidance") {
+ debug_avoidance = true;
} else if (I->get() == "--debug-stringnames") {
StringName::set_debug_stringnames(true);
#endif
@@ -1396,9 +1444,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Network file system needs to be configured before globals, since globals are based on the
// 'project.godot' file which will only be available through the network if this is enabled
- FileAccessNetwork::configure();
if (!remotefs.is_empty()) {
- file_access_network_client = memnew(FileAccessNetworkClient);
int port;
if (remotefs.contains(":")) {
port = remotefs.get_slicec(':', 1).to_int();
@@ -1406,14 +1452,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else {
port = 6010;
}
+ Error err = OS::get_singleton()->setup_remote_filesystem(remotefs, port, remotefs_pass, project_path);
- Error err = file_access_network_client->connect(remotefs, port, remotefs_pass);
if (err) {
OS::get_singleton()->printerr("Could not connect to remotefs: %s:%i.\n", remotefs.utf8().get_data(), port);
goto error;
}
-
- FileAccess::make_default<FileAccessNetwork>(FileAccess::ACCESS_RESOURCES);
}
if (globals->setup(project_path, main_pack, upwards, editor) == OK) {
@@ -1432,6 +1476,19 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#endif
}
+ // Initialize WorkerThreadPool.
+ {
+ int worker_threads = GLOBAL_GET("threading/worker_pool/max_threads");
+ bool low_priority_use_system_threads = GLOBAL_GET("threading/worker_pool/use_system_threads_for_low_priority_tasks");
+ float low_property_ratio = GLOBAL_GET("threading/worker_pool/low_priority_thread_ratio");
+
+ if (editor || project_manager) {
+ WorkerThreadPool::get_singleton()->init();
+ } else {
+ WorkerThreadPool::get_singleton()->init(worker_threads, low_priority_use_system_threads, low_property_ratio);
+ }
+ }
+
// Initialize user data dir.
OS::get_singleton()->ensure_user_data_dir();
@@ -1949,9 +2006,6 @@ error:
if (packed_data) {
memdelete(packed_data);
}
- if (file_access_network_client) {
- memdelete(file_access_network_client);
- }
unregister_core_driver_types();
unregister_core_extensions();
@@ -2482,6 +2536,9 @@ bool Main::start() {
String _export_preset;
bool export_debug = false;
bool export_pack_only = false;
+#ifdef MODULE_GDSCRIPT_ENABLED
+ String gdscript_docs_path;
+#endif
#ifndef DISABLE_DEPRECATED
bool converting_project = false;
bool validating_converting_project = false;
@@ -2542,6 +2599,10 @@ bool Main::start() {
doc_tool_path = ".";
parsed_pair = false;
}
+#ifdef MODULE_GDSCRIPT_ENABLED
+ } else if (args[i] == "--gdscript-docs") {
+ gdscript_docs_path = args[i + 1];
+#endif
} else if (args[i] == "--export-release") {
editor = true; //needs editor
_export_preset = args[i + 1];
@@ -2573,6 +2634,38 @@ 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);
#ifdef TOOLS_ENABLED
+#ifdef MODULE_GDSCRIPT_ENABLED
+ if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) {
+ DocTools docs;
+ Error err;
+
+ Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd");
+ ERR_FAIL_COND_V_MSG(paths.size() == 0, false, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path);
+
+ for (const String &path : paths) {
+ Ref<GDScript> gdscript = ResourceLoader::load(path);
+ for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) {
+ docs.add_doc(class_doc);
+ }
+ }
+
+ if (doc_tool_path == ".") {
+ doc_tool_path = "./docs";
+ }
+
+ Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path);
+ err = da->make_dir_recursive(doc_tool_path);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err));
+
+ HashMap<String, String> doc_data_classes;
+ err = docs.save_classes(doc_tool_path, doc_data_classes, false);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving GDScript docs:" + itos(err));
+
+ OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
+ return false;
+ }
+#endif // MODULE_GDSCRIPT_ENABLED
+
if (!doc_tool_path.is_empty()) {
// Needed to instance editor-only classes for their default values
Engine::get_singleton()->set_editor_hint(true);
@@ -2802,11 +2895,26 @@ bool Main::start() {
}
if (debug_navigation) {
sml->set_debug_navigation_hint(true);
+ }
+ if (debug_avoidance) {
+ sml->set_debug_avoidance_hint(true);
+ }
+ if (debug_navigation || debug_avoidance) {
NavigationServer3D::get_singleton()->set_active(true);
NavigationServer3D::get_singleton()->set_debug_enabled(true);
+ if (debug_navigation) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_enabled(true);
+ }
+ if (debug_avoidance) {
+ NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(true);
+ }
}
#endif
+ if (single_threaded_scene) {
+ sml->set_disable_node_threading(true);
+ }
+
bool embed_subwindows = GLOBAL_GET("display/window/subwindows/embed_subwindows");
if (single_window || (!project_manager && !editor && embed_subwindows) || !DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) {
@@ -3323,10 +3431,11 @@ bool Main::iteration() {
}
if (movie_writer) {
- RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
- RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
- Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
- movie_writer->add_frame(vp_tex);
+ movie_writer->add_frame();
+ }
+
+ if ((quit_after > 0) && (Engine::get_singleton()->_process_frames >= quit_after)) {
+ exit = true;
}
if (fixed_fps != -1) {
@@ -3352,7 +3461,7 @@ bool Main::iteration() {
}
#endif
- return exit || auto_quit;
+ return exit;
}
void Main::force_redraw() {
@@ -3378,6 +3487,8 @@ void Main::cleanup(bool p_force) {
movie_writer->end();
}
+ ResourceLoader::clear_thread_load_tasks();
+
ResourceLoader::remove_custom_loaders();
ResourceSaver::remove_custom_savers();
@@ -3394,8 +3505,6 @@ void Main::cleanup(bool p_force) {
ResourceLoader::clear_translation_remaps();
ResourceLoader::clear_path_remaps();
- ResourceLoader::clear_thread_load_tasks();
-
ScriptServer::finish_languages();
// Sync pending commands that may have been queued from a different thread during ScriptServer finalization
@@ -3462,9 +3571,6 @@ void Main::cleanup(bool p_force) {
if (packed_data) {
memdelete(packed_data);
}
- if (file_access_network_client) {
- memdelete(file_access_network_client);
- }
if (performance) {
memdelete(performance);
}
diff --git a/misc/scripts/codespell.sh b/misc/scripts/codespell.sh
index 775d193a66..d15b6593f7 100755
--- a/misc/scripts/codespell.sh
+++ b/misc/scripts/codespell.sh
@@ -3,6 +3,6 @@ SKIP_LIST="./.*,./**/.*,./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./A
SKIP_LIST+="./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/renames_map_3_to_4.cpp,./misc/scripts/codespell.sh,"
SKIP_LIST+="./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json,"
-IGNORE_LIST="curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te"
+IGNORE_LIST="curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te,vai"
codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}" --builtin "clear,rare,en-GB_to_en-US"
diff --git a/modules/astcenc/SCsub b/modules/astcenc/SCsub
index 0f04f2bc28..691c74b4a7 100644
--- a/modules/astcenc/SCsub
+++ b/modules/astcenc/SCsub
@@ -29,7 +29,6 @@ thirdparty_sources = [
"astcenc_partition_tables.cpp",
"astcenc_percentile_tables.cpp",
"astcenc_pick_best_endpoint_format.cpp",
- "astcenc_platform_isa_detection.cpp",
"astcenc_quantization.cpp",
"astcenc_symbolic_physical.cpp",
"astcenc_weight_align.cpp",
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index dee95d1ac5..878664bb9c 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -467,7 +467,7 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_b
// Use a limit to speed up bvh and limit the depth.
#define BVH_LIMIT 8
-int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) {
+int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *r_facebvhptr, FaceBVH **r_facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) {
if (p_depth > r_max_depth) {
r_max_depth = p_depth;
}
@@ -478,15 +478,15 @@ int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **fac
if (p_size <= BVH_LIMIT) {
for (int i = 0; i < p_size - 1; i++) {
- facebvhptrptr[p_from + i]->next = facebvhptrptr[p_from + i + 1] - facebvhptr;
+ r_facebvhptrptr[p_from + i]->next = r_facebvhptrptr[p_from + i + 1] - r_facebvhptr;
}
- return facebvhptrptr[p_from] - facebvhptr;
+ return r_facebvhptrptr[p_from] - r_facebvhptr;
}
AABB aabb;
- aabb = facebvhptrptr[p_from]->aabb;
+ aabb = r_facebvhptrptr[p_from]->aabb;
for (int i = 1; i < p_size; i++) {
- aabb.merge_with(facebvhptrptr[p_from + i]->aabb);
+ aabb.merge_with(r_facebvhptrptr[p_from + i]->aabb);
}
int li = aabb.get_longest_axis_index();
@@ -494,28 +494,28 @@ int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **fac
switch (li) {
case Vector3::AXIS_X: {
SortArray<FaceBVH *, FaceBVHCmpX> sort_x;
- sort_x.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
+ sort_x.nth_element(0, p_size, p_size / 2, &r_facebvhptrptr[p_from]);
//sort_x.sort(&p_bb[p_from],p_size);
} break;
case Vector3::AXIS_Y: {
SortArray<FaceBVH *, FaceBVHCmpY> sort_y;
- sort_y.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
+ sort_y.nth_element(0, p_size, p_size / 2, &r_facebvhptrptr[p_from]);
//sort_y.sort(&p_bb[p_from],p_size);
} break;
case Vector3::AXIS_Z: {
SortArray<FaceBVH *, FaceBVHCmpZ> sort_z;
- sort_z.nth_element(0, p_size, p_size / 2, &facebvhptrptr[p_from]);
+ sort_z.nth_element(0, p_size, p_size / 2, &r_facebvhptrptr[p_from]);
//sort_z.sort(&p_bb[p_from],p_size);
} break;
}
- int left = _create_bvh(facebvhptr, facebvhptrptr, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
- int right = _create_bvh(facebvhptr, facebvhptrptr, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
+ int left = _create_bvh(r_facebvhptr, r_facebvhptrptr, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
+ int right = _create_bvh(r_facebvhptr, r_facebvhptrptr, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc);
int index = r_max_alloc++;
- FaceBVH *_new = &facebvhptr[index];
+ FaceBVH *_new = &r_facebvhptr[index];
_new->aabb = aabb;
_new->center = aabb.get_center();
_new->face = -1;
@@ -526,20 +526,22 @@ int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **fac
return index;
}
-void CSGBrushOperation::MeshMerge::_add_distance(List<real_t> &r_intersectionsA, List<real_t> &r_intersectionsB, bool p_from_B, real_t p_distance) const {
- List<real_t> &intersections = p_from_B ? r_intersectionsB : r_intersectionsA;
+void CSGBrushOperation::MeshMerge::_add_distance(List<IntersectionDistance> &r_intersectionsA, List<IntersectionDistance> &r_intersectionsB, bool p_from_B, real_t p_distance_squared, bool p_is_conormal) const {
+ List<IntersectionDistance> &intersections = p_from_B ? r_intersectionsB : r_intersectionsA;
// Check if distance exists.
- for (const real_t E : intersections) {
- if (Math::is_equal_approx(E, p_distance)) {
+ for (const IntersectionDistance E : intersections) {
+ if (E.is_conormal == p_is_conormal && Math::is_equal_approx(E.distance_squared, p_distance_squared)) {
return;
}
}
-
- intersections.push_back(p_distance);
+ IntersectionDistance distance;
+ distance.is_conormal = p_is_conormal;
+ distance.distance_squared = p_distance_squared;
+ intersections.push_back(distance);
}
-bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const {
+bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *r_facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const {
Face face = faces[p_face_idx];
Vector3 face_points[3] = {
points[face.points[0]],
@@ -561,8 +563,11 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
VISITED_BIT_MASK = ~NODE_IDX_MASK
};
- List<real_t> intersectionsA;
- List<real_t> intersectionsB;
+ List<IntersectionDistance> intersectionsA;
+ List<IntersectionDistance> intersectionsB;
+
+ Intersection closest_intersection;
+ closest_intersection.found = false;
int level = 0;
int pos = p_bvh_first;
@@ -570,7 +575,7 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
while (true) {
uint32_t node = stack[level] & NODE_IDX_MASK;
- const FaceBVH *current_facebvhptr = &(facebvhptr[node]);
+ const FaceBVH *current_facebvhptr = &(r_facebvhptr[node]);
bool done = false;
switch (stack[level] >> VISITED_BIT_SHIFT) {
@@ -587,22 +592,66 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
};
Vector3 current_normal = Plane(current_points[0], current_points[1], current_points[2]).normal;
Vector3 intersection_point;
-
// Check if faces are co-planar.
if (current_normal.is_equal_approx(face_normal) &&
is_point_in_triangle(face_center, current_points)) {
// Only add an intersection if not a B face.
if (!face.from_b) {
- _add_distance(intersectionsA, intersectionsB, current_face.from_b, 0);
+ _add_distance(intersectionsA, intersectionsB, current_face.from_b, 0, true);
}
} else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) {
- real_t distance = face_center.distance_to(intersection_point);
- _add_distance(intersectionsA, intersectionsB, current_face.from_b, distance);
+ real_t distance_squared = face_center.distance_squared_to(intersection_point);
+ real_t inner = current_normal.dot(face_normal);
+ // If the faces are perpendicular, ignore this face.
+ // The triangles on the side should be intersected and result in the correct behavior.
+ if (!Math::is_zero_approx(inner)) {
+ _add_distance(intersectionsA, intersectionsB, current_face.from_b, distance_squared, inner > 0.0f);
+ }
+ }
+
+ if (face.from_b != current_face.from_b) {
+ if (current_normal.is_equal_approx(face_normal) &&
+ is_point_in_triangle(face_center, current_points)) {
+ // Only add an intersection if not a B face.
+ if (!face.from_b) {
+ closest_intersection.found = true;
+ closest_intersection.conormal = 1.0f;
+ closest_intersection.distance_squared = 0.0f;
+ closest_intersection.origin_angle = -FLT_MAX;
+ }
+ } else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) {
+ Intersection potential_intersection;
+ potential_intersection.found = true;
+ potential_intersection.conormal = face_normal.dot(current_normal);
+ potential_intersection.distance_squared = face_center.distance_squared_to(intersection_point);
+ potential_intersection.origin_angle = Math::abs(potential_intersection.conormal);
+ real_t intersection_dist_from_face = face_normal.dot(intersection_point - face_center);
+ for (int i = 0; i < 3; i++) {
+ real_t point_dist_from_face = face_normal.dot(current_points[i] - face_center);
+ if (!Math::is_equal_approx(point_dist_from_face, intersection_dist_from_face) &&
+ point_dist_from_face < intersection_dist_from_face) {
+ potential_intersection.origin_angle = -potential_intersection.origin_angle;
+ break;
+ }
+ }
+ if (potential_intersection.conormal != 0.0f) {
+ if (!closest_intersection.found) {
+ closest_intersection = potential_intersection;
+ } else if (!Math::is_equal_approx(potential_intersection.distance_squared, closest_intersection.distance_squared) &&
+ potential_intersection.distance_squared < closest_intersection.distance_squared) {
+ closest_intersection = potential_intersection;
+ } else if (Math::is_equal_approx(potential_intersection.distance_squared, closest_intersection.distance_squared)) {
+ if (potential_intersection.origin_angle < closest_intersection.origin_angle) {
+ closest_intersection = potential_intersection;
+ }
+ }
+ }
+ }
}
}
if (current_facebvhptr->next != -1) {
- current_facebvhptr = &facebvhptr[current_facebvhptr->next];
+ current_facebvhptr = &r_facebvhptr[current_facebvhptr->next];
} else {
current_facebvhptr = nullptr;
}
@@ -652,8 +701,11 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
}
}
- // Inside if face normal intersects other faces an odd number of times.
- return (intersectionsA.size() + intersectionsB.size()) & 1;
+ if (!closest_intersection.found) {
+ return false;
+ } else {
+ return closest_intersection.conormal > 0.0f;
+ }
}
void CSGBrushOperation::MeshMerge::mark_inside_faces() {
@@ -1016,6 +1068,8 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
}
void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_segment_points[2], Vector<int> &r_segment_indices) {
+ LocalVector<Vector<Vector2>> processed_edges;
+
// For each face.
for (int face_idx = 0; face_idx < faces.size(); ++face_idx) {
Face2D face = faces[face_idx];
@@ -1027,17 +1081,32 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
// Check each edge.
for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
- Vector2 edge_points[2] = {
+ Vector<Vector2> edge_points_and_uvs = {
face_vertices[face_edge_idx].point,
- face_vertices[(face_edge_idx + 1) % 3].point
- };
- Vector2 edge_uvs[2] = {
+ face_vertices[(face_edge_idx + 1) % 3].point,
face_vertices[face_edge_idx].uv,
face_vertices[(face_edge_idx + 1) % 3].uv
};
- Vector2 intersection_point;
+
+ Vector2 edge_points[2] = {
+ edge_points_and_uvs[0],
+ edge_points_and_uvs[1],
+ };
+ Vector2 edge_uvs[2] = {
+ edge_points_and_uvs[2],
+ edge_points_and_uvs[3],
+ };
+
+ // Check if edge has already been processed.
+ if (processed_edges.find(edge_points_and_uvs) != -1) {
+ continue;
+ }
+
+ processed_edges.push_back(edge_points_and_uvs);
// First check if the ends of the segment are on the edge.
+ Vector2 intersection_point;
+
bool on_edge = false;
for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) {
intersection_point = Geometry2D::get_closest_point_to_segment(p_segment_points[edge_point_idx], edge_points);
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 1513a01f9e..2a0831e1ce 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -135,6 +135,17 @@ struct CSGBrushOperation {
return h;
}
};
+ struct Intersection {
+ bool found = false;
+ real_t conormal = FLT_MAX;
+ real_t distance_squared = FLT_MAX;
+ real_t origin_angle = FLT_MAX;
+ };
+
+ struct IntersectionDistance {
+ bool is_conormal;
+ real_t distance_squared;
+ };
Vector<Vector3> points;
Vector<Face> faces;
@@ -143,9 +154,9 @@ struct CSGBrushOperation {
OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
float vertex_snap = 0.0;
- inline void _add_distance(List<real_t> &r_intersectionsA, List<real_t> &r_intersectionsB, bool p_from_B, real_t p_distance) const;
- inline bool _bvh_inside(FaceBVH *facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const;
- inline int _create_bvh(FaceBVH *facebvhptr, FaceBVH **facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc);
+ inline void _add_distance(List<IntersectionDistance> &r_intersectionsA, List<IntersectionDistance> &r_intersectionsB, bool p_from_B, real_t p_distance, bool p_is_conormal) const;
+ inline bool _bvh_inside(FaceBVH *r_facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const;
+ inline int _create_bvh(FaceBVH *r_facebvhptr, FaceBVH **r_facebvhptrptr, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc);
void add_face(const Vector3 p_points[3], const Vector2 p_uvs[3], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
void mark_inside_faces();
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index afb8e62eea..c8fe39dab0 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -476,6 +476,43 @@ void CSGShape3D::_update_collision_faces() {
}
root_collision_shape->set_faces(physics_faces);
+
+ if (_is_debug_collision_shape_visible()) {
+ _update_debug_collision_shape();
+ }
+ }
+}
+
+bool CSGShape3D::_is_debug_collision_shape_visible() {
+ return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint());
+}
+
+void CSGShape3D::_update_debug_collision_shape() {
+ // NOTE: This is called only for the root shape with collision, when root_collision_shape is valid.
+
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
+ if (root_collision_debug_instance.is_null()) {
+ root_collision_debug_instance = RS::get_singleton()->instance_create();
+ }
+
+ Ref<Mesh> debug_mesh = root_collision_shape->get_debug_mesh();
+ RS::get_singleton()->instance_set_scenario(root_collision_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_base(root_collision_debug_instance, debug_mesh->get_rid());
+ RS::get_singleton()->instance_set_transform(root_collision_debug_instance, get_global_transform());
+}
+
+void CSGShape3D::_clear_debug_collision_shape() {
+ if (root_collision_debug_instance.is_valid()) {
+ RS::get_singleton()->free(root_collision_debug_instance);
+ root_collision_debug_instance = RID();
+ }
+}
+
+void CSGShape3D::_on_transform_changed() {
+ if (root_collision_debug_instance.is_valid() && !debug_shape_old_transform.is_equal_approx(get_global_transform())) {
+ debug_shape_old_transform = get_global_transform();
+ RS::get_singleton()->instance_set_transform(root_collision_debug_instance, debug_shape_old_transform);
}
}
@@ -558,6 +595,7 @@ void CSGShape3D::_notification(int p_what) {
set_collision_layer(collision_layer);
set_collision_mask(collision_mask);
set_collision_priority(collision_priority);
+ debug_shape_old_transform = get_global_transform();
_make_dirty();
}
} break;
@@ -567,6 +605,7 @@ void CSGShape3D::_notification(int p_what) {
PhysicsServer3D::get_singleton()->free(root_collision_instance);
root_collision_instance = RID();
root_collision_shape.unref();
+ _clear_debug_collision_shape();
}
} break;
@@ -574,6 +613,7 @@ void CSGShape3D::_notification(int p_what) {
if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
}
+ _on_transform_changed();
} break;
}
}
@@ -653,7 +693,7 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001,suffix:m"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
ADD_GROUP("Collision", "collision_");
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index c244107bfb..d0e4d0c8cd 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -68,6 +68,8 @@ private:
real_t collision_priority = 1.0;
Ref<ConcavePolygonShape3D> root_collision_shape;
RID root_collision_instance;
+ RID root_collision_debug_instance;
+ Transform3D debug_shape_old_transform;
bool calculate_tangents = true;
@@ -107,6 +109,10 @@ private:
void _update_shape();
void _update_collision_faces();
+ bool _is_debug_collision_shape_visible();
+ void _update_debug_collision_shape();
+ void _clear_debug_collision_shape();
+ void _on_transform_changed();
protected:
void _notification(int p_what);
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index e92bb05b51..cae30ed446 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -15,14 +15,14 @@
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
+ Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
+ Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_meshes" qualifiers="const">
@@ -42,7 +42,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_layer], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
@@ -50,7 +50,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_mask], given a [param layer_number] between 1 and 32.
</description>
</method>
</methods>
@@ -73,7 +73,7 @@
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap" default="0.001">
- Snap makes the mesh snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust.
+ Snap makes the mesh vertices snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust.
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false">
Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. See also [member collision_mask] and [member collision_priority].
diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml
index 365d12bb7a..3c1f559b9f 100644
--- a/modules/enet/doc_classes/ENetConnection.xml
+++ b/modules/enet/doc_classes/ENetConnection.xml
@@ -24,7 +24,7 @@
<param index="1" name="packet" type="PackedByteArray" />
<param index="2" name="flags" type="int" />
<description>
- Queues a [code]packet[/code] to be sent to all peers associated with the host over the specified [code]channel[/code]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags.
+ Queues a [param packet] to be sent to all peers associated with the host over the specified [param channel]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags.
</description>
</method>
<method name="channel_limit">
@@ -50,7 +50,7 @@
<param index="2" name="channels" type="int" default="0" />
<param index="3" name="data" type="int" default="0" />
<description>
- Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocating the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer.
+ Initiates a connection to a foreign [param address] using the specified [param port] and allocating the requested [param channels]. Optional [param data] can be passed during connection in the form of a 32 bit integer.
[b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method.
</description>
</method>
@@ -61,7 +61,7 @@
<param index="2" name="in_bandwidth" type="int" default="0" />
<param index="3" name="out_bandwidth" type="int" default="0" />
<description>
- Create an ENetHost that will allow up to [code]max_peers[/code] connected peers, each allocating up to [code]max_channels[/code] channels, optionally limiting bandwidth to [code]in_bandwidth[/code] and [code]out_bandwidth[/code].
+ Create an ENetHost that will allow up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth].
</description>
</method>
<method name="create_host_bound">
@@ -73,7 +73,7 @@
<param index="4" name="in_bandwidth" type="int" default="0" />
<param index="5" name="out_bandwidth" type="int" default="0" />
<description>
- Create an ENetHost like [method create_host] which is also bound to the given [code]bind_address[/code] and [code]bind_port[/code].
+ Create an ENetHost like [method create_host] which is also bound to the given [param bind_address] and [param bind_port].
</description>
</method>
<method name="destroy">
@@ -87,7 +87,7 @@
<param index="0" name="hostname" type="String" />
<param index="1" name="client_options" type="TLSOptions" default="null" />
<description>
- Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS validating the server certificate against [code]hostname[/code]. You can pass the optional [param client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe].
+ Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS validating the server certificate against [param hostname]. You can pass the optional [param client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe].
</description>
</method>
<method name="dtls_server_setup">
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
index 0e81244a1c..646cf37b55 100644
--- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -17,8 +17,8 @@
<param index="0" name="peer_id" type="int" />
<param index="1" name="host" type="ENetConnection" />
<description>
- Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code].
- [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
+ Add a new remote peer with the given [param peer_id] connected to the given [param host].
+ [b]Note:[/b] The [param host] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
</description>
</method>
<method name="create_client">
@@ -30,14 +30,14 @@
<param index="4" name="out_bandwidth" type="int" default="0" />
<param index="5" name="local_port" type="int" default="0" />
<description>
- Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
+ Create client that connects to a server at [param address] using specified [param port]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [param port] is the port the server is listening on. The [param channel_count] parameter can be used to specify the number of ENet channels allocated for the connection. The [param in_bandwidth] and [param out_bandwidth] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [param local_port] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
</description>
</method>
<method name="create_mesh">
<return type="int" enum="Error" />
<param index="0" name="unique_id" type="int" />
<description>
- Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server).
+ Initialize this [MultiplayerPeer] in mesh mode. The provided [param unique_id] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server).
</description>
</method>
<method name="create_server">
@@ -48,14 +48,14 @@
<param index="3" name="in_bandwidth" type="int" default="0" />
<param index="4" name="out_bandwidth" type="int" default="0" />
<description>
- Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the server could not be created.
+ Create server that listens to connections via [param port]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [param max_clients] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the server could not be created.
</description>
</method>
<method name="get_peer" qualifiers="const">
<return type="ENetPacketPeer" />
<param index="0" name="id" type="int" />
<description>
- Returns the [ENetPacketPeer] associated to the given [code]id[/code].
+ Returns the [ENetPacketPeer] associated to the given [param id].
</description>
</method>
<method name="set_bind_ip">
diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml
index 9c7ee3c17d..0e531b0e89 100644
--- a/modules/enet/doc_classes/ENetPacketPeer.xml
+++ b/modules/enet/doc_classes/ENetPacketPeer.xml
@@ -40,7 +40,7 @@
<return type="float" />
<param index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" />
<description>
- Returns the requested [code]statistic[/code] for this peer. See [enum PeerStatistic].
+ Returns the requested [param statistic] for this peer. See [enum PeerStatistic].
</description>
</method>
<method name="is_active" qualifiers="const">
@@ -80,7 +80,7 @@
<return type="void" />
<param index="0" name="ping_interval" type="int" />
<description>
- Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds.
+ Sets the [param ping_interval] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds.
</description>
</method>
<method name="reset">
@@ -95,7 +95,7 @@
<param index="1" name="packet" type="PackedByteArray" />
<param index="2" name="flags" type="int" />
<description>
- Queues a [code]packet[/code] to be sent over the specified [code]channel[/code]. See [code]FLAG_*[/code] constants for available packet flags.
+ Queues a [param packet] to be sent over the specified [param channel]. See [code]FLAG_*[/code] constants for available packet flags.
</description>
</method>
<method name="set_timeout">
@@ -105,7 +105,7 @@
<param index="2" name="timeout_max" type="int" />
<description>
Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds.
- The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped.
+ The [param timeout] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [param timeout_min]. The [param timeout_max] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped.
</description>
</method>
<method name="throttle_configure">
@@ -115,7 +115,7 @@
<param index="2" name="deceleration" type="int" />
<description>
Configures throttle parameter for a peer.
- Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probability by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
+ Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [param interval], ENet will either increase the probability by the amount specified in the [param acceleration] parameter, or decrease it by the amount specified in the [param deceleration] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent.
When the throttle has a value of [code]0[/code], all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions.
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 16a59d3880..14cce2686c 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -66,7 +66,7 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
case Image::USED_CHANNELS_RG:
return EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG;
case Image::USED_CHANNELS_RGB:
- return EtcpakType::ETCPAK_TYPE_DXT5;
+ return EtcpakType::ETCPAK_TYPE_DXT1;
case Image::USED_CHANNELS_RGBA:
return EtcpakType::ETCPAK_TYPE_DXT5;
default:
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 1234abc62e..e3f5502391 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -139,7 +139,7 @@
print(is_instance_of(a, MyClass))
print(is_instance_of(a, MyClass.InnerClass))
[/codeblock]
- [b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise an runtime error.
+ [b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise a runtime error.
See also [method @GlobalScope.typeof], [method type_exists], [method Array.is_same_typed] (and other [Array] methods).
</description>
</method>
@@ -170,6 +170,7 @@
[b]Important:[/b] The path must be absolute. A relative path will always return [code]null[/code].
This function is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios.
[b]Note:[/b] Files have to be imported into the engine first to load them using this function. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data].
+ [b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code].
</description>
</method>
<method name="preload">
@@ -227,8 +228,8 @@
To iterate over an [Array] backwards, use:
[codeblock]
var array = [3, 6, 9]
- for i in range(array.size(), 0, -1):
- print(array[i - 1])
+ for i in range(array.size() - 1, -1, -1):
+ print(array[i])
[/codeblock]
Output:
[codeblock]
@@ -457,6 +458,16 @@
[/codeblock]
</description>
</annotation>
+ <annotation name="@export_flags_avoidance">
+ <return type="void" />
+ <description>
+ Export an integer property as a bit flag field for navigation avoidance layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/avoidance/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_AVOIDANCE].
+ [codeblock]
+ @export_flags_avoidance var avoidance_layers: int
+ [/codeblock]
+ </description>
+ </annotation>
<annotation name="@export_global_dir">
<return type="void" />
<description>
@@ -609,7 +620,7 @@
<param index="3" name="transfer_channel" type="int" default="0" />
<description>
Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url].
- The order of [code]mode[/code], [code]sync[/code] and [code]transfer_mode[/code] does not matter and all arguments can be omitted, but [code]transfer_channel[/code] always has to be the last argument. The accepted values for [code]mode[/code] are [code]"any_peer"[/code] or [code]"authority"[/code], for [code]sync[/code] are [code]"call_remote"[/code] or [code]"call_local"[/code] and for [code]transfer_mode[/code] are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code] or [code]"reliable"[/code].
+ The order of [param mode], [param sync] and [param transfer_mode] does not matter and all arguments can be omitted, but [param transfer_channel] always has to be the last argument. The accepted values for [param mode] are [code]"any_peer"[/code] or [code]"authority"[/code], for [param sync] are [code]"call_remote"[/code] or [code]"call_local"[/code] and for [param transfer_mode] are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code] or [code]"reliable"[/code].
[codeblock]
@rpc
func fn(): pass
@@ -622,6 +633,12 @@
[/codeblock]
</description>
</annotation>
+ <annotation name="@static_unload">
+ <return type="void" />
+ <description>
+ Make a script with static variables to not persist after all references are lost. If the script is loaded again the static variables will revert to their default values.
+ </description>
+ </annotation>
<annotation name="@tool">
<return type="void" />
<description>
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
new file mode 100644
index 0000000000..ce64d79747
--- /dev/null
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -0,0 +1,272 @@
+/**************************************************************************/
+/* gdscript_docgen.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 "gdscript_docgen.h"
+#include "../gdscript.h"
+
+using GDP = GDScriptParser;
+using GDType = GDP::DataType;
+
+static String _get_script_path(const String &p_path) {
+ return vformat(R"("%s")", p_path.get_slice("://", 1));
+}
+
+static String _get_class_name(const GDP::ClassNode &p_class) {
+ const GDP::ClassNode *curr_class = &p_class;
+ if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class
+ return _get_script_path(curr_class->fqcn);
+ }
+
+ String full_name = curr_class->identifier->name;
+ while (curr_class->outer) {
+ curr_class = curr_class->outer;
+ if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class
+ return vformat("%s.%s", _get_script_path(curr_class->fqcn), full_name);
+ }
+ full_name = vformat("%s.%s", curr_class->identifier->name, full_name);
+ }
+ return full_name;
+}
+
+static PropertyInfo _property_info_from_datatype(const GDType &p_type) {
+ PropertyInfo pi;
+ pi.type = p_type.builtin_type;
+ if (p_type.kind == GDType::CLASS) {
+ pi.class_name = _get_class_name(*p_type.class_type);
+ } else if (p_type.kind == GDType::ENUM && p_type.enum_type != StringName()) {
+ pi.type = Variant::INT; // Only int types are recognized as enums by the EditorHelp
+ pi.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
+ // Replace :: from enum's use of fully qualified class names with regular .
+ pi.class_name = String(p_type.native_type).replace("::", ".");
+ } else if (p_type.kind == GDType::NATIVE) {
+ pi.class_name = p_type.native_type;
+ }
+ return pi;
+}
+
+void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) {
+ p_script->_clear_doc();
+
+ DocData::ClassDoc &doc = p_script->doc;
+
+ doc.script_path = _get_script_path(p_script->get_script_path());
+ if (p_script->name.is_empty()) {
+ doc.name = doc.script_path;
+ } else {
+ doc.name = p_script->name;
+ }
+
+ if (p_script->_owner) {
+ doc.name = p_script->_owner->doc.name + "." + doc.name;
+ doc.script_path = doc.script_path + "." + doc.name;
+ }
+
+ doc.is_script_doc = true;
+
+ if (p_script->base.is_valid() && p_script->base->is_valid()) {
+ if (!p_script->base->doc.name.is_empty()) {
+ doc.inherits = p_script->base->doc.name;
+ } else {
+ doc.inherits = p_script->base->get_instance_base_type();
+ }
+ } else if (p_script->native.is_valid()) {
+ 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) {
+ DocData::TutorialDoc td;
+ td.title = p.first;
+ td.link = p.second;
+ doc.tutorials.append(td);
+ }
+
+ for (const GDP::ClassNode::Member &member : p_class->members) {
+ switch (member.type) {
+ case GDP::ClassNode::Member::CLASS: {
+ const GDP::ClassNode *inner_class = member.m_class;
+ const StringName &class_name = inner_class->identifier->name;
+
+ p_script->member_lines[class_name] = inner_class->start_line;
+
+ // Recursively generate inner class docs
+ // Needs inner GDScripts to exist: previously generated in GDScriptCompiler::make_scripts()
+ GDScriptDocGen::generate_docs(*p_script->subclasses[class_name], inner_class);
+ } break;
+
+ case GDP::ClassNode::Member::CONSTANT: {
+ const GDP::ConstantNode *m_const = member.constant;
+ const StringName &const_name = member.constant->identifier->name;
+
+ 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);
+ doc.constants.push_back(const_doc);
+ } break;
+
+ case GDP::ClassNode::Member::FUNCTION: {
+ const GDP::FunctionNode *m_func = member.function;
+ const StringName &func_name = m_func->identifier->name;
+
+ p_script->member_lines[func_name] = m_func->start_line;
+
+ MethodInfo mi;
+ mi.name = func_name;
+
+ if (m_func->return_type) {
+ mi.return_val = _property_info_from_datatype(m_func->return_type->get_datatype());
+ }
+ for (const GDScriptParser::ParameterNode *p : m_func->parameters) {
+ PropertyInfo pi = _property_info_from_datatype(p->get_datatype());
+ pi.name = p->identifier->name;
+ mi.arguments.push_back(pi);
+ }
+
+ DocData::MethodDoc method_doc;
+ DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_description);
+ doc.methods.push_back(method_doc);
+ } break;
+
+ case GDP::ClassNode::Member::SIGNAL: {
+ const GDP::SignalNode *m_signal = member.signal;
+ const StringName &signal_name = m_signal->identifier->name;
+
+ p_script->member_lines[signal_name] = m_signal->start_line;
+
+ MethodInfo mi;
+ mi.name = signal_name;
+ for (const GDScriptParser::ParameterNode *p : m_signal->parameters) {
+ PropertyInfo pi = _property_info_from_datatype(p->get_datatype());
+ pi.name = p->identifier->name;
+ mi.arguments.push_back(pi);
+ }
+
+ DocData::MethodDoc signal_doc;
+ DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_description);
+ doc.signals.push_back(signal_doc);
+ } break;
+
+ case GDP::ClassNode::Member::VARIABLE: {
+ const GDP::VariableNode *m_var = member.variable;
+ const StringName &var_name = m_var->identifier->name;
+
+ p_script->member_lines[var_name] = m_var->start_line;
+
+ DocData::PropertyDoc prop_doc;
+
+ prop_doc.name = var_name;
+ prop_doc.description = m_var->doc_description;
+
+ GDType dt = m_var->get_datatype();
+ switch (dt.kind) {
+ case GDType::CLASS:
+ prop_doc.type = _get_class_name(*dt.class_type);
+ break;
+ case GDType::VARIANT:
+ prop_doc.type = "Variant";
+ break;
+ case GDType::ENUM:
+ prop_doc.type = Variant::get_type_name(dt.builtin_type);
+ // Replace :: from enum's use of fully qualified class names with regular .
+ prop_doc.enumeration = String(dt.native_type).replace("::", ".");
+ break;
+ case GDType::NATIVE:;
+ prop_doc.type = dt.native_type;
+ break;
+ case GDType::BUILTIN:
+ prop_doc.type = Variant::get_type_name(dt.builtin_type);
+ break;
+ default:
+ // SCRIPT: can be preload()'d and perhaps used as types directly?
+ // RESOLVING & UNRESOLVED should never happen since docgen requires analyzing w/o errors
+ break;
+ }
+
+ if (m_var->property == GDP::VariableNode::PROP_SETGET) {
+ if (m_var->setter_pointer != nullptr) {
+ prop_doc.setter = m_var->setter_pointer->name;
+ }
+ if (m_var->getter_pointer != nullptr) {
+ prop_doc.getter = m_var->getter_pointer->name;
+ }
+ }
+
+ if (m_var->initializer && m_var->initializer->is_constant) {
+ prop_doc.default_value = m_var->initializer->reduced_value.get_construct_string().replace("\n", "");
+ }
+
+ prop_doc.overridden = false;
+
+ doc.properties.push_back(prop_doc);
+ } break;
+
+ case GDP::ClassNode::Member::ENUM: {
+ const GDP::EnumNode *m_enum = member.m_enum;
+ StringName name = m_enum->identifier->name;
+
+ p_script->member_lines[name] = m_enum->start_line;
+
+ 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;
+
+ doc.enums[const_doc.name] = const_doc.description;
+ doc.constants.push_back(const_doc);
+ }
+
+ } break;
+
+ case GDP::ClassNode::Member::ENUM_VALUE: {
+ const GDP::EnumNode::Value &m_enum_val = member.enum_value;
+ const StringName &name = m_enum_val.identifier->name;
+
+ 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);
+ } break;
+ case GDP::ClassNode::Member::GROUP:
+ case GDP::ClassNode::Member::UNDEFINED:
+ default:
+ break;
+ }
+ }
+
+ // Add doc to the outer-most class.
+ p_script->_add_doc(doc);
+}
diff --git a/modules/gdscript/editor/gdscript_docgen.h b/modules/gdscript/editor/gdscript_docgen.h
new file mode 100644
index 0000000000..bb3647196a
--- /dev/null
+++ b/modules/gdscript/editor/gdscript_docgen.h
@@ -0,0 +1,42 @@
+/**************************************************************************/
+/* gdscript_docgen.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 GDSCRIPT_DOCGEN_H
+#define GDSCRIPT_DOCGEN_H
+
+#include "../gdscript_parser.h"
+#include "core/doc_data.h"
+
+class GDScriptDocGen {
+public:
+ static void generate_docs(GDScript *p_script, const GDScriptParser::ClassNode *p_class);
+};
+
+#endif // GDSCRIPT_DOCGEN_H
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 2646c1ad15..d0790aba25 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -52,6 +52,7 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_paths.h"
+#include "editor/gdscript_docgen.h"
#endif
///////////////////////////
@@ -340,12 +341,11 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
r_list->push_back(E);
}
- props.clear();
-
if (!p_include_base) {
break;
}
+ props.clear();
sptr = sptr->_base;
}
}
@@ -461,9 +461,9 @@ void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List
}
void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
- if (_owner) {
+ if (_owner) { // Only the top-level class stores doc info
_owner->_add_doc(p_inner_class);
- } else {
+ } else { // Remove old docs, add new
for (int i = 0; i < docs.size(); i++) {
if (docs[i].name == p_inner_class.name) {
docs.remove_at(i);
@@ -478,167 +478,6 @@ void GDScript::_clear_doc() {
docs.clear();
doc = DocData::ClassDoc();
}
-
-void GDScript::_update_doc() {
- _clear_doc();
-
- doc.script_path = vformat(R"("%s")", get_script_path().get_slice("://", 1));
- if (!name.is_empty()) {
- doc.name = name;
- } else {
- doc.name = doc.script_path;
- }
-
- if (_owner) {
- doc.name = _owner->doc.name + "." + doc.name;
- doc.script_path = doc.script_path + "." + doc.name;
- }
-
- doc.is_script_doc = true;
-
- if (base.is_valid() && base->is_valid()) {
- if (!base->doc.name.is_empty()) {
- doc.inherits = base->doc.name;
- } else {
- doc.inherits = base->get_instance_base_type();
- }
- } else if (native.is_valid()) {
- doc.inherits = native->get_name();
- }
-
- doc.brief_description = doc_brief_description;
- doc.description = doc_description;
- doc.tutorials = doc_tutorials;
-
- for (const KeyValue<String, DocData::EnumDoc> &E : doc_enums) {
- if (!E.value.description.is_empty()) {
- doc.enums[E.key] = E.value.description;
- }
- }
-
- List<MethodInfo> methods;
- _get_script_method_list(&methods, false);
- for (int i = 0; i < methods.size(); i++) {
- // Ignore internal methods.
- if (methods[i].name[0] == '@') {
- continue;
- }
-
- DocData::MethodDoc method_doc;
- const String &class_name = methods[i].name;
- if (member_functions.has(class_name)) {
- GDScriptFunction *fn = member_functions[class_name];
-
- // Change class name if return type is script reference.
- GDScriptDataType return_type = fn->get_return_type();
- if (return_type.kind == GDScriptDataType::GDSCRIPT) {
- methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type));
- }
-
- // Change class name if argument is script reference.
- for (int j = 0; j < fn->get_argument_count(); j++) {
- GDScriptDataType arg_type = fn->get_argument_type(j);
- if (arg_type.kind == GDScriptDataType::GDSCRIPT) {
- methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type));
- }
- }
- }
- if (doc_functions.has(methods[i].name)) {
- DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]);
- } else {
- DocData::method_doc_from_methodinfo(method_doc, methods[i], String());
- }
- doc.methods.push_back(method_doc);
- }
-
- List<PropertyInfo> props;
- _get_script_property_list(&props, false);
- for (int i = 0; i < props.size(); i++) {
- if (props[i].usage & PROPERTY_USAGE_CATEGORY || props[i].usage & PROPERTY_USAGE_GROUP || props[i].usage & PROPERTY_USAGE_SUBGROUP) {
- continue;
- }
- ScriptMemberInfo scr_member_info;
- scr_member_info.propinfo = props[i];
- scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- if (member_indices.has(props[i].name)) {
- const MemberInfo &mi = member_indices[props[i].name];
- scr_member_info.setter = mi.setter;
- scr_member_info.getter = mi.getter;
- if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) {
- scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name(
- Object::cast_to<GDScript>(mi.data_type.script_type));
- }
- }
- if (member_default_values.has(props[i].name)) {
- scr_member_info.has_default_value = true;
- scr_member_info.default_value = member_default_values[props[i].name];
- }
- if (doc_variables.has(props[i].name)) {
- scr_member_info.doc_string = doc_variables[props[i].name];
- }
-
- DocData::PropertyDoc prop_doc;
- DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info);
- doc.properties.push_back(prop_doc);
- }
-
- List<MethodInfo> signals;
- _get_script_signal_list(&signals, false);
- for (int i = 0; i < signals.size(); i++) {
- DocData::MethodDoc signal_doc;
- if (doc_signals.has(signals[i].name)) {
- DocData::signal_doc_from_methodinfo(signal_doc, signals[i], doc_signals[signals[i].name]);
- } else {
- DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
- }
- doc.signals.push_back(signal_doc);
- }
-
- for (const KeyValue<StringName, Variant> &E : constants) {
- if (subclasses.has(E.key)) {
- continue;
- }
-
- // Enums.
- bool is_enum = false;
- if (E.value.get_type() == Variant::DICTIONARY) {
- if (doc_enums.has(E.key)) {
- is_enum = true;
- for (int i = 0; i < doc_enums[E.key].values.size(); i++) {
- doc_enums[E.key].values.write[i].enumeration = E.key;
- doc.constants.push_back(doc_enums[E.key].values[i]);
- }
- }
- }
- if (!is_enum && doc_enums.has("@unnamed_enums")) {
- for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) {
- if (E.key == doc_enums["@unnamed_enums"].values[i].name) {
- is_enum = true;
- DocData::ConstantDoc constant_doc;
- constant_doc.enumeration = "@unnamed_enums";
- DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_enums["@unnamed_enums"].values[i].description);
- doc.constants.push_back(constant_doc);
- break;
- }
- }
- }
- if (!is_enum) {
- DocData::ConstantDoc constant_doc;
- String const_description;
- if (doc_constants.has(E.key)) {
- const_description = doc_constants[E.key];
- }
- DocData::constant_doc_from_variant(constant_doc, E.key, E.value, const_description);
- doc.constants.push_back(constant_doc);
- }
- }
-
- for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
- E.value->_update_doc();
- }
-
- _add_doc(doc);
-}
#endif
bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
@@ -812,6 +651,49 @@ String GDScript::_get_debug_path() const {
}
}
+Error GDScript::_static_init() {
+ if (static_initializer) {
+ Callable::CallError call_err;
+ static_initializer->call(nullptr, nullptr, 0, call_err);
+ if (call_err.error != Callable::CallError::CALL_OK) {
+ return ERR_CANT_CREATE;
+ }
+ }
+ Error err = OK;
+ for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
+ err = inner.value->_static_init();
+ if (err) {
+ break;
+ }
+ }
+ return err;
+}
+
+#ifdef TOOLS_ENABLED
+
+void GDScript::_save_old_static_data() {
+ old_static_variables_indices = static_variables_indices;
+ old_static_variables = static_variables;
+ for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
+ inner.value->_save_old_static_data();
+ }
+}
+
+void GDScript::_restore_old_static_data() {
+ for (KeyValue<StringName, MemberInfo> &E : old_static_variables_indices) {
+ if (static_variables_indices.has(E.key)) {
+ static_variables.write[static_variables_indices[E.key].index] = old_static_variables[E.value.index];
+ }
+ }
+ old_static_variables_indices.clear();
+ old_static_variables.clear();
+ for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
+ inner.value->_restore_old_static_data();
+ }
+}
+
+#endif
+
Error GDScript::reload(bool p_keep_state) {
if (reloading) {
return OK;
@@ -857,6 +739,14 @@ Error GDScript::reload(bool p_keep_state) {
}
}
+ bool can_run = ScriptServer::is_scripting_enabled() || is_tool();
+
+#ifdef TOOLS_ENABLED
+ if (p_keep_state && can_run && is_valid()) {
+ _save_old_static_data();
+ }
+#endif
+
valid = false;
GDScriptParser parser;
Error err = parser.parse(source, path, false);
@@ -887,7 +777,7 @@ Error GDScript::reload(bool p_keep_state) {
return ERR_PARSE_ERROR;
}
- bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
+ can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
@@ -905,6 +795,13 @@ Error GDScript::reload(bool p_keep_state) {
return err;
}
}
+
+#ifdef TOOLS_ENABLED
+ // Done after compilation because it needs the GDScript object's inner class GDScript objects,
+ // which are made by calling make_scripts() within compiler.compile() above.
+ GDScriptDocGen::generate_docs(this, parser.get_tree());
+#endif
+
#ifdef DEBUG_ENABLED
for (const GDScriptWarning &warning : parser.get_warnings()) {
if (EngineDebugger::is_active()) {
@@ -914,6 +811,19 @@ Error GDScript::reload(bool p_keep_state) {
}
#endif
+ if (can_run) {
+ err = _static_init();
+ if (err) {
+ return err;
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ if (can_run && p_keep_state) {
+ _restore_old_static_data();
+ }
+#endif
+
reloading = false;
return OK;
}
@@ -942,6 +852,10 @@ const Variant GDScript::get_rpc_config() const {
return rpc_config;
}
+void GDScript::unload_static() const {
+ GDScriptCache::remove_script(fully_qualified_name);
+}
+
Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
@@ -978,6 +892,19 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
}
+
+ {
+ HashMap<StringName, MemberInfo>::ConstIterator E = static_variables_indices.find(p_name);
+ if (E) {
+ if (E->value.getter) {
+ Callable::CallError ce;
+ r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce);
+ return true;
+ }
+ r_ret = static_variables[E->value.index];
+ return true;
+ }
+ }
top = top->_base;
}
@@ -995,7 +922,32 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
set_source_code(p_value);
reload();
} else {
- return false;
+ const GDScript *top = this;
+ while (top) {
+ HashMap<StringName, MemberInfo>::ConstIterator E = static_variables_indices.find(p_name);
+ if (E) {
+ const GDScript::MemberInfo *member = &E->value;
+ Variant value = p_value;
+ if (member->data_type.has_type && !member->data_type.is_type(value)) {
+ const Variant *args = &p_value;
+ Callable::CallError err;
+ Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
+ if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
+ return false;
+ }
+ }
+ if (member->setter) {
+ const Variant *args = &value;
+ Callable::CallError err;
+ callp(member->setter, &args, 1, err);
+ return err.error == Callable::CallError::CALL_OK;
+ } else {
+ static_variables.write[member->index] = value;
+ return true;
+ }
+ }
+ top = top->_base;
+ }
}
return true;
@@ -1266,7 +1218,6 @@ void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_
else if (base_cache.is_valid()) {
base_cache->get_script_signal_list(r_list);
}
-
#endif
}
@@ -1448,6 +1399,13 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) {
E.value.data_type.script_type_ref = Ref<Script>();
}
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : static_variables_indices) {
+ clear_data->scripts.insert(E.value.data_type.script_type_ref);
+ E.value.data_type.script_type_ref = Ref<Script>();
+ }
+ static_variables.clear();
+ static_variables_indices.clear();
+
if (implicit_initializer) {
clear_data->functions.insert(implicit_initializer);
implicit_initializer = nullptr;
@@ -1458,6 +1416,11 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) {
implicit_ready = nullptr;
}
+ if (static_initializer) {
+ clear_data->functions.insert(static_initializer);
+ static_initializer = nullptr;
+ }
+
_save_orphaned_subclasses(clear_data);
#ifdef TOOLS_ENABLED
@@ -1512,10 +1475,6 @@ GDScript::~GDScript() {
GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
}
-
- if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
- GDScriptCache::remove_script(get_path());
- }
}
//////////////////////////////
@@ -2546,6 +2505,7 @@ GDScriptLanguage::GDScriptLanguage() {
ERR_FAIL_COND(singleton);
singleton = this;
strings._init = StaticCString::create("_init");
+ strings._static_init = StaticCString::create("_static_init");
strings._notification = StaticCString::create("_notification");
strings._set = StaticCString::create("_set");
strings._get = StaticCString::create("_get");
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 0117ed40ab..60bd9eef53 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -83,6 +83,7 @@ class GDScript : public Script {
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
+ friend class GDScriptDocGen;
friend class GDScriptLanguage;
friend struct GDScriptUtilityFunctionsDefinitions;
@@ -93,6 +94,8 @@ class GDScript : public Script {
HashSet<StringName> members; //members are just indices to the instantiated script.
HashMap<StringName, Variant> constants;
+ HashMap<StringName, MemberInfo> static_variables_indices;
+ Vector<Variant> static_variables;
HashMap<StringName, GDScriptFunction *> member_functions;
HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
HashMap<StringName, Ref<GDScript>> subclasses;
@@ -101,6 +104,12 @@ class GDScript : public Script {
#ifdef TOOLS_ENABLED
+ // For static data storage during hot-reloading.
+ HashMap<StringName, MemberInfo> old_static_variables_indices;
+ Vector<Variant> old_static_variables;
+ void _save_old_static_data();
+ void _restore_old_static_data();
+
HashMap<StringName, int> member_lines;
HashMap<StringName, Variant> member_default_values;
List<PropertyInfo> members_cache;
@@ -113,16 +122,7 @@ class GDScript : public Script {
DocData::ClassDoc doc;
Vector<DocData::ClassDoc> docs;
- String doc_brief_description;
- String doc_description;
- Vector<DocData::TutorialDoc> doc_tutorials;
- HashMap<String, String> doc_functions;
- HashMap<String, String> doc_variables;
- HashMap<String, String> doc_constants;
- HashMap<String, String> doc_signals;
- HashMap<String, DocData::EnumDoc> doc_enums;
void _clear_doc();
- void _update_doc();
void _add_doc(const DocData::ClassDoc &p_inner_class);
#endif
@@ -131,6 +131,9 @@ class GDScript : public Script {
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
GDScriptFunction *implicit_ready = nullptr;
+ GDScriptFunction *static_initializer = nullptr;
+
+ Error _static_init();
int subclass_count = 0;
RBSet<Object *> instances;
@@ -276,6 +279,8 @@ public:
virtual const Variant get_rpc_config() const override;
+ void unload_static() const;
+
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
#endif
@@ -447,6 +452,7 @@ public:
struct {
StringName _init;
+ StringName _static_init;
StringName _notification;
StringName _set;
StringName _get;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index aa91f8fe8d..9092ae2969 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -879,6 +879,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
#endif
switch (member.type) {
case GDScriptParser::ClassNode::Member::VARIABLE: {
+ bool previous_static_context = static_context;
+ static_context = member.variable->is_static;
check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
member.variable->set_datatype(resolving_datatype);
resolve_variable(member.variable, false);
@@ -890,6 +892,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
E->apply(parser, member.variable);
}
}
+ static_context = previous_static_context;
#ifdef DEBUG_ENABLED
if (member.variable->exported && member.variable->onready) {
parser->push_warning(member.variable, GDScriptWarning::ONREADY_WITH_EXPORT);
@@ -897,7 +900,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
if (member.variable->initializer) {
// Check if it is call to get_node() on self (using shorthand $ or not), so we can check if @onready is needed.
// This could be improved by traversing the expression fully and checking the presence of get_node at any level.
- if (!member.variable->onready && member.variable->initializer && (member.variable->initializer->type == GDScriptParser::Node::GET_NODE || member.variable->initializer->type == GDScriptParser::Node::CALL || member.variable->initializer->type == GDScriptParser::Node::CAST)) {
+ if (!member.variable->is_static && !member.variable->onready && member.variable->initializer && (member.variable->initializer->type == GDScriptParser::Node::GET_NODE || member.variable->initializer->type == GDScriptParser::Node::CALL || member.variable->initializer->type == GDScriptParser::Node::CAST)) {
GDScriptParser::Node *expr = member.variable->initializer;
if (expr->type == GDScriptParser::Node::CAST) {
expr = static_cast<GDScriptParser::CastNode *>(expr)->operand;
@@ -1082,6 +1085,10 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
p_source = p_class;
}
+#ifdef DEBUG_ENABLED
+ bool has_static_data = p_class->has_static_data;
+#endif
+
if (!p_class->resolved_interface) {
if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
@@ -1124,7 +1131,29 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
for (int i = 0; i < p_class->members.size(); i++) {
resolve_class_member(p_class, i);
+
+#ifdef DEBUG_ENABLED
+ if (!has_static_data) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ has_static_data = member.m_class->has_static_data;
+ }
+ }
+#endif
}
+
+#ifdef DEBUG_ENABLED
+ if (!has_static_data && p_class->annotated_static_unload) {
+ GDScriptParser::Node *static_unload = nullptr;
+ for (GDScriptParser::AnnotationNode *node : p_class->annotations) {
+ if (node->name == "@static_unload") {
+ static_unload = node;
+ break;
+ }
+ }
+ parser->push_warning(static_unload ? static_unload : p_class, GDScriptWarning::REDUNDANT_STATIC_UNLOAD);
+ }
+#endif
}
}
@@ -1307,10 +1336,11 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable);
}
}
+ }
+
#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
+ parser->ignored_warnings = previously_ignored_warnings;
#endif // DEBUG_ENABLED
- }
}
}
@@ -1499,6 +1529,8 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
+ bool previous_static_context = static_context;
+ static_context = p_function->is_static;
GDScriptParser::DataType prev_datatype = p_function->get_datatype();
@@ -1514,7 +1546,11 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
resolve_parameter(p_function->parameters[i]);
#ifdef DEBUG_ENABLED
if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
- parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_name, p_function->parameters[i]->identifier->name);
+ String visible_name = function_name;
+ if (function_name == StringName()) {
+ visible_name = p_is_lambda ? "<anonymous lambda>" : "<unknown function>";
+ }
+ 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");
#endif // DEBUG_ENABLED
@@ -1542,6 +1578,18 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
push_error("Constructor cannot have an explicit return type.", p_function->return_type);
}
}
+ } else if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._static_init) {
+ // Static constructor.
+ GDScriptParser::DataType return_type;
+ return_type.kind = GDScriptParser::DataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
+ p_function->set_datatype(return_type);
+ if (p_function->return_type) {
+ GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type);
+ if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) {
+ push_error("Static constructor cannot have an explicit return type.", p_function->return_type);
+ }
+ }
} else {
if (p_function->return_type != nullptr) {
p_function->set_datatype(type_from_metatype(resolve_datatype(p_function->return_type)));
@@ -1625,6 +1673,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->ignored_warnings = previously_ignored_warnings;
#endif
parser->current_function = previous_function;
+ static_context = previous_static_context;
}
void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda) {
@@ -2027,7 +2076,7 @@ void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
if (p_assert->condition->is_constant) {
if (p_assert->condition->reduced_value.booleanize()) {
parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_TRUE);
- } else {
+ } else if (!(p_assert->condition->type == GDScriptParser::Node::LITERAL && static_cast<GDScriptParser::LiteralNode *>(p_assert->condition)->value.get_type() == Variant::BOOL)) {
parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_FALSE);
}
}
@@ -3050,13 +3099,17 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
base_type.is_meta_type = false;
}
- if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
- // Get the parent function above any lambda.
- GDScriptParser::FunctionNode *parent_function = parser->current_function;
- while (parent_function->source_lambda) {
- parent_function = parent_function->source_lambda->parent_function;
+ if (is_self && static_context && !is_static) {
+ if (parser->current_function) {
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
+ } else {
+ push_error(vformat(R"*(Cannot call non-static function "%s()" for static variable initializer.)*", p_call->function_name), p_call);
}
- push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
} else if (!is_self && base_type.is_meta_type && !is_static) {
base_type.is_meta_type = false; // For `to_string()`.
push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call);
@@ -3073,7 +3126,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
}
- if (is_static && !base_type.is_meta_type && !(is_self && parser->current_function != nullptr && parser->current_function->is_static)) {
+ if (is_static && !is_constructor && !base_type.is_meta_type && !(is_self && static_context)) {
String caller_type = String(base_type.native_type);
if (caller_type.is_empty()) {
@@ -3125,7 +3178,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
- if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type);
if (renamed_function_name) {
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()");
@@ -3326,7 +3379,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
} else if (base.is_hard_type()) {
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
- if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
if (renamed_identifier_name) {
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
@@ -3366,7 +3419,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (base.is_hard_type()) {
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
- if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
if (renamed_identifier_name) {
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
@@ -3428,9 +3481,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
case GDScriptParser::ClassNode::Member::VARIABLE: {
- if (is_base && !base.is_meta_type) {
+ if (is_base && (!base.is_meta_type || member.variable->is_static)) {
p_identifier->set_datatype(member.get_datatype());
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
p_identifier->variable_source = member.variable;
member.variable->usages += 1;
return;
@@ -3572,6 +3625,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
mark_lambda_use_self();
p_identifier->variable_source->usages++;
[[fallthrough]];
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
found_source = true;
@@ -3602,13 +3656,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (found_source) {
bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
- if ((source_is_variable || source_is_signal) && parser->current_function && parser->current_function->is_static) {
- // Get the parent function above any lambda.
- GDScriptParser::FunctionNode *parent_function = parser->current_function;
- while (parent_function->source_lambda) {
- parent_function = parent_function->source_lambda->parent_function;
+ if ((source_is_variable || source_is_signal) && static_context) {
+ if (parser->current_function) {
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+ push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
+ } else {
+ push_error(vformat(R"*(Cannot access %s "%s" for a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier);
}
- push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
}
if (!lambda_stack.is_empty()) {
@@ -3750,7 +3808,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
} else {
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
- if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
if (renamed_identifier_name) {
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 5902035bcd..0c7bf4125b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -43,6 +43,7 @@ class GDScriptAnalyzer {
const GDScriptParser::EnumNode *current_enum = nullptr;
List<GDScriptParser::LambdaNode *> lambda_stack;
+ bool static_context = false;
// Tests for detecting invalid overloading of script members
static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node, const GDScriptParser::Node *p_member);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 42c6f80455..fc684e4d8f 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -366,6 +366,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return p_address.address | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
case Address::CONSTANT:
return p_address.address | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ case Address::STATIC_VARIABLE:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_STATIC_VAR << GDScriptFunction::ADDR_BITS);
case Address::LOCAL_VARIABLE:
case Address::FUNCTION_PARAMETER:
return p_address.address | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index a009e8e0a8..126fccbbf0 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -342,6 +342,16 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
return err;
}
+void GDScriptCache::add_static_script(Ref<GDScript> p_script) {
+ ERR_FAIL_COND_MSG(p_script.is_null(), "Trying to cache empty script as static.");
+ ERR_FAIL_COND_MSG(!p_script->is_valid(), "Trying to cache non-compiled script as static.");
+ singleton->static_gdscript_cache[p_script->get_fully_qualified_name()] = p_script;
+}
+
+void GDScriptCache::remove_static_script(const String &p_fqcn) {
+ singleton->static_gdscript_cache.erase(p_fqcn);
+}
+
Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) {
MutexLock lock(singleton->mutex);
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index c7f40f6e82..28266a1c0b 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -78,6 +78,7 @@ class GDScriptCache {
HashMap<String, GDScriptParserRef *> parser_map;
HashMap<String, Ref<GDScript>> shallow_gdscript_cache;
HashMap<String, Ref<GDScript>> full_gdscript_cache;
+ HashMap<String, Ref<GDScript>> static_gdscript_cache;
HashMap<String, HashSet<String>> dependencies;
HashMap<String, Ref<PackedScene>> packed_scene_cache;
HashMap<String, HashSet<String>> packed_scene_dependencies;
@@ -101,6 +102,8 @@ public:
static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String(), bool p_update_from_disk = false);
static Ref<GDScript> get_cached_script(const String &p_path);
static Error finish_compiling(const String &p_owner);
+ static void add_static_script(Ref<GDScript> p_script);
+ static void remove_static_script(const String &p_fqcn);
static Ref<PackedScene> get_packed_scene(const String &p_path, Error &r_error, const String &p_owner = "");
static void clear_unreferenced_packed_scenes();
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index e82b4b08ab..dbc2466393 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -44,6 +44,7 @@ public:
CLASS,
MEMBER,
CONSTANT,
+ STATIC_VARIABLE,
LOCAL_VARIABLE,
FUNCTION_PARAMETER,
TEMPORARY,
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 42619a12a8..327e24ef11 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -254,13 +254,29 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
return temp;
} else {
- // No getter or inside getter: direct member access.,
+ // 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.
+ if (codegen.script->static_variables_indices.has(identifier)) {
+ if (codegen.script->static_variables_indices[identifier].getter != StringName() && codegen.script->static_variables_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->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, codegen.script->static_variables_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct variable access.
+ int idx = codegen.script->static_variables_indices[identifier].index;
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::STATIC_VARIABLE, idx, codegen.script->static_variables_indices[identifier].data_type);
+ }
+ }
+
// Try class constants.
{
GDScript *owner = codegen.script;
@@ -563,7 +579,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Not exact arguments, but still can use method bind call.
gen->write_call_method_bind(result, self, method, arguments);
}
- } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+ } else if (codegen.is_static || (codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
if (is_awaited) {
@@ -909,6 +925,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool is_member_property = false;
bool member_property_has_setter = false;
bool member_property_is_in_setter = false;
+ bool is_static = false;
StringName member_property_setter_function;
List<const GDScriptParser::SubscriptNode *> chain;
@@ -925,14 +942,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName var_name = identifier->name;
if (_is_class_member_property(codegen, var_name)) {
assign_class_member_property = var_name;
- } else if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
+ } else if (!_is_local_or_parameter(codegen, var_name) && (codegen.script->member_indices.has(var_name) || codegen.script->static_variables_indices.has(var_name))) {
is_member_property = true;
- member_property_setter_function = codegen.script->member_indices[var_name].setter;
+ is_static = codegen.script->static_variables_indices.has(var_name);
+ const GDScript::MemberInfo &minfo = is_static ? codegen.script->static_variables_indices[var_name] : codegen.script->member_indices[var_name];
+ member_property_setter_function = minfo.setter;
member_property_has_setter = member_property_setter_function != StringName();
member_property_is_in_setter = member_property_has_setter && member_property_setter_function == codegen.function_name;
- target_member_property.mode = GDScriptCodeGenerator::Address::MEMBER;
- target_member_property.address = codegen.script->member_indices[var_name].index;
- target_member_property.type = codegen.script->member_indices[var_name].data_type;
+ target_member_property.mode = is_static ? GDScriptCodeGenerator::Address::STATIC_VARIABLE : GDScriptCodeGenerator::Address::MEMBER;
+ target_member_property.address = minfo.index;
+ target_member_property.type = minfo.data_type;
}
}
break;
@@ -1085,7 +1104,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (member_property_has_setter && !member_property_is_in_setter) {
Vector<GDScriptCodeGenerator::Address> args;
args.push_back(assigned);
- gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
+ GDScriptCodeGenerator::Address self = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);
+ gen->write_call(GDScriptCodeGenerator::Address(), self, member_property_setter_function, args);
} else {
gen->write_assign(target_member_property, assigned);
}
@@ -1134,16 +1154,19 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool is_member = false;
bool has_setter = false;
bool is_in_setter = false;
+ bool is_static = false;
StringName setter_function;
StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
+ if (!_is_local_or_parameter(codegen, var_name) && (codegen.script->member_indices.has(var_name) || codegen.script->static_variables_indices.has(var_name))) {
is_member = true;
- setter_function = codegen.script->member_indices[var_name].setter;
+ is_static = codegen.script->static_variables_indices.has(var_name);
+ GDScript::MemberInfo &minfo = is_static ? codegen.script->static_variables_indices[var_name] : codegen.script->member_indices[var_name];
+ setter_function = minfo.setter;
has_setter = setter_function != StringName();
is_in_setter = has_setter && setter_function == codegen.function_name;
- member.mode = GDScriptCodeGenerator::Address::MEMBER;
- member.address = codegen.script->member_indices[var_name].index;
- member.type = codegen.script->member_indices[var_name].data_type;
+ member.mode = is_static ? GDScriptCodeGenerator::Address::STATIC_VARIABLE : GDScriptCodeGenerator::Address::MEMBER;
+ member.address = minfo.index;
+ member.type = minfo.data_type;
}
GDScriptCodeGenerator::Address target;
@@ -2001,6 +2024,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
codegen.function_name = func_name;
+ codegen.is_static = is_static;
codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
int optional_parameters = 0;
@@ -2024,7 +2048,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
bool is_implicit_ready = !p_func && p_for_ready;
if (!p_for_lambda && is_implicit_initializer) {
- // Initialize the default values for type variables before anything.
+ // Initialize the default values for typed variables before anything.
// This avoids crashes if they are accessed with validated calls before being properly initialized.
// It may happen with out-of-order access or with `@onready` variables.
for (const GDScriptParser::ClassNode::Member &member : p_class->members) {
@@ -2033,6 +2057,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
const GDScriptParser::VariableNode *field = member.variable;
+ if (field->is_static) {
+ continue;
+ }
+
GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);
GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
@@ -2056,6 +2084,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
continue;
}
const GDScriptParser::VariableNode *field = p_class->members[i].variable;
+ if (field->is_static) {
+ continue;
+ }
+
if (field->onready != is_implicit_ready) {
// Only initialize in @implicit_ready.
continue;
@@ -2145,12 +2177,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
if (p_func) {
codegen.generator->set_initial_line(p_func->start_line);
-#ifdef TOOLS_ENABLED
- if (!p_for_lambda) {
- p_script->member_lines[func_name] = p_func->start_line;
- p_script->doc_functions[func_name] = p_func->doc_description;
- }
-#endif
} else {
codegen.generator->set_initial_line(0);
}
@@ -2189,6 +2215,135 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
return gd_function;
}
+GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class) {
+ r_error = OK;
+ CodeGen codegen;
+ codegen.generator = memnew(GDScriptByteCodeGenerator);
+
+ codegen.class_node = p_class;
+ codegen.script = p_script;
+
+ StringName func_name = SNAME("@static_initializer");
+ bool is_static = true;
+ Variant rpc_config;
+ GDScriptDataType return_type;
+ return_type.has_type = true;
+ return_type.kind = GDScriptDataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
+
+ codegen.function_name = func_name;
+ codegen.is_static = is_static;
+ codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
+
+ GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
+
+ // Initialize the default values for typed variables before anything.
+ // This avoids crashes if they are accessed with validated calls before being properly initialized.
+ // It may happen with out-of-order access or with `@onready` variables.
+ for (const GDScriptParser::ClassNode::Member &member : p_class->members) {
+ if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+
+ const GDScriptParser::VariableNode *field = member.variable;
+ if (!field->is_static) {
+ continue;
+ }
+
+ GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);
+
+ if (field_type.has_type) {
+ codegen.generator->write_newline(field->start_line);
+
+ if (field_type.has_container_element_type()) {
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
+ codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_set_named(class_addr, field->identifier->name, temp);
+ codegen.generator->pop_temporary();
+
+ } else if (field_type.kind == GDScriptDataType::BUILTIN) {
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
+ codegen.generator->write_construct(temp, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_set_named(class_addr, field->identifier->name, temp);
+ codegen.generator->pop_temporary();
+ }
+ // The `else` branch is for objects, in such case we leave it as `null`.
+ }
+ }
+
+ for (int i = 0; i < p_class->members.size(); i++) {
+ // Initialize static fields.
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+ const GDScriptParser::VariableNode *field = p_class->members[i].variable;
+ if (!field->is_static) {
+ continue;
+ }
+
+ GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);
+
+ if (field->initializer) {
+ // Emit proper line change.
+ codegen.generator->write_newline(field->initializer->start_line);
+
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);
+ if (r_error) {
+ memdelete(codegen.generator);
+ return nullptr;
+ }
+
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type);
+ if (field->use_conversion_assign) {
+ codegen.generator->write_assign_with_conversion(temp, src_address);
+ } else {
+ codegen.generator->write_assign(temp, src_address);
+ }
+ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ codegen.generator->write_set_named(class_addr, field->identifier->name, temp);
+ codegen.generator->pop_temporary();
+ }
+ }
+
+ if (p_script->has_method(GDScriptLanguage::get_singleton()->strings._static_init)) {
+ codegen.generator->write_newline(p_class->start_line);
+ codegen.generator->write_call(GDScriptCodeGenerator::Address(), class_addr, GDScriptLanguage::get_singleton()->strings._static_init, Vector<GDScriptCodeGenerator::Address>());
+ }
+
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active()) {
+ String signature;
+ // Path.
+ if (!p_script->get_script_path().is_empty()) {
+ signature += p_script->get_script_path();
+ }
+ // Location.
+ signature += "::0";
+
+ // Function and class.
+
+ if (p_class->identifier) {
+ signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
+ } else {
+ signature += "::" + String(func_name);
+ }
+
+ codegen.generator->set_signature(signature);
+ }
+#endif
+
+ codegen.generator->set_initial_line(p_class->start_line);
+
+ GDScriptFunction *gd_function = codegen.generator->write_end();
+
+ memdelete(codegen.generator);
+
+ return gd_function;
+}
+
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
Error err = OK;
@@ -2219,23 +2374,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
parsing_classes.insert(p_script);
p_script->clearing = true;
-#ifdef TOOLS_ENABLED
- p_script->doc_functions.clear();
- p_script->doc_variables.clear();
- p_script->doc_constants.clear();
- p_script->doc_enums.clear();
- p_script->doc_signals.clear();
- p_script->doc_tutorials.clear();
-
- p_script->doc_brief_description = p_class->doc_brief_description;
- p_script->doc_description = p_class->doc_description;
- for (int i = 0; i < p_class->doc_tutorials.size(); i++) {
- DocData::TutorialDoc td;
- td.title = p_class->doc_tutorials[i].first;
- td.link = p_class->doc_tutorials[i].second;
- p_script->doc_tutorials.append(td);
- }
-#endif
p_script->native = Ref<GDScriptNativeClass>();
p_script->base = Ref<GDScript>();
@@ -2259,19 +2397,28 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
member_functions.clear();
+ p_script->static_variables.clear();
+
if (p_script->implicit_initializer) {
memdelete(p_script->implicit_initializer);
}
if (p_script->implicit_ready) {
memdelete(p_script->implicit_ready);
}
+ if (p_script->static_initializer) {
+ memdelete(p_script->static_initializer);
+ }
+
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
+ p_script->static_variables_indices.clear();
+ p_script->static_variables.clear();
p_script->_signals.clear();
p_script->initializer = nullptr;
p_script->implicit_initializer = nullptr;
p_script->implicit_ready = nullptr;
+ p_script->static_initializer = nullptr;
p_script->clearing = false;
@@ -2379,13 +2526,15 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
} else {
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
-#ifdef TOOLS_ENABLED
- p_script->doc_variables[name] = variable->doc_description;
-#endif
- p_script->member_info[name] = prop_info;
- p_script->member_indices[name] = minfo;
- p_script->members.insert(name);
+ if (variable->is_static) {
+ minfo.index = p_script->static_variables_indices.size();
+ p_script->static_variables_indices[name] = minfo;
+ } else {
+ p_script->member_info[name] = prop_info;
+ p_script->member_indices[name] = minfo;
+ p_script->members.insert(name);
+ }
#ifdef TOOLS_ENABLED
if (variable->initializer != nullptr && variable->initializer->is_constant) {
@@ -2394,7 +2543,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
} else {
p_script->member_default_values.erase(name);
}
- p_script->member_lines[name] = variable->start_line;
#endif
} break;
@@ -2403,12 +2551,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
StringName name = constant->identifier->name;
p_script->constants.insert(name, constant->initializer->reduced_value);
-#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = constant->start_line;
- if (!constant->doc_description.is_empty()) {
- p_script->doc_constants[name] = constant->doc_description;
- }
-#endif
} break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
@@ -2416,18 +2558,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
StringName name = enum_value.identifier->name;
p_script->constants.insert(name, enum_value.value);
-#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = enum_value.identifier->start_line;
- if (!p_script->doc_enums.has("@unnamed_enums")) {
- p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc();
- p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums";
- }
- DocData::ConstantDoc const_doc;
- const_doc.name = enum_value.identifier->name;
- const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int.
- const_doc.description = enum_value.doc_description;
- p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc);
-#endif
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -2440,11 +2570,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
parameters_names.write[j] = signal->parameters[j]->identifier->name;
}
p_script->_signals[name] = parameters_names;
-#ifdef TOOLS_ENABLED
- if (!signal->doc_description.is_empty()) {
- p_script->doc_signals[name] = signal->doc_description;
- }
-#endif
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -2452,19 +2577,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
StringName name = enum_n->identifier->name;
p_script->constants.insert(name, enum_n->dictionary);
-#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = enum_n->start_line;
- p_script->doc_enums[name] = DocData::EnumDoc();
- p_script->doc_enums[name].name = name;
- p_script->doc_enums[name].description = enum_n->doc_description;
- for (int j = 0; j < enum_n->values.size(); j++) {
- DocData::ConstantDoc const_doc;
- const_doc.name = enum_n->values[j].identifier->name;
- const_doc.value = Variant(enum_n->values[j].value).operator String();
- const_doc.description = enum_n->values[j].doc_description;
- p_script->doc_enums[name].values.push_back(const_doc);
- }
-#endif
} break;
case GDScriptParser::ClassNode::Member::GROUP: {
@@ -2490,6 +2602,8 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
}
+ p_script->static_variables.resize(p_script->static_variables_indices.size());
+
parsed_classes.insert(p_script);
parsing_classes.erase(p_script);
@@ -2512,9 +2626,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
}
-#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = inner_class->start_line;
-#endif
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
}
@@ -2569,6 +2680,15 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
}
}
+ if (p_class->has_static_data) {
+ Error err = OK;
+ GDScriptFunction *func = _make_static_initializer(err, p_script, p_class);
+ p_script->static_initializer = func;
+ if (err) {
+ return err;
+ }
+ }
+
#ifdef DEBUG_ENABLED
//validate instances if keeping state
@@ -2607,7 +2727,7 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
//well, tough luck, not gonna do anything here
}
}
-#endif
+#endif // TOOLS_ENABLED
} else {
GDScriptInstance *gi = static_cast<GDScriptInstance *>(si);
gi->reload_members();
@@ -2616,7 +2736,9 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
E = N;
}
}
-#endif
+#endif //DEBUG_ENABLED
+
+ has_static_data = p_class->has_static_data;
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
@@ -2630,6 +2752,8 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
if (err) {
return err;
}
+
+ has_static_data = has_static_data || inner_class->has_static_data;
}
p_script->_init_rpc_methods_properties();
@@ -2716,9 +2840,9 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
return err;
}
-#ifdef TOOLS_ENABLED
- p_script->_update_doc();
-#endif
+ if (has_static_data && !root->annotated_static_unload) {
+ GDScriptCache::add_static_script(p_script);
+ }
return GDScriptCache::finish_compiling(main_script->get_path());
}
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 5328c17c73..2d15d461fb 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -52,6 +52,7 @@ class GDScriptCompiler {
HashMap<StringName, GDScriptCodeGenerator::Address> parameters;
HashMap<StringName, GDScriptCodeGenerator::Address> locals;
List<HashMap<StringName, GDScriptCodeGenerator::Address>> locals_stack;
+ bool is_static = false;
GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
uint32_t addr = generator->add_local(p_name, p_type);
@@ -130,6 +131,7 @@ class GDScriptCompiler {
void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
+ GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _populate_class_members(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
@@ -138,6 +140,7 @@ class GDScriptCompiler {
StringName source;
String error;
GDScriptParser::ExpressionNode *awaited_node = nullptr;
+ bool has_static_data = false;
public:
static void convert_to_initializer_type(Variant &p_variant, const GDScriptParser::VariableNode *p_node);
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index 0acc03be3d..45ad8792d9 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -851,7 +851,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
}
text += ")";
- incr = 3 + captures_count;
+ incr = 4 + captures_count;
} break;
case OPCODE_CREATE_SELF_LAMBDA: {
int instr_var_args = _code_ptr[++ip];
@@ -871,7 +871,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
}
text += ")";
- incr = 3 + captures_count;
+ incr = 4 + captures_count;
} break;
case OPCODE_JUMP: {
text += "jump ";
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index be33c7c591..829567d734 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2992,6 +2992,15 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<MethodInfo> virtual_methods;
ClassDB::get_virtual_methods(class_name, &virtual_methods);
+
+ {
+ // Not truly a virtual method, but can also be "overridden".
+ MethodInfo static_init("_static_init");
+ static_init.return_val.type = Variant::NIL;
+ static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL;
+ virtual_methods.push_back(static_init);
+ }
+
for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
if (method_hint.contains(":")) {
@@ -3089,12 +3098,7 @@ String GDScriptLanguage::_get_indentation() const {
if (use_space_indentation) {
int indent_size = EDITOR_GET("text_editor/behavior/indent/size");
-
- String space_indent = "";
- for (int i = 0; i < indent_size; i++) {
- space_indent += " ";
- }
- return space_indent;
+ return String(" ").repeat(indent_size);
}
}
#endif
@@ -3141,12 +3145,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
}
if (i >= p_from_line) {
- l = "";
- for (int j = 0; j < indent_stack.size(); j++) {
- l += indent;
- }
- l += st;
-
+ l = indent.repeat(indent_stack.size()) + st;
} else if (i > p_to_line) {
break;
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 1a5e9eef53..390e562e6f 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -409,7 +409,8 @@ public:
ADDR_TYPE_STACK = 0,
ADDR_TYPE_CONSTANT = 1,
ADDR_TYPE_MEMBER = 2,
- ADDR_TYPE_MAX = 3,
+ ADDR_TYPE_STATIC_VAR = 3,
+ ADDR_TYPE_MAX = 4,
};
enum FixedAddresses {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index bcc116cda2..3bce258072 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -81,6 +81,8 @@ GDScriptParser::GDScriptParser() {
// TODO: Should this be static?
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
+
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
@@ -102,6 +104,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
@@ -623,7 +626,7 @@ bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
return false;
}
-GDScriptParser::ClassNode *GDScriptParser::parse_class() {
+GDScriptParser::ClassNode *GDScriptParser::parse_class(bool p_is_static) {
ClassNode *n_class = alloc_node<ClassNode>();
ClassNode *previous_class = current_class;
@@ -724,7 +727,7 @@ void GDScriptParser::parse_extends() {
}
template <class T>
-void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind) {
+void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static) {
advance();
#ifdef TOOLS_ENABLED
@@ -749,7 +752,7 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
#endif // TOOLS_ENABLED
}
- T *member = (this->*p_parse_function)();
+ T *member = (this->*p_parse_function)(p_is_static);
if (member == nullptr) {
return;
}
@@ -761,7 +764,11 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
#ifdef TOOLS_ENABLED
// Consume doc comments.
class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
- if (has_comment(doc_comment_line)) {
+
+ // 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);
+ } 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);
} else {
@@ -799,10 +806,15 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
void GDScriptParser::parse_class_body(bool p_is_multiline) {
bool class_end = false;
+ bool next_is_static = false;
while (!class_end && !is_at_end()) {
- switch (current.type) {
+ GDScriptTokenizer::Token token = current;
+ switch (token.type) {
case GDScriptTokenizer::Token::VAR:
- parse_class_member(&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, "variable");
+ parse_class_member(&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, "variable", next_is_static);
+ if (next_is_static) {
+ current_class->has_static_data = true;
+ }
break;
case GDScriptTokenizer::Token::CONST:
parse_class_member(&GDScriptParser::parse_constant, AnnotationInfo::CONSTANT, "constant");
@@ -810,9 +822,8 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
case GDScriptTokenizer::Token::SIGNAL:
parse_class_member(&GDScriptParser::parse_signal, AnnotationInfo::SIGNAL, "signal");
break;
- case GDScriptTokenizer::Token::STATIC:
case GDScriptTokenizer::Token::FUNC:
- parse_class_member(&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, "function");
+ parse_class_member(&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, "function", next_is_static);
break;
case GDScriptTokenizer::Token::CLASS:
parse_class_member(&GDScriptParser::parse_class, AnnotationInfo::CLASS, "class");
@@ -820,6 +831,13 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
case GDScriptTokenizer::Token::ENUM:
parse_class_member(&GDScriptParser::parse_enum, AnnotationInfo::NONE, "enum");
break;
+ case GDScriptTokenizer::Token::STATIC: {
+ advance();
+ next_is_static = true;
+ if (!check(GDScriptTokenizer::Token::FUNC) && !check(GDScriptTokenizer::Token::VAR)) {
+ push_error(R"(Expected "func" or "var" after "static".)");
+ }
+ } break;
case GDScriptTokenizer::Token::ANNOTATION: {
advance();
@@ -866,6 +884,9 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
advance();
break;
}
+ if (token.type != GDScriptTokenizer::Token::STATIC) {
+ next_is_static = false;
+ }
if (panic_mode) {
synchronize();
}
@@ -875,11 +896,11 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
}
}
-GDScriptParser::VariableNode *GDScriptParser::parse_variable() {
- return parse_variable(true);
+GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static) {
+ return parse_variable(p_is_static, true);
}
-GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) {
+GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static, bool p_allow_property) {
VariableNode *variable = alloc_node<VariableNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
@@ -889,6 +910,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
variable->identifier = parse_identifier();
variable->export_info.name = variable->identifier->name;
+ variable->is_static = p_is_static;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check(GDScriptTokenizer::Token::NEWLINE)) {
@@ -1032,6 +1054,7 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_setter";
function->identifier = identifier;
+ function->is_static = p_variable->is_static;
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
@@ -1083,6 +1106,7 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_getter";
function->identifier = identifier;
+ function->is_static = p_variable->is_static;
FunctionNode *previous_function = current_function;
current_function = function;
@@ -1107,7 +1131,7 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
}
}
-GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
+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".)")) {
@@ -1174,7 +1198,7 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
return parameter;
}
-GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
+GDScriptParser::SignalNode *GDScriptParser::parse_signal(bool p_is_static) {
SignalNode *signal = alloc_node<SignalNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
@@ -1219,7 +1243,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
return signal;
}
-GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
+GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
EnumNode *enum_node = alloc_node<EnumNode>();
bool named = false;
@@ -1368,23 +1392,23 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
}
}
+ if (!p_function->source_lambda && p_function->identifier && p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._static_init) {
+ if (!p_function->is_static) {
+ push_error(R"(Static constructor must be declared static.)");
+ }
+ if (p_function->parameters.size() != 0) {
+ push_error(R"(Static constructor cannot have parameters.)");
+ }
+ current_class->has_static_data = true;
+ }
+
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after %s declaration.)", p_type));
}
-GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_static) {
FunctionNode *function = alloc_node<FunctionNode>();
- bool _static = false;
- if (previous.type == GDScriptTokenizer::Token::STATIC) {
- // TODO: Improve message if user uses "static" with "var" or "const"
- if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
- complete_extents(function);
- return nullptr;
- }
- _static = true;
- }
-
make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
@@ -1396,7 +1420,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
current_function = function;
function->identifier = parse_identifier();
- function->is_static = _static;
+ function->is_static = p_is_static;
SuiteNode *body = alloc_node<SuiteNode>();
SuiteNode *previous_suite = current_suite;
@@ -1608,11 +1632,11 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
break;
case GDScriptTokenizer::Token::VAR:
advance();
- result = parse_variable();
+ result = parse_variable(false, false);
break;
case GDScriptTokenizer::Token::CONST:
advance();
- result = parse_constant();
+ result = parse_constant(false);
break;
case GDScriptTokenizer::Token::IF:
advance();
@@ -1642,7 +1666,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
advance();
ReturnNode *n_return = alloc_node<ReturnNode>();
if (!is_statement_end()) {
- if (current_function && current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
+ if (current_function && (current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init || current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._static_init)) {
push_error(R"(Constructor cannot return a value.)");
}
n_return->return_value = parse_expression(false);
@@ -3296,8 +3320,15 @@ static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins
}
}
-bool GDScriptParser::has_comment(int p_line) {
- return tokenizer.get_comments().has(p_line);
+bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
+ bool has_comment = tokenizer.get_comments().has(p_line);
+ // If there are no comments or if we don't care whether the comment
+ // is a docstring, we have our result.
+ if (!p_must_be_doc || !has_comment) {
+ return has_comment;
+ }
+
+ return tokenizer.get_comments()[p_line].comment.begins_with("##");
}
String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
@@ -4090,6 +4121,17 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_
return true;
}
+bool GDScriptParser::static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target) {
+ ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name));
+ ClassNode *p_class = static_cast<ClassNode *>(p_target);
+ if (p_class->annotated_static_unload) {
+ push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation);
+ return false;
+ }
+ p_class->annotated_static_unload = true;
+ return true;
+}
+
GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const {
switch (type) {
case CONSTANT:
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 29841ab060..8f0265510f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -709,6 +709,8 @@ public:
ClassNode *outer = nullptr;
bool extends_used = false;
bool onready_used = false;
+ bool has_static_data = false;
+ bool annotated_static_unload = false;
String extends_path;
Vector<IdentifierNode *> extends; // List for indexing: extends A.B.C
DataType base_type;
@@ -847,6 +849,7 @@ public:
LOCAL_BIND, // Pattern bind.
MEMBER_SIGNAL,
MEMBER_VARIABLE,
+ STATIC_VARIABLE,
MEMBER_CONSTANT,
INHERITED_VARIABLE,
};
@@ -1202,6 +1205,7 @@ public:
bool onready = false;
PropertyInfo export_info;
int assignments = 0;
+ bool is_static = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@@ -1405,16 +1409,16 @@ private:
// Main blocks.
void parse_program();
- ClassNode *parse_class();
+ ClassNode *parse_class(bool p_is_static);
void parse_class_name();
void parse_extends();
void parse_class_body(bool p_is_multiline);
template <class T>
- void parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind);
- SignalNode *parse_signal();
- EnumNode *parse_enum();
+ void parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static = false);
+ SignalNode *parse_signal(bool p_is_static);
+ EnumNode *parse_enum(bool p_is_static);
ParameterNode *parse_parameter();
- FunctionNode *parse_function();
+ FunctionNode *parse_function(bool p_is_static);
void parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type);
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
@@ -1431,14 +1435,15 @@ private:
bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool rpc_annotation(const AnnotationNode *p_annotation, Node *p_target);
+ bool static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target);
// Statements.
Node *parse_statement();
- VariableNode *parse_variable();
- VariableNode *parse_variable(bool p_allow_property);
+ VariableNode *parse_variable(bool p_is_static);
+ VariableNode *parse_variable(bool p_is_static, bool p_allow_property);
VariableNode *parse_property(VariableNode *p_variable, bool p_need_indent);
void parse_property_getter(VariableNode *p_variable);
void parse_property_setter(VariableNode *p_variable);
- ConstantNode *parse_constant();
+ ConstantNode *parse_constant(bool p_is_static);
AssertNode *parse_assert();
BreakNode *parse_break();
ContinueNode *parse_continue();
@@ -1480,7 +1485,7 @@ private:
#ifdef TOOLS_ENABLED
// Doc comments.
int class_doc_line = 0x7FFFFFFF;
- bool has_comment(int p_line);
+ 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);
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 7098e4cd40..bebf34cbb3 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -680,10 +680,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool awaited = false;
#endif
#ifdef DEBUG_ENABLED
- int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 };
+ int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0, script->static_variables.size() };
#endif
- Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr };
+ Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr, script->static_variables.ptrw() };
#ifdef DEBUG_ENABLED
OPCODE_WHILE(ip < _code_size) {
@@ -774,7 +774,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool result = false;
if (value->get_type() == Variant::ARRAY) {
Array *array = VariantInternal::get_array(value);
- result = array->get_typed_builtin() == ((uint32_t)builtin_type) && array->get_typed_class_name() == native_type && array->get_typed_script() == *script_type && array->get_typed_class_name() == native_type;
+ result = array->get_typed_builtin() == ((uint32_t)builtin_type) && array->get_typed_class_name() == native_type && array->get_typed_script() == *script_type;
}
*dst = result;
@@ -1252,7 +1252,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Array *array = VariantInternal::get_array(src);
- if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) {
+ if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to assign an array of type "%s" to a variable of type "Array[%s]".)",
_get_var_type(src), _get_element_type(builtin_type, native_type, *script_type));
@@ -1651,10 +1651,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool was_freed = false;
Object *obj = ret->get_validated_object_with_check(was_freed);
- if (was_freed) {
- err_text = "Got a freed object as a result of the call.";
- OPCODE_BREAK;
- }
if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
err_text = R"(Trying to call an async function without "await".)";
OPCODE_BREAK;
@@ -2583,7 +2579,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Array *array = VariantInternal::get_array(r);
- if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) {
+ if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) {
#ifdef DEBUG_ENABLED
err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)",
_get_var_type(r), _get_element_type(builtin_type, native_type, *script_type));
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 0cb8e3a2af..8de78d2b9a 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -38,153 +38,115 @@ String GDScriptWarning::get_message() const {
#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
switch (code) {
- case UNASSIGNED_VARIABLE_OP_ASSIGN: {
+ case UNASSIGNED_VARIABLE:
CHECK_SYMBOLS(1);
- return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value.";
- } break;
- case UNASSIGNED_VARIABLE: {
+ return vformat(R"(The variable "%s" was used but never assigned a value.)", symbols[0]);
+ case UNASSIGNED_VARIABLE_OP_ASSIGN:
CHECK_SYMBOLS(1);
- return "The variable '" + symbols[0] + "' was used but never assigned a value.";
- } break;
- case UNUSED_VARIABLE: {
+ return vformat(R"(Using assignment with operation but the variable "%s" was not previously assigned a value.)", symbols[0]);
+ case UNUSED_VARIABLE:
CHECK_SYMBOLS(1);
- return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'";
- } break;
- case UNUSED_LOCAL_CONSTANT: {
+ return vformat(R"(The local variable "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)", symbols[0], symbols[0]);
+ case UNUSED_LOCAL_CONSTANT:
CHECK_SYMBOLS(1);
- return "The local constant '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'";
- } break;
- case SHADOWED_VARIABLE: {
+ return vformat(R"(The local constant "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)", symbols[0], symbols[0]);
+ case UNUSED_PRIVATE_CLASS_VARIABLE:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The class variable "%s" is declared but never used in the script.)", symbols[0]);
+ case UNUSED_PARAMETER:
+ CHECK_SYMBOLS(2);
+ return vformat(R"*(The parameter "%s" is never used in the function "%s()". If this is intended, prefix it with an underscore: "_%s".)*", symbols[1], symbols[0], symbols[1]);
+ case UNUSED_SIGNAL:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The signal "%s" is declared but never emitted.)", symbols[0]);
+ case SHADOWED_VARIABLE:
CHECK_SYMBOLS(4);
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)", symbols[0], symbols[1], symbols[2], symbols[3]);
- } break;
- case SHADOWED_VARIABLE_BASE_CLASS: {
+ case SHADOWED_VARIABLE_BASE_CLASS:
CHECK_SYMBOLS(4);
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
- } break;
- case UNUSED_PRIVATE_CLASS_VARIABLE: {
- CHECK_SYMBOLS(1);
- return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
- } break;
- case UNUSED_PARAMETER: {
- CHECK_SYMBOLS(2);
- return "The parameter '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'";
- } break;
- case UNREACHABLE_CODE: {
+ case SHADOWED_GLOBAL_IDENTIFIER:
+ CHECK_SYMBOLS(3);
+ return vformat(R"(The %s "%s" has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
+ case UNREACHABLE_CODE:
CHECK_SYMBOLS(1);
- return "Unreachable code (statement after return) in function '" + symbols[0] + "()'.";
- } break;
- case UNREACHABLE_PATTERN: {
+ return vformat(R"*(Unreachable code (statement after return) in function "%s()".)*", symbols[0]);
+ case UNREACHABLE_PATTERN:
return "Unreachable pattern (pattern after wildcard or bind).";
- } break;
- case STANDALONE_EXPRESSION: {
+ case STANDALONE_EXPRESSION:
return "Standalone expression (the line has no effect).";
- } break;
- case NARROWING_CONVERSION: {
- return "Narrowing conversion (float is converted to int and loses precision).";
- } break;
- case INCOMPATIBLE_TERNARY: {
+ case STANDALONE_TERNARY:
+ return "Standalone ternary conditional operator: the return value is being discarded.";
+ case INCOMPATIBLE_TERNARY:
return "Values of the ternary conditional are not mutually compatible.";
- } break;
- case UNUSED_SIGNAL: {
- CHECK_SYMBOLS(1);
- return "The signal '" + symbols[0] + "' is declared but never emitted.";
- } break;
- case RETURN_VALUE_DISCARDED: {
- CHECK_SYMBOLS(1);
- return "The function '" + symbols[0] + "()' returns a value that will be discarded if not used.";
- } break;
- case PROPERTY_USED_AS_FUNCTION: {
+ case PROPERTY_USED_AS_FUNCTION:
CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?";
- } break;
- case CONSTANT_USED_AS_FUNCTION: {
+ return vformat(R"*(The method "%s()" was not found in base "%s" but there's a property with the same name. Did you mean to access it?)*", symbols[0], symbols[1]);
+ case CONSTANT_USED_AS_FUNCTION:
CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?";
- } break;
- case FUNCTION_USED_AS_PROPERTY: {
+ return vformat(R"*(The method "%s()" was not found in base "%s" but there's a constant with the same name. Did you mean to access it?)*", symbols[0], symbols[1]);
+ case FUNCTION_USED_AS_PROPERTY:
CHECK_SYMBOLS(2);
- return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?";
- } break;
- case INTEGER_DIVISION: {
- return "Integer division, decimal part will be discarded.";
- } break;
- case UNSAFE_PROPERTY_ACCESS: {
+ return vformat(R"(The property "%s" was not found in base "%s" but there's a method with the same name. Did you mean to call it?)", symbols[0], symbols[1]);
+ case UNSAFE_PROPERTY_ACCESS:
CHECK_SYMBOLS(2);
- return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
- } break;
- case UNSAFE_METHOD_ACCESS: {
+ return vformat(R"(The property "%s" is not present on the inferred type "%s" (but may be present on a subtype).)", symbols[0], symbols[1]);
+ case UNSAFE_METHOD_ACCESS:
CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
- } break;
- case UNSAFE_CAST: {
+ return vformat(R"*(The method "%s()" is not present on the inferred type "%s" (but may be present on a subtype).)*", symbols[0], symbols[1]);
+ case UNSAFE_CAST:
CHECK_SYMBOLS(1);
- return "The value is cast to '" + symbols[0] + "' but has an unknown type.";
- } break;
- case UNSAFE_CALL_ARGUMENT: {
+ return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]);
+ case UNSAFE_CALL_ARGUMENT:
CHECK_SYMBOLS(4);
- return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
- } break;
- case UNSAFE_VOID_RETURN: {
+ return vformat(R"*(The argument %s of the function "%s()" requires a the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]);
+ case UNSAFE_VOID_RETURN:
CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "()' returns 'void' but it's trying to return a call to '" + symbols[1] + "()' that can't be ensured to also be 'void'.";
- } break;
- case DEPRECATED_KEYWORD: {
+ return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*", symbols[0], symbols[1]);
+ case RETURN_VALUE_DISCARDED:
+ CHECK_SYMBOLS(1);
+ return vformat(R"*(The function "%s()" returns a value that will be discarded if not used.)*", symbols[0]);
+ case STATIC_CALLED_ON_INSTANCE:
CHECK_SYMBOLS(2);
- return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
- } break;
- case STANDALONE_TERNARY: {
- return "Standalone ternary conditional operator: the return value is being discarded.";
- }
- case ASSERT_ALWAYS_TRUE: {
+ return vformat(R"*(The function "%s()" is a static function but was called from an instance. Instead, it should be directly called from the type: "%s.%s()".)*", symbols[0], symbols[1], symbols[0]);
+ case REDUNDANT_STATIC_UNLOAD:
+ return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)";
+ case REDUNDANT_AWAIT:
+ return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)";
+ case ASSERT_ALWAYS_TRUE:
return "Assert statement is redundant because the expression is always true.";
- }
- case ASSERT_ALWAYS_FALSE: {
+ case ASSERT_ALWAYS_FALSE:
return "Assert statement will raise an error because the expression is always false.";
- }
- case REDUNDANT_AWAIT: {
- return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)";
- }
- case EMPTY_FILE: {
- return "Empty script file.";
- }
- case SHADOWED_GLOBAL_IDENTIFIER: {
- CHECK_SYMBOLS(3);
- return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
- }
- case INT_AS_ENUM_WITHOUT_CAST: {
+ case INTEGER_DIVISION:
+ return "Integer division, decimal part will be discarded.";
+ case NARROWING_CONVERSION:
+ return "Narrowing conversion (float is converted to int and loses precision).";
+ case INT_AS_ENUM_WITHOUT_CAST:
return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
- }
- case INT_AS_ENUM_WITHOUT_MATCH: {
+ case INT_AS_ENUM_WITHOUT_MATCH:
CHECK_SYMBOLS(3);
return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]);
- } break;
- case STATIC_CALLED_ON_INSTANCE: {
+ case EMPTY_FILE:
+ return "Empty script file.";
+ case DEPRECATED_KEYWORD:
CHECK_SYMBOLS(2);
- return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]);
- }
- case CONFUSABLE_IDENTIFIER: {
+ return vformat(R"(The "%s" keyword is deprecated and will be removed in a future release, please replace its uses by "%s".)", symbols[0], symbols[1]);
+ case RENAMED_IN_GODOT_4_HINT:
+ break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here.
+ 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 RENAMED_IN_GD4_HINT: {
- break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here.
- }
- case INFERENCE_ON_VARIANT: {
+ 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]);
- }
- case NATIVE_METHOD_OVERRIDE: {
+ case NATIVE_METHOD_OVERRIDE:
CHECK_SYMBOLS(2);
- return vformat(R"(The method "%s" overrides a method from native class "%s". This won't be called by the engine and may not work as expected.)", symbols[0], symbols[1]);
- }
- case GET_NODE_DEFAULT_WITHOUT_ONREADY: {
+ return vformat(R"*(The method "%s()" overrides a method from native class "%s". This won't be called by the engine and may not work as expected.)*", symbols[0], symbols[1]);
+ case GET_NODE_DEFAULT_WITHOUT_ONREADY:
CHECK_SYMBOLS(1);
return vformat(R"*(The default value is using "%s" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.)*", symbols[0]);
- }
- case ONREADY_WITH_EXPORT: {
+ case ONREADY_WITH_EXPORT:
return R"("@onready" will set the default value after "@export" takes effect and will override it.)";
- }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -200,7 +162,7 @@ int GDScriptWarning::get_default_value(Code p_code) {
PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
// Making this a separate function in case a warning needs different PropertyInfo in the future.
- if (p_code == Code::RENAMED_IN_GD4_HINT) {
+ if (p_code == Code::RENAMED_IN_GODOT_4_HINT) {
return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code));
}
return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
@@ -218,38 +180,39 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"UNASSIGNED_VARIABLE_OP_ASSIGN",
"UNUSED_VARIABLE",
"UNUSED_LOCAL_CONSTANT",
- "SHADOWED_VARIABLE",
- "SHADOWED_VARIABLE_BASE_CLASS",
"UNUSED_PRIVATE_CLASS_VARIABLE",
"UNUSED_PARAMETER",
+ "UNUSED_SIGNAL",
+ "SHADOWED_VARIABLE",
+ "SHADOWED_VARIABLE_BASE_CLASS",
+ "SHADOWED_GLOBAL_IDENTIFIER",
"UNREACHABLE_CODE",
"UNREACHABLE_PATTERN",
"STANDALONE_EXPRESSION",
- "NARROWING_CONVERSION",
+ "STANDALONE_TERNARY",
"INCOMPATIBLE_TERNARY",
- "UNUSED_SIGNAL",
- "RETURN_VALUE_DISCARDED",
"PROPERTY_USED_AS_FUNCTION",
"CONSTANT_USED_AS_FUNCTION",
"FUNCTION_USED_AS_PROPERTY",
- "INTEGER_DIVISION",
"UNSAFE_PROPERTY_ACCESS",
"UNSAFE_METHOD_ACCESS",
"UNSAFE_CAST",
"UNSAFE_CALL_ARGUMENT",
"UNSAFE_VOID_RETURN",
- "DEPRECATED_KEYWORD",
- "STANDALONE_TERNARY",
+ "RETURN_VALUE_DISCARDED",
+ "STATIC_CALLED_ON_INSTANCE",
+ "REDUNDANT_STATIC_UNLOAD",
+ "REDUNDANT_AWAIT",
"ASSERT_ALWAYS_TRUE",
"ASSERT_ALWAYS_FALSE",
- "REDUNDANT_AWAIT",
- "EMPTY_FILE",
- "SHADOWED_GLOBAL_IDENTIFIER",
+ "INTEGER_DIVISION",
+ "NARROWING_CONVERSION",
"INT_AS_ENUM_WITHOUT_CAST",
"INT_AS_ENUM_WITHOUT_MATCH",
- "STATIC_CALLED_ON_INSTANCE",
- "CONFUSABLE_IDENTIFIER",
+ "EMPTY_FILE",
+ "DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
+ "CONFUSABLE_IDENTIFIER",
"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 f0123c518c..ae6207fcdc 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -50,38 +50,39 @@ public:
UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc).
UNUSED_VARIABLE, // Local variable is declared but never used.
UNUSED_LOCAL_CONSTANT, // Local constant is declared but never used.
- SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
- SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the file.
UNUSED_PARAMETER, // Function parameter is never used.
+ UNUSED_SIGNAL, // Signal is defined but never emitted.
+ SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
+ SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
+ SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
UNREACHABLE_CODE, // Code after a return statement.
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
STANDALONE_EXPRESSION, // Expression not assigned to a variable.
- NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
+ STANDALONE_TERNARY, // Return value of ternary expression is discarded.
INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
- UNUSED_SIGNAL, // Signal is defined but never emitted.
- RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name.
- INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded.
UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes).
UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes).
UNSAFE_CAST, // Cast used in an unknown type.
- UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument.
+ UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the required type.
UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked.
- DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
- STANDALONE_TERNARY, // Return value of ternary expression is discarded.
+ RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
+ STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
+ REDUNDANT_STATIC_UNLOAD, // The `@static_unload` annotation is used but the class does not have static data.
+ REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
- REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
- EMPTY_FILE, // A script file is empty.
- SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
+ INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded.
+ NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting.
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
- STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
+ EMPTY_FILE, // A script file is empty.
+ 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").
- RENAMED_IN_GD4_HINT, // A variable or function that could not be found has been renamed in Godot 4
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.
@@ -94,38 +95,39 @@ public:
WARN, // UNASSIGNED_VARIABLE_OP_ASSIGN
WARN, // UNUSED_VARIABLE
WARN, // UNUSED_LOCAL_CONSTANT
- WARN, // SHADOWED_VARIABLE
- WARN, // SHADOWED_VARIABLE_BASE_CLASS
WARN, // UNUSED_PRIVATE_CLASS_VARIABLE
WARN, // UNUSED_PARAMETER
+ WARN, // UNUSED_SIGNAL
+ WARN, // SHADOWED_VARIABLE
+ WARN, // SHADOWED_VARIABLE_BASE_CLASS
+ WARN, // SHADOWED_GLOBAL_IDENTIFIER
WARN, // UNREACHABLE_CODE
WARN, // UNREACHABLE_PATTERN
WARN, // STANDALONE_EXPRESSION
- WARN, // NARROWING_CONVERSION
+ WARN, // STANDALONE_TERNARY
WARN, // INCOMPATIBLE_TERNARY
- WARN, // UNUSED_SIGNAL
- IGNORE, // RETURN_VALUE_DISCARDED // Too spammy by default on common cases (connect, Tween, etc.).
WARN, // PROPERTY_USED_AS_FUNCTION
WARN, // CONSTANT_USED_AS_FUNCTION
WARN, // FUNCTION_USED_AS_PROPERTY
- WARN, // INTEGER_DIVISION
IGNORE, // UNSAFE_PROPERTY_ACCESS // Too common in untyped scenarios.
IGNORE, // UNSAFE_METHOD_ACCESS // Too common in untyped scenarios.
IGNORE, // UNSAFE_CAST // Too common in untyped scenarios.
IGNORE, // UNSAFE_CALL_ARGUMENT // Too common in untyped scenarios.
WARN, // UNSAFE_VOID_RETURN
- WARN, // DEPRECATED_KEYWORD
- WARN, // STANDALONE_TERNARY
+ IGNORE, // RETURN_VALUE_DISCARDED // Too spammy by default on common cases (connect, Tween, etc.).
+ WARN, // STATIC_CALLED_ON_INSTANCE
+ WARN, // REDUNDANT_STATIC_UNLOAD
+ WARN, // REDUNDANT_AWAIT
WARN, // ASSERT_ALWAYS_TRUE
WARN, // ASSERT_ALWAYS_FALSE
- WARN, // REDUNDANT_AWAIT
- WARN, // EMPTY_FILE
- WARN, // SHADOWED_GLOBAL_IDENTIFIER
+ WARN, // INTEGER_DIVISION
+ WARN, // NARROWING_CONVERSION
WARN, // INT_AS_ENUM_WITHOUT_CAST
WARN, // INT_AS_ENUM_WITHOUT_MATCH
- WARN, // STATIC_CALLED_ON_INSTANCE
+ WARN, // EMPTY_FILE
+ WARN, // DEPRECATED_KEYWORD
+ WARN, // RENAMED_IN_GODOT_4_HINT
WARN, // CONFUSABLE_IDENTIFIER
- WARN, // RENAMED_IN_GD4_HINT
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/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h
index 5b5327bdb7..b9a54cf818 100644
--- a/modules/gdscript/language_server/godot_lsp.h
+++ b/modules/gdscript/language_server/godot_lsp.h
@@ -1005,7 +1005,9 @@ struct CompletionItem {
if (commitCharacters.size()) {
dict["commitCharacters"] = commitCharacters;
}
- dict["command"] = command.to_json();
+ if (!command.command.is_empty()) {
+ dict["command"] = command.to_json();
+ }
}
return dict;
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 57405aa1ce..b8448d16c2 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -350,13 +350,13 @@ void GDScriptTestRunner::handle_cmdline() {
for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
String &cmd = E->get();
if (cmd == "--gdscript-generate-tests") {
- if (E->next() == nullptr) {
- ERR_PRINT("Needed a path for the test files.");
- exit(-1);
+ String path;
+ if (E->next()) {
+ path = E->next()->get();
+ } else {
+ path = "modules/gdscript/tests/scripts";
}
- const String &path = E->next()->get();
-
GDScriptTestRunner runner(path, false, cmdline_args.find("--print-filenames") != nullptr);
bool completed = runner.generate_outputs();
@@ -566,6 +566,14 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
ERR_FAIL_V_MSG(result, "\nCould not find test function on: '" + source_file + "'");
}
+ // Setup output handlers.
+ ErrorHandlerData error_data(&result, this);
+
+ _print_handler.userdata = &result;
+ _error_handler.userdata = &error_data;
+ add_print_handler(&_print_handler);
+ add_error_handler(&_error_handler);
+
script->reload();
// Create object instance for test.
@@ -577,14 +585,6 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
obj->set_script(script);
GDScriptInstance *instance = static_cast<GDScriptInstance *>(obj->get_script_instance());
- // Setup output handlers.
- ErrorHandlerData error_data(&result, this);
-
- _print_handler.userdata = &result;
- _error_handler.userdata = &error_data;
- add_print_handler(&_print_handler);
- add_error_handler(&_error_handler);
-
// Call test function.
Callable::CallError call_err;
instance->callp(GDScriptTestRunner::test_function_name, nullptr, 0, call_err);
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.gd
new file mode 100644
index 0000000000..3dac751ba9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.gd
@@ -0,0 +1,5 @@
+static func _static_init() -> int:
+ print("static init")
+
+func test():
+ print("done")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.out b/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.out
new file mode 100644
index 0000000000..42294b7afb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_constructor_with_return_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Static constructor cannot have an explicit return type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.gd
new file mode 100644
index 0000000000..1ae814ea34
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.gd
@@ -0,0 +1,9 @@
+@static_unload
+
+func non_static():
+ return "non static"
+
+static var static_var = non_static()
+
+func test():
+ print("does not run")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.out
new file mode 100644
index 0000000000..f1e9ec34f2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_call.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call non-static function "non_static()" for static variable initializer.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out
index 7c0416371f..f49d93620f 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out
@@ -2,7 +2,7 @@ GDTEST_OK
>> WARNING
>> Line: 20
>> UNSAFE_VOID_RETURN
->> The method 'return_side_effect()' returns 'void' but it's trying to return a call to 'side_effect()' that can't be ensured to also be 'void'.
+>> The method "return_side_effect()" returns "void" but it's trying to return a call to "side_effect()" that can't be ensured to also be "void".
hello
effect
effect
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.gd b/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.gd
new file mode 100644
index 0000000000..d6c3cfc50e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.gd
@@ -0,0 +1,6 @@
+func test():
+ var never: Variant = false
+ if never:
+ assert(false)
+ assert(false, 'message')
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.out b/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assert_literal_false.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
index 481016138a..22cd5d3a17 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
@@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 6
>> UNSAFE_METHOD_ACCESS
->> The method 'free' is not present on the inferred type 'Variant' (but may be present on a subtype).
+>> The method "free()" is not present on the inferred type "Variant" (but may be present on a subtype).
Ok
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
index 32e230fc80..fe6083c329 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_PARAMETER
->> The parameter 'unused' is never used in the function ''. If this is intended, prefix it with an underscore: '_unused'
+>> The parameter "unused" is never used in the function "<anonymous lambda>()". If this is intended, prefix it with an underscore: "_unused".
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/overriding_native_method.out b/modules/gdscript/tests/scripts/analyzer/warnings/overriding_native_method.out
index 793faa05d4..c76950179d 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/overriding_native_method.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/overriding_native_method.out
@@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 4
>> NATIVE_METHOD_OVERRIDE
->> The method "get" overrides a method from native class "Object". This won't be called by the engine and may not work as expected.
+>> The method "get()" overrides a method from native class "Object". This won't be called by the engine and may not work as expected.
warn
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
index 9d0e567534..8467697a96 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
@@ -2,19 +2,19 @@ GDTEST_OK
>> WARNING
>> Line: 5
>> SHADOWED_GLOBAL_IDENTIFIER
->> The variable 'Array' has the same name as a built-in type.
+>> The variable "Array" has the same name as a built-in type.
>> WARNING
>> Line: 6
>> SHADOWED_GLOBAL_IDENTIFIER
->> The variable 'Node' has the same name as a global class.
+>> The variable "Node" has the same name as a global class.
>> WARNING
>> Line: 7
>> SHADOWED_GLOBAL_IDENTIFIER
->> The variable 'is_same' has the same name as a built-in function.
+>> The variable "is_same" has the same name as a built-in function.
>> WARNING
>> Line: 8
>> SHADOWED_GLOBAL_IDENTIFIER
->> The variable 'sqrt' has the same name as a built-in function.
+>> The variable "sqrt" has the same name as a built-in function.
>> WARNING
>> Line: 9
>> SHADOWED_VARIABLE
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd
new file mode 100644
index 0000000000..5ca8ceffdd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.gd
@@ -0,0 +1,10 @@
+# GH-72135
+
+var _a
+@warning_ignore("unused_private_class_variable")
+var _b
+@warning_ignore("unused_private_class_variable") var _c
+var _d
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out
new file mode 100644
index 0000000000..fd88d23950
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> UNUSED_PRIVATE_CLASS_VARIABLE
+>> The class variable "_a" is declared but never used in the script.
+>> WARNING
+>> Line: 7
+>> UNUSED_PRIVATE_CLASS_VARIABLE
+>> The class variable "_d" is declared but never used in the script.
diff --git a/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.gd b/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.gd
new file mode 100644
index 0000000000..cbfa1f314f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.gd
@@ -0,0 +1,5 @@
+func _static_init():
+ print("static init")
+
+func test():
+ print("done")
diff --git a/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.out b/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.out
new file mode 100644
index 0000000000..b2b8711e96
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/static_constructor_not_static.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Static constructor must be declared static.
diff --git a/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.gd b/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.gd
new file mode 100644
index 0000000000..711243f822
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.gd
@@ -0,0 +1,6 @@
+static func _static_init():
+ print("static init")
+ return true
+
+func test():
+ print("done")
diff --git a/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.out b/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.out
new file mode 100644
index 0000000000..a034850e86
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/static_constructor_returning_something.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Constructor cannot return a value.
diff --git a/modules/gdscript/tests/scripts/parser/features/constants.out b/modules/gdscript/tests/scripts/parser/features/constants.out
index 6093e4a6ca..7ec33470d3 100644
--- a/modules/gdscript/tests/scripts/parser/features/constants.out
+++ b/modules/gdscript/tests/scripts/parser/features/constants.out
@@ -2,32 +2,32 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '__TEST'
+>> The local constant "_TEST" is declared but never used in the block. If this is intended, prefix it with an underscore: "__TEST".
>> WARNING
>> Line: 3
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_STRING' is declared but never used in the block. If this is intended, prefix it with an underscore: '__STRING'
+>> The local constant "_STRING" is declared but never used in the block. If this is intended, prefix it with an underscore: "__STRING".
>> WARNING
>> Line: 4
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_VECTOR' is declared but never used in the block. If this is intended, prefix it with an underscore: '__VECTOR'
+>> The local constant "_VECTOR" is declared but never used in the block. If this is intended, prefix it with an underscore: "__VECTOR".
>> WARNING
>> Line: 5
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_ARRAY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__ARRAY'
+>> The local constant "_ARRAY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__ARRAY".
>> WARNING
>> Line: 6
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_DICTIONARY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__DICTIONARY'
+>> The local constant "_DICTIONARY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__DICTIONARY".
>> WARNING
>> Line: 9
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_HELLO' is declared but never used in the block. If this is intended, prefix it with an underscore: '__HELLO'
+>> The local constant "_HELLO" is declared but never used in the block. If this is intended, prefix it with an underscore: "__HELLO".
>> WARNING
>> Line: 10
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INFINITY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INFINITY'
+>> The local constant "_INFINITY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INFINITY".
>> WARNING
>> Line: 11
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_NOT_A_NUMBER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__NOT_A_NUMBER'
+>> The local constant "_NOT_A_NUMBER" is declared but never used in the block. If this is intended, prefix it with an underscore: "__NOT_A_NUMBER".
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
index 057c1b11e5..44d29cb82d 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
@@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 9
>> UNUSED_VARIABLE
->> The local variable 'value' is declared but never used in the block. If this is intended, prefix it with an underscore: '_value'
+>> The local variable "value" is declared but never used in the block. If this is intended, prefix it with an underscore: "_value".
value
diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.out b/modules/gdscript/tests/scripts/parser/features/static_typing.out
index 207d90fef1..40a8f97416 100644
--- a/modules/gdscript/tests/scripts/parser/features/static_typing.out
+++ b/modules/gdscript/tests/scripts/parser/features/static_typing.out
@@ -2,20 +2,20 @@ GDTEST_OK
>> WARNING
>> Line: 11
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INTEGER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER'
+>> The local constant "_INTEGER" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER".
>> WARNING
>> Line: 12
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INTEGER_REDUNDANT_TYPED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED'
+>> The local constant "_INTEGER_REDUNDANT_TYPED" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_TYPED".
>> WARNING
>> Line: 13
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INTEGER_REDUNDANT_TYPED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED2'
+>> The local constant "_INTEGER_REDUNDANT_TYPED2" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_TYPED2".
>> WARNING
>> Line: 14
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INTEGER_REDUNDANT_INFERRED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED'
+>> The local constant "_INTEGER_REDUNDANT_INFERRED" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_INFERRED".
>> WARNING
>> Line: 15
>> UNUSED_LOCAL_CONSTANT
->> The local constant '_INTEGER_REDUNDANT_INFERRED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED2'
+>> The local constant "_INTEGER_REDUNDANT_INFERRED2" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_INFERRED2".
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
index e89bb9226f..f2db4e9307 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 6
>> RETURN_VALUE_DISCARDED
->> The function 'i_return_int()' returns a value that will be discarded if not used.
+>> The function "i_return_int()" returns a value that will be discarded if not used.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
index 9c9417e11d..75fa01f928 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
@@ -2,7 +2,7 @@ GDTEST_OK
>> WARNING
>> Line: 8
>> UNUSED_LOCAL_CONSTANT
->> The local constant 'TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '_TEST'
+>> The local constant "TEST" is declared but never used in the block. If this is intended, prefix it with an underscore: "_TEST".
>> WARNING
>> Line: 8
>> SHADOWED_VARIABLE
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out
index c613140eb8..75a02c5d3c 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out
@@ -2,8 +2,8 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_VARIABLE
->> The local variable 'abs' is declared but never used in the block. If this is intended, prefix it with an underscore: '_abs'
+>> The local variable "abs" is declared but never used in the block. If this is intended, prefix it with an underscore: "_abs".
>> WARNING
>> Line: 2
>> SHADOWED_GLOBAL_IDENTIFIER
->> The variable 'abs' has the same name as a built-in function.
+>> The variable "abs" has the same name as a built-in function.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
index 82e467b368..aab27e78e2 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
@@ -2,7 +2,7 @@ GDTEST_OK
>> WARNING
>> Line: 8
>> UNUSED_VARIABLE
->> The local variable 'foo' is declared but never used in the block. If this is intended, prefix it with an underscore: '_foo'
+>> The local variable "foo" is declared but never used in the block. If this is intended, prefix it with an underscore: "_foo".
>> WARNING
>> Line: 8
>> SHADOWED_VARIABLE
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
index 26ce0465b1..e3cd358126 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
@@ -2,7 +2,7 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_VARIABLE
->> The local variable 'test' is declared but never used in the block. If this is intended, prefix it with an underscore: '_test'
+>> The local variable "test" is declared but never used in the block. If this is intended, prefix it with an underscore: "_test".
>> WARNING
>> Line: 2
>> SHADOWED_VARIABLE
diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
index 3933a35178..77994ce9ba 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
@@ -2,6 +2,6 @@ GDTEST_OK
>> WARNING
>> Line: 11
>> STATIC_CALLED_ON_INSTANCE
->> The function 'num_uint64()' is a static function but was called from an instance. Instead, it should be directly called from the type: 'String.num_uint64()'.
+>> The function "num_uint64()" is a static function but was called from an instance. Instead, it should be directly called from the type: "String.num_uint64()".
8589934592
8589934592
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out
index cf14502e9a..10f89be132 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNASSIGNED_VARIABLE
->> The variable '__' was used but never assigned a value.
+>> The variable "__" was used but never assigned a value.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out
index ba55a4e0f8..4fc91487f2 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 4
>> UNASSIGNED_VARIABLE_OP_ASSIGN
->> Using assignment with operation but the variable '__' was not previously assigned a value.
+>> Using assignment with operation but the variable "__" was not previously assigned a value.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out
index 9316abd5eb..f67dbdcd03 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 7
>> UNREACHABLE_CODE
->> Unreachable code (statement after return) in function 'test()'.
+>> Unreachable code (statement after return) in function "test()".
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out
index 92f3308f85..3a03406f92 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_PARAMETER
->> The parameter 'p_arg2' is never used in the function 'function_with_unused_argument'. If this is intended, prefix it with an underscore: '_p_arg2'
+>> The parameter "p_arg2" is never used in the function "function_with_unused_argument()". If this is intended, prefix it with an underscore: "_p_arg2".
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
index 270e0e69c0..b9b3968473 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 2
>> UNUSED_VARIABLE
->> The local variable 'unused' is declared but never used in the block. If this is intended, prefix it with an underscore: '_unused'
+>> The local variable "unused" is declared but never used in the block. If this is intended, prefix it with an underscore: "_unused".
diff --git a/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.gd b/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.gd
new file mode 100644
index 0000000000..a2d09bf7d3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.gd
@@ -0,0 +1,15 @@
+# https://github.com/godotengine/godot/issues/68184
+
+var node: Node:
+ get:
+ return node
+ set(n):
+ node = n
+
+
+func test():
+ node = Node.new()
+ node.free()
+
+ if !is_instance_valid(node):
+ print("It is freed")
diff --git a/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.out b/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.out
new file mode 100644
index 0000000000..b380f593d9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/getter_with_freed_object.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+It is freed
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_constructor.gd b/modules/gdscript/tests/scripts/runtime/features/static_constructor.gd
new file mode 100644
index 0000000000..e08f77df12
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_constructor.gd
@@ -0,0 +1,13 @@
+@static_unload
+
+static var foo = "bar"
+
+static func _static_init():
+ print("static init called")
+ prints("foo is", foo)
+
+func test():
+ var _lambda = func _static_init():
+ print("lambda does not conflict with static constructor")
+
+ print("done")
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_constructor.out b/modules/gdscript/tests/scripts/runtime/features/static_constructor.out
new file mode 100644
index 0000000000..7f72a0ac2c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_constructor.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+static init called
+foo is bar
+done
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd
new file mode 100644
index 0000000000..e193312381
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd
@@ -0,0 +1,56 @@
+@static_unload
+
+static var perm := 0
+
+static var prop := "Hello!":
+ get: return prop + " suffix"
+ set(value): prop = "prefix " + str(value)
+
+static func get_data():
+ return "data"
+
+static var data = get_data()
+
+class Inner:
+ static var prop := "inner"
+ static func _static_init() -> void:
+ prints("Inner._static_init", prop)
+
+ class InnerInner:
+ static var prop := "inner inner"
+ static func _static_init() -> void:
+ prints("InnerInner._static_init", prop)
+
+func test():
+ prints("data:", data)
+
+ prints("perm:", perm)
+ prints("prop:", prop)
+
+ perm = 1
+ prop = "World!"
+
+ prints("perm:", perm)
+ prints("prop:", prop)
+
+ print("other.perm:", StaticVariablesOther.perm)
+ print("other.prop:", StaticVariablesOther.prop)
+
+ StaticVariablesOther.perm = 2
+ StaticVariablesOther.prop = "foo"
+
+ print("other.perm:", StaticVariablesOther.perm)
+ print("other.prop:", StaticVariablesOther.prop)
+
+ @warning_ignore("unsafe_method_access")
+ var path = get_script().get_path().get_base_dir()
+ var other = load(path + "/static_variables_load.gd")
+
+ print("load.perm:", other.perm)
+ print("load.prop:", other.prop)
+
+ other.perm = 3
+ other.prop = "bar"
+
+ print("load.perm:", other.perm)
+ print("load.prop:", other.prop)
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.out b/modules/gdscript/tests/scripts/runtime/features/static_variables.out
new file mode 100644
index 0000000000..d2491aef5e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.out
@@ -0,0 +1,16 @@
+GDTEST_OK
+Inner._static_init inner
+InnerInner._static_init inner inner
+data: data
+perm: 0
+prop: prefix Hello! suffix
+perm: 1
+prop: prefix World! suffix
+other.perm:0
+other.prop:prefix Hello! suffix
+other.perm:2
+other.prop:prefix foo suffix
+load.perm:0
+load.prop:prefix Hello! suffix
+load.perm:3
+load.prop:prefix bar suffix
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_load.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables_load.gd
new file mode 100644
index 0000000000..8913b02756
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_load.gd
@@ -0,0 +1,10 @@
+@static_unload
+
+static var perm := 0
+
+static var prop := "Hello!":
+ get: return prop + " suffix"
+ set(value): prop = "prefix " + str(value)
+
+func test():
+ print("ok")
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_load.out b/modules/gdscript/tests/scripts/runtime/features/static_variables_load.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_load.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_other.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables_other.gd
new file mode 100644
index 0000000000..7a3b0acca6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_other.gd
@@ -0,0 +1,11 @@
+@static_unload
+class_name StaticVariablesOther
+
+static var perm := 0
+
+static var prop := "Hello!":
+ get: return prop + " suffix"
+ set(value): prop = "prefix " + str(value)
+
+func test():
+ print("ok")
diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_other.out b/modules/gdscript/tests/scripts/runtime/features/static_variables_other.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_other.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index a6517a7fde..e4e4b79d6e 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -7,7 +7,7 @@
<tutorials>
</tutorials>
<members>
- <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="0">
+ <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1">
</member>
<member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
</member>
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index 5f6ec5904c..554fe5a422 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/gui/editor_file_dialog.h"
+#include "scene/gui/popup_menu.h"
String SceneExporterGLTFPlugin::get_name() const {
return "ConvertGLTF2";
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 2efaaa7d4d..91271da331 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -466,8 +466,8 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
}
bool found = false;
- for (const String &path : mdfind_paths) {
- found = _autodetect_path(path.path_join("Contents/MacOS"));
+ for (const String &found_path : mdfind_paths) {
+ found = _autodetect_path(found_path.path_join("Contents/MacOS"));
if (found) {
break;
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 4a07183b0d..251aa6375f 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -7190,9 +7190,9 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Erro
const int32_t header_size = 12;
const int32_t chunk_header_size = 8;
- for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) {
- json += " ";
- }
+ int32_t padding = (chunk_header_size + json.utf8().length()) % 4;
+ json += String(" ").repeat(padding);
+
CharString cs = json.utf8();
const uint32_t text_chunk_length = cs.length();
diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h
index 5b4afc79c4..1a5a910048 100644
--- a/modules/gltf/structures/gltf_accessor.h
+++ b/modules/gltf/structures/gltf_accessor.h
@@ -39,7 +39,7 @@ struct GLTFAccessor : public Resource {
friend class GLTFDocument;
private:
- GLTFBufferViewIndex buffer_view = 0;
+ GLTFBufferViewIndex buffer_view = -1;
int byte_offset = 0;
int component_type = 0;
bool normalized = false;
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 8e25d0fb84..e413560c16 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -73,14 +73,14 @@
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
+ Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
- Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
+ Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_meshes" qualifiers="const">
@@ -113,7 +113,7 @@
<return type="Vector3i[]" />
<param index="0" name="item" type="int" />
<description>
- Returns an array of all cells with the given item index specified in [code]item[/code].
+ Returns an array of all cells with the given item index specified in [param item].
</description>
</method>
<method name="local_to_map" qualifiers="const">
@@ -161,7 +161,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_layer], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
@@ -169,7 +169,7 @@
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
- Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
+ Based on [param value], enables or disables the specified layer in the [member collision_mask], given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_navigation_map">
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index db8c645558..c77fa98be2 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -907,7 +907,7 @@ void GridMap::_notification(int p_what) {
#ifdef DEBUG_ENABLED
case NOTIFICATION_ENTER_TREE: {
- if (bake_navigation && NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (bake_navigation && NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
_update_navigation_debug_edge_connections();
}
} break;
@@ -1352,7 +1352,7 @@ void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const Octant
ERR_FAIL_COND(!octant_map.has(p_key));
Octant &g = *octant_map[p_key];
- if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (g.navigation_debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false);
}
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 9133fdef35..7c1204d2b7 100644
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -100,10 +100,14 @@ if env["builtin_mbedtls"]:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"])
+ env_mbed_tls.Append(
+ CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')]
+ )
env_thirdparty = env_mbed_tls.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_module_mbedtls_config.h")
env.modules_sources += thirdparty_obj
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 5b52af3068..68b213d79e 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -36,12 +36,14 @@
#include "core/config/project_settings.h"
#include "core/io/certs_compressed.gen.h"
#include "core/io/compression.h"
+#include "core/os/os.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
#define PEM_END_CRT "-----END CERTIFICATE-----\n"
+#define PEM_MIN_SIZE 54
#include <mbedtls/debug.h>
#include <mbedtls/md.h>
@@ -181,6 +183,35 @@ Error X509CertificateMbedTLS::save(String p_path) {
return OK;
}
+String X509CertificateMbedTLS::save_to_string() {
+ String buffer;
+ mbedtls_x509_crt *crt = &cert;
+ while (crt) {
+ unsigned char w[4096];
+ size_t wrote = 0;
+ int ret = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, cert.raw.p, cert.raw.len, w, sizeof(w), &wrote);
+ ERR_FAIL_COND_V_MSG(ret != 0 || wrote == 0, String(), "Error saving the certificate.");
+
+ buffer += String((char *)w, wrote);
+ crt = crt->next;
+ }
+ if (buffer.length() <= PEM_MIN_SIZE) {
+ // When the returned value of variable 'buffer' would consist of no Base-64 data, return an empty String instead.
+ return String();
+ }
+ return buffer;
+}
+
+Error X509CertificateMbedTLS::load_from_string(const String &p_string_key) {
+ ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use");
+ CharString cs = p_string_key.utf8();
+
+ int ret = mbedtls_x509_crt_parse(&cert, (const unsigned char *)cs.get_data(), cs.size());
+ ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing some certificates: " + itos(ret));
+
+ return OK;
+}
+
bool HMACContextMbedTLS::is_md_type_allowed(mbedtls_md_type_t p_md_type) {
switch (p_md_type) {
case MBEDTLS_MD_SHA1:
@@ -307,20 +338,26 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
if (!p_path.is_empty()) {
// Use certs defined in project settings.
default_certs->load(p_path);
- }
+ } else {
+ // Try to use system certs otherwise.
+ String system_certs = OS::get_singleton()->get_system_ca_certificates();
+ if (!system_certs.is_empty()) {
+ CharString cs = system_certs.utf8();
+ default_certs->load_from_memory((const uint8_t *)cs.get_data(), cs.size());
+ print_verbose("Loaded system CA certificates");
+ }
#ifdef BUILTIN_CERTS_ENABLED
- else {
- // Use builtin certs only if user did not override it in project settings.
- PackedByteArray out;
- out.resize(_certs_uncompressed_size + 1);
- Compression::decompress(out.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
- out.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
-#ifdef DEBUG_ENABLED
- print_verbose("Loaded builtin certs");
+ else {
+ // Use builtin certs if there are no system certs.
+ PackedByteArray certs;
+ certs.resize(_certs_uncompressed_size + 1);
+ Compression::decompress(certs.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
+ certs.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
+ default_certs->load_from_memory(certs.ptr(), certs.size());
+ print_verbose("Loaded builtin CA certificates");
+ }
#endif
- default_certs->load_from_memory(out.ptr(), out.size());
}
-#endif
}
Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 7422ebad3e..0168e1f663 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -85,6 +85,8 @@ public:
virtual Error load(String p_path);
virtual Error load_from_memory(const uint8_t *p_buffer, int p_len);
virtual Error save(String p_path);
+ virtual String save_to_string();
+ virtual Error load_from_string(const String &p_string_key);
X509CertificateMbedTLS() {
mbedtls_x509_crt_init(&cert);
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp
index e8eb32f88d..ed1a97cc2c 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.cpp
+++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "packet_peer_mbed_dtls.h"
-#include "mbedtls/platform_util.h"
#include "core/io/file_access.h"
#include "core/io/stream_peer_tls.h"
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a77b1d83ad..0d6436594e 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -522,12 +522,7 @@ String CSharpLanguage::_get_indentation() const {
if (use_space_indentation) {
int indent_size = EDITOR_GET("text_editor/behavior/indent/size");
-
- String space_indent = "";
- for (int i = 0; i < indent_size; i++) {
- space_indent += " ";
- }
- return space_indent;
+ return String(" ").repeat(indent_size);
}
}
#endif
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
index b30c8c240e..834beaa131 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -86,7 +86,8 @@ namespace Godot.SourceGenerators
NodeType = 34,
HideQuaternionEdit = 35,
Password = 36,
- Max = 37
+ LayersAvoidance = 37,
+ Max = 38
}
[Flags]
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 8504fb2ac6..6b52c8eaaf 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -128,7 +128,7 @@ void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::Ty
{
// The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
r_enum_itype.c_in = "%5%0 %1_in = %1;\n";
- r_enum_itype.c_out = "%5return (%0)%1;\n";
+ r_enum_itype.c_out = "%5return (%0)(%1);\n";
r_enum_itype.c_type = "long";
r_enum_itype.c_arg_in = "&%s_in";
}
@@ -1893,7 +1893,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Assume the index parameter is an enum
const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == nullptr);
- p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
+ p_output.append("(" + idx_arg_type->proxy_name + ")(" + itos(p_iprop.index) + ")");
} else {
p_output.append(itos(p_iprop.index));
}
@@ -1911,7 +1911,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Assume the index parameter is an enum
const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == nullptr);
- p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
+ p_output.append("(" + idx_arg_type->proxy_name + ")(" + itos(p_iprop.index) + "), ");
} else {
p_output.append(itos(p_iprop.index) + ", ");
}
@@ -3286,7 +3286,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
break;
case Variant::INT:
if (r_iarg.type.cname != name_cache.type_int) {
- r_iarg.default_argument = "(%s)" + r_iarg.default_argument;
+ r_iarg.default_argument = "(%s)(" + r_iarg.default_argument + ")";
}
break;
case Variant::FLOAT:
@@ -3508,7 +3508,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype = TypeInterface::create_value_type(String(m_name)); \
if (itype.name != "long" && itype.name != "ulong") { \
itype.c_in = "%5%0 %1_in = %1;\n"; \
- itype.c_out = "%5return (%0)%1;\n"; \
+ itype.c_out = "%5return (%0)(%1);\n"; \
itype.c_type = "long"; \
itype.c_arg_in = "&%s_in"; \
} else { \
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index ad9ab66cc9..34b9974d10 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -42,6 +42,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/plugins/script_editor_plugin.h"
#include "main/main.h"
@@ -156,11 +157,11 @@ void godot_icall_Internal_EditorNodeShowScriptScreen() {
}
void godot_icall_Internal_EditorRunPlay() {
- EditorNode::get_singleton()->run_play();
+ EditorRunBar::get_singleton()->play_main_scene();
}
void godot_icall_Internal_EditorRunStop() {
- EditorNode::get_singleton()->run_stop();
+ EditorRunBar::get_singleton()->stop_playing();
}
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index ca963cbf4f..36f5d8e2ab 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -613,6 +613,43 @@ namespace Godot
}
/// <summary>
+ /// Creates a <see cref="Basis"/> with a rotation such that the forward
+ /// axis (-Z) points towards the <paramref name="target"/> position.
+ /// The up axis (+Y) points as close to the <paramref name="up"/> vector
+ /// as possible while staying perpendicular to the forward axis.
+ /// The resulting Basis is orthonormalized.
+ /// The <paramref name="target"/> and <paramref name="up"/> vectors
+ /// cannot be zero, and cannot be parallel to each other.
+ /// </summary>
+ /// <param name="target">The position to look at.</param>
+ /// <param name="up">The relative up direction.</param>
+ /// <returns>The resulting basis matrix.</returns>
+ public static Basis LookingAt(Vector3 target, Vector3 up)
+ {
+#if DEBUG
+ if (target.IsZeroApprox())
+ {
+ throw new ArgumentException("The vector can't be zero.", nameof(target));
+ }
+ if (up.IsZeroApprox())
+ {
+ throw new ArgumentException("The vector can't be zero.", nameof(up));
+ }
+#endif
+ Vector3 column2 = -target.Normalized();
+ Vector3 column0 = up.Cross(column2);
+#if DEBUG
+ if (column0.IsZeroApprox())
+ {
+ throw new ArgumentException("The target vector and up vector can't be parallel to each other.");
+ }
+#endif
+ column0.Normalize();
+ Vector3 column1 = column2.Cross(column0);
+ return new Basis(column0, column1, column2);
+ }
+
+ /// <summary>
/// Returns the orthonormalized version of the basis matrix (useful to
/// call occasionally to avoid rounding errors for orthogonal matrices).
/// This performs a Gram-Schmidt orthonormalization on the basis of the matrix.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index b34e95c04d..1e2aaa299f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -164,14 +164,14 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the transform rotated such that its
- /// -Z axis (forward) points towards the <paramref name="target"/> position.
- ///
- /// The transform will first be rotated around the given <paramref name="up"/> vector,
- /// and then fully aligned to the <paramref name="target"/> by a further rotation around
- /// an axis perpendicular to both the <paramref name="target"/> and <paramref name="up"/> vectors.
- ///
- /// Operations take place in global space.
+ /// Returns a copy of the transform rotated such that the forward axis (-Z)
+ /// points towards the <paramref name="target"/> position.
+ /// The up axis (+Y) points as close to the <paramref name="up"/> vector
+ /// as possible while staying perpendicular to the forward axis.
+ /// The resulting transform is orthonormalized.
+ /// The existing rotation, scale, and skew information from the original transform is discarded.
+ /// The <paramref name="target"/> and <paramref name="up"/> vectors cannot be zero,
+ /// cannot be parallel to each other, and are defined in global/parent space.
/// </summary>
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
@@ -249,24 +249,7 @@ namespace Godot
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
- // Make rotation matrix
- // Z vector
- Vector3 column2 = eye - target;
-
- column2.Normalize();
-
- Vector3 column1 = up;
-
- Vector3 column0 = column1.Cross(column2);
-
- // Recompute Y = Z cross X
- column1 = column2.Cross(column0);
-
- column0.Normalize();
- column1.Normalize();
-
- Basis = new Basis(column0, column1, column2);
-
+ Basis = Basis.LookingAt(target - eye, up);
Origin = eye;
}
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index 36f69f2155..e6564a8aac 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -41,7 +41,7 @@
<return type="Node" />
<param index="0" name="data" type="Variant" default="null" />
<description>
- Requests a custom spawn, with [code]data[/code] passed to [member spawn_function] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path].
+ Requests a custom spawn, with [param data] passed to [member spawn_function] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path].
[b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns.
</description>
</method>
diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
index a3fb0c786e..2c93539ae1 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
@@ -17,14 +17,14 @@
<param index="0" name="filter" type="Callable" />
<description>
Adds a peer visibility filter for this synchronizer.
- [code]filter[/code] should take a peer ID [int] and return a [bool].
+ [param filter] should take a peer ID [int] and return a [bool].
</description>
</method>
<method name="get_visibility_for" qualifiers="const">
<return type="bool" />
<param index="0" name="peer" type="int" />
<description>
- Queries the current visibility for peer [code]peer[/code].
+ Queries the current visibility for peer [param peer].
</description>
</method>
<method name="remove_visibility_filter">
@@ -39,14 +39,14 @@
<param index="0" name="peer" type="int" />
<param index="1" name="visible" type="bool" />
<description>
- Sets the visibility of [code]peer[/code] to [code]visible[/code]. If [code]peer[/code] is [code]0[/code], the value of [member public_visibility] will be updated instead.
+ Sets the visibility of [param peer] to [param visible]. If [param peer] is [code]0[/code], the value of [member public_visibility] will be updated instead.
</description>
</method>
<method name="update_visibility">
<return type="void" />
<param index="0" name="for_peer" type="int" default="0" />
<description>
- Updates the visibility of [code]peer[/code] according to visibility filters. If [code]peer[/code] is [code]0[/code] (the default), all peers' visibilties are updated.
+ Updates the visibility of [param for_peer] according to visibility filters. If [param for_peer] is [code]0[/code] (the default), all peers' visibilties are updated.
</description>
</method>
</methods>
@@ -77,7 +77,7 @@
<signal name="visibility_changed">
<param index="0" name="for_peer" type="int" />
<description>
- Emitted when visibility of [code]for_peer[/code] is updated. See [method update_visibility].
+ Emitted when visibility of [param for_peer] is updated. See [method update_visibility].
</description>
</signal>
</signals>
diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
index 1ab37bfd82..2445d60f48 100644
--- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml
+++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
@@ -55,7 +55,7 @@
<param index="2" name="mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
<param index="3" name="channel" type="int" default="0" />
<description>
- Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method MultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers.
+ Sends the given raw [param bytes] to a specific peer identified by [param id] (see [method MultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers.
</description>
</method>
</methods>
@@ -100,7 +100,7 @@
<param index="0" name="id" type="int" />
<param index="1" name="packet" type="PackedByteArray" />
<description>
- Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] receives a [code]packet[/code] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet.
+ Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] receives a [param packet] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet.
</description>
</signal>
</signals>
diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
index 56ae219bab..53ea1d19a1 100644
--- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
+++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
@@ -13,7 +13,7 @@
<param index="0" name="path" type="NodePath" />
<param index="1" name="index" type="int" default="-1" />
<description>
- Adds the property identified by the given [code]path[/code] to the list of the properties being synchronized, optionally passing an [code]index[/code].
+ Adds the property identified by the given [param path] to the list of the properties being synchronized, optionally passing an [param index].
</description>
</method>
<method name="get_properties" qualifiers="const">
@@ -26,28 +26,28 @@
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the given [code]path[/code] is configured for synchronization.
+ Returns whether the given [param path] is configured for synchronization.
</description>
</method>
<method name="property_get_index" qualifiers="const">
<return type="int" />
<param index="0" name="path" type="NodePath" />
<description>
- Finds the index of the given [code]path[/code].
+ Finds the index of the given [param path].
</description>
</method>
<method name="property_get_spawn">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn.
+ Returns whether the property identified by the given [param path] is configured to be synchronized on spawn.
</description>
</method>
<method name="property_get_sync">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on process.
+ Returns whether the property identified by the given [param path] is configured to be synchronized on process.
</description>
</method>
<method name="property_set_spawn">
@@ -55,7 +55,7 @@
<param index="0" name="path" type="NodePath" />
<param index="1" name="enabled" type="bool" />
<description>
- Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn.
+ Sets whether the property identified by the given [param path] is configured to be synchronized on spawn.
</description>
</method>
<method name="property_set_sync">
@@ -63,14 +63,14 @@
<param index="0" name="path" type="NodePath" />
<param index="1" name="enabled" type="bool" />
<description>
- Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on process.
+ Sets whether the property identified by the given [param path] is configured to be synchronized on process.
</description>
</method>
<method name="remove_property">
<return type="void" />
<param index="0" name="path" type="NodePath" />
<description>
- Removes the property identified by the given [code]path[/code] from the configuration.
+ Removes the property identified by the given [param path] from the configuration.
</description>
</method>
</methods>
diff --git a/modules/navigation/SCsub b/modules/navigation/SCsub
index a9277657f4..46bcb0fba4 100644
--- a/modules/navigation/SCsub
+++ b/modules/navigation/SCsub
@@ -33,12 +33,14 @@ if env["builtin_recastnavigation"]:
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-# RVO Thirdparty source files
-if env["builtin_rvo2"]:
- thirdparty_dir = "#thirdparty/rvo2/"
+# RVO 2D Thirdparty source files
+if env["builtin_rvo2_2d"]:
+ thirdparty_dir = "#thirdparty/rvo2/rvo2_2d/"
thirdparty_sources = [
- "Agent.cpp",
- "KdTree.cpp",
+ "Agent2d.cpp",
+ "Obstacle2d.cpp",
+ "KdTree2d.cpp",
+ "RVOSimulator2d.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
@@ -48,9 +50,24 @@ if env["builtin_rvo2"]:
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+# RVO 3D Thirdparty source files
+if env["builtin_rvo2_3d"]:
+ thirdparty_dir = "#thirdparty/rvo2/rvo2_3d/"
+ thirdparty_sources = [
+ "Agent3d.cpp",
+ "KdTree3d.cpp",
+ "RVOSimulator3d.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.modules_sources += thirdparty_obj
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir])
+ env_thirdparty = env_navigation.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+
+env.modules_sources += thirdparty_obj
# Godot source files
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index ee9687cf3d..b0b52a6732 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -166,6 +166,20 @@ real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
return map->get_cell_size();
}
+COMMAND_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ map->set_use_edge_connections(p_enabled);
+}
+
+bool GodotNavigationServer::map_get_use_edge_connections(RID p_map) const {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, false);
+
+ return map->get_use_edge_connections();
+}
+
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
@@ -271,6 +285,18 @@ TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const {
return agents_rids;
}
+TypedArray<RID> GodotNavigationServer::map_get_obstacles(RID p_map) const {
+ TypedArray<RID> obstacles_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, obstacles_rids);
+ const LocalVector<NavObstacle *> obstacles = map->get_obstacles();
+ obstacles_rids.resize(obstacles.size());
+ for (uint32_t i = 0; i < obstacles.size(); i++) {
+ obstacles_rids[i] = obstacles[i]->get_self();
+ }
+ return obstacles_rids;
+}
+
RID GodotNavigationServer::region_get_map(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, RID());
@@ -300,6 +326,20 @@ RID GodotNavigationServer::region_create() {
return rid;
}
+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);
+
+ region->set_use_edge_connections(p_enabled);
+}
+
+bool GodotNavigationServer::region_get_use_edge_connections(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, false);
+
+ return region->get_use_edge_connections();
+}
+
COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
@@ -584,6 +624,34 @@ RID GodotNavigationServer::agent_create() {
return rid;
}
+COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled) {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_avoidance_enabled(p_enabled);
+}
+
+bool GodotNavigationServer::agent_get_avoidance_enabled(RID p_agent) const {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, false);
+
+ return agent->is_avoidance_enabled();
+}
+
+COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled) {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_use_3d_avoidance(p_enabled);
+}
+
+bool GodotNavigationServer::agent_get_use_3d_avoidance(RID p_agent) const {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, false);
+
+ return agent->get_use_3d_avoidance();
+}
+
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
@@ -605,7 +673,7 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
agent->set_map(map);
map->add_agent(agent);
- if (agent->has_callback()) {
+ if (agent->has_avoidance_callback()) {
map->set_agent_as_controlled(agent);
}
}
@@ -615,63 +683,75 @@ COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->neighborDist_ = p_distance;
+ agent->set_neighbor_distance(p_distance);
}
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->maxNeighbors_ = p_count;
+ agent->set_max_neighbors(p_count);
}
-COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
+COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->timeHorizon_ = p_time;
+ agent->set_time_horizon_agents(p_time_horizon);
+}
+
+COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_time_horizon_obstacles(p_time_horizon);
}
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->radius_ = p_radius;
+ agent->set_radius(p_radius);
}
-COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
+COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->maxSpeed_ = p_max_speed;
+ agent->set_height(p_height);
}
-COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
+COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ agent->set_max_speed(p_max_speed);
}
-COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
+COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ agent->set_velocity(p_velocity);
}
-COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
+COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z);
+ agent->set_velocity_forced(p_velocity);
}
-COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) {
+COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->ignore_y_ = p_ignore;
+ agent->set_position(p_position);
}
bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
@@ -681,11 +761,11 @@ bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
return agent->is_map_changed();
}
-COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback) {
+COMMAND_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback) {
NavAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->set_callback(p_callback);
+ agent->set_avoidance_callback(p_callback);
if (agent->get_map()) {
if (p_callback.is_valid()) {
@@ -696,6 +776,91 @@ COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback) {
}
}
+COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers) {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_layers(p_layers);
+}
+
+COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask) {
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_mask(p_mask);
+}
+
+COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ NavAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_priority(p_priority);
+}
+
+RID GodotNavigationServer::obstacle_create() {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
+ MutexLock lock(mut_this->operations_mutex);
+ RID rid = obstacle_owner.make_rid();
+ NavObstacle *obstacle = obstacle_owner.get_or_null(rid);
+ obstacle->set_self(rid);
+ return rid;
+}
+
+COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+
+ if (obstacle->get_map()) {
+ if (obstacle->get_map()->get_self() == p_map) {
+ return; // Pointless
+ }
+
+ obstacle->get_map()->remove_obstacle(obstacle);
+ }
+
+ obstacle->set_map(nullptr);
+
+ if (p_map.is_valid()) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ obstacle->set_map(map);
+ map->add_obstacle(obstacle);
+ }
+}
+
+RID GodotNavigationServer::obstacle_get_map(RID p_obstacle) const {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND_V(obstacle == nullptr, RID());
+ if (obstacle->get_map()) {
+ return obstacle->get_map()->get_self();
+ }
+ return RID();
+}
+
+COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_height(p_height);
+}
+
+COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_position(p_position);
+}
+
+void GodotNavigationServer::obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_vertices(p_vertices);
+}
+
+COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_avoidance_layers(p_layers);
+}
+
COMMAND_1(free, RID, p_object) {
if (map_owner.owns(p_object)) {
NavMap *map = map_owner.get_or_null(p_object);
@@ -718,6 +883,12 @@ COMMAND_1(free, RID, p_object) {
agent->set_map(nullptr);
}
+ // Remove any assigned obstacles
+ for (NavObstacle *obstacle : map->get_obstacles()) {
+ map->remove_obstacle(obstacle);
+ obstacle->set_map(nullptr);
+ }
+
int map_index = active_maps.find(map);
active_maps.remove_at(map_index);
active_maps_update_id.remove_at(map_index);
@@ -756,6 +927,17 @@ COMMAND_1(free, RID, p_object) {
agent_owner.free(p_object);
+ } else if (obstacle_owner.owns(p_object)) {
+ NavObstacle *obstacle = obstacle_owner.get_or_null(p_object);
+
+ // Removes this agent from the map if assigned
+ if (obstacle->get_map() != nullptr) {
+ obstacle->get_map()->remove_obstacle(obstacle);
+ obstacle->set_map(nullptr);
+ }
+
+ obstacle_owner.free(p_object);
+
} else {
ERR_PRINT("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
}
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 0b113b77d4..5d68844a9b 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -39,6 +39,7 @@
#include "nav_agent.h"
#include "nav_link.h"
#include "nav_map.h"
+#include "nav_obstacle.h"
#include "nav_region.h"
/// The commands are functions executed during the `sync` phase.
@@ -72,6 +73,7 @@ class GodotNavigationServer : public NavigationServer3D {
mutable RID_Owner<NavMap> map_owner;
mutable RID_Owner<NavRegion> region_owner;
mutable RID_Owner<NavAgent> agent_owner;
+ mutable RID_Owner<NavObstacle> obstacle_owner;
bool active = true;
LocalVector<NavMap *> active_maps;
@@ -105,6 +107,9 @@ public:
COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size);
virtual real_t map_get_cell_size(RID p_map) const override;
+ COMMAND_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled);
+ virtual bool map_get_use_edge_connections(RID p_map) const override;
+
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const override;
@@ -121,11 +126,15 @@ public:
virtual TypedArray<RID> map_get_links(RID p_map) const override;
virtual TypedArray<RID> map_get_regions(RID p_map) const override;
virtual TypedArray<RID> map_get_agents(RID p_map) const override;
+ virtual TypedArray<RID> map_get_obstacles(RID p_map) const override;
virtual void map_force_update(RID p_map) override;
virtual RID region_create() 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;
+
COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost);
virtual real_t region_get_enter_cost(RID p_region) const override;
COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost);
@@ -166,19 +175,35 @@ public:
virtual ObjectID link_get_owner_id(RID p_link) const override;
virtual RID agent_create() override;
+ COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled);
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const override;
+ COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled);
+ virtual bool agent_get_use_3d_avoidance(RID p_agent) const override;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
virtual RID agent_get_map(RID p_agent) const override;
COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance);
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
- COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
+ COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon);
+ COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon);
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius);
+ COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height);
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed);
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity);
- COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity);
+ COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity);
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position);
- COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore);
virtual bool agent_is_map_changed(RID p_agent) const override;
- COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback);
+ COMMAND_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback);
+ COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers);
+ COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask);
+ COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority);
+
+ virtual RID obstacle_create() override;
+ COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map);
+ virtual RID obstacle_get_map(RID p_obstacle) const override;
+ COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position);
+ COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height);
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
+ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
COMMAND_1(free, RID, p_object);
diff --git a/modules/navigation/nav_agent.cpp b/modules/navigation/nav_agent.cpp
index 293544c0a5..a0efe4c74c 100644
--- a/modules/navigation/nav_agent.cpp
+++ b/modules/navigation/nav_agent.cpp
@@ -32,8 +32,66 @@
#include "nav_map.h"
+NavAgent::NavAgent() {
+}
+
+void NavAgent::set_avoidance_enabled(bool p_enabled) {
+ avoidance_enabled = p_enabled;
+ _update_rvo_agent_properties();
+}
+
+void NavAgent::set_use_3d_avoidance(bool p_enabled) {
+ use_3d_avoidance = p_enabled;
+ _update_rvo_agent_properties();
+}
+
+void NavAgent::_update_rvo_agent_properties() {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.neighborDist_ = neighbor_distance;
+ rvo_agent_3d.maxNeighbors_ = max_neighbors;
+ rvo_agent_3d.timeHorizon_ = time_horizon_agents;
+ rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
+ rvo_agent_3d.radius_ = radius;
+ rvo_agent_3d.maxSpeed_ = max_speed;
+ rvo_agent_3d.position_ = RVO3D::Vector3(position.x, position.y, position.z);
+ // Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
+ //rvo_agent_3d.velocity_ = RVO3D::Vector3(velocity.x, velocity.y ,velocity.z);
+ rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
+ rvo_agent_3d.height_ = height;
+ rvo_agent_3d.avoidance_layers_ = avoidance_layers;
+ rvo_agent_3d.avoidance_mask_ = avoidance_mask;
+ rvo_agent_3d.avoidance_priority_ = avoidance_priority;
+ } else {
+ rvo_agent_2d.neighborDist_ = neighbor_distance;
+ rvo_agent_2d.maxNeighbors_ = max_neighbors;
+ rvo_agent_2d.timeHorizon_ = time_horizon_agents;
+ rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
+ rvo_agent_2d.radius_ = radius;
+ rvo_agent_2d.maxSpeed_ = max_speed;
+ rvo_agent_2d.position_ = RVO2D::Vector2(position.x, position.z);
+ rvo_agent_2d.elevation_ = position.y;
+ // Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
+ //rvo_agent_2d.velocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ rvo_agent_2d.height_ = height;
+ rvo_agent_2d.avoidance_layers_ = avoidance_layers;
+ rvo_agent_2d.avoidance_mask_ = avoidance_mask;
+ rvo_agent_2d.avoidance_priority_ = avoidance_priority;
+ }
+
+ if (map != nullptr) {
+ if (avoidance_enabled) {
+ map->set_agent_as_controlled(this);
+ } else {
+ map->remove_agent_as_controlled(this);
+ }
+ }
+ agent_dirty = true;
+}
+
void NavAgent::set_map(NavMap *p_map) {
map = p_map;
+ agent_dirty = true;
}
bool NavAgent::is_map_changed() {
@@ -46,25 +104,241 @@ bool NavAgent::is_map_changed() {
}
}
-void NavAgent::set_callback(Callable p_callback) {
- callback = p_callback;
+void NavAgent::set_avoidance_callback(Callable p_callback) {
+ avoidance_callback = p_callback;
}
-bool NavAgent::has_callback() const {
- return callback.is_valid();
+bool NavAgent::has_avoidance_callback() const {
+ return avoidance_callback.is_valid();
}
-void NavAgent::dispatch_callback() {
- if (!callback.is_valid()) {
+void NavAgent::dispatch_avoidance_callback() {
+ if (!avoidance_callback.is_valid()) {
return;
}
- Vector3 new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z());
+ Vector3 new_velocity;
+
+ if (use_3d_avoidance) {
+ new_velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ } else {
+ new_velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ }
+
+ if (clamp_speed) {
+ new_velocity = new_velocity.limit_length(max_speed);
+ }
// Invoke the callback with the new velocity.
Variant args[] = { new_velocity };
const Variant *args_p[] = { &args[0] };
Variant return_value;
Callable::CallError call_error;
- callback.callp(args_p, 1, return_value, call_error);
+
+ avoidance_callback.callp(args_p, 1, return_value, call_error);
+}
+
+void NavAgent::set_neighbor_distance(real_t p_neighbor_distance) {
+ neighbor_distance = p_neighbor_distance;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.neighborDist_ = neighbor_distance;
+ } else {
+ rvo_agent_2d.neighborDist_ = neighbor_distance;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_max_neighbors(int p_max_neighbors) {
+ max_neighbors = p_max_neighbors;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.maxNeighbors_ = max_neighbors;
+ } else {
+ rvo_agent_2d.maxNeighbors_ = max_neighbors;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_time_horizon_agents(real_t p_time_horizon) {
+ time_horizon_agents = p_time_horizon;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.timeHorizon_ = time_horizon_agents;
+ } else {
+ rvo_agent_2d.timeHorizon_ = time_horizon_agents;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_time_horizon_obstacles(real_t p_time_horizon) {
+ time_horizon_obstacles = p_time_horizon;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
+ } else {
+ rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_radius(real_t p_radius) {
+ radius = p_radius;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.radius_ = radius;
+ } else {
+ rvo_agent_2d.radius_ = radius;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_height(real_t p_height) {
+ height = p_height;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.height_ = height;
+ } else {
+ rvo_agent_2d.height_ = height;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_max_speed(real_t p_max_speed) {
+ max_speed = p_max_speed;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.maxSpeed_ = max_speed;
+ } else {
+ rvo_agent_2d.maxSpeed_ = max_speed;
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_position(const Vector3 p_position) {
+ position = p_position;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.position_ = RVO3D::Vector3(p_position.x, p_position.y, p_position.z);
+ } else {
+ rvo_agent_2d.elevation_ = p_position.y;
+ rvo_agent_2d.position_ = RVO2D::Vector2(p_position.x, p_position.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_target_position(const Vector3 p_target_position) {
+ target_position = p_target_position;
+}
+
+void NavAgent::set_velocity(const Vector3 p_velocity) {
+ // Sets the "wanted" velocity for an agent as a suggestion
+ // This velocity is not guaranteed, RVO simulation will only try to fulfill it
+ velocity = p_velocity;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
+ } else {
+ rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_velocity_forced(const Vector3 p_velocity) {
+ // This function replaces the internal rvo simulation velocity
+ // should only be used after the agent was teleported
+ // as it destroys consistency in movement in cramped situations
+ // use velocity instead to update with a safer "suggestion"
+ velocity_forced = p_velocity;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.velocity_ = RVO3D::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ } else {
+ rvo_agent_2d.velocity_ = RVO2D::Vector2(p_velocity.x, p_velocity.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::update() {
+ // Updates this agent with the calculated results from the rvo simulation
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ } else {
+ velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ }
+ }
+}
+
+void NavAgent::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_mask_ = avoidance_mask;
+ } else {
+ rvo_agent_2d.avoidance_mask_ = avoidance_mask;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_layers_ = avoidance_layers;
+ } else {
+ rvo_agent_2d.avoidance_layers_ = avoidance_layers;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_priority_ = avoidance_priority;
+ } else {
+ rvo_agent_2d.avoidance_priority_ = avoidance_priority;
+ }
+ agent_dirty = true;
+};
+
+bool NavAgent::check_dirty() {
+ const bool was_dirty = agent_dirty;
+ agent_dirty = false;
+ return was_dirty;
+}
+
+const Dictionary NavAgent::get_avoidance_data() const {
+ // Returns debug data from RVO simulation internals of this agent.
+ Dictionary _avoidance_data;
+ if (use_3d_avoidance) {
+ _avoidance_data["max_neighbors"] = int(rvo_agent_3d.maxNeighbors_);
+ _avoidance_data["max_speed"] = float(rvo_agent_3d.maxSpeed_);
+ _avoidance_data["neighbor_distance"] = float(rvo_agent_3d.neighborDist_);
+ _avoidance_data["new_velocity"] = Vector3(rvo_agent_3d.newVelocity_.x(), rvo_agent_3d.newVelocity_.y(), rvo_agent_3d.newVelocity_.z());
+ _avoidance_data["velocity"] = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ _avoidance_data["position"] = Vector3(rvo_agent_3d.position_.x(), rvo_agent_3d.position_.y(), rvo_agent_3d.position_.z());
+ _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_3d.prefVelocity_.x(), rvo_agent_3d.prefVelocity_.y(), rvo_agent_3d.prefVelocity_.z());
+ _avoidance_data["radius"] = float(rvo_agent_3d.radius_);
+ _avoidance_data["time_horizon_agents"] = float(rvo_agent_3d.timeHorizon_);
+ _avoidance_data["time_horizon_obstacles"] = 0.0;
+ _avoidance_data["height"] = float(rvo_agent_3d.height_);
+ _avoidance_data["avoidance_layers"] = int(rvo_agent_3d.avoidance_layers_);
+ _avoidance_data["avoidance_mask"] = int(rvo_agent_3d.avoidance_mask_);
+ _avoidance_data["avoidance_priority"] = float(rvo_agent_3d.avoidance_priority_);
+ } else {
+ _avoidance_data["max_neighbors"] = int(rvo_agent_2d.maxNeighbors_);
+ _avoidance_data["max_speed"] = float(rvo_agent_2d.maxSpeed_);
+ _avoidance_data["neighbor_distance"] = float(rvo_agent_2d.neighborDist_);
+ _avoidance_data["new_velocity"] = Vector3(rvo_agent_2d.newVelocity_.x(), 0.0, rvo_agent_2d.newVelocity_.y());
+ _avoidance_data["velocity"] = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ _avoidance_data["position"] = Vector3(rvo_agent_2d.position_.x(), 0.0, rvo_agent_2d.position_.y());
+ _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_2d.prefVelocity_.x(), 0.0, rvo_agent_2d.prefVelocity_.y());
+ _avoidance_data["radius"] = float(rvo_agent_2d.radius_);
+ _avoidance_data["time_horizon_agents"] = float(rvo_agent_2d.timeHorizon_);
+ _avoidance_data["time_horizon_obstacles"] = float(rvo_agent_2d.timeHorizonObst_);
+ _avoidance_data["height"] = float(rvo_agent_2d.height_);
+ _avoidance_data["avoidance_layers"] = int(rvo_agent_2d.avoidance_layers_);
+ _avoidance_data["avoidance_mask"] = int(rvo_agent_2d.avoidance_mask_);
+ _avoidance_data["avoidance_priority"] = float(rvo_agent_2d.avoidance_priority_);
+ }
+ return _avoidance_data;
}
diff --git a/modules/navigation/nav_agent.h b/modules/navigation/nav_agent.h
index f154ce14d9..497b239f84 100644
--- a/modules/navigation/nav_agent.h
+++ b/modules/navigation/nav_agent.h
@@ -32,34 +32,121 @@
#define NAV_AGENT_H
#include "core/object/class_db.h"
+#include "core/templates/local_vector.h"
+#include "nav_agent.h"
#include "nav_rid.h"
-#include <Agent.h>
+#include <Agent2d.h>
+#include <Agent3d.h>
class NavMap;
class NavAgent : public NavRid {
+ Vector3 position;
+ Vector3 target_position;
+ Vector3 velocity;
+ Vector3 velocity_forced;
+ real_t height = 1.0;
+ real_t radius = 1.0;
+ real_t max_speed = 1.0;
+ real_t time_horizon_agents = 1.0;
+ real_t time_horizon_obstacles = 0.0;
+ int max_neighbors = 5;
+ real_t neighbor_distance = 5.0;
+ Vector3 safe_velocity;
+ bool clamp_speed = true; // Experimental, clamps velocity to max_speed.
+
NavMap *map = nullptr;
- RVO::Agent agent;
- Callable callback = Callable();
+
+ RVO2D::Agent2D rvo_agent_2d;
+ RVO3D::Agent3D rvo_agent_3d;
+ bool use_3d_avoidance = false;
+ bool avoidance_enabled = false;
+
+ uint32_t avoidance_layers = 1;
+ uint32_t avoidance_mask = 1;
+ real_t avoidance_priority = 1.0;
+
+ Callable avoidance_callback = Callable();
+
+ bool agent_dirty = true;
+
uint32_t map_update_id = 0;
public:
- void set_map(NavMap *p_map);
- NavMap *get_map() {
- return map;
- }
+ NavAgent();
+
+ void set_avoidance_enabled(bool p_enabled);
+ bool is_avoidance_enabled() { return avoidance_enabled; }
- RVO::Agent *get_agent() {
- return &agent;
- }
+ void set_use_3d_avoidance(bool p_enabled);
+ bool get_use_3d_avoidance() { return use_3d_avoidance; }
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() { return map; }
bool is_map_changed();
- void set_callback(Callable p_callback);
- bool has_callback() const;
+ RVO2D::Agent2D *get_rvo_agent_2d() { return &rvo_agent_2d; }
+ RVO3D::Agent3D *get_rvo_agent_3d() { return &rvo_agent_3d; }
+
+ void set_avoidance_callback(Callable p_callback);
+ bool has_avoidance_callback() const;
+
+ void dispatch_avoidance_callback();
+
+ void set_neighbor_distance(real_t p_neighbor_distance);
+ real_t get_neighbor_distance() const { return neighbor_distance; }
+
+ void set_max_neighbors(int p_max_neighbors);
+ int get_max_neighbors() const { return max_neighbors; }
+
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
+
+ void set_radius(real_t p_radius);
+ real_t get_radius() const { return radius; }
+
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_max_speed(real_t p_max_speed);
+ real_t get_max_speed() const { return max_speed; }
+
+ void set_position(const Vector3 p_position);
+ const Vector3 &get_position() const { return position; }
+
+ void set_target_position(const Vector3 p_target_position);
+ const Vector3 &get_target_position() const { return target_position; }
+
+ void set_velocity(const Vector3 p_velocity);
+ const Vector3 &get_velocity() const { return velocity; }
+
+ void set_velocity_forced(const Vector3 p_velocity);
+ const Vector3 &get_velocity_forced() const { return velocity_forced; }
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const { return avoidance_layers; };
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const { return avoidance_mask; };
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const { return avoidance_priority; };
+
+ bool check_dirty();
+
+ // Updates this agent with rvo data after the rvo simulation avoidance step.
+ void update();
+
+ // RVO debug data from the last frame update.
+ const Dictionary get_avoidance_data() const;
- void dispatch_callback();
+private:
+ void _update_rvo_agent_properties();
};
#endif // NAV_AGENT_H
diff --git a/modules/navigation/nav_base.h b/modules/navigation/nav_base.h
index d4354f929d..b5cdc117f2 100644
--- a/modules/navigation/nav_base.h
+++ b/modules/navigation/nav_base.h
@@ -48,6 +48,9 @@ protected:
public:
NavigationUtilities::PathSegmentType get_type() const { return type; }
+ virtual void set_use_edge_connections(bool p_enabled) {}
+ virtual bool get_use_edge_connections() const { return false; }
+
void set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; }
uint32_t get_navigation_layers() const { return navigation_layers; }
@@ -59,6 +62,8 @@ public:
void set_owner_id(ObjectID p_owner_id) { owner_id = p_owner_id; }
ObjectID get_owner_id() const { return owner_id; }
+
+ virtual ~NavBase(){};
};
#endif // NAV_BASE_H
diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp
index ad87cc0b05..5607a3253e 100644
--- a/modules/navigation/nav_link.cpp
+++ b/modules/navigation/nav_link.cpp
@@ -33,21 +33,33 @@
#include "nav_map.h"
void NavLink::set_map(NavMap *p_map) {
+ if (map == p_map) {
+ return;
+ }
map = p_map;
link_dirty = true;
}
void NavLink::set_bidirectional(bool p_bidirectional) {
+ if (bidirectional == p_bidirectional) {
+ return;
+ }
bidirectional = p_bidirectional;
link_dirty = true;
}
void NavLink::set_start_position(const Vector3 p_position) {
+ if (start_position == p_position) {
+ return;
+ }
start_position = p_position;
link_dirty = true;
}
void NavLink::set_end_position(const Vector3 p_position) {
+ if (end_position == p_position) {
+ return;
+ }
end_position = p_position;
link_dirty = true;
}
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 91b13ba9c4..37e0f232e5 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -30,11 +30,14 @@
#include "nav_map.h"
+#include "core/config/project_settings.h"
#include "core/object/worker_thread_pool.h"
#include "nav_agent.h"
#include "nav_link.h"
+#include "nav_obstacle.h"
#include "nav_region.h"
-#include <algorithm>
+
+#include <Obstacle2d.h>
#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
@@ -51,21 +54,41 @@
}
void NavMap::set_up(Vector3 p_up) {
+ if (up == p_up) {
+ return;
+ }
up = p_up;
regenerate_polygons = true;
}
void NavMap::set_cell_size(real_t p_cell_size) {
+ if (cell_size == p_cell_size) {
+ return;
+ }
cell_size = p_cell_size;
regenerate_polygons = true;
}
+void NavMap::set_use_edge_connections(bool p_enabled) {
+ if (use_edge_connections == p_enabled) {
+ return;
+ }
+ use_edge_connections = p_enabled;
+ regenerate_links = true;
+}
+
void NavMap::set_edge_connection_margin(real_t p_edge_connection_margin) {
+ if (edge_connection_margin == p_edge_connection_margin) {
+ return;
+ }
edge_connection_margin = p_edge_connection_margin;
regenerate_links = true;
}
void NavMap::set_link_connection_radius(real_t p_link_connection_radius) {
+ if (link_connection_radius == p_link_connection_radius) {
+ return;
+ }
link_connection_radius = p_link_connection_radius;
regenerate_links = true;
}
@@ -522,9 +545,7 @@ gd::ClosestPointQueryResult NavMap::get_closest_point_info(const Vector3 &p_poin
gd::ClosestPointQueryResult result;
real_t closest_point_ds = FLT_MAX;
- for (size_t i(0); i < polygons.size(); i++) {
- const gd::Polygon &p = polygons[i];
-
+ for (const gd::Polygon &p : polygons) {
// For each face check the distance to the point
for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) {
const Face3 f(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
@@ -549,7 +570,7 @@ void NavMap::add_region(NavRegion *p_region) {
void NavMap::remove_region(NavRegion *p_region) {
int64_t region_index = regions.find(p_region);
- if (region_index != -1) {
+ if (region_index >= 0) {
regions.remove_at_unordered(region_index);
regenerate_links = true;
}
@@ -562,14 +583,14 @@ void NavMap::add_link(NavLink *p_link) {
void NavMap::remove_link(NavLink *p_link) {
int64_t link_index = links.find(p_link);
- if (link_index != -1) {
+ if (link_index >= 0) {
links.remove_at_unordered(link_index);
regenerate_links = true;
}
}
bool NavMap::has_agent(NavAgent *agent) const {
- return (agents.find(agent) != -1);
+ return (agents.find(agent) >= 0);
}
void NavMap::add_agent(NavAgent *agent) {
@@ -582,25 +603,57 @@ void NavMap::add_agent(NavAgent *agent) {
void NavMap::remove_agent(NavAgent *agent) {
remove_agent_as_controlled(agent);
int64_t agent_index = agents.find(agent);
- if (agent_index != -1) {
+ if (agent_index >= 0) {
agents.remove_at_unordered(agent_index);
agents_dirty = true;
}
}
+bool NavMap::has_obstacle(NavObstacle *obstacle) const {
+ return (obstacles.find(obstacle) >= 0);
+}
+
+void NavMap::add_obstacle(NavObstacle *obstacle) {
+ if (!has_obstacle(obstacle)) {
+ obstacles.push_back(obstacle);
+ obstacles_dirty = true;
+ }
+}
+
+void NavMap::remove_obstacle(NavObstacle *obstacle) {
+ int64_t obstacle_index = obstacles.find(obstacle);
+ if (obstacle_index >= 0) {
+ obstacles.remove_at_unordered(obstacle_index);
+ obstacles_dirty = true;
+ }
+}
+
void NavMap::set_agent_as_controlled(NavAgent *agent) {
- const bool exist = (controlled_agents.find(agent) != -1);
- if (!exist) {
- ERR_FAIL_COND(!has_agent(agent));
- controlled_agents.push_back(agent);
- agents_dirty = true;
+ remove_agent_as_controlled(agent);
+ if (agent->get_use_3d_avoidance()) {
+ int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
+ if (agent_3d_index < 0) {
+ active_3d_avoidance_agents.push_back(agent);
+ agents_dirty = true;
+ }
+ } else {
+ int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
+ if (agent_2d_index < 0) {
+ active_2d_avoidance_agents.push_back(agent);
+ agents_dirty = true;
+ }
}
}
void NavMap::remove_agent_as_controlled(NavAgent *agent) {
- int64_t active_avoidance_agent_index = controlled_agents.find(agent);
- if (active_avoidance_agent_index != -1) {
- controlled_agents.remove_at_unordered(active_avoidance_agent_index);
+ int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
+ if (agent_3d_index >= 0) {
+ active_3d_avoidance_agents.remove_at_unordered(agent_3d_index);
+ agents_dirty = true;
+ }
+ int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
+ if (agent_2d_index >= 0) {
+ active_2d_avoidance_agents.remove_at_unordered(agent_2d_index);
agents_dirty = true;
}
}
@@ -706,7 +759,9 @@ void NavMap::sync() {
_new_pm_edge_merge_count += 1;
} else {
CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
- free_edges.push_back(E.value[0]);
+ if (use_edge_connections && E.value[0].polygon->owner->get_use_edge_connections()) {
+ free_edges.push_back(E.value[0]);
+ }
}
}
@@ -891,22 +946,30 @@ void NavMap::sync() {
map_update_id = (map_update_id + 1) % 9999999;
}
- // Update agents tree.
- if (agents_dirty) {
- // cannot use LocalVector here as RVO library expects std::vector to build KdTree
- std::vector<RVO::Agent *> raw_agents;
- raw_agents.reserve(controlled_agents.size());
- for (NavAgent *controlled_agent : controlled_agents) {
- raw_agents.push_back(controlled_agent->get_agent());
+ // Do we have modified obstacle positions?
+ for (NavObstacle *obstacle : obstacles) {
+ if (obstacle->check_dirty()) {
+ obstacles_dirty = true;
}
- rvo.buildAgentTree(raw_agents);
+ }
+ // Do we have modified agent arrays?
+ for (NavAgent *agent : agents) {
+ if (agent->check_dirty()) {
+ agents_dirty = true;
+ }
+ }
+
+ // Update avoidance worlds.
+ if (obstacles_dirty || agents_dirty) {
+ _update_rvo_simulation();
}
regenerate_polygons = false;
regenerate_links = false;
+ obstacles_dirty = false;
agents_dirty = false;
- // Performance Monitor
+ // Performance Monitor.
pm_region_count = _new_pm_region_count;
pm_agent_count = _new_pm_agent_count;
pm_link_count = _new_pm_link_count;
@@ -917,22 +980,155 @@ void NavMap::sync() {
pm_edge_free_count = _new_pm_edge_free_count;
}
-void NavMap::compute_single_step(uint32_t index, NavAgent **agent) {
- (*(agent + index))->get_agent()->computeNeighbors(&rvo);
- (*(agent + index))->get_agent()->computeNewVelocity(deltatime);
+void NavMap::_update_rvo_obstacles_tree_2d() {
+ int obstacle_vertex_count = 0;
+ for (NavObstacle *obstacle : obstacles) {
+ obstacle_vertex_count += obstacle->get_vertices().size();
+ }
+
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree
+ std::vector<RVO2D::Obstacle2D *> raw_obstacles;
+ raw_obstacles.reserve(obstacle_vertex_count);
+
+ // The following block is modified copy from RVO2D::AddObstacle()
+ // Obstacles are linked and depend on all other obstacles.
+ for (NavObstacle *obstacle : obstacles) {
+ const Vector3 &_obstacle_position = obstacle->get_position();
+ const Vector<Vector3> &_obstacle_vertices = obstacle->get_vertices();
+
+ if (_obstacle_vertices.size() < 2) {
+ continue;
+ }
+
+ std::vector<RVO2D::Vector2> rvo_2d_vertices;
+ rvo_2d_vertices.reserve(_obstacle_vertices.size());
+
+ uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();
+
+ for (const Vector3 &_obstacle_vertex : _obstacle_vertices) {
+ rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z));
+ }
+
+ const size_t obstacleNo = raw_obstacles.size();
+
+ for (size_t i = 0; i < rvo_2d_vertices.size(); i++) {
+ RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D();
+ rvo_2d_obstacle->point_ = rvo_2d_vertices[i];
+ rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;
+
+ if (i != 0) {
+ rvo_2d_obstacle->prevObstacle_ = raw_obstacles.back();
+ rvo_2d_obstacle->prevObstacle_->nextObstacle_ = rvo_2d_obstacle;
+ }
+
+ if (i == rvo_2d_vertices.size() - 1) {
+ rvo_2d_obstacle->nextObstacle_ = raw_obstacles[obstacleNo];
+ rvo_2d_obstacle->nextObstacle_->prevObstacle_ = rvo_2d_obstacle;
+ }
+
+ rvo_2d_obstacle->unitDir_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]);
+
+ if (rvo_2d_vertices.size() == 2) {
+ rvo_2d_obstacle->isConvex_ = true;
+ } else {
+ rvo_2d_obstacle->isConvex_ = (leftOf(rvo_2d_vertices[(i == 0 ? rvo_2d_vertices.size() - 1 : i - 1)], rvo_2d_vertices[i], rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);
+ }
+
+ rvo_2d_obstacle->id_ = raw_obstacles.size();
+
+ raw_obstacles.push_back(rvo_2d_obstacle);
+ }
+ }
+
+ rvo_simulation_2d.kdTree_->buildObstacleTree(raw_obstacles);
+}
+
+void NavMap::_update_rvo_agents_tree_2d() {
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
+ std::vector<RVO2D::Agent2D *> raw_agents;
+ raw_agents.reserve(active_2d_avoidance_agents.size());
+ for (NavAgent *agent : active_2d_avoidance_agents) {
+ raw_agents.push_back(agent->get_rvo_agent_2d());
+ }
+ rvo_simulation_2d.kdTree_->buildAgentTree(raw_agents);
+}
+
+void NavMap::_update_rvo_agents_tree_3d() {
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
+ std::vector<RVO3D::Agent3D *> raw_agents;
+ raw_agents.reserve(active_3d_avoidance_agents.size());
+ for (NavAgent *agent : active_3d_avoidance_agents) {
+ raw_agents.push_back(agent->get_rvo_agent_3d());
+ }
+ rvo_simulation_3d.kdTree_->buildAgentTree(raw_agents);
+}
+
+void NavMap::_update_rvo_simulation() {
+ if (obstacles_dirty) {
+ _update_rvo_obstacles_tree_2d();
+ }
+ if (agents_dirty) {
+ _update_rvo_agents_tree_2d();
+ _update_rvo_agents_tree_3d();
+ }
+}
+
+void NavMap::compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent) {
+ (*(agent + index))->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
+ (*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
+ (*(agent + index))->get_rvo_agent_2d()->update(&rvo_simulation_2d);
+ (*(agent + index))->update();
+}
+
+void NavMap::compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent) {
+ (*(agent + index))->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
+ (*(agent + index))->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
+ (*(agent + index))->get_rvo_agent_3d()->update(&rvo_simulation_3d);
+ (*(agent + index))->update();
}
void NavMap::step(real_t p_deltatime) {
deltatime = p_deltatime;
- if (controlled_agents.size() > 0) {
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap::compute_single_step, controlled_agents.ptr(), controlled_agents.size(), -1, true, SNAME("NavigationMapAgents"));
- WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+
+ rvo_simulation_2d.setTimeStep(float(deltatime));
+ rvo_simulation_3d.setTimeStep(float(deltatime));
+
+ if (active_2d_avoidance_agents.size() > 0) {
+ if (use_threads && avoidance_use_multiple_threads) {
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap::compute_single_avoidance_step_2d, active_2d_avoidance_agents.ptr(), active_2d_avoidance_agents.size(), -1, true, SNAME("RVOAvoidanceAgents2D"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+ } else {
+ for (NavAgent *agent : active_2d_avoidance_agents) {
+ agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);
+ agent->update();
+ }
+ }
+ }
+
+ if (active_3d_avoidance_agents.size() > 0) {
+ if (use_threads && avoidance_use_multiple_threads) {
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap::compute_single_avoidance_step_3d, active_3d_avoidance_agents.ptr(), active_3d_avoidance_agents.size(), -1, true, SNAME("RVOAvoidanceAgents3D"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+ } else {
+ for (NavAgent *agent : active_3d_avoidance_agents) {
+ agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);
+ agent->update();
+ }
+ }
}
}
void NavMap::dispatch_callbacks() {
- for (NavAgent *agent : controlled_agents) {
- agent->dispatch_callback();
+ for (NavAgent *agent : active_2d_avoidance_agents) {
+ agent->dispatch_avoidance_callback();
+ }
+
+ for (NavAgent *agent : active_3d_avoidance_agents) {
+ agent->dispatch_avoidance_callback();
}
}
@@ -970,6 +1166,8 @@ void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys
}
NavMap::NavMap() {
+ avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");
+ avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");
}
NavMap::~NavMap() {
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index 5ec2c2826c..2bd9459626 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -38,11 +38,16 @@
#include "core/templates/rb_map.h"
#include "nav_utils.h"
-#include <KdTree.h>
+#include <KdTree2d.h>
+#include <RVOSimulator2d.h>
+
+#include <KdTree3d.h>
+#include <RVOSimulator3d.h>
class NavLink;
class NavRegion;
class NavAgent;
+class NavObstacle;
class NavMap : public NavRid {
/// Map Up
@@ -52,6 +57,7 @@ class NavMap : public NavRid {
/// each cell has the following cell_size.
real_t cell_size = 0.25;
+ bool use_edge_connections = true;
/// This value is used to detect the near edges to connect.
real_t edge_connection_margin = 0.25;
@@ -71,17 +77,25 @@ class NavMap : public NavRid {
/// Map polygons
LocalVector<gd::Polygon> polygons;
- /// Rvo world
- RVO::KdTree rvo;
+ /// RVO avoidance worlds
+ RVO2D::RVOSimulator2D rvo_simulation_2d;
+ RVO3D::RVOSimulator3D rvo_simulation_3d;
+
+ /// avoidance controlled agents
+ LocalVector<NavAgent *> active_2d_avoidance_agents;
+ LocalVector<NavAgent *> active_3d_avoidance_agents;
- /// Is agent array modified?
- bool agents_dirty = false;
+ /// dirty flag when one of the agent's arrays are modified
+ bool agents_dirty = true;
/// All the Agents (even the controlled one)
LocalVector<NavAgent *> agents;
- /// Controlled agents
- LocalVector<NavAgent *> controlled_agents;
+ /// All the avoidance obstacles (both static and dynamic)
+ LocalVector<NavObstacle *> obstacles;
+
+ /// Are rvo obstacles modified?
+ bool obstacles_dirty = true;
/// Physics delta time
real_t deltatime = 0.0;
@@ -89,6 +103,10 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id = 0;
+ bool use_threads = true;
+ bool avoidance_use_multiple_threads = true;
+ bool avoidance_use_high_priority_threads = true;
+
// Performance Monitor
int pm_region_count = 0;
int pm_agent_count = 0;
@@ -113,6 +131,11 @@ public:
return cell_size;
}
+ void set_use_edge_connections(bool p_enabled);
+ bool get_use_edge_connections() const {
+ return use_edge_connections;
+ }
+
void set_edge_connection_margin(real_t p_edge_connection_margin);
real_t get_edge_connection_margin() const {
return edge_connection_margin;
@@ -154,6 +177,13 @@ public:
void set_agent_as_controlled(NavAgent *agent);
void remove_agent_as_controlled(NavAgent *agent);
+ bool has_obstacle(NavObstacle *obstacle) const;
+ void add_obstacle(NavObstacle *obstacle);
+ void remove_obstacle(NavObstacle *obstacle);
+ const LocalVector<NavObstacle *> &get_obstacles() const {
+ return obstacles;
+ }
+
uint32_t get_map_update_id() const {
return map_update_id;
}
@@ -174,7 +204,15 @@ public:
private:
void compute_single_step(uint32_t index, NavAgent **agent);
+
+ void compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent);
+ void compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent);
+
void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const;
+ void _update_rvo_simulation();
+ void _update_rvo_obstacles_tree_2d();
+ void _update_rvo_agents_tree_2d();
+ void _update_rvo_agents_tree_3d();
};
#endif // NAV_MAP_H
diff --git a/modules/navigation/nav_obstacle.cpp b/modules/navigation/nav_obstacle.cpp
new file mode 100644
index 0000000000..5d0bc59cbb
--- /dev/null
+++ b/modules/navigation/nav_obstacle.cpp
@@ -0,0 +1,86 @@
+/**************************************************************************/
+/* nav_obstacle.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 "nav_obstacle.h"
+
+#include "nav_map.h"
+
+NavObstacle::NavObstacle() {}
+
+NavObstacle::~NavObstacle() {}
+
+void NavObstacle::set_map(NavMap *p_map) {
+ if (map != p_map) {
+ map = p_map;
+ obstacle_dirty = true;
+ }
+}
+
+void NavObstacle::set_position(const Vector3 p_position) {
+ if (position != p_position) {
+ position = p_position;
+ obstacle_dirty = true;
+ }
+}
+
+void NavObstacle::set_height(const real_t p_height) {
+ if (height != p_height) {
+ height = p_height;
+ obstacle_dirty = true;
+ }
+}
+
+void NavObstacle::set_vertices(const Vector<Vector3> &p_vertices) {
+ if (vertices != p_vertices) {
+ vertices = p_vertices;
+ obstacle_dirty = true;
+ }
+}
+
+bool NavObstacle::is_map_changed() {
+ if (map) {
+ bool is_changed = map->get_map_update_id() != map_update_id;
+ map_update_id = map->get_map_update_id();
+ return is_changed;
+ } else {
+ return false;
+ }
+}
+
+void NavObstacle::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ obstacle_dirty = true;
+}
+
+bool NavObstacle::check_dirty() {
+ const bool was_dirty = obstacle_dirty;
+ obstacle_dirty = false;
+ return was_dirty;
+}
diff --git a/modules/navigation/nav_obstacle.h b/modules/navigation/nav_obstacle.h
new file mode 100644
index 0000000000..f59eba5200
--- /dev/null
+++ b/modules/navigation/nav_obstacle.h
@@ -0,0 +1,78 @@
+/**************************************************************************/
+/* nav_obstacle.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 NAV_OBSTACLE_H
+#define NAV_OBSTACLE_H
+
+#include "core/object/class_db.h"
+#include "core/templates/local_vector.h"
+#include "nav_agent.h"
+#include "nav_rid.h"
+
+class NavMap;
+
+class NavObstacle : public NavRid {
+ NavMap *map = nullptr;
+ Vector3 position;
+ Vector<Vector3> vertices;
+
+ real_t height = 0.0;
+
+ uint32_t avoidance_layers = 1;
+
+ bool obstacle_dirty = true;
+
+ uint32_t map_update_id = 0;
+
+public:
+ NavObstacle();
+ ~NavObstacle();
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() { return map; }
+
+ void set_position(const Vector3 p_position);
+ const Vector3 &get_position() const { return position; }
+
+ void set_height(const real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_vertices(const Vector<Vector3> &p_vertices);
+ const Vector<Vector3> &get_vertices() const { return vertices; }
+
+ bool is_map_changed();
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const { return avoidance_layers; };
+
+ bool check_dirty();
+};
+
+#endif // NAV_OBSTACLE_H
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index cad4678e5a..d0f055874e 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -33,6 +33,9 @@
#include "nav_map.h"
void NavRegion::set_map(NavMap *p_map) {
+ if (map == p_map) {
+ return;
+ }
map = p_map;
polygons_dirty = true;
if (!map) {
@@ -40,7 +43,17 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
+void NavRegion::set_use_edge_connections(bool p_enabled) {
+ if (use_edge_connections != p_enabled) {
+ use_edge_connections = p_enabled;
+ polygons_dirty = true;
+ }
+}
+
void NavRegion::set_transform(Transform3D p_transform) {
+ if (transform == p_transform) {
+ return;
+ }
transform = p_transform;
polygons_dirty = true;
}
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index 0942aa22f0..72299e8874 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -42,6 +42,8 @@ class NavRegion : public NavBase {
Ref<NavigationMesh> mesh;
Vector<gd::Edge::Connection> connections;
+ bool use_edge_connections = true;
+
bool polygons_dirty = true;
/// Cache
@@ -61,6 +63,11 @@ public:
return map;
}
+ void set_use_edge_connections(bool p_enabled);
+ bool get_use_edge_connections() const {
+ return use_edge_connections;
+ }
+
void set_transform(Transform3D transform);
const Transform3D &get_transform() const {
return transform;
diff --git a/modules/noise/config.py b/modules/noise/config.py
index 2318d28c53..e6d5e1a646 100644
--- a/modules/noise/config.py
+++ b/modules/noise/config.py
@@ -11,6 +11,7 @@ def get_doc_classes():
"FastNoiseLite",
"Noise",
"NoiseTexture2D",
+ "NoiseTexture3D",
]
diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml
index d214a02cf1..c075b5b629 100644
--- a/modules/noise/doc_classes/Noise.xml
+++ b/modules/noise/doc_classes/Noise.xml
@@ -19,8 +19,20 @@
<param index="3" name="in_3d_space" type="bool" default="false" />
<param index="4" name="normalize" type="bool" default="true" />
<description>
- Returns a 2D [Image] noise image.
- Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
+ Returns an [Image] containing 2D noise values.
+ [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
+ </description>
+ </method>
+ <method name="get_image_3d" qualifiers="const">
+ <return type="Image[]" />
+ <param index="0" name="width" type="int" />
+ <param index="1" name="height" type="int" />
+ <param index="2" name="depth" type="int" />
+ <param index="3" name="invert" type="bool" default="false" />
+ <param index="4" name="normalize" type="bool" default="true" />
+ <description>
+ Returns an [Array] of [Image]s containing 3D noise values for use with [method ImageTexture3D.create].
+ [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
<method name="get_noise_1d" qualifiers="const">
@@ -70,8 +82,21 @@
<param index="4" name="skirt" type="float" default="0.1" />
<param index="5" name="normalize" type="bool" default="true" />
<description>
- Returns a seamless 2D [Image] noise image.
- Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
+ Returns an [Image] containing seamless 2D noise values.
+ [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
+ </description>
+ </method>
+ <method name="get_seamless_image_3d" qualifiers="const">
+ <return type="Image[]" />
+ <param index="0" name="width" type="int" />
+ <param index="1" name="height" type="int" />
+ <param index="2" name="depth" type="int" />
+ <param index="3" name="invert" type="bool" default="false" />
+ <param index="4" name="skirt" type="float" default="0.1" />
+ <param index="5" name="normalize" type="bool" default="true" />
+ <description>
+ Returns an [Array] of [Image]s containing seamless 3D noise values for use with [method ImageTexture3D.create].
+ [b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
</methods>
diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml
new file mode 100644
index 0000000000..7394e7ff08
--- /dev/null
+++ b/modules/noise/doc_classes/NoiseTexture3D.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NoiseTexture3D" inherits="Texture3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ A texture filled with noise generated by a [Noise] object.
+ </brief_description>
+ <description>
+ Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size.
+ The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image:
+ [codeblock]
+ var texture = NoiseTexture3D.new()
+ texture.noise = FastNoiseLite.new()
+ await texture.changed
+ var data = texture.get_data()
+ [/codeblock]
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
+ A [Gradient] which is used to map the luminance of each pixel to a color value.
+ </member>
+ <member name="depth" type="int" setter="set_depth" getter="get_depth" default="64">
+ Depth of the generated texture.
+ </member>
+ <member name="height" type="int" setter="set_height" getter="get_height" default="64">
+ Height of the generated texture.
+ </member>
+ <member name="invert" type="bool" setter="set_invert" getter="get_invert" default="false">
+ If [code]true[/code], inverts the noise texture. White becomes black, black becomes white.
+ </member>
+ <member name="noise" type="Noise" setter="set_noise" getter="get_noise">
+ The instance of the [Noise] object.
+ </member>
+ <member name="normalize" type="bool" setter="set_normalize" getter="is_normalized" default="true">
+ If [code]true[/code], the noise image coming from the noise generator is normalized to the range [code]0.0[/code] to [code]1.0[/code].
+ Turning normalization off can affect the contrast and allows you to generate non repeating tileable noise textures.
+ </member>
+ <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
+ If [code]true[/code], a seamless texture is requested from the [Noise] resource.
+ [b]Note:[/b] Seamless noise textures may take longer to generate and/or can have a lower contrast compared to non-seamless noise depending on the used [Noise] resource. This is because some implementations use higher dimensions for generating seamless noise.
+ </member>
+ <member name="seamless_blend_skirt" type="float" setter="set_seamless_blend_skirt" getter="get_seamless_blend_skirt" default="0.1">
+ Used for the default/fallback implementation of the seamless texture generation. It determines the distance over which the seams are blended. High values may result in less details and contrast. See [Noise] for further details.
+ </member>
+ <member name="width" type="int" setter="set_width" getter="get_width" default="64">
+ Width of the generated texture.
+ </member>
+ </members>
+</class>
diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp
index 224c082c0f..4aea98c4de 100644
--- a/modules/noise/fastnoise_lite.cpp
+++ b/modules/noise/fastnoise_lite.cpp
@@ -416,7 +416,7 @@ void FastNoiseLite::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "noise_type", PROPERTY_HINT_ENUM, "Simplex,Simplex Smooth,Cellular,Perlin,Value Cubic,Value"), "set_noise_type", "get_noise_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".001,1"), "set_frequency", "get_frequency");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".0001,1,.0001"), "set_frequency", "get_frequency");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "offset", PROPERTY_HINT_RANGE, "-999999999,999999999,0.01"), "set_offset", "get_offset");
ADD_GROUP("Fractal", "fractal_");
diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp
index e95788b863..1115d92f58 100644
--- a/modules/noise/noise.cpp
+++ b/modules/noise/noise.cpp
@@ -32,21 +32,40 @@
#include <float.h>
-Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
- ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());
+Vector<Ref<Image>> Noise::_get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
+ ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>());
int skirt_width = MAX(1, p_width * p_blend_skirt);
int skirt_height = MAX(1, p_height * p_blend_skirt);
+ int skirt_depth = MAX(1, p_depth * p_blend_skirt);
int src_width = p_width + skirt_width;
int src_height = p_height + skirt_height;
+ int src_depth = p_depth + skirt_depth;
+
+ Vector<Ref<Image>> src = _get_image(src_width, src_height, src_depth, p_invert, p_in_3d_space, p_normalize);
+ bool grayscale = (src[0]->get_format() == Image::FORMAT_L8);
- Ref<Image> src = get_image(src_width, src_height, p_invert, p_in_3d_space, p_normalize);
- bool grayscale = (src->get_format() == Image::FORMAT_L8);
if (grayscale) {
- return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt);
+ return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt);
} else {
- return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_invert, p_blend_skirt);
+ return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt);
+ }
+}
+
+Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
+ Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_blend_skirt, p_normalize);
+ return images[0];
+}
+
+TypedArray<Image> Noise::get_seamless_image_3d(int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt, bool p_normalize) const {
+ Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, p_depth, p_invert, true, p_blend_skirt, p_normalize);
+
+ TypedArray<Image> ret;
+ ret.resize(images.size());
+ for (int i = 0; i < images.size(); i++) {
+ ret[i] = images[i];
}
+ return ret;
}
// Template specialization for faster grayscale blending.
@@ -58,61 +77,104 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co
return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8);
}
-Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
- ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());
-
- Vector<uint8_t> data;
- data.resize(p_width * p_height);
+Vector<Ref<Image>> Noise::_get_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
+ ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>());
- uint8_t *wd8 = data.ptrw();
+ Vector<Ref<Image>> images;
+ images.resize(p_depth);
if (p_normalize) {
// Get all values and identify min/max values.
- Vector<real_t> values;
- values.resize(p_width * p_height);
+ LocalVector<real_t> values;
+ values.resize(p_width * p_height * p_depth);
+
real_t min_val = FLT_MAX;
real_t max_val = -FLT_MAX;
- for (int y = 0, i = 0; y < p_height; y++) {
- for (int x = 0; x < p_width; x++, i++) {
- values.set(i, p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y));
- if (values[i] > max_val) {
- max_val = values[i];
- }
- if (values[i] < min_val) {
- min_val = values[i];
+ int idx = 0;
+ for (int d = 0; d < p_depth; d++) {
+ for (int y = 0; y < p_height; y++) {
+ for (int x = 0; x < p_width; x++) {
+ values[idx] = p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y);
+ if (values[idx] > max_val) {
+ max_val = values[idx];
+ }
+ if (values[idx] < min_val) {
+ min_val = values[idx];
+ }
+ idx++;
}
}
}
+ idx = 0;
// Normalize values and write to texture.
- uint8_t ivalue;
- for (int i = 0, x = 0; i < p_height; i++) {
- for (int j = 0; j < p_width; j++, x++) {
- if (max_val == min_val) {
- ivalue = 0;
- } else {
- ivalue = static_cast<uint8_t>(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255));
- }
-
- if (p_invert) {
- ivalue = 255 - ivalue;
+ for (int d = 0; d < p_depth; d++) {
+ Vector<uint8_t> data;
+ data.resize(p_width * p_height);
+
+ uint8_t *wd8 = data.ptrw();
+ uint8_t ivalue;
+
+ for (int y = 0; y < p_height; y++) {
+ for (int x = 0; x < p_width; x++) {
+ if (max_val == min_val) {
+ ivalue = 0;
+ } else {
+ ivalue = static_cast<uint8_t>(CLAMP((values[idx] - min_val) / (max_val - min_val) * 255.f, 0, 255));
+ }
+
+ if (p_invert) {
+ ivalue = 255 - ivalue;
+ }
+
+ wd8[x + y * p_width] = ivalue;
+ idx++;
}
-
- wd8[x] = ivalue;
}
+ Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
+ images.write[d] = img;
}
} else {
// Without normalization, the expected range of the noise function is [-1, 1].
- uint8_t ivalue;
- for (int y = 0, i = 0; y < p_height; y++) {
- for (int x = 0; x < p_width; x++, i++) {
- float value = (p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y));
- ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f));
- wd8[i] = p_invert ? (255 - ivalue) : ivalue;
+
+ for (int d = 0; d < p_depth; d++) {
+ Vector<uint8_t> data;
+ data.resize(p_width * p_height);
+
+ uint8_t *wd8 = data.ptrw();
+
+ uint8_t ivalue;
+ int idx = 0;
+ for (int y = 0; y < p_height; y++) {
+ for (int x = 0; x < p_width; x++) {
+ float value = (p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y));
+ ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f));
+ wd8[idx] = p_invert ? (255 - ivalue) : ivalue;
+ idx++;
+ }
}
+
+ Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
+ images.write[d] = img;
}
}
- return memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
+ return images;
+}
+
+Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
+ Vector<Ref<Image>> images = _get_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_normalize);
+ return images[0];
+}
+
+TypedArray<Image> Noise::get_image_3d(int p_width, int p_height, int p_depth, bool p_invert, bool p_normalize) const {
+ Vector<Ref<Image>> images = _get_image(p_width, p_height, p_depth, p_invert, true, p_normalize);
+
+ TypedArray<Image> ret;
+ ret.resize(images.size());
+ for (int i = 0; i < images.size(); i++) {
+ ret[i] = images[i];
+ }
+ return ret;
}
void Noise::_bind_methods() {
@@ -126,4 +188,6 @@ void Noise::_bind_methods() {
// Textures.
ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_image_3d", "width", "height", "depth", "invert", "normalize"), &Noise::get_image_3d, DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_seamless_image_3d", "width", "height", "depth", "invert", "skirt", "normalize"), &Noise::get_seamless_image_3d, DEFVAL(false), DEFVAL(0.1), DEFVAL(true));
}
diff --git a/modules/noise/noise.h b/modules/noise/noise.h
index f7e615c2aa..6c49c12bc2 100644
--- a/modules/noise/noise.h
+++ b/modules/noise/noise.h
@@ -32,6 +32,7 @@
#define NOISE_H
#include "core/io/image.h"
+#include "core/variant/typed_array.h"
class Noise : public Resource {
GDCLASS(Noise, Resource);
@@ -81,7 +82,7 @@ class Noise : public Resource {
};
template <typename T>
- Ref<Image> _generate_seamless_image(Ref<Image> p_src, int p_width, int p_height, bool p_invert, real_t p_blend_skirt) const {
+ Vector<Ref<Image>> _generate_seamless_image(Vector<Ref<Image>> p_src, int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt) const {
/*
To make a seamless image, we swap the quadrants so the edges are perfect matches.
We initially get a 10% larger image so we have an overlap we can use to blend over the seams.
@@ -101,7 +102,7 @@ class Noise : public Resource {
on Source it's translated to
corner of Q1/s3 unless the ALT_XY modulo moves it to Q4
*/
- ERR_FAIL_COND_V(p_blend_skirt < 0, Ref<Image>());
+ ERR_FAIL_COND_V(p_blend_skirt < 0, Vector<Ref<Image>>());
int skirt_width = MAX(1, p_width * p_blend_skirt);
int skirt_height = MAX(1, p_height * p_blend_skirt);
@@ -112,83 +113,139 @@ class Noise : public Resource {
int skirt_edge_x = half_width + skirt_width;
int skirt_edge_y = half_height + skirt_height;
- Vector<uint8_t> dest;
- dest.resize(p_width * p_height * Image::get_format_pixel_size(p_src->get_format()));
-
- img_buff<T> rd_src = {
- (T *)p_src->get_data().ptr(),
- src_width, src_height,
- half_width, half_height,
- p_width, p_height
- };
-
- // `wr` is setup for straight x/y coordinate array access.
- img_buff<T> wr = {
- (T *)dest.ptrw(),
- p_width, p_height,
- 0, 0, 0, 0
- };
- // `rd_dest` is a readable pointer to `wr`, i.e. what has already been written to the output buffer.
- img_buff<T> rd_dest = {
- (T *)dest.ptr(),
- p_width, p_height,
- 0, 0, 0, 0
- };
+ Image::Format format = p_src[0]->get_format();
+ int pixel_size = Image::get_format_pixel_size(format);
+
+ Vector<Ref<Image>> images;
+ images.resize(p_src.size());
+
+ // First blend across x and y for all slices.
+ for (int d = 0; d < images.size(); d++) {
+ Vector<uint8_t> dest;
+ dest.resize(p_width * p_height * pixel_size);
+
+ img_buff<T> rd_src = {
+ (T *)p_src[d]->get_data().ptr(),
+ src_width, src_height,
+ half_width, half_height,
+ p_width, p_height
+ };
+
+ // `wr` is setup for straight x/y coordinate array access.
+ img_buff<T> wr = {
+ (T *)dest.ptrw(),
+ p_width, p_height,
+ 0, 0, 0, 0
+ };
+ // `rd_dest` is a readable pointer to `wr`, i.e. what has already been written to the output buffer.
+ img_buff<T> rd_dest = {
+ (T *)dest.ptr(),
+ p_width, p_height,
+ 0, 0, 0, 0
+ };
+
+ // Swap the quadrants to make edges seamless.
+ for (int y = 0; y < p_height; y++) {
+ for (int x = 0; x < p_width; x++) {
+ // rd_src has a half offset and the shorter modulo ignores the skirt.
+ // It reads and writes in Q1-4 order (see map above), skipping the skirt.
+ wr(x, y) = rd_src(x, y, img_buff<T>::ALT_XY);
+ }
+ }
- // Swap the quadrants to make edges seamless.
- for (int y = 0; y < p_height; y++) {
- for (int x = 0; x < p_width; x++) {
- // rd_src has a half offset and the shorter modulo ignores the skirt.
- // It reads and writes in Q1-4 order (see map above), skipping the skirt.
- wr(x, y) = rd_src(x, y, img_buff<T>::ALT_XY);
+ // Blend the vertical skirt over the middle seam.
+ for (int x = half_width; x < skirt_edge_x; x++) {
+ int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
+ for (int y = 0; y < p_height; y++) {
+ // Skip the center square
+ if (y == half_height) {
+ y = skirt_edge_y - 1;
+ } else {
+ // Starts reading at s2, ALT_Y skips s3, and continues with s1.
+ wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_Y), alpha);
+ }
+ }
}
- }
- // Blend the vertical skirt over the middle seam.
- for (int x = half_width; x < skirt_edge_x; x++) {
- int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
- for (int y = 0; y < p_height; y++) {
- // Skip the center square
- if (y == half_height) {
- y = skirt_edge_y - 1;
- } else {
- // Starts reading at s2, ALT_Y skips s3, and continues with s1.
- wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_Y), alpha);
+ // Blend the horizontal skirt over the middle seam.
+ for (int y = half_height; y < skirt_edge_y; y++) {
+ int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
+ for (int x = 0; x < p_width; x++) {
+ // Skip the center square
+ if (x == half_width) {
+ x = skirt_edge_x - 1;
+ } else {
+ // Starts reading at s4, skips s3, continues with s5.
+ wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_X), alpha);
+ }
}
}
- }
- // Blend the horizontal skirt over the middle seam.
- for (int y = half_height; y < skirt_edge_y; y++) {
- int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
- for (int x = 0; x < p_width; x++) {
- // Skip the center square
- if (x == half_width) {
- x = skirt_edge_x - 1;
- } else {
- // Starts reading at s4, skips s3, continues with s5.
- wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_X), alpha);
+ // Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used.
+ for (int y = half_height; y < skirt_edge_y; y++) {
+ for (int x = half_width; x < skirt_edge_x; x++) {
+ int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
+ int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
+
+ // Blend s3(Q1) onto s5(Q2) for the top half.
+ T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos);
+ // Blend s1(Q3) onto Q4 for the bottom half.
+ T bottom_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_XY), rd_src(x, y, img_buff<T>::ALT_Y), xpos);
+ // Blend the top half onto the bottom half.
+ wr(x, y) = _alpha_blend<T>(bottom_blend, top_blend, ypos);
}
}
+ Ref<Image> image = memnew(Image(p_width, p_height, false, format, dest));
+ p_src.write[d].unref();
+ images.write[d] = image;
}
- // Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used.
- for (int y = half_height; y < skirt_edge_y; y++) {
- for (int x = half_width; x < skirt_edge_x; x++) {
- int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
- int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
-
- // Blend s3(Q1) onto s5(Q2) for the top half.
- T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos);
- // Blend s1(Q3) onto Q4 for the bottom half.
- T bottom_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_XY), rd_src(x, y, img_buff<T>::ALT_Y), xpos);
- // Blend the top half onto the bottom half.
- wr(x, y) = _alpha_blend<T>(bottom_blend, top_blend, ypos);
+ // Now blend across z.
+ if (p_depth > 1) {
+ int skirt_depth = MAX(1, p_depth * p_blend_skirt);
+ int half_depth = p_depth * 0.5;
+ int skirt_edge_z = half_depth + skirt_depth;
+
+ // Swap halves on depth.
+ for (int i = 0; i < half_depth; i++) {
+ Ref<Image> img = images[i];
+ images.write[i] = images[i + half_depth];
+ images.write[i + half_depth] = img;
}
+
+ Vector<Ref<Image>> new_images = images;
+ new_images.resize(p_depth);
+
+ // Scale seamless generation to third dimension.
+ for (int z = half_depth; z < skirt_edge_z; z++) {
+ int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(z - half_depth) / float(skirt_depth)));
+
+ Vector<uint8_t> img = images[z % p_depth]->get_data();
+ Vector<uint8_t> skirt = images[(z - half_depth) + p_depth]->get_data();
+
+ Vector<uint8_t> dest;
+ dest.resize(images[0]->get_width() * images[0]->get_height() * Image::get_format_pixel_size(images[0]->get_format()));
+
+ for (int i = 0; i < img.size(); i++) {
+ uint8_t fg, bg, out;
+
+ fg = skirt[i];
+ bg = img[i];
+
+ uint16_t a = alpha + 1;
+ uint16_t inv_a = 256 - alpha;
+
+ out = (uint8_t)((a * fg + inv_a * bg) >> 8);
+
+ dest.write[i] = out;
+ }
+
+ Ref<Image> new_image = memnew(Image(images[0]->get_width(), images[0]->get_height(), false, images[0]->get_format(), dest));
+ new_images.write[z % p_depth] = new_image;
+ }
+ return new_images;
}
- Ref<Image> image = memnew(Image(p_width, p_height, false, p_src->get_format(), dest));
- p_src.unref();
- return image;
+ return images;
}
template <typename T>
@@ -233,8 +290,13 @@ public:
virtual real_t get_noise_3dv(Vector3 p_v) const = 0;
virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const = 0;
+ Vector<Ref<Image>> _get_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const;
virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const;
+ virtual TypedArray<Image> get_image_3d(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_normalize = true) const;
+
+ Vector<Ref<Image>> _get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const;
virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const;
+ virtual TypedArray<Image> get_seamless_image_3d(int p_width, int p_height, int p_depth, bool p_invert = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const;
};
#endif // NOISE_H
diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp
index 0d5e778875..e4b2e0b4ac 100644
--- a/modules/noise/noise_texture_2d.cpp
+++ b/modules/noise/noise_texture_2d.cpp
@@ -44,7 +44,9 @@ NoiseTexture2D::~NoiseTexture2D() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
- noise_thread.wait_to_finish();
+ if (noise_thread.is_started()) {
+ noise_thread.wait_to_finish();
+ }
}
void NoiseTexture2D::_bind_methods() {
diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp
new file mode 100644
index 0000000000..25d75b8ffb
--- /dev/null
+++ b/modules/noise/noise_texture_3d.cpp
@@ -0,0 +1,352 @@
+/**************************************************************************/
+/* noise_texture_3d.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 "noise_texture_3d.h"
+
+#include "core/core_string_names.h"
+#include "noise.h"
+
+NoiseTexture3D::NoiseTexture3D() {
+ noise = Ref<Noise>();
+
+ _queue_update();
+}
+
+NoiseTexture3D::~NoiseTexture3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (texture.is_valid()) {
+ RS::get_singleton()->free(texture);
+ }
+ if (noise_thread.is_started()) {
+ noise_thread.wait_to_finish();
+ }
+}
+
+void NoiseTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture3D::_update_texture);
+ ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture3D::_generate_texture);
+ ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture3D::_thread_done);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture3D::set_width);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture3D::set_height);
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &NoiseTexture3D::set_depth);
+
+ ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture3D::set_invert);
+ ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture3D::get_invert);
+
+ ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture3D::set_seamless);
+ ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture3D::get_seamless);
+
+ ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture3D::set_seamless_blend_skirt);
+ ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture3D::get_seamless_blend_skirt);
+
+ ClassDB::bind_method(D_METHOD("set_normalize", "normalize"), &NoiseTexture3D::set_normalize);
+ ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture3D::is_normalized);
+
+ ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture3D::set_color_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture3D::get_color_ramp);
+
+ ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture3D::set_noise);
+ ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture3D::get_noise);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "depth", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0.05,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
+}
+
+void NoiseTexture3D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "seamless_blend_skirt") {
+ if (!seamless) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
+}
+
+void NoiseTexture3D::_set_texture_data(const TypedArray<Image> &p_data) {
+ if (!p_data.is_empty()) {
+ Vector<Ref<Image>> data;
+
+ data.resize(p_data.size());
+
+ for (int i = 0; i < data.size(); i++) {
+ data.write[i] = p_data[i];
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
+ }
+ }
+ emit_changed();
+}
+
+void NoiseTexture3D::_thread_done(const TypedArray<Image> &p_data) {
+ _set_texture_data(p_data);
+ noise_thread.wait_to_finish();
+ if (regen_queued) {
+ noise_thread.start(_thread_function, this);
+ regen_queued = false;
+ }
+}
+
+void NoiseTexture3D::_thread_function(void *p_ud) {
+ NoiseTexture3D *tex = static_cast<NoiseTexture3D *>(p_ud);
+ tex->call_deferred(SNAME("_thread_done"), tex->_generate_texture());
+}
+
+void NoiseTexture3D::_queue_update() {
+ if (update_queued) {
+ return;
+ }
+
+ update_queued = true;
+ call_deferred(SNAME("_update_texture"));
+}
+
+TypedArray<Image> NoiseTexture3D::_generate_texture() {
+ // Prevent memdelete due to unref() on other thread.
+ Ref<Noise> ref_noise = noise;
+
+ if (ref_noise.is_null()) {
+ return TypedArray<Image>();
+ }
+
+ Vector<Ref<Image>> images;
+
+ if (seamless) {
+ images = ref_noise->_get_seamless_image(width, height, depth, invert, true, seamless_blend_skirt, normalize);
+ } else {
+ images = ref_noise->_get_image(width, height, depth, invert, true, normalize);
+ }
+
+ if (color_ramp.is_valid()) {
+ for (int i = 0; i < images.size(); i++) {
+ images.write[i] = _modulate_with_gradient(images[i], color_ramp);
+ }
+ }
+
+ TypedArray<Image> new_data;
+ new_data.resize(images.size());
+
+ for (int i = 0; i < new_data.size(); i++) {
+ new_data[i] = images[i];
+ }
+
+ return new_data;
+}
+
+Ref<Image> NoiseTexture3D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
+ int w = p_image->get_width();
+ int h = p_image->get_height();
+
+ Ref<Image> new_image = Image::create_empty(w, h, false, Image::FORMAT_RGBA8);
+
+ for (int row = 0; row < h; row++) {
+ for (int col = 0; col < w; col++) {
+ Color pixel_color = p_image->get_pixel(col, row);
+ Color ramp_color = p_gradient->get_color_at_offset(pixel_color.get_luminance());
+ new_image->set_pixel(col, row, ramp_color);
+ }
+ }
+
+ return new_image;
+}
+
+void NoiseTexture3D::_update_texture() {
+ bool use_thread = true;
+ if (first_time) {
+ use_thread = false;
+ first_time = false;
+ }
+ if (use_thread) {
+ if (!noise_thread.is_started()) {
+ noise_thread.start(_thread_function, this);
+ regen_queued = false;
+ } else {
+ regen_queued = true;
+ }
+
+ } else {
+ TypedArray<Image> new_data = _generate_texture();
+ _set_texture_data(new_data);
+ }
+ update_queued = false;
+}
+
+void NoiseTexture3D::set_noise(Ref<Noise> p_noise) {
+ if (p_noise == noise) {
+ return;
+ }
+ if (noise.is_valid()) {
+ noise->disconnect(CoreStringNames::get_singleton()->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));
+ }
+ _queue_update();
+}
+
+Ref<Noise> NoiseTexture3D::get_noise() {
+ return noise;
+}
+
+void NoiseTexture3D::set_width(int p_width) {
+ ERR_FAIL_COND(p_width <= 0);
+ if (p_width == width) {
+ return;
+ }
+ width = p_width;
+ _queue_update();
+}
+
+void NoiseTexture3D::set_height(int p_height) {
+ ERR_FAIL_COND(p_height <= 0);
+ if (p_height == height) {
+ return;
+ }
+ height = p_height;
+ _queue_update();
+}
+
+void NoiseTexture3D::set_depth(int p_depth) {
+ ERR_FAIL_COND(p_depth <= 0);
+ if (p_depth == depth) {
+ return;
+ }
+ depth = p_depth;
+ _queue_update();
+}
+
+void NoiseTexture3D::set_invert(bool p_invert) {
+ if (p_invert == invert) {
+ return;
+ }
+ invert = p_invert;
+ _queue_update();
+}
+
+bool NoiseTexture3D::get_invert() const {
+ return invert;
+}
+
+void NoiseTexture3D::set_seamless(bool p_seamless) {
+ if (p_seamless == seamless) {
+ return;
+ }
+ seamless = p_seamless;
+ _queue_update();
+ notify_property_list_changed();
+}
+
+bool NoiseTexture3D::get_seamless() {
+ return seamless;
+}
+
+void NoiseTexture3D::set_seamless_blend_skirt(real_t p_blend_skirt) {
+ ERR_FAIL_COND(p_blend_skirt < 0.05 || p_blend_skirt > 1);
+
+ if (p_blend_skirt == seamless_blend_skirt) {
+ return;
+ }
+ seamless_blend_skirt = p_blend_skirt;
+ _queue_update();
+}
+real_t NoiseTexture3D::get_seamless_blend_skirt() {
+ return seamless_blend_skirt;
+}
+
+void NoiseTexture3D::set_color_ramp(const Ref<Gradient> &p_gradient) {
+ if (p_gradient == color_ramp) {
+ return;
+ }
+ if (color_ramp.is_valid()) {
+ color_ramp->disconnect(CoreStringNames::get_singleton()->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));
+ }
+ _queue_update();
+}
+
+void NoiseTexture3D::set_normalize(bool p_normalize) {
+ if (normalize == p_normalize) {
+ return;
+ }
+ normalize = p_normalize;
+ _queue_update();
+}
+
+bool NoiseTexture3D::is_normalized() const {
+ return normalize;
+}
+
+Ref<Gradient> NoiseTexture3D::get_color_ramp() const {
+ return color_ramp;
+}
+
+int NoiseTexture3D::get_width() const {
+ return width;
+}
+
+int NoiseTexture3D::get_height() const {
+ return height;
+}
+
+int NoiseTexture3D::get_depth() const {
+ return depth;
+}
+
+RID NoiseTexture3D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_3d_placeholder_create();
+ }
+
+ return texture;
+}
+
+Vector<Ref<Image>> NoiseTexture3D::get_data() const {
+ ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
+ return RS::get_singleton()->texture_3d_get(texture);
+}
+
+Image::Format NoiseTexture3D::get_format() const {
+ ERR_FAIL_COND_V(!texture.is_valid(), Image::FORMAT_L8);
+ return RS::get_singleton()->texture_3d_get(texture)[0]->get_format();
+}
diff --git a/modules/noise/noise_texture_3d.h b/modules/noise/noise_texture_3d.h
new file mode 100644
index 0000000000..397711ca98
--- /dev/null
+++ b/modules/noise/noise_texture_3d.h
@@ -0,0 +1,113 @@
+/**************************************************************************/
+/* noise_texture_3d.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 NOISE_TEXTURE_3D_H
+#define NOISE_TEXTURE_3D_H
+
+#include "noise.h"
+
+#include "core/object/ref_counted.h"
+#include "scene/resources/texture.h"
+
+class NoiseTexture3D : public Texture3D {
+ GDCLASS(NoiseTexture3D, Texture3D);
+
+private:
+ Thread noise_thread;
+
+ bool first_time = true;
+ bool update_queued = false;
+ bool regen_queued = false;
+
+ mutable RID texture;
+ uint32_t flags = 0;
+
+ int width = 64;
+ int height = 64;
+ int depth = 64;
+ bool invert = false;
+ bool seamless = false;
+ real_t seamless_blend_skirt = 0.1;
+ bool normalize = true;
+
+ Ref<Gradient> color_ramp;
+ Ref<Noise> noise;
+
+ void _thread_done(const TypedArray<Image> &p_data);
+ static void _thread_function(void *p_ud);
+
+ void _queue_update();
+ TypedArray<Image> _generate_texture();
+ void _update_texture();
+ void _set_texture_data(const TypedArray<Image> &p_data);
+
+ Ref<Image> _modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient);
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ void set_noise(Ref<Noise> p_noise);
+ Ref<Noise> get_noise();
+
+ void set_width(int p_width);
+ void set_height(int p_height);
+ void set_depth(int p_depth);
+
+ void set_invert(bool p_invert);
+ bool get_invert() const;
+
+ void set_seamless(bool p_seamless);
+ bool get_seamless();
+
+ void set_seamless_blend_skirt(real_t p_blend_skirt);
+ real_t get_seamless_blend_skirt();
+
+ void set_normalize(bool p_normalize);
+ bool is_normalized() const;
+
+ void set_color_ramp(const Ref<Gradient> &p_gradient);
+ Ref<Gradient> get_color_ramp() const;
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_depth() const override;
+
+ virtual RID get_rid() const override;
+
+ virtual Vector<Ref<Image>> get_data() const override;
+ virtual Image::Format get_format() const override;
+
+ NoiseTexture3D();
+ virtual ~NoiseTexture3D();
+};
+
+#endif // NOISE_TEXTURE_3D_H
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index 1298d72e98..f48d4e7e4d 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -33,6 +33,7 @@
#include "fastnoise_lite.h"
#include "noise.h"
#include "noise_texture_2d.h"
+#include "noise_texture_3d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_plugin.h"
@@ -41,6 +42,7 @@
void initialize_noise_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(NoiseTexture3D);
GDREGISTER_CLASS(NoiseTexture2D);
GDREGISTER_ABSTRACT_CLASS(Noise);
GDREGISTER_CLASS(FastNoiseLite);
diff --git a/modules/noise/tests/test_noise_texture_3d.h b/modules/noise/tests/test_noise_texture_3d.h
new file mode 100644
index 0000000000..b46d386296
--- /dev/null
+++ b/modules/noise/tests/test_noise_texture_3d.h
@@ -0,0 +1,235 @@
+/**************************************************************************/
+/* test_noise_texture_3d.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_NOISE_TEXTURE_3D_H
+#define TEST_NOISE_TEXTURE_3D_H
+
+#include "tests/test_macros.h"
+
+#include "modules/noise/noise_texture_3d.h"
+
+namespace TestNoiseTexture3D {
+
+class NoiseTexture3DTester : public RefCounted {
+ GDCLASS(NoiseTexture3DTester, RefCounted);
+
+ const NoiseTexture3D *const texture;
+
+public:
+ NoiseTexture3DTester(const NoiseTexture3D *const p_texture) :
+ texture{ p_texture } {};
+
+ Color compute_average_color(const Ref<Image> &p_noise_image) {
+ Color r_avg_color{};
+
+ for (int i = 0; i < p_noise_image->get_width(); ++i) {
+ for (int j = 0; j < p_noise_image->get_height(); ++j) {
+ const Color pixel = p_noise_image->get_pixel(i, j);
+ r_avg_color += pixel;
+ }
+ }
+
+ int pixel_count = p_noise_image->get_width() * p_noise_image->get_height();
+ r_avg_color /= pixel_count;
+ return r_avg_color;
+ }
+
+ void check_mip_and_color_ramp() {
+ const Vector<Ref<Image>> noise_data = texture->get_data();
+
+ for (int i = 0; i < noise_data.size(); i++) {
+ const Ref<Image> noise_image = noise_data[i];
+
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
+ CHECK(!noise_image->has_mipmaps());
+
+ Color avg_color = compute_average_color(noise_image);
+
+ // Check that the noise texture is modulated correctly by the color ramp (Gradient).
+ CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(0.0), "The noise texture should not be all black");
+ CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(noise_image->get_width() * noise_image->get_height() * 3.0), "The noise texture should not be all white");
+ CHECK_MESSAGE(avg_color.g == doctest::Approx(0.0), "The noise texture should not have any green when modulated correctly by the color ramp");
+ }
+ }
+
+ void check_seamless_texture_grayscale() {
+ const Vector<Ref<Image>> noise_data = texture->get_data();
+
+ for (int i = 0; i < noise_data.size(); i++) {
+ const Ref<Image> noise_image = noise_data[i];
+
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_L8);
+
+ Color avg_color = compute_average_color(noise_image);
+
+ // Since it's a grayscale image and every channel except the alpha channel has the
+ // same values (conversion happens in Image::get_pixel) we only need to test one channel.
+ CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
+ }
+ }
+
+ void check_seamless_texture_rgba() {
+ const Vector<Ref<Image>> noise_data = texture->get_data();
+
+ for (int i = 0; i < noise_data.size(); i++) {
+ const Ref<Image> noise_image = noise_data[i];
+
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
+
+ // Check that the noise texture is modulated correctly by the color ramp (Gradient).
+ Color avg_color = compute_average_color(noise_image);
+
+ // We use a default (black to white) gradient, so the average of the red, green and blue channels should be the same.
+ CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.b == doctest::Approx(0.5).epsilon(0.05));
+ }
+ }
+};
+
+TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") {
+ Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise_texture->set_noise(noise);
+ CHECK(noise_texture->get_noise() == noise);
+ noise_texture->set_noise(nullptr);
+ CHECK(noise_texture->get_noise() == nullptr);
+
+ noise_texture->set_width(8);
+ noise_texture->set_height(4);
+ noise_texture->set_depth(2);
+ CHECK(noise_texture->get_width() == 8);
+ CHECK(noise_texture->get_height() == 4);
+ CHECK(noise_texture->get_depth() == 2);
+
+ ERR_PRINT_OFF;
+ noise_texture->set_width(-1);
+ noise_texture->set_height(-1);
+ noise_texture->set_depth(-1);
+ ERR_PRINT_ON;
+ CHECK(noise_texture->get_width() == 8);
+ CHECK(noise_texture->get_height() == 4);
+ CHECK(noise_texture->get_depth() == 2);
+
+ noise_texture->set_invert(true);
+ CHECK(noise_texture->get_invert() == true);
+ noise_texture->set_invert(false);
+ CHECK(noise_texture->get_invert() == false);
+
+ noise_texture->set_seamless(true);
+ CHECK(noise_texture->get_seamless() == true);
+ noise_texture->set_seamless(false);
+ CHECK(noise_texture->get_seamless() == false);
+
+ noise_texture->set_seamless_blend_skirt(0.45);
+ CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
+
+ ERR_PRINT_OFF;
+ noise_texture->set_seamless_blend_skirt(-1.0);
+ noise_texture->set_seamless_blend_skirt(2.0);
+ CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
+ ERR_PRINT_ON;
+
+ Ref<Gradient> gradient = memnew(Gradient);
+ noise_texture->set_color_ramp(gradient);
+ CHECK(noise_texture->get_color_ramp() == gradient);
+ noise_texture->set_color_ramp(nullptr);
+ CHECK(noise_texture->get_color_ramp() == nullptr);
+}
+
+TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") {
+ Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise_texture->set_noise(noise);
+
+ Ref<Gradient> gradient = memnew(Gradient);
+ Vector<Gradient::Point> points;
+ points.push_back({ 0.0, Color(1, 0, 0) });
+ points.push_back({ 1.0, Color(0, 0, 1) });
+ gradient->set_points(points);
+ noise_texture->set_color_ramp(gradient);
+ noise_texture->set_width(16);
+ noise_texture->set_height(16);
+ 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));
+ MessageQueue::get_singleton()->flush();
+}
+
+TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") {
+ Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise->set_frequency(0.5);
+ noise_texture->set_noise(noise);
+ noise_texture->set_width(16);
+ noise_texture->set_height(16);
+ noise_texture->set_depth(16);
+ noise_texture->set_seamless(true);
+
+ Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr()));
+
+ 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));
+ MessageQueue::get_singleton()->flush();
+ }
+
+ SUBCASE("16x16x16 modulated with default (transparent)black and white gradient (RGBA8), with seamless blend skirt of 1.0") {
+ Ref<Gradient> gradient = memnew(Gradient);
+ Vector<Gradient::Point> points;
+ points.push_back({ 0.0, Color(0, 0, 0, 0) });
+ points.push_back({ 1.0, Color(1, 1, 1, 1) });
+ 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));
+ MessageQueue::get_singleton()->flush();
+ }
+}
+
+} //namespace TestNoiseTexture3D
+
+#endif // TEST_NOISE_TEXTURE_3D_H
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index d2f6be2233..63abbf0d71 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -415,7 +415,7 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
- profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select
+ profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
@@ -439,7 +439,7 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
- profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select
+ profile->add_new_binding(select_button, "/user/hand/right/input/system/click"); // we'll map system to select
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
diff --git a/modules/openxr/extensions/openxr_htc_controller_extension.cpp b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
index 4d141b0695..116762ee8d 100644
--- a/modules/openxr/extensions/openxr_htc_controller_extension.cpp
+++ b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
@@ -108,7 +108,7 @@ void OpenXRHTCControllerExtension::on_register_metadata() {
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
- metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch ", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze touch", "/user/hand/left", "/user/hand/left/input/squeeze/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
@@ -122,6 +122,7 @@ void OpenXRHTCControllerExtension::on_register_metadata() {
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbrest touch", "/user/hand/left", "/user/hand/left/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbrest touch", "/user/hand/right", "/user/hand/right/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 20ccfe3906..83d31036a4 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -37,7 +37,7 @@
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
-// OpenXR requires us to submit sRGB textures so that it recognises the content
+// OpenXR requires us to submit sRGB textures so that it recognizes the content
// as being in sRGB color space. We do fall back on "normal" textures but this
// will likely result in incorrect colors as OpenXR will double the sRGB conversion.
// All major XR runtimes support sRGB textures.
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 9394f71e40..8c03115f05 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -604,7 +604,9 @@ RaycastOcclusionCull::~RaycastOcclusionCull() {
for (KeyValue<RID, Scenario> &K : scenarios) {
Scenario &scenario = K.value;
if (scenario.commit_thread) {
- scenario.commit_thread->wait_to_finish();
+ if (scenario.commit_thread->is_started()) {
+ scenario.commit_thread->wait_to_finish();
+ }
memdelete(scenario.commit_thread);
}
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 73c0851dc7..49b15c7a90 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -520,7 +520,11 @@ if env["builtin_icu4c"]:
module_obj = []
if env["builtin_msdfgen"] and msdfgen_enabled:
- env_text_server_adv.Prepend(CPPPATH=["#thirdparty/msdfgen"])
+ # Treat msdfgen headers as system headers to avoid raising warnings. Not supported on MSVC.
+ if not env.msvc:
+ env_text_server_adv.Append(CPPFLAGS=["-isystem", Dir("#thirdparty/msdfgen").path])
+ else:
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/msdfgen"])
if env["builtin_freetype"] and freetype_enabled:
env_text_server_adv.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index f1d57ec4d3..582e622147 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -12,7 +12,11 @@ if "svg" in env.module_list:
env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
if env["builtin_msdfgen"] and msdfgen_enabled:
- env_text_server_fb.Prepend(CPPPATH=["#thirdparty/msdfgen"])
+ # Treat msdfgen headers as system headers to avoid raising warnings. Not supported on MSVC.
+ if not env.msvc:
+ env_text_server_fb.Append(CPPFLAGS=["-isystem", Dir("#thirdparty/msdfgen").path])
+ else:
+ env_text_server_fb.Prepend(CPPPATH=["#thirdparty/msdfgen"])
if env["builtin_freetype"] and freetype_enabled:
env_text_server_fb.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8b210bdc38..b76214cf94 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3865,8 +3865,8 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
}
prev_font = gl.font_rid;
- double scale = _font_get_scale(gl.font_rid, gl.font_size);
if (gl.font_rid.is_valid()) {
+ double scale = _font_get_scale(gl.font_rid, gl.font_size);
bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_AUTO && gl.font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
index 1159cbb43a..a099a193bb 100644
--- a/modules/upnp/doc_classes/UPNP.xml
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -74,11 +74,11 @@
<param index="3" name="proto" type="String" default="&quot;UDP&quot;" />
<param index="4" name="duration" type="int" default="0" />
<description>
- Adds a mapping to forward the external [code]port[/code] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]"TCP"[/code] or [code]"UDP"[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device.
+ Adds a mapping to forward the external [param port] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [param port_internal] on the local machine for the given protocol [param proto] (either [code]"TCP"[/code] or [code]"UDP"[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device.
Depending on the gateway device, if a mapping for that port already exists, it will either be updated or it will refuse this command due to that conflict, especially if the existing mapping for that port wasn't created via UPnP or points to a different network address (or device) than this one.
- If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
- The description ([code]desc[/code]) is shown in some routers management UIs and can be used to point out which application added the mapping.
- The mapping's lease [code]duration[/code] can be limited by specifying a duration in seconds. The default of [code]0[/code] means no duration, i.e. a permanent lease and notably some devices only support these permanent leases. Note that whether permanent or not, this is only a request and the gateway may still decide at any point to remove the mapping (which usually happens on a reboot of the gateway, when its external IP address changes, or on some models when it detects a port mapping has become inactive, i.e. had no traffic for multiple minutes). If not [code]0[/code] (permanent), the allowed range according to spec is between [code]120[/code] (2 minutes) and [code]86400[/code] seconds (24 hours).
+ If [param port_internal] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [param port] value).
+ The description ([param desc]) is shown in some routers management UIs and can be used to point out which application added the mapping.
+ The mapping's lease [param duration] can be limited by specifying a duration in seconds. The default of [code]0[/code] means no duration, i.e. a permanent lease and notably some devices only support these permanent leases. Note that whether permanent or not, this is only a request and the gateway may still decide at any point to remove the mapping (which usually happens on a reboot of the gateway, when its external IP address changes, or on some models when it detects a port mapping has become inactive, i.e. had no traffic for multiple minutes). If not [code]0[/code] (permanent), the allowed range according to spec is between [code]120[/code] (2 minutes) and [code]86400[/code] seconds (24 hours).
See [enum UPNPResult] for possible return values.
</description>
</method>
@@ -93,7 +93,7 @@
<param index="0" name="port" type="int" />
<param index="1" name="proto" type="String" default="&quot;UDP&quot;" />
<description>
- Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]"TCP"[/code] or [code]"UDP"[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values.
+ Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [param port] must be a valid port between 1 and 65535, [param proto] can be either [code]"TCP"[/code] or [code]"UDP"[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values.
</description>
</method>
<method name="discover">
@@ -103,7 +103,7 @@
<param index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;" />
<description>
Discovers local [UPNPDevice]s. Clears the list of previously discovered devices.
- Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in milliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing.
+ Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [param timeout] is the time to wait for responses in milliseconds. [param ttl] is the time-to-live; only touch this if you know what you're doing.
See [enum UPNPResult] for possible return values.
</description>
</method>
@@ -111,7 +111,7 @@
<return type="UPNPDevice" />
<param index="0" name="index" type="int" />
<description>
- Returns the [UPNPDevice] at the given [code]index[/code].
+ Returns the [UPNPDevice] at the given [param index].
</description>
</method>
<method name="get_device_count" qualifiers="const">
@@ -136,7 +136,7 @@
<return type="void" />
<param index="0" name="index" type="int" />
<description>
- Removes the device at [code]index[/code] from the list of discovered devices.
+ Removes the device at [param index] from the list of discovered devices.
</description>
</method>
<method name="set_device">
@@ -144,7 +144,7 @@
<param index="0" name="index" type="int" />
<param index="1" name="device" type="UPNPDevice" />
<description>
- Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code].
+ Sets the device at [param index] from the list of discovered devices to [param device].
</description>
</method>
</methods>
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index 607b8c05e2..fa58bf5d58 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -18,8 +18,8 @@
<param index="1" name="peer_id" type="int" />
<param index="2" name="unreliable_lifetime" type="int" default="1" />
<description>
- Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
- Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
+ Add a new peer to the mesh with the given [param peer_id]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
+ Three channels will be created for reliable, unreliable, and ordered transport. The value of [param unreliable_lifetime] will be passed to the [code]"maxPacketLifetime"[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
</description>
</method>
<method name="create_client">
@@ -27,8 +27,8 @@
<param index="0" name="peer_id" type="int" />
<param index="1" name="channels_config" type="Array" default="[]" />
<description>
- Initialize the multiplayer peer as a client with the given [code]peer_id[/code] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [code]peer_id[/code] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
- You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ Initialize the multiplayer peer as a client with the given [param peer_id] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [param peer_id] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
+ You can optionally specify a [param channels_config] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="create_mesh">
@@ -36,7 +36,7 @@
<param index="0" name="peer_id" type="int" />
<param index="1" name="channels_config" type="Array" default="[]" />
<description>
- Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [code]peer_id[/code] (must be between 1 and 2147483647).
+ Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [param peer_id] (must be between 1 and 2147483647).
</description>
</method>
<method name="create_server">
@@ -44,14 +44,14 @@
<param index="0" name="channels_config" type="Array" default="[]" />
<description>
Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
- You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ You can optionally specify a [param channels_config] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="get_peer">
<return type="Dictionary" />
<param index="0" name="peer_id" type="int" />
<description>
- Returns a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
+ Returns a dictionary representation of the peer with given [param peer_id] with three keys. [code]"connection"[/code] containing the [WebRTCPeerConnection] to this peer, [code]"channels"[/code] an array of three [WebRTCDataChannel], and [code]"connected"[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
</description>
</method>
<method name="get_peers">
@@ -64,14 +64,14 @@
<return type="bool" />
<param index="0" name="peer_id" type="int" />
<description>
- Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
+ Returns [code]true[/code] if the given [param peer_id] is in the peers map (it might not be connected though).
</description>
</method>
<method name="remove_peer">
<return type="void" />
<param index="0" name="peer_id" type="int" />
<description>
- Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted.
+ Remove the peer with given [param peer_id] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted.
</description>
</method>
</methods>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 1e1f54d8b2..99a5381f0c 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -35,9 +35,9 @@
<param index="0" name="label" type="String" />
<param index="1" name="options" type="Dictionary" default="{}" />
<description>
- Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
- There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
- Valid [code]options[/code] are:
+ Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [param label] and optionally configured via the [param options] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
+ There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]"negotiated"[/code] option set to [code]true[/code].
+ Valid [param options] are:
[codeblock]
{
"negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. "data_channel_received" will not be called.
@@ -83,8 +83,8 @@
<return type="int" enum="Error" />
<param index="0" name="configuration" type="Dictionary" default="{}" />
<description>
- Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
- Valid [code]options[/code] are:
+ Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [param configuration] options can be passed to configure the peer connection.
+ Valid [param configuration] options are:
[codeblock]
{
"iceServers": [
@@ -111,7 +111,7 @@
<return type="void" />
<param index="0" name="extension_class" type="StringName" />
<description>
- Sets the [code]extension_class[/code] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
+ Sets the [param extension_class] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
</description>
</method>
<method name="set_local_description">
@@ -129,8 +129,8 @@
<param index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server.
- If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer.
- If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created].
+ If [param type] is [code]"offer"[/code] the peer will emit [signal session_description_created] with the appropriate answer.
+ If [param type] is [code]"answer"[/code] the peer will start emitting [signal ice_candidate_created].
</description>
</method>
</methods>
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index 57feadcc99..ff94d4452a 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -32,7 +32,7 @@
<return type="WebSocketPeer" />
<param index="0" name="peer_id" type="int" />
<description>
- Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
+ Returns the [WebSocketPeer] associated to the given [param peer_id].
</description>
</method>
<method name="get_peer_address" qualifiers="const">
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index 580adfd2d2..61f0be22f6 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -61,7 +61,7 @@
<param index="1" name="tls_client_options" type="TLSOptions" default="null" />
<description>
Connects to the given URL. TLS certificates will be verified against the hostname when connecting using the [code]wss://[/code] protocol. You can pass the optional [param tls_client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe].
- [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's TLS certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the TLS certificate.
+ [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [param url] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's TLS certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the TLS certificate.
</description>
</method>
<method name="get_close_code" qualifiers="const">
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index 816d5276b9..109d5102c3 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -332,7 +332,7 @@ void WSLPeer::_do_client_handshake() {
if (connection == tcp) {
// Start SSL handshake
tls = Ref<StreamPeerTLS>(StreamPeerTLS::create());
- ERR_FAIL_COND_MSG(tls.is_null(), "SSL is not available in this build.");
+ ERR_FAIL_COND(tls.is_null());
if (tls->connect_to_stream(tcp, requested_host, tls_options) != OK) {
close(-1);
return; // Error.
@@ -501,6 +501,8 @@ Error WSLPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_options) {
path = "/";
}
+ ERR_FAIL_COND_V_MSG(use_tls && !StreamPeerTLS::is_available(), ERR_UNAVAILABLE, "WSS is not available in this build.");
+
requested_url = p_url;
requested_host = host;
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index ed162b1da2..9591afa536 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -109,7 +109,7 @@
<return type="int" enum="WebXRInterface.TargetRayMode" />
<param index="0" name="input_source_id" type="int" />
<description>
- Returns the target ray mode for the given [code]input_source_id[/code].
+ Returns the target ray mode for the given [param input_source_id].
This can help interpret the input coming from that input source. See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource/targetRayMode]XRInputSource.targetRayMode[/url] for more information.
</description>
</method>
@@ -117,7 +117,7 @@
<return type="XRPositionalTracker" />
<param index="0" name="input_source_id" type="int" />
<description>
- Gets an [XRPositionalTracker] for the given [code]input_source_id[/code].
+ Gets an [XRPositionalTracker] for the given [param input_source_id].
In the context of WebXR, an input source can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional input source is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
Use this method to get information about the input source that triggered one of these signals:
- [signal selectstart]
@@ -132,14 +132,14 @@
<return type="bool" />
<param index="0" name="input_source_id" type="int" />
<description>
- Returns [code]true[/code] if there is an active input source with the given [code]input_source_id[/code].
+ Returns [code]true[/code] if there is an active input source with the given [param input_source_id].
</description>
</method>
<method name="is_session_supported">
<return type="void" />
<param index="0" name="session_mode" type="String" />
<description>
- Checks if the given [code]session_mode[/code] is supported by the user's browser.
+ Checks if the given [param session_mode] is supported by the user's browser.
Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code].
This method returns nothing, instead it emits the [signal session_supported] signal with the result.
</description>
@@ -229,7 +229,7 @@
<param index="0" name="message" type="String" />
<description>
Emitted by [method XRInterface.initialize] if the session fails to start.
- [code]message[/code] may optionally contain an error message from WebXR, or an empty string if no message is available.
+ [param message] may optionally contain an error message from WebXR, or an empty string if no message is available.
</description>
</signal>
<signal name="session_started">
@@ -242,7 +242,7 @@
<param index="0" name="session_mode" type="String" />
<param index="1" name="supported" type="bool" />
<description>
- Emitted by [method is_session_supported] to indicate if the given [code]session_mode[/code] is supported or not.
+ Emitted by [method is_session_supported] to indicate if the given [param session_mode] is supported or not.
</description>
</signal>
<signal name="squeeze">
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index b2400d81dc..ad1cbddb08 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -39,6 +39,8 @@ class RenderingDeviceVulkan;
#endif
class DisplayServerAndroid : public DisplayServer {
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
+
String rendering_driver;
// https://developer.android.com/reference/android/view/PointerIcon
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 570e8f01f1..11129ca149 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -58,21 +58,27 @@
</member>
<member name="keystore/debug" type="String" setter="" getter="">
Path of the debug keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PATH[/code].
</member>
<member name="keystore/debug_password" type="String" setter="" getter="">
Password for the debug keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD[/code].
</member>
<member name="keystore/debug_user" type="String" setter="" getter="">
User name for the debug keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_USER[/code].
</member>
<member name="keystore/release" type="String" setter="" getter="">
Path of the release keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PATH[/code].
</member>
<member name="keystore/release_password" type="String" setter="" getter="">
Password for the release keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD[/code].
</member>
<member name="keystore/release_user" type="String" setter="" getter="">
User name for the release keystore file.
+ Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code].
</member>
<member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter="">
Background layer of the application adaptive icon file.
@@ -153,7 +159,7 @@
Must be required by a HostApduService or OffHostApduService to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NFC_SERVICE]BIND_NFC_SERVICE[/url].
</member>
<member name="permissions/bind_notification_listener_service" type="bool" setter="" getter="">
- Must be required by an NotificationListenerService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE]BIND_NOTIFICATION_LISTENER_SERVICE[/url].
+ Must be required by a NotificationListenerService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE]BIND_NOTIFICATION_LISTENER_SERVICE[/url].
</member>
<member name="permissions/bind_print_service" type="bool" setter="" getter="">
Must be required by a PrintService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_PRINT_SERVICE]BIND_PRINT_SERVICE[/url].
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index f52edf2b61..d8dd453faf 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1825,12 +1825,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default));
}
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
@@ -2277,9 +2277,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
// Validate the rest of the export configuration.
- String dk = p_preset->get("keystore/debug");
- String dk_user = p_preset->get("keystore/debug_user");
- String dk_password = p_preset->get("keystore/debug_password");
+ String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
+ String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) {
valid = false;
@@ -2294,9 +2294,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
}
- String rk = p_preset->get("keystore/release");
- String rk_user = p_preset->get("keystore/release_user");
- String rk_password = p_preset->get("keystore/release_password");
+ String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
+ String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) {
valid = false;
@@ -2507,9 +2507,9 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
- String release_keystore = p_preset->get("keystore/release");
- String release_username = p_preset->get("keystore/release_user");
- String release_password = p_preset->get("keystore/release_password");
+ String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
+ String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
@@ -2529,9 +2529,9 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String password;
String user;
if (p_debug) {
- keystore = p_preset->get("keystore/debug");
- password = p_preset->get("keystore/debug_password");
- user = p_preset->get("keystore/debug_user");
+ keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
+ user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
if (keystore.is_empty()) {
keystore = EDITOR_GET("export/android/debug_keystore");
@@ -2791,7 +2791,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
CustomExportData user_data;
user_data.assets_directory = assets_directory;
user_data.debug = p_debug;
- err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
+ if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
+ } else {
+ err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
+ }
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project."));
return err;
@@ -2882,9 +2886,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (should_sign) {
if (p_debug) {
- String debug_keystore = p_preset->get("keystore/debug");
- String debug_password = p_preset->get("keystore/debug_password");
- String debug_user = p_preset->get("keystore/debug_user");
+ String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
+ String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
if (debug_keystore.is_empty()) {
debug_keystore = EDITOR_GET("export/android/debug_keystore");
@@ -2904,9 +2908,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
} else {
// Pass the release keystore info as well
- String release_keystore = p_preset->get("keystore/release");
- String release_username = p_preset->get("keystore/release_user");
- String release_password = p_preset->get("keystore/release_password");
+ String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
+ String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if (release_keystore.is_relative_path()) {
release_keystore = OS::get_singleton()->get_resource_dir().path_join(release_keystore).simplify_path();
}
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index f9dad5ce5e..390b8c6465 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -49,6 +49,15 @@ const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="
</layer-list>
)SPLASH";
+// Optional environment variables for defining confidential information. If any
+// of these is set, they will override the values set in the credentials file.
+const String ENV_ANDROID_KEYSTORE_DEBUG_PATH = "GODOT_ANDROID_KEYSTORE_DEBUG_PATH";
+const String ENV_ANDROID_KEYSTORE_DEBUG_USER = "GODOT_ANDROID_KEYSTORE_DEBUG_USER";
+const String ENV_ANDROID_KEYSTORE_DEBUG_PASS = "GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD";
+const String ENV_ANDROID_KEYSTORE_RELEASE_PATH = "GODOT_ANDROID_KEYSTORE_RELEASE_PATH";
+const String ENV_ANDROID_KEYSTORE_RELEASE_USER = "GODOT_ANDROID_KEYSTORE_RELEASE_USER";
+const String ENV_ANDROID_KEYSTORE_RELEASE_PASS = "GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD";
+
struct LauncherIcon {
const char *export_path;
int dimensions = 0;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index e111bd18ca..99527ccf3a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -1044,6 +1044,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
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
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
index af1f38f89c..7dc5fb6f83 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -197,7 +197,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (event.actionMasked == MotionEvent.ACTION_UP) {
nextDownIsDoubleTap = false
GodotInputHandler.handleMotionEvent(event)
+ } else if (event.actionMasked == MotionEvent.ACTION_MOVE && panningAndScalingEnabled == false) {
+ GodotInputHandler.handleMotionEvent(event)
}
+
return true
}
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 401c105cd7..c31d56a3e1 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
@@ -33,11 +33,17 @@ package org.godotengine.godot.utils;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiManager;
+import android.util.Base64;
import android.util.Log;
+import java.io.StringWriter;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+
/**
* This class handles Android-specific networking functions.
- * For now, it only provides access to WifiManager.MulticastLock, which is needed on some devices
+ * It provides access to the CA certificates KeyStore, and the WifiManager.MulticastLock, which is needed on some devices
* to receive broadcast and multicast packets.
*/
public class GodotNetUtils {
@@ -79,4 +85,34 @@ public class GodotNetUtils {
Log.e("Godot", "Exception during multicast lock release: " + e);
}
}
+
+ /**
+ * Retrieves the list of trusted CA certificates from the "AndroidCAStore" and returns them in PRM format.
+ * @see https://developer.android.com/reference/java/security/KeyStore .
+ * @return A string of concatenated X509 certificates in PEM format.
+ */
+ public static String getCACertificates() {
+ try {
+ KeyStore ks = KeyStore.getInstance("AndroidCAStore");
+ StringBuilder writer = new StringBuilder();
+
+ if (ks != null) {
+ ks.load(null, null);
+ Enumeration<String> aliases = ks.aliases();
+
+ while (aliases.hasMoreElements()) {
+ String alias = (String)aliases.nextElement();
+
+ X509Certificate cert = (X509Certificate)ks.getCertificate(alias);
+ writer.append("-----BEGIN CERTIFICATE-----\n");
+ writer.append(Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT));
+ writer.append("-----END CERTIFICATE-----\n");
+ }
+ }
+ return writer.toString();
+ } catch (Exception e) {
+ Log.e("Godot", "Exception while reading CA certificates: " + e);
+ return "";
+ }
+ }
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index f32617e674..18091649e3 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -199,10 +199,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
if (p_surface) {
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
os_android->set_native_window(native_window);
-
- DisplayServerAndroid::get_singleton()->reset_window();
- DisplayServerAndroid::get_singleton()->notify_surface_changed(p_width, p_height);
}
+ DisplayServerAndroid::get_singleton()->reset_window();
+ DisplayServerAndroid::get_singleton()->notify_surface_changed(p_width, p_height);
}
}
}
@@ -447,39 +446,29 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
Object *obj = ObjectDB::get_instance(ObjectID(ID));
ERR_FAIL_NULL(obj);
- int res = env->PushLocalFrame(16);
- ERR_FAIL_COND(res != 0);
-
String str_method = jstring_to_string(method, env);
int count = env->GetArrayLength(params);
+
Variant *vlist = (Variant *)alloca(sizeof(Variant) * count);
- Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count);
+ const Variant **vptr = (const Variant **)alloca(sizeof(Variant *) * count);
+
for (int i = 0; i < count; i++) {
jobject jobj = env->GetObjectArrayElement(params, i);
- Variant v;
- if (jobj) {
- v = _jobject_to_variant(env, jobj);
- }
- memnew_placement(&vlist[i], Variant);
- vlist[i] = v;
+ ERR_FAIL_NULL(jobj);
+ memnew_placement(&vlist[i], Variant(_jobject_to_variant(env, jobj)));
vptr[i] = &vlist[i];
env->DeleteLocalRef(jobj);
}
Callable::CallError err;
- obj->callp(str_method, (const Variant **)vptr, count, err);
-
- env->PopLocalFrame(nullptr);
+ obj->callp(str_method, vptr, count, err);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) {
Object *obj = ObjectDB::get_instance(ObjectID(ID));
ERR_FAIL_NULL(obj);
- int res = env->PushLocalFrame(16);
- ERR_FAIL_COND(res != 0);
-
String str_method = jstring_to_string(method, env);
int count = env->GetArrayLength(params);
@@ -489,16 +478,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
for (int i = 0; i < count; i++) {
jobject jobj = env->GetObjectArrayElement(params, i);
- if (jobj) {
- args[i] = _jobject_to_variant(env, jobj);
- }
- env->DeleteLocalRef(jobj);
+ ERR_FAIL_NULL(jobj);
+ memnew_placement(&args[i], Variant(_jobject_to_variant(env, jobj)));
argptrs[i] = &args[i];
+ env->DeleteLocalRef(jobj);
}
- MessageQueue::get_singleton()->push_callp(obj, str_method, (const Variant **)argptrs, count);
-
- env->PopLocalFrame(nullptr);
+ MessageQueue::get_singleton()->push_callp(obj, str_method, argptrs, count);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 9d9d087896..2b504ad69b 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -70,6 +70,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
_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");
@@ -310,6 +311,17 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
return permissions_list;
}
+String GodotJavaWrapper::get_ca_certificates() const {
+ if (_get_ca_certificates) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, String());
+ jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates);
+ return jstring_to_string(s, env);
+ } else {
+ return String();
+ }
+}
+
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 1bd79584d8..05144380e6 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -60,6 +60,7 @@ private:
jmethodID _request_permission = nullptr;
jmethodID _request_permissions = nullptr;
jmethodID _get_granted_permissions = nullptr;
+ jmethodID _get_ca_certificates = nullptr;
jmethodID _init_input_devices = nullptr;
jmethodID _get_surface = nullptr;
jmethodID _is_activity_resumed = nullptr;
@@ -98,6 +99,7 @@ public:
bool request_permission(const String &p_name);
bool request_permissions();
Vector<String> get_granted_permissions() const;
+ String get_ca_certificates() const;
void init_input_devices();
jobject get_surface();
bool is_activity_resumed();
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 725fea8d54..73081e35e7 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -311,7 +311,11 @@ String OS_Android::get_resource_dir() const {
#ifdef TOOLS_ENABLED
return OS_Unix::get_resource_dir();
#else
- return "/"; //android has its own filesystem for resources inside the APK
+ if (remote_fs_dir.is_empty()) {
+ return "/"; // Android has its own filesystem for resources inside the APK
+ } else {
+ return remote_fs_dir;
+ }
#endif
}
@@ -753,5 +757,19 @@ Error OS_Android::kill(const ProcessID &p_pid) {
return OS_Unix::kill(p_pid);
}
+String OS_Android::get_system_ca_certificates() {
+ return godot_java->get_ca_certificates();
+}
+
+Error OS_Android::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
+ r_project_path = get_user_data_dir();
+ Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path);
+ if (err == OK) {
+ remote_fs_dir = r_project_path;
+ FileAccess::make_default<FileAccessFilesystemJAndroid>(FileAccess::ACCESS_RESOURCES);
+ }
+ return err;
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 53910b1498..f1d08b7cfe 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -57,6 +57,7 @@ private:
mutable String data_dir_cache;
mutable String cache_dir_cache;
+ mutable String remote_fs_dir;
AudioDriverOpenSL audio_driver_android;
@@ -159,6 +160,9 @@ public:
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
virtual Error kill(const ProcessID &p_pid) override;
+ virtual String get_system_ca_certificates() override;
+
+ virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 4bb90cb971..843c015d49 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -120,7 +120,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS
for (int i = 0; i < count; i++) {
jobject j_param = env->GetObjectArrayElement(j_signal_params, i);
- variant_params[i] = _jobject_to_variant(env, j_param);
+ ERR_FAIL_NULL(j_param);
+ memnew_placement(&variant_params[i], Variant(_jobject_to_variant(env, j_param)));
args[i] = &variant_params[i];
env->DeleteLocalRef(j_param);
}
diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h
index 6eaa7c8edc..57f601a858 100644
--- a/platform/ios/display_server_ios.h
+++ b/platform/ios/display_server_ios.h
@@ -55,7 +55,7 @@
#import <QuartzCore/CAMetalLayer.h>
class DisplayServerIOS : public DisplayServer {
- GDCLASS(DisplayServerIOS, DisplayServer)
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
_THREAD_SAFE_CLASS_
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 249ee4323c..381884067b 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -10,7 +10,7 @@
</tutorials>
<members>
<member name="application/app_store_team_id" type="String" setter="" getter="">
- Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organisational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
+ Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
</member>
<member name="application/bundle_identifier" type="String" setter="" getter="">
Unique application identifier in a reverse-DNS format, can only contain alphanumeric characters ([code]A-Z[/code], [code]a-z[/code], and [code]0-9[/code]), hyphens ([code]-[/code]), and periods ([code].[/code]).
@@ -35,9 +35,11 @@
</member>
<member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
+ Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code].
</member>
<member name="application/provisioning_profile_uuid_release" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
+ Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
@@ -69,15 +71,15 @@
<member name="icons/app_store_1024x1024" type="String" setter="" getter="">
App Store application icon file. If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
+ <member name="icons/ipad_76x76" type="String" setter="" getter="">
+ Home screen application icon file on iPad (1x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
+ </member>
<member name="icons/ipad_152x152" type="String" setter="" getter="">
Home screen application icon file on iPad (2x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
<member name="icons/ipad_167x167" type="String" setter="" getter="">
Home screen application icon file on iPad (3x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
- <member name="icons/ipad_76x76" type="String" setter="" getter="">
- Home screen application icon file on iPad (1x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
- </member>
<member name="icons/iphone_120x120" type="String" setter="" getter="">
Home screen application icon file on iPhone (2x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
@@ -114,25 +116,25 @@
<member name="landscape_launch_screens/iphone_2436x1125" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
- <member name="portrait_launch_screens/ipad_1536x2048" type="String" setter="" getter="">
- Application launch screen image file, if left empty project splash screen is used instead.
- </member>
<member name="portrait_launch_screens/ipad_768x1024" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
- <member name="portrait_launch_screens/iphone_1125x2436" type="String" setter="" getter="">
+ <member name="portrait_launch_screens/ipad_1536x2048" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
- <member name="portrait_launch_screens/iphone_1242x2208" type="String" setter="" getter="">
+ <member name="portrait_launch_screens/iphone_640x960" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
<member name="portrait_launch_screens/iphone_640x1136" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
- <member name="portrait_launch_screens/iphone_640x960" type="String" setter="" getter="">
+ <member name="portrait_launch_screens/iphone_750x1334" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
- <member name="portrait_launch_screens/iphone_750x1334" type="String" setter="" getter="">
+ <member name="portrait_launch_screens/iphone_1125x2436" type="String" setter="" getter="">
+ Application launch screen image file, if left empty project splash screen is used instead.
+ </member>
+ <member name="portrait_launch_screens/iphone_1242x2208" type="String" setter="" getter="">
Application launch screen image file, if left empty project splash screen is used instead.
</member>
<member name="privacy/camera_usage_description" type="String" setter="" getter="">
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 88f0c7c30b..06741a12e4 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -33,6 +33,7 @@
#include "core/string/translation.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
#include "platform/ios/logo_svg.gen.h"
#include "modules/modules_enabled.gen.h" // For svg.
@@ -159,10 +160,10 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
@@ -252,8 +253,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
};
String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
- bool dbg_manual = !p_preset->get("application/provisioning_profile_uuid_debug").operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
- bool rel_manual = !p_preset->get("application/provisioning_profile_uuid_release").operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
+ bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
+ bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
String str;
String strnew;
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
@@ -287,9 +288,9 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
} else if (lines[i].find("$provisioning_profile_uuid_release") != -1) {
- strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n";
+ strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE)) + "\n";
} else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) {
- strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n";
+ strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG)) + "\n";
} else if (lines[i].find("$code_sign_style_debug") != -1) {
if (dbg_manual) {
strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n";
@@ -303,7 +304,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n";
}
} else if (lines[i].find("$provisioning_profile_uuid") != -1) {
- String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release");
+ String uuid = p_debug ? p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG) : p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE);
strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
} else if (lines[i].find("$code_sign_identity_debug") != -1) {
strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n";
@@ -1983,6 +1984,8 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
#ifndef ANDROID_ENABLED
quit_request.set();
- check_for_changes_thread.wait_to_finish();
+ if (check_for_changes_thread.is_started()) {
+ check_for_changes_thread.wait_to_finish();
+ }
#endif
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index 0fde3b7c0b..9afefef121 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -49,6 +49,11 @@
#include <sys/stat.h>
+// Optional environment variables for defining confidential information. If any
+// of these is set, they will override the values set in the credentials file.
+const String ENV_IOS_PROFILE_UUID_DEBUG = "GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG";
+const String ENV_IOS_PROFILE_UUID_RELEASE = "GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE";
+
class EditorExportPlatformIOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);
diff --git a/platform/ios/ios.mm b/platform/ios/ios.mm
index 1065f5fd2a..c911a512a5 100644
--- a/platform/ios/ios.mm
+++ b/platform/ios/ios.mm
@@ -72,8 +72,8 @@ CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) {
if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy...
if (supports_haptic_engine()) {
- CHHapticEngine *haptic_engine = get_haptic_engine_instance();
- if (haptic_engine) {
+ CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
+ if (cur_haptic_engine) {
NSDictionary *hapticDict = @{
CHHapticPatternKeyPattern : @[
@{CHHapticPatternKeyEvent : @{
@@ -88,7 +88,7 @@ void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13))
NSError *error;
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
- [[haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error];
+ [[cur_haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error];
NSLog(@"Could not vibrate using haptic engine: %@", error);
}
@@ -103,9 +103,9 @@ void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13))
void iOS::start_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
- CHHapticEngine *haptic_engine = get_haptic_engine_instance();
- if (haptic_engine) {
- [haptic_engine startWithCompletionHandler:^(NSError *returnedError) {
+ CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
+ if (cur_haptic_engine) {
+ [cur_haptic_engine startWithCompletionHandler:^(NSError *returnedError) {
if (returnedError) {
NSLog(@"Could not start haptic engine: %@", returnedError);
}
@@ -122,9 +122,9 @@ void iOS::start_haptic_engine() {
void iOS::stop_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
- CHHapticEngine *haptic_engine = get_haptic_engine_instance();
- if (haptic_engine) {
- [haptic_engine stopWithCompletionHandler:^(NSError *returnedError) {
+ CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
+ if (cur_haptic_engine) {
+ [cur_haptic_engine stopWithCompletionHandler:^(NSError *returnedError) {
if (returnedError) {
NSLog(@"Could not stop haptic engine: %@", returnedError);
}
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index b2f72c310f..739db419a3 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -240,10 +240,20 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle,
}
if (!FileAccess::exists(path)) {
+ // Load .dylib converted to framework from within the executable path.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework"));
+ }
+
+ if (!FileAccess::exists(path)) {
// Load .dylib or framework from a standard iOS location.
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file()));
}
+ if (!FileAccess::exists(path)) {
+ // Load .dylib converted to framework from a standard iOS location.
+ path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework"));
+ }
+
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 317c6575b3..9544cc761d 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
#include "platform/linuxbsd/logo_svg.gen.h"
#include "platform/linuxbsd/run_icon_svg.gen.h"
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index b45b7e676d..6dfa8ed93c 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -138,6 +138,11 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
bool ver_ok = false;
int version_major = 0;
int version_minor = 0;
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
index 78f2337599..cf179b5735 100644
--- a/platform/linuxbsd/freedesktop_screensaver.cpp
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -141,6 +141,11 @@ FreeDesktopScreenSaver::FreeDesktopScreenSaver() {
#else
unsupported = false;
#endif
+
+ if (unsupported) {
+ return;
+ }
+
bool ver_ok = false;
int version_major = 0;
int version_minor = 0;
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5c623b8ba2..a9725fff2e 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -32,6 +32,8 @@
#include "joypad_linux.h"
+#include "core/os/os.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -72,6 +74,26 @@ void JoypadLinux::Joypad::reset() {
events.clear();
}
+// This function is derived from SDL:
+// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+static bool detect_sandbox() {
+ if (access("/.flatpak-info", F_OK) == 0) {
+ return true;
+ }
+
+ // For Snap, we check multiple variables because they might be set for
+ // unrelated reasons. This is the same thing WebKitGTK does.
+ if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
+ return true;
+ }
+
+ if (access("/run/host/container-manager", F_OK) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
#ifdef SOWRAP_ENABLED
@@ -80,17 +102,25 @@ JoypadLinux::JoypadLinux(Input *in) {
#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) {
- // There's no API to check version, check if functions are available instead.
- use_udev = false;
- print_verbose("JoypadLinux: Unsupported udev library version!");
+ 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 {
+ 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) {
+ // There's no API to check version, check if functions are available instead.
+ use_udev = false;
+ print_verbose("JoypadLinux: Unsupported udev library version!");
+ } else {
+ print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+ }
} else {
- print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+ print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
}
- } else {
- print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
}
#endif
#else
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 11c81be4a9..8d8c8ce27b 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -30,6 +30,7 @@
#include "os_linuxbsd.h"
+#include "core/io/certs_compressed.gen.h"
#include "core/io/dir_access.h"
#include "main/main.h"
#include "servers/display_server.h"
@@ -195,6 +196,10 @@ void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
+String OS_LinuxBSD::get_identifier() const {
+ return "linuxbsd";
+}
+
String OS_LinuxBSD::get_name() const {
#ifdef __linux__
return "Linux";
@@ -490,6 +495,11 @@ bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
return true;
}
+ // Match against the specific OS (linux, freebsd, etc).
+ if (p_feature == get_name().to_lower()) {
+ return true;
+ }
+
return false;
}
@@ -1076,6 +1086,40 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
return OK;
}
+String OS_LinuxBSD::get_system_ca_certificates() {
+ String certfile;
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ // Compile time preferred certificates path.
+ if (!String(_SYSTEM_CERTS_PATH).is_empty() && da->file_exists(_SYSTEM_CERTS_PATH)) {
+ certfile = _SYSTEM_CERTS_PATH;
+ } else if (da->file_exists("/etc/ssl/certs/ca-certificates.crt")) {
+ // Debian/Ubuntu
+ certfile = "/etc/ssl/certs/ca-certificates.crt";
+ } else if (da->file_exists("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")) {
+ // Fedora
+ certfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem";
+ } else if (da->file_exists("/etc/ca-certificates/extracted/tls-ca-bundle.pem")) {
+ // Arch Linux
+ certfile = "/etc/ca-certificates/extracted/tls-ca-bundle.pem";
+ } else if (da->file_exists("/var/lib/ca-certificates/ca-bundle.pem")) {
+ // openSUSE
+ certfile = "/var/lib/ca-certificates/ca-bundle.pem";
+ } else if (da->file_exists("/etc/ssl/cert.pem")) {
+ // FreeBSD/OpenBSD
+ certfile = "/etc/ssl/cert.pem";
+ }
+
+ if (certfile.is_empty()) {
+ return "";
+ }
+
+ Ref<FileAccess> f = FileAccess::open(certfile, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), "", vformat("Failed to open system CA certificates file: '%s'", certfile));
+
+ return f->get_as_text();
+}
+
OS_LinuxBSD::OS_LinuxBSD() {
main_loop = nullptr;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 9423514944..c1e735b0d4 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -96,6 +96,7 @@ protected:
virtual void set_main_loop(MainLoop *p_main_loop) override;
public:
+ virtual String get_identifier() const override;
virtual String get_name() const override;
virtual String get_distribution_name() const override;
virtual String get_version() const override;
@@ -132,6 +133,8 @@ public:
virtual Error move_to_trash(const String &p_path) override;
+ virtual String get_system_ca_certificates() override;
+
OS_LinuxBSD();
~OS_LinuxBSD();
};
diff --git a/platform/linuxbsd/x11/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp
index 3d07be1c76..78778a8b56 100644
--- a/platform/linuxbsd/x11/detect_prime_x11.cpp
+++ b/platform/linuxbsd/x11/detect_prime_x11.cpp
@@ -60,6 +60,9 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+// To prevent shadowing warnings
+#undef glGetString
+
struct vendor {
const char *glxvendor = nullptr;
int priority = 0;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index ac4fbe2068..13f261aa43 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -186,6 +186,7 @@ bool DisplayServerX11::_refresh_device_info() {
xi.absolute_devices.clear();
xi.touch_devices.clear();
xi.pen_inverted_devices.clear();
+ xi.last_relative_time = 0;
int dev_count;
XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
@@ -2724,8 +2725,8 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
XcursorImageDestroy(cursor_image);
} else {
// Reset to default system cursor
- if (img[p_shape]) {
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
+ if (cursor_img[p_shape]) {
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_img[p_shape]);
}
CursorShape c = current_cursor;
@@ -3497,8 +3498,8 @@ void DisplayServerX11::_window_changed(XEvent *event) {
// Query display server about a possible new window state.
wd.fullscreen = _window_fullscreen_check(window_id);
- wd.minimized = _window_minimize_check(window_id);
- wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE");
+ wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE") && !wd.fullscreen;
+ wd.minimized = _window_minimize_check(window_id) && !wd.fullscreen && !wd.maximized;
// Readjusting the window position if the window is being reparented by the window manager for decoration
Window root, parent, *children;
@@ -5359,7 +5360,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;
- img[i] = nullptr;
+ cursor_img[i] = nullptr;
}
XInitThreads(); //always use threads
@@ -5716,8 +5717,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
"question_arrow"
};
- img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
- if (!img[i]) {
+ cursor_img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
+ if (!cursor_img[i]) {
const char *fallback = nullptr;
switch (i) {
@@ -5755,7 +5756,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
fallback = "bd_double_arrow";
break;
case CURSOR_MOVE:
- img[i] = img[CURSOR_DRAG];
+ cursor_img[i] = cursor_img[CURSOR_DRAG];
break;
case CURSOR_VSPLIT:
fallback = "sb_v_double_arrow";
@@ -5768,11 +5769,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
break;
}
if (fallback != nullptr) {
- img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
+ cursor_img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
}
}
- if (img[i]) {
- cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
+ if (cursor_img[i]) {
+ cursors[i] = XcursorImageLoadCursor(x11_display, cursor_img[i]);
} else {
print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
}
@@ -5911,8 +5912,8 @@ DisplayServerX11::~DisplayServerX11() {
if (cursors[i] != None) {
XFreeCursor(x11_display, cursors[i]);
}
- if (img[i] != nullptr) {
- XcursorImageDestroy(img[i]);
+ if (cursor_img[i] != nullptr) {
+ XcursorImageDestroy(cursor_img[i]);
}
}
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index e8e0680c14..6d343be3ab 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -119,8 +119,7 @@ typedef struct _xrr_monitor_info {
#undef CursorShape
class DisplayServerX11 : public DisplayServer {
- //No need to register, it's platform-specific and nothing is added
- //GDCLASS(DisplayServerX11, DisplayServer)
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
_THREAD_SAFE_CLASS_
@@ -307,7 +306,7 @@ class DisplayServerX11 : public DisplayServer {
const char *cursor_theme = nullptr;
int cursor_size = 0;
- XcursorImage *img[CURSOR_MAX];
+ XcursorImage *cursor_img[CURSOR_MAX];
Cursor cursors[CURSOR_MAX];
Cursor null_cursor;
CursorShape current_cursor = CURSOR_ARROW;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index ee767dfa80..1e579c9f01 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -44,6 +44,9 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+// To prevent shadowing warnings
+#undef glXCreateContextAttribsARB
+
struct GLManager_X11_Private {
::GLXContext glx_context;
};
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 1fefdb3c68..7b8d3fd853 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -235,6 +235,8 @@ def configure(env: "Environment"):
"CoreMedia",
"-framework",
"QuartzCore",
+ "-framework",
+ "Security",
]
)
env.Append(LIBS=["pthread", "z"])
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index a71ac3a310..a1cd83280d 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -56,7 +56,7 @@
#undef CursorShape
class DisplayServerMacOS : public DisplayServer {
- GDCLASS(DisplayServerMacOS, DisplayServer)
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
_THREAD_SAFE_CLASS_
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 5a7c309448..32f9441484 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -1840,9 +1840,9 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
if (!p_callback.is_null()) {
Variant button = button_pressed;
Variant *buttonp = &button;
- Variant ret;
+ Variant fun_ret;
Callable::CallError ce;
- p_callback.callp((const Variant **)&buttonp, 1, ret, ce);
+ p_callback.callp((const Variant **)&buttonp, 1, fun_ret, ce);
}
return OK;
@@ -1872,9 +1872,9 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description
if (!p_callback.is_null()) {
Variant text = ret;
Variant *textp = &text;
- Variant ret;
+ Variant fun_ret;
Callable::CallError ce;
- p_callback.callp((const Variant **)&textp, 1, ret, ce);
+ p_callback.callp((const Variant **)&textp, 1, fun_ret, ce);
}
return OK;
@@ -1897,7 +1897,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED);
if (show_cursor && !previously_shown) {
- WindowID window_id = get_window_at_screen_position(mouse_get_position());
+ window_id = get_window_at_screen_position(mouse_get_position());
if (window_id != INVALID_WINDOW_ID) {
send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 1a9fd431c7..64e1efde26 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -46,13 +46,15 @@
Official export templates include [code]universal[/code] binaries only.
</member>
<member name="codesign/apple_team_id" type="String" setter="" getter="">
- Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organisational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
+ Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
</member>
<member name="codesign/certificate_file" type="String" setter="" getter="">
PKCS #12 certificate file used to sign [code].app[/code] bundle.
+ Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_FILE[/code].
</member>
<member name="codesign/certificate_password" type="String" setter="" getter="">
Password for the certificate file used to sign [code].app[/code] bundle.
+ Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD[/code].
</member>
<member name="codesign/codesign" type="int" setter="" getter="">
Tool to use for code signing.
@@ -138,6 +140,7 @@
</member>
<member name="codesign/provisioning_profile" type="String" setter="" getter="">
Provisioning profile file downloaded from Apple developer account dashboard. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
+ Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE[/code].
</member>
<member name="custom_template/debug" type="String" setter="" getter="">
Path to the custom export template. If left empty, default template is used.
@@ -156,18 +159,23 @@
</member>
<member name="notarization/api_key" type="String" setter="" getter="">
Apple App Store Connect API issuer key file.
+ Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY[/code].
</member>
<member name="notarization/api_key_id" type="String" setter="" getter="">
Apple App Store Connect API issuer key ID.
+ Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY_ID[/code].
</member>
<member name="notarization/api_uuid" type="String" setter="" getter="">
Apple App Store Connect API issuer UUID.
+ Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_UUID[/code].
</member>
<member name="notarization/apple_id_name" type="String" setter="" getter="">
Apple ID account name (email address).
+ Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME[/code].
</member>
<member name="notarization/apple_id_password" type="String" setter="" getter="">
Apple ID app-specific password.
+ Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD[/code].
</member>
<member name="notarization/notarization" type="int" setter="" getter="">
Tool to use for notarization.
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index cd9d17dd4f..be677f4da2 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -73,7 +73,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
ad_hoc = true;
} break;
case 2: { // "rcodesign"
- ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
+ ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
@@ -114,7 +114,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
}
if (p_name == "codesign/provisioning_profile" && dist_type == 2) {
- String pprof = p_preset->get("codesign/provisioning_profile");
+ String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE);
if (pprof.is_empty()) {
return TTR("Provisioning profile is required for App Store distribution.");
}
@@ -154,8 +154,8 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
if (notary_tool == 2 || notary_tool == 3) {
if (p_name == "notarization/apple_id_name" || p_name == "notarization/api_uuid") {
- String apple_id = p_preset->get("notarization/apple_id_name");
- String api_uuid = p_preset->get("notarization/api_uuid");
+ String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID);
+ String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
if (apple_id.is_empty() && api_uuid.is_empty()) {
return TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified.");
}
@@ -164,28 +164,28 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
}
}
if (p_name == "notarization/apple_id_password") {
- String apple_id = p_preset->get("notarization/apple_id_name");
- String apple_pass = p_preset->get("notarization/apple_id_password");
+ String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID);
+ String apple_pass = p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS);
if (!apple_id.is_empty() && apple_pass.is_empty()) {
return TTR("Apple ID password not specified.");
}
}
if (p_name == "notarization/api_key_id") {
- String api_uuid = p_preset->get("notarization/api_uuid");
- String api_key = p_preset->get("notarization/api_key_id");
+ String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
+ String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID);
if (!api_uuid.is_empty() && api_key.is_empty()) {
return TTR("App Store Connect API key ID not specified.");
}
}
} else if (notary_tool == 1) {
if (p_name == "notarization/api_uuid") {
- String api_uuid = p_preset->get("notarization/api_uuid");
+ String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
if (api_uuid.is_empty()) {
return TTR("App Store Connect issuer ID name not specified.");
}
}
if (p_name == "notarization/api_key_id") {
- String api_key = p_preset->get("notarization/api_key_id");
+ String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID);
if (api_key.is_empty()) {
return TTR("App Store Connect API key ID not specified.");
}
@@ -398,10 +398,10 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
// "codesign" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
// "rcodesign" only options:
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
// "codesign" and "rcodesign" only options:
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "", true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
@@ -434,12 +434,12 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,rcodesign"), 0, true));
#endif
// "altool" and "notarytool" only options:
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
// "altool", "notarytool" and "rcodesign" only options:
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8"), "", false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
@@ -776,24 +776,24 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back("notary-submit");
- if (p_preset->get("notarization/api_uuid") == "") {
+ if (p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
- if (p_preset->get("notarization/api_key") == "") {
+ if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--api-issuer");
- args.push_back(p_preset->get("notarization/api_uuid"));
+ args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
args.push_back("--api-key");
- args.push_back(p_preset->get("notarization/api_key_id"));
+ args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
- if (!p_preset->get("notarization/api_key").operator String().is_empty()) {
+ if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) {
args.push_back("--api-key-path");
- args.push_back(p_preset->get("notarization/api_key"));
+ args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY));
}
args.push_back(p_path);
@@ -840,40 +840,40 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back(p_path);
- if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
- if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
return Error::FAILED;
}
- if (p_preset->get("notarization/apple_id_name") != "") {
- if (p_preset->get("notarization/apple_id_password") == "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") {
+ if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
return Error::FAILED;
}
args.push_back("--apple-id");
- args.push_back(p_preset->get("notarization/apple_id_name"));
+ args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID));
args.push_back("--password");
- args.push_back(p_preset->get("notarization/apple_id_password"));
+ args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS));
} else {
- if (p_preset->get("notarization/api_key_id") == "") {
+ if (p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--issuer");
- args.push_back(p_preset->get("notarization/api_uuid"));
+ args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
- if (!p_preset->get("notarization/api_key").operator String().is_empty()) {
+ if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) {
args.push_back("--key");
- args.push_back(p_preset->get("notarization/api_key"));
+ args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY));
}
args.push_back("--key-id");
- args.push_back(p_preset->get("notarization/api_key_id"));
+ args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
}
args.push_back("--no-progress");
@@ -925,35 +925,35 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back("--primary-bundle-id");
args.push_back(p_preset->get("application/bundle_identifier"));
- if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
- if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
return Error::FAILED;
}
- if (p_preset->get("notarization/apple_id_name") != "") {
- if (p_preset->get("notarization/apple_id_password") == "") {
+ if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") {
+ if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
return Error::FAILED;
}
args.push_back("--username");
- args.push_back(p_preset->get("notarization/apple_id_name"));
+ args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID));
args.push_back("--password");
- args.push_back(p_preset->get("notarization/apple_id_password"));
+ args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS));
} else {
- if (p_preset->get("notarization/api_key") == "") {
+ if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--apiIssuer");
- args.push_back(p_preset->get("notarization/api_uuid"));
+ args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
args.push_back("--apiKey");
- args.push_back(p_preset->get("notarization/api_key_id"));
+ args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
}
args.push_back("--type");
@@ -1032,8 +1032,8 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
args.push_back(p_ent_path);
}
- String certificate_file = p_preset->get("codesign/certificate_file");
- String certificate_pass = p_preset->get("codesign/certificate_password");
+ String certificate_file = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE);
+ String certificate_pass = p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS);
if (!certificate_file.is_empty() && !certificate_pass.is_empty()) {
args.push_back("--p12-file");
args.push_back(certificate_file);
@@ -1763,7 +1763,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
ad_hoc = true;
} break;
case 2: { // "rcodesign"
- ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
+ ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
@@ -1857,7 +1857,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
int dist_type = p_preset->get("export/distribution_type");
if (dist_type == 2) {
- String pprof = p_preset->get("codesign/provisioning_profile");
+ String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE);
String teamid = p_preset->get("codesign/apple_team_id");
String bid = p_preset->get("application/bundle_identifier");
if (!pprof.is_empty() && !teamid.is_empty()) {
@@ -1990,7 +1990,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (err == OK && sign_enabled) {
int dist_type = p_preset->get("export/distribution_type");
if (dist_type == 2) {
- String pprof = p_preset->get("codesign/provisioning_profile").operator String();
+ String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE).operator String();
if (!pprof.is_empty()) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = da->copy(pprof, tmp_app_path_name + "/Contents/embedded.provisionprofile");
@@ -2147,7 +2147,7 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
ad_hoc = true;
} break;
case 2: { // "rcodesign"
- ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
+ ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index a8caf535c4..0477a8c0cc 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -43,6 +43,17 @@
#include <sys/stat.h>
+// Optional environment variables for defining confidential information. If any
+// of these is set, they will override the values set in the credentials file.
+const String ENV_MAC_CODESIGN_CERT_FILE = "GODOT_MACOS_CODESIGN_CERTIFICATE_FILE";
+const String ENV_MAC_CODESIGN_CERT_PASS = "GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD";
+const String ENV_MAC_CODESIGN_PROFILE = "GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE";
+const String ENV_MAC_NOTARIZATION_UUID = "GODOT_MACOS_NOTARIZATION_API_UUID";
+const String ENV_MAC_NOTARIZATION_KEY = "GODOT_MACOS_NOTARIZATION_API_KEY";
+const String ENV_MAC_NOTARIZATION_KEY_ID = "GODOT_MACOS_NOTARIZATION_API_KEY_ID";
+const String ENV_MAC_NOTARIZATION_APPLE_ID = "GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME";
+const String ENV_MAC_NOTARIZATION_APPLE_PASS = "GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD";
+
class EditorExportPlatformMacOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformMacOS, EditorExportPlatform);
diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h
index eb7a30203a..07bae479be 100644
--- a/platform/macos/os_macos.h
+++ b/platform/macos/os_macos.h
@@ -119,6 +119,8 @@ public:
virtual Error move_to_trash(const String &p_path) override;
+ virtual String get_system_ca_certificates() override;
+
void run();
OS_MacOS();
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 74cdef6f25..838ae742fd 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -30,6 +30,7 @@
#include "os_macos.h"
+#include "core/crypto/crypto_core.h"
#include "core/version_generated.gen.h"
#include "main/main.h"
@@ -671,6 +672,34 @@ Error OS_MacOS::move_to_trash(const String &p_path) {
return OK;
}
+String OS_MacOS::get_system_ca_certificates() {
+ CFArrayRef result;
+ SecCertificateRef item;
+ CFDataRef der;
+
+ OSStatus ret = SecTrustCopyAnchorCertificates(&result);
+ ERR_FAIL_COND_V(ret != noErr, "");
+
+ CFIndex l = CFArrayGetCount(result);
+ String certs;
+ PackedByteArray pba;
+ for (CFIndex i = 0; i < l; i++) {
+ item = (SecCertificateRef)CFArrayGetValueAtIndex(result, i);
+ der = SecCertificateCopyData(item);
+ int derlen = CFDataGetLength(der);
+ if (pba.size() < derlen * 3) {
+ pba.resize(derlen * 3);
+ }
+ size_t b64len = 0;
+ Error err = CryptoCore::b64_encode(pba.ptrw(), pba.size(), &b64len, (unsigned char *)CFDataGetBytePtr(der), derlen);
+ CFRelease(der);
+ ERR_CONTINUE(err != OK);
+ certs += "-----BEGIN CERTIFICATE-----\n" + String((char *)pba.ptr(), b64len) + "\n-----END CERTIFICATE-----\n";
+ }
+ CFRelease(result);
+ return certs;
+}
+
void OS_MacOS::run() {
if (!main_loop) {
return;
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 85964c53e6..c20e3316a5 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -31,6 +31,7 @@
#include "export.h"
#include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
#include "export_plugin.h"
void register_uwp_exporter_types() {
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 163236e506..aac61184b1 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -80,8 +80,8 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1));
@@ -465,8 +465,8 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
int cert_alg = EDITOR_GET("export/uwp/debug_algorithm");
if (!p_debug) {
- cert_path = p_preset->get("signing/certificate");
- cert_pass = p_preset->get("signing/password");
+ cert_path = p_preset->get_or_env("signing/certificate", ENV_UWP_SIGNING_CERT);
+ cert_pass = p_preset->get_or_env("signing/password", ENV_UWP_SIGNING_PASS);
cert_alg = p_preset->get("signing/algorithm");
}
diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h
index 37a32b1f7f..b42a2ae6d9 100644
--- a/platform/uwp/export/export_plugin.h
+++ b/platform/uwp/export/export_plugin.h
@@ -85,6 +85,11 @@ static const char *uwp_device_capabilities[] = {
nullptr
};
+// Optional environment variables for defining confidential information. If any
+// of these is set, they will override the values set in the credentials file.
+const String ENV_UWP_SIGNING_CERT = "GODOT_UWP_SIGNING_CERTIFICATE";
+const String ENV_UWP_SIGNING_PASS = "GODOT_UWP_SIGNING_PASSWORD";
+
class EditorExportPlatformUWP : public EditorExportPlatform {
GDCLASS(EditorExportPlatformUWP, EditorExportPlatform);
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index 1d7b96d707..c6c67db3de 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -103,7 +103,7 @@ void AudioDriverWeb::_audio_driver_capture(int p_from, int p_samples) {
Error AudioDriverWeb::init() {
int latency = GLOBAL_GET("audio/driver/output_latency");
if (!audio_context.inited) {
- audio_context.mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ audio_context.mix_rate = _get_configured_mix_rate();
audio_context.channel_count = godot_audio_init(&audio_context.mix_rate, latency, &_state_change_callback, &_latency_update_callback);
audio_context.inited = true;
}
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 2e50a6bbc8..a72977e3c3 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -37,6 +37,8 @@
#include <emscripten/html5.h>
class DisplayServerWeb : public DisplayServer {
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
+
private:
struct JSTouchEvent {
uint32_t identifier[32] = { 0 };
diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp
index 54d9774da5..80c29024a8 100644
--- a/platform/web/export/export.cpp
+++ b/platform/web/export/export.cpp
@@ -31,6 +31,7 @@
#include "export.h"
#include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
#include "export_plugin.h"
void register_web_exporter_types() {
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 7a62cd2a4a..2fff628c85 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
#include "platform/web/logo_svg.gen.h"
#include "platform/web/run_icon_svg.gen.h"
@@ -686,5 +687,7 @@ EditorExportPlatformWeb::~EditorExportPlatformWeb() {
server->stop();
}
server_quit = true;
- server_thread.wait_to_finish();
+ if (server_thread.is_started()) {
+ server_thread.wait_to_finish();
+ }
}
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index cd6d461a93..963f533d67 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -413,6 +413,7 @@ def configure_msvc(env, vcvars_msvc_config):
"dxguid",
"imm32",
"bcrypt",
+ "Crypt32",
"Avrt",
"dwmapi",
"dwrite",
@@ -592,6 +593,7 @@ def configure_mingw(env):
"ksuser",
"imm32",
"bcrypt",
+ "crypt32",
"avrt",
"uuid",
"dwmapi",
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 80c75c63b5..a8dcc6bad0 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -280,8 +280,7 @@ typedef struct {
} ICONDIR, *LPICONDIR;
class DisplayServerWindows : public DisplayServer {
- //No need to register, it's platform-specific and nothing is added
- //GDCLASS(DisplayServerWindows, DisplayServer)
+ // No need to register with GDCLASS, it's platform-specific and nothing is added.
_THREAD_SAFE_CLASS_
diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
index fee8a118bc..ec2b105f58 100644
--- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml
+++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
@@ -64,12 +64,15 @@
</member>
<member name="codesign/identity" type="String" setter="" getter="">
PKCS #12 certificate file used to sign executable or certificate SHA-1 hash (if [member codesign/identity_type] is set to "Use certificate store"). See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
+ Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY[/code].
</member>
<member name="codesign/identity_type" type="int" setter="" getter="">
Type of identity to use. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
+ Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE[/code].
</member>
<member name="codesign/password" type="String" setter="" getter="">
Password for the certificate file used to sign executable. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
+ Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_PASSWORD[/code].
</member>
<member name="codesign/timestamp" type="bool" setter="" getter="">
If [code]true[/code], time-stamp is added to the signature. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 05b25eae03..ca390236fb 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
#include "platform/windows/logo_svg.gen.h"
#include "platform/windows/run_icon_svg.gen.h"
@@ -327,9 +328,9 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64"));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1));
@@ -517,21 +518,21 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
//identity
#ifdef WINDOWS_ENABLED
- int id_type = p_preset->get("codesign/identity_type");
+ int id_type = p_preset->get_or_env("codesign/identity_type", ENV_WIN_CODESIGN_ID_TYPE);
if (id_type == 0) { //auto select
args.push_back("/a");
} else if (id_type == 1) { //pkcs12
- if (p_preset->get("codesign/identity") != "") {
+ if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("/f");
- args.push_back(p_preset->get("codesign/identity"));
+ args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
}
} else if (id_type == 2) { //Windows certificate store
- if (p_preset->get("codesign/identity") != "") {
+ if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("/sha1");
- args.push_back(p_preset->get("codesign/identity"));
+ args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
@@ -542,9 +543,9 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
#else
int id_type = 1;
- if (p_preset->get("codesign/identity") != "") {
+ if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("-pkcs12");
- args.push_back(p_preset->get("codesign/identity"));
+ args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
@@ -552,13 +553,13 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
#endif
//password
- if ((id_type == 1) && (p_preset->get("codesign/password") != "")) {
+ if ((id_type == 1) && (p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS) != "")) {
#ifdef WINDOWS_ENABLED
args.push_back("/p");
#else
args.push_back("-pass");
#endif
- args.push_back(p_preset->get("codesign/password"));
+ args.push_back(p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS));
}
//timestamp
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index c466971202..184b2f96f8 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -36,6 +36,12 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
+// Optional environment variables for defining confidential information. If any
+// of these is set, they will override the values set in the credentials file.
+const String ENV_WIN_CODESIGN_ID_TYPE = "GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE";
+const String ENV_WIN_CODESIGN_ID = "GODOT_WINDOWS_CODESIGN_IDENTITY";
+const String ENV_WIN_CODESIGN_PASS = "GODOT_WINDOWS_CODESIGN_PASSWORD";
+
class EditorExportPlatformWindows : public EditorExportPlatformPC {
GDCLASS(EditorExportPlatformWindows, EditorExportPlatformPC);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 5a749a9d6f..bd7d9f178a 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -55,6 +55,7 @@
#include <regstr.h>
#include <shlobj.h>
#include <wbemcli.h>
+#include <wincrypt.h>
#ifdef DEBUG_ENABLED
#pragma pack(push, before_imagehlp, 8)
@@ -435,8 +436,6 @@ String OS_Windows::get_distribution_name() const {
}
String OS_Windows::get_version() const {
- typedef LONG NTSTATUS;
- typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion");
if (version_ptr != nullptr) {
RTL_OSVERSIONINFOW fow;
@@ -554,9 +553,9 @@ OS::DateTime OS_Windows::get_datetime(bool p_utc) const {
//Get DST information from Windows, but only if p_utc is false.
TIME_ZONE_INFORMATION info;
- bool daylight = false;
+ bool is_daylight = false;
if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) {
- daylight = true;
+ is_daylight = true;
}
DateTime dt;
@@ -567,20 +566,20 @@ OS::DateTime OS_Windows::get_datetime(bool p_utc) const {
dt.hour = systemtime.wHour;
dt.minute = systemtime.wMinute;
dt.second = systemtime.wSecond;
- dt.dst = daylight;
+ dt.dst = is_daylight;
return dt;
}
OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
TIME_ZONE_INFORMATION info;
- bool daylight = false;
+ bool is_daylight = false;
if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) {
- daylight = true;
+ is_daylight = true;
}
// Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update.
TimeZoneInfo ret;
- if (daylight) {
+ if (is_daylight) {
ret.name = info.DaylightName;
ret.bias = info.Bias + info.DaylightBias;
} else {
@@ -706,16 +705,16 @@ Dictionary OS_Windows::get_memory_info() const {
}
if (pref_info.PhysicalTotal * pref_info.PageSize != 0) {
- meminfo["physical"] = pref_info.PhysicalTotal * pref_info.PageSize;
+ meminfo["physical"] = static_cast<int64_t>(pref_info.PhysicalTotal * pref_info.PageSize);
}
if (pref_info.PhysicalAvailable * pref_info.PageSize != 0) {
- meminfo["free"] = pref_info.PhysicalAvailable * pref_info.PageSize;
+ meminfo["free"] = static_cast<int64_t>(pref_info.PhysicalAvailable * pref_info.PageSize);
}
if (pref_info.CommitLimit * pref_info.PageSize != 0) {
- meminfo["available"] = pref_info.CommitLimit * pref_info.PageSize;
+ meminfo["available"] = static_cast<int64_t>(pref_info.CommitLimit * pref_info.PageSize);
}
if (HighLimit - LowLimit != 0) {
- meminfo["stack"] = HighLimit - LowLimit;
+ meminfo["stack"] = static_cast<int64_t>(HighLimit - LowLimit);
}
return meminfo;
@@ -1677,6 +1676,26 @@ Error OS_Windows::move_to_trash(const String &p_path) {
return OK;
}
+String OS_Windows::get_system_ca_certificates() {
+ HCERTSTORE cert_store = CertOpenSystemStoreA(0, "ROOT");
+ ERR_FAIL_COND_V_MSG(!cert_store, "", "Failed to read the root certificate store.");
+
+ String certs;
+ PCCERT_CONTEXT curr = CertEnumCertificatesInStore(cert_store, nullptr);
+ while (curr) {
+ DWORD size = 0;
+ bool success = CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, nullptr, &size);
+ ERR_CONTINUE(!success);
+ PackedByteArray pba;
+ pba.resize(size);
+ CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, (char *)pba.ptrw(), &size);
+ certs += String((char *)pba.ptr(), size);
+ curr = CertEnumCertificatesInStore(cert_store, curr);
+ }
+ CertCloseStore(cert_store, 0);
+ return certs;
+}
+
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 960c3f30a9..c5f95870b3 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -226,6 +226,8 @@ public:
virtual Error move_to_trash(const String &p_path) override;
+ virtual String get_system_ca_certificates() override;
+
void set_main_window(HWND p_main_window) { main_window = p_main_window; }
HINSTANCE get_hinstance() { return hInstance; }
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index ba3b0cec5c..ab42c52913 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -118,6 +118,15 @@ void CollisionObject2D::_notification(int p_what) {
}
} break;
+ case NOTIFICATION_WORLD_2D_CHANGED: {
+ RID space = get_world_2d()->get_space();
+ if (area) {
+ PhysicsServer2D::get_singleton()->area_set_space(rid, space);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_space(rid, space);
+ }
+ } break;
+
case NOTIFICATION_DISABLED: {
_apply_disabled();
} break;
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index d101cac4eb..936244fdb1 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -56,8 +56,11 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent2D::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent2D::get_max_neighbors);
- ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent2D::set_time_horizon);
- ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent2D::get_time_horizon);
+ ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent2D::set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent2D::get_time_horizon_agents);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent2D::set_time_horizon_obstacles);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent2D::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent2D::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent2D::get_max_speed);
@@ -87,9 +90,16 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent2D::get_next_path_position);
- ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
+
+ ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent2D::set_velocity_forced);
+
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent2D::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent2D::get_current_navigation_path);
ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent2D::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached);
@@ -99,6 +109,17 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent2D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent2D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent2D::set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent2D::get_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent2D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent2D::get_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent2D::set_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent2D::get_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent2D::set_avoidance_priority);
+ ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent2D::get_avoidance_priority);
+
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,or_greater,suffix:px"), "set_path_desired_distance", "get_path_desired_distance");
@@ -111,11 +132,16 @@ void NavigationAgent2D::_bind_methods() {
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01,or_greater,suffix:px"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01,or_greater,suffix:px"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_distance", PROPERTY_HINT_RANGE, "0.1,100000,0.01,or_greater,suffix:px"), "set_neighbor_distance", "get_neighbor_distance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,or_greater,1"), "set_max_neighbors", "get_max_neighbors");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10,0.01,or_greater,suffix:s"), "set_time_horizon", "get_time_horizon");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,or_greater,suffix:px/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1,or_greater"), "set_max_neighbors", "get_max_neighbors");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.01,100000,0.01,or_greater,suffix:px/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent2D::get_debug_enabled);
@@ -143,6 +169,34 @@ void NavigationAgent2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR2, "safe_velocity")));
}
+#ifndef DISABLE_DEPRECATED
+// Compatibility with Godot 4.0 beta 10 or below.
+// Functions in block below all renamed or replaced in 4.0 beta 1X avoidance rework.
+bool NavigationAgent2D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "time_horizon") {
+ set_time_horizon_agents(p_value);
+ return true;
+ }
+ if (p_name == "target_location") {
+ set_target_position(p_value);
+ return true;
+ }
+ return false;
+}
+
+bool NavigationAgent2D::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "time_horizon") {
+ r_ret = get_time_horizon_agents();
+ return true;
+ }
+ if (p_name == "target_location") {
+ r_ret = get_target_position();
+ return true;
+ }
+ return false;
+}
+#endif // DISABLE_DEPRECATED
+
void NavigationAgent2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POST_ENTER_TREE: {
@@ -151,11 +205,16 @@ void NavigationAgent2D::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
+ if (agent_parent && avoidance_enabled) {
+ NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ }
+
#ifdef DEBUG_ENABLED
if (NavigationServer2D::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
}
#endif // DEBUG_ENABLED
+
} break;
case NOTIFICATION_PARENTED: {
@@ -208,10 +267,18 @@ void NavigationAgent2D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent && target_position_submitted) {
- if (avoidance_enabled) {
- // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
- // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
- NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ NavigationServer2D::get_singleton()->agent_set_velocity(agent, velocity);
+ }
+ }
+ if (velocity_forced_submitted) {
+ velocity_forced_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer2D::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
+ }
}
_check_distance_to_target();
}
@@ -226,9 +293,11 @@ void NavigationAgent2D::_notification(int p_what) {
NavigationAgent2D::NavigationAgent2D() {
agent = NavigationServer2D::get_singleton()->agent_create();
+
NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
- NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed);
@@ -239,6 +308,11 @@ NavigationAgent2D::NavigationAgent2D() {
navigation_result = Ref<NavigationPathQueryResult2D>();
navigation_result.instantiate();
+ set_avoidance_layers(avoidance_layers);
+ set_avoidance_mask(avoidance_mask);
+ set_avoidance_priority(avoidance_priority);
+ set_avoidance_enabled(avoidance_enabled);
+
#ifdef DEBUG_ENABLED
NavigationServer2D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed));
#endif // DEBUG_ENABLED
@@ -267,9 +341,11 @@ void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
+ NavigationServer2D::get_singleton()->agent_set_avoidance_enabled(agent, true);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
} else {
- NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
+ NavigationServer2D::get_singleton()->agent_set_avoidance_enabled(agent, false);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_callback(agent, Callable());
}
}
@@ -283,7 +359,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
}
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
- NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
+ NavigationServer2D::get_singleton()->agent_set_avoidance_callback(agent, Callable());
if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
@@ -296,7 +372,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
// create new avoidance callback if enabled
if (avoidance_enabled) {
- NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
+ NavigationServer2D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done));
}
} else {
agent_parent = nullptr;
@@ -401,6 +477,7 @@ void NavigationAgent2D::set_target_desired_distance(real_t p_target_desired_dist
}
void NavigationAgent2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
if (Math::is_equal_approx(radius, p_radius)) {
return;
}
@@ -430,21 +507,29 @@ void NavigationAgent2D::set_max_neighbors(int p_count) {
NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
-void NavigationAgent2D::set_time_horizon(real_t p_time) {
- if (Math::is_equal_approx(time_horizon, p_time)) {
+void NavigationAgent2D::set_time_horizon_agents(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
return;
}
+ time_horizon_agents = p_time_horizon;
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+}
- time_horizon = p_time;
-
- NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+void NavigationAgent2D::set_time_horizon_obstacles(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
+ return;
+ }
+ time_horizon_obstacles = p_time_horizon;
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent2D::set_max_speed(real_t p_max_speed) {
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
if (Math::is_equal_approx(max_speed, p_max_speed)) {
return;
}
-
max_speed = p_max_speed;
NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed);
@@ -516,29 +601,24 @@ Vector2 NavigationAgent2D::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
-void NavigationAgent2D::set_velocity(Vector2 p_velocity) {
+void NavigationAgent2D::set_velocity_forced(Vector2 p_velocity) {
// Intentionally not checking for equality of the parameter.
// We need to always submit the velocity to the navigation server, even when it is the same, in order to run avoidance every frame.
// Revisit later when the navigation server can update avoidance without users resubmitting the velocity.
- target_velocity = p_velocity;
- velocity_submitted = true;
+ velocity_forced = p_velocity;
+ velocity_forced_submitted = true;
+}
- NavigationServer2D::get_singleton()->agent_set_target_velocity(agent, target_velocity);
- NavigationServer2D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+void NavigationAgent2D::set_velocity(const Vector2 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
}
void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
- const Vector2 velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
- prev_safe_velocity = velocity;
-
- if (!velocity_submitted) {
- target_velocity = Vector2();
- return;
- }
- velocity_submitted = false;
-
- emit_signal(SNAME("velocity_computed"), velocity);
+ const Vector2 new_safe_velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
+ safe_velocity = new_safe_velocity;
+ emit_signal(SNAME("velocity_computed"), safe_velocity);
}
PackedStringArray NavigationAgent2D::get_configuration_warnings() const {
@@ -561,9 +641,6 @@ void NavigationAgent2D::update_navigation() {
if (!target_position_submitted) {
return;
}
- if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
- return;
- }
update_frame_id = Engine::get_singleton()->get_physics_frames();
@@ -709,6 +786,71 @@ void NavigationAgent2D::_check_distance_to_target() {
}
}
+void NavigationAgent2D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer2D::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
+}
+
+uint32_t NavigationAgent2D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationAgent2D::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ NavigationServer2D::get_singleton()->agent_set_avoidance_mask(get_rid(), p_mask);
+}
+
+uint32_t NavigationAgent2D::get_avoidance_mask() const {
+ return avoidance_mask;
+}
+
+void NavigationAgent2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationAgent2D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationAgent2D::set_avoidance_mask_value(int p_mask_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_avoidance_mask();
+ if (p_value) {
+ mask |= 1 << (p_mask_number - 1);
+ } else {
+ mask &= ~(1 << (p_mask_number - 1));
+ }
+ set_avoidance_mask(mask);
+}
+
+bool NavigationAgent2D::get_avoidance_mask_value(int p_mask_number) const {
+ ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ return get_avoidance_mask() & (1 << (p_mask_number - 1));
+}
+
+void NavigationAgent2D::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ NavigationServer2D::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
+}
+
+real_t NavigationAgent2D::get_avoidance_priority() const {
+ return avoidance_priority;
+}
+
////////DEBUG////////////////////////////////////////////////////////////
void NavigationAgent2D::set_debug_enabled(bool p_enabled) {
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index b7febba89d..ca43abf833 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -47,6 +47,9 @@ class NavigationAgent2D : public Node {
RID map_override;
bool avoidance_enabled = false;
+ uint32_t avoidance_layers = 1;
+ uint32_t avoidance_mask = 1;
+ real_t avoidance_priority = 1.0;
uint32_t navigation_layers = 1;
NavigationPathQueryParameters2D::PathfindingAlgorithm pathfinding_algorithm = NavigationPathQueryParameters2D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
NavigationPathQueryParameters2D::PathPostProcessing path_postprocessing = NavigationPathQueryParameters2D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
@@ -57,19 +60,33 @@ class NavigationAgent2D : public Node {
real_t radius = 10.0;
real_t neighbor_distance = 500.0;
int max_neighbors = 10;
- real_t time_horizon = 1.0;
+ real_t time_horizon_agents = 1.0;
+ real_t time_horizon_obstacles = 0.0;
real_t max_speed = 100.0;
real_t path_max_distance = 100.0;
Vector2 target_position;
- bool target_position_submitted = false;
+
Ref<NavigationPathQueryParameters2D> navigation_query;
Ref<NavigationPathQueryResult2D> navigation_result;
int navigation_path_index = 0;
+
+ // the velocity result of the avoidance simulation step
+ Vector2 safe_velocity;
+
+ /// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
+ // this velocity is not guaranteed, the simulation will try to fulfil it if possible
+ // if other agents or obstacles interfere it will be changed accordingly
+ Vector2 velocity;
bool velocity_submitted = false;
- Vector2 prev_safe_velocity;
- /// The submitted target velocity
- Vector2 target_velocity;
+
+ /// The submitted forced velocity, overrides the rvo agent velocity on the next update
+ // should only be used very intentionally and not every frame as it interferes with the simulation stability
+ Vector2 velocity_forced;
+ bool velocity_forced_submitted = false;
+
+ bool target_position_submitted = false;
+
bool target_reached = false;
bool navigation_finished = true;
// No initialized on purpose
@@ -81,27 +98,27 @@ class NavigationAgent2D : public Node {
float debug_path_custom_line_width = -1.0;
bool debug_use_custom = false;
Color debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0);
+
#ifdef DEBUG_ENABLED
// Debug properties internal only
bool debug_path_dirty = true;
RID debug_path_instance;
-
-private:
- void _navigation_debug_changed();
- void _update_debug_path();
#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
void _notification(int p_what);
+#ifndef DISABLE_DEPRECATED
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+#endif // DISABLE_DEPRECATED
+
public:
NavigationAgent2D();
virtual ~NavigationAgent2D();
- RID get_rid() const {
- return agent;
- }
+ RID get_rid() const { return agent; }
void set_avoidance_enabled(bool p_enabled);
bool get_avoidance_enabled() const;
@@ -133,39 +150,28 @@ public:
RID get_navigation_map() const;
void set_path_desired_distance(real_t p_dd);
- real_t get_path_desired_distance() const {
- return path_desired_distance;
- }
+ real_t get_path_desired_distance() const { return path_desired_distance; }
void set_target_desired_distance(real_t p_dd);
- real_t get_target_desired_distance() const {
- return target_desired_distance;
- }
+ real_t get_target_desired_distance() const { return target_desired_distance; }
void set_radius(real_t p_radius);
- real_t get_radius() const {
- return radius;
- }
+ real_t get_radius() const { return radius; }
void set_neighbor_distance(real_t p_distance);
- real_t get_neighbor_distance() const {
- return neighbor_distance;
- }
+ real_t get_neighbor_distance() const { return neighbor_distance; }
void set_max_neighbors(int p_count);
- int get_max_neighbors() const {
- return max_neighbors;
- }
+ int get_max_neighbors() const { return max_neighbors; }
- void set_time_horizon(real_t p_time);
- real_t get_time_horizon() const {
- return time_horizon;
- }
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
- real_t get_max_speed() const {
- return max_speed;
- }
+ real_t get_max_speed() const { return max_speed; }
void set_path_max_distance(real_t p_pmd);
real_t get_path_max_distance();
@@ -175,15 +181,11 @@ public:
Vector2 get_next_path_position();
- Ref<NavigationPathQueryResult2D> get_current_navigation_result() const {
- return navigation_result;
- }
- const Vector<Vector2> &get_current_navigation_path() const {
- return navigation_result->get_path();
- }
- int get_current_navigation_path_index() const {
- return navigation_path_index;
- }
+ Ref<NavigationPathQueryResult2D> get_current_navigation_result() const { return navigation_result; }
+
+ const Vector<Vector2> &get_current_navigation_path() const { return navigation_result->get_path(); }
+
+ int get_current_navigation_path_index() const { return navigation_path_index; }
real_t distance_to_target() const;
bool is_target_reached() const;
@@ -191,11 +193,30 @@ public:
bool is_navigation_finished();
Vector2 get_final_position();
- void set_velocity(Vector2 p_velocity);
+ void set_velocity(const Vector2 p_velocity);
+ Vector2 get_velocity() { return velocity; }
+
+ void set_velocity_forced(const Vector2 p_velocity);
+
void _avoidance_done(Vector3 p_new_velocity);
PackedStringArray get_configuration_warnings() const override;
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_avoidance_mask_value(int p_mask_number, bool p_value);
+ bool get_avoidance_mask_value(int p_mask_number) const;
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const;
+
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
@@ -215,6 +236,11 @@ private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
+
+#ifdef DEBUG_ENABLED
+ void _navigation_debug_changed();
+ void _update_debug_path();
+#endif // DEBUG_ENABLED
};
#endif // NAVIGATION_AGENT_2D_H
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 8adb7c6305..3664040e7b 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -106,19 +106,29 @@ void NavigationLink2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled) {
NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
-
- // Update global positions for the link.
- Transform2D gt = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
}
+ current_global_transform = get_global_transform();
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
- // Update global positions for the link.
- Transform2D gt = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
+ set_physics_process_internal(true);
} 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()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ queue_redraw();
+ }
+ }
+ } break;
+
case NOTIFICATION_EXIT_TREE: {
NavigationServer2D::get_singleton()->link_set_map(link, RID());
} break;
@@ -242,8 +252,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
return;
}
- Transform2D gt = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
update_configuration_warnings();
@@ -265,8 +274,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
return;
}
- Transform2D gt = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
update_configuration_warnings();
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 8a24d611c9..4259740c90 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -45,6 +45,8 @@ class NavigationLink2D : public Node2D {
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
+ Transform2D current_global_transform;
+
protected:
static void _bind_methods();
void _notification(int p_what);
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 2366cb696e..d368a4d436 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -30,223 +30,280 @@
#include "navigation_obstacle_2d.h"
-#include "scene/2d/collision_shape_2d.h"
-#include "scene/2d/physics_body_2d.h"
+#include "core/math/geometry_2d.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
+#include "servers/navigation_server_3d.h"
void NavigationObstacle2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
+ ClassDB::bind_method(D_METHOD("get_obstacle_rid"), &NavigationObstacle2D::get_obstacle_rid);
+ ClassDB::bind_method(D_METHOD("get_agent_rid"), &NavigationObstacle2D::get_agent_rid);
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle2D::set_navigation_map);
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle2D::get_navigation_map);
- ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
- ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius");
-}
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle2D::get_velocity);
-void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "radius") {
- if (estimate_radius) {
- p_property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
- }
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle2D::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle2D::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle2D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle2D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle2D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle2D::get_avoidance_layer_value);
+
+ ADD_GROUP("Avoidance", "avoidance_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
}
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POST_ENTER_TREE: {
- set_agent_parent(get_parent());
+ if (map_override.is_valid()) {
+ _update_map(map_override);
+ } else if (is_inside_tree()) {
+ _update_map(get_world_2d()->get_navigation_map());
+ } else {
+ _update_map(RID());
+ }
+ 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()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+ _update_position(get_global_transform().get_origin());
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
- set_agent_parent(nullptr);
- set_physics_process_internal(false);
- } break;
-
- case NOTIFICATION_PARENTED: {
- if (is_inside_tree() && (get_parent() != parent_node2d)) {
- set_agent_parent(get_parent());
- set_physics_process_internal(true);
- }
- } break;
-
- case NOTIFICATION_UNPARENTED: {
- set_agent_parent(nullptr);
set_physics_process_internal(false);
+ _update_map(RID());
} break;
case NOTIFICATION_PAUSED: {
- if (parent_node2d && !parent_node2d->can_process()) {
- map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid());
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_UNPAUSED: {
- if (parent_node2d && !parent_node2d->can_process()) {
- map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid());
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node2d && parent_node2d->is_inside_tree()) {
- NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_position());
+ if (is_inside_tree()) {
+ _update_position(get_global_transform().get_origin());
+
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ // only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
+ if (!previous_velocity.is_equal_approx(velocity)) {
+ NavigationServer2D::get_singleton()->agent_set_velocity(fake_agent, velocity);
+ }
+ previous_velocity = velocity;
+ }
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree()) {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (NavigationServer2D::get_singleton()->get_debug_enabled() && NavigationServer2D::get_singleton()->get_debug_avoidance_enabled()) {
+ is_debug_enabled = true;
+ }
+
+ if (is_debug_enabled) {
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+ }
}
+#endif // DEBUG_ENABLED
} break;
}
}
NavigationObstacle2D::NavigationObstacle2D() {
- agent = NavigationServer2D::get_singleton()->agent_create();
- initialize_agent();
+ obstacle = NavigationServer2D::get_singleton()->obstacle_create();
+ fake_agent = NavigationServer2D::get_singleton()->agent_create();
+
+ // change properties of the fake agent so it can act as fake obstacle with a radius
+ NavigationServer2D::get_singleton()->agent_set_neighbor_distance(fake_agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_max_neighbors(fake_agent, 0);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_agents(fake_agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_time_horizon_obstacles(fake_agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_max_speed(fake_agent, 0.0);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_mask(fake_agent, 0);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_priority(fake_agent, 1.0);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+
+ set_radius(radius);
+ set_vertices(vertices);
+ set_avoidance_layers(avoidance_layers);
}
NavigationObstacle2D::~NavigationObstacle2D() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
- NavigationServer2D::get_singleton()->free(agent);
- agent = RID(); // Pointless
-}
-PackedStringArray NavigationObstacle2D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ NavigationServer2D::get_singleton()->free(obstacle);
+ obstacle = RID();
- if (!Object::cast_to<Node2D>(get_parent())) {
- warnings.push_back(RTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."));
- }
-
- if (Object::cast_to<StaticBody2D>(get_parent())) {
- warnings.push_back(RTR("The NavigationObstacle2D is intended for constantly moving bodies like CharacterBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
- "\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail"));
- }
-
- return warnings;
+ NavigationServer2D::get_singleton()->free(fake_agent);
+ fake_agent = RID();
}
-void NavigationObstacle2D::initialize_agent() {
- NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, 0.0);
- NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0);
- NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0);
- NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0);
+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();
+ }
}
-void NavigationObstacle2D::reevaluate_agent_radius() {
- if (!estimate_radius) {
- NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node2d && parent_node2d->is_inside_tree()) {
- NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
}
+ map_override = p_navigation_map;
+ _update_map(map_override);
}
-real_t NavigationObstacle2D::estimate_agent_radius() const {
- if (parent_node2d && parent_node2d->is_inside_tree()) {
- // Estimate the radius of this physics body
- real_t max_radius = 0.0;
- for (int i(0); i < parent_node2d->get_child_count(); i++) {
- // For each collision shape
- CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(parent_node2d->get_child(i));
- if (cs && cs->is_inside_tree()) {
- // Take the distance between the Body center to the shape center
- real_t r = cs->get_transform().get_origin().length();
- if (cs->get_shape().is_valid()) {
- // and add the enclosing shape radius
- r += cs->get_shape()->get_enclosing_radius();
- }
- Size2 s = cs->get_global_scale();
- r *= MAX(s.x, s.y);
- // Takes the biggest radius
- max_radius = MAX(max_radius, r);
- } else if (cs && !cs->is_inside_tree()) {
- WARN_PRINT("A CollisionShape2D of the NavigationObstacle2D parent node was not inside the SceneTree when estimating the obstacle radius."
- "\nMove the NavigationObstacle2D to a child position below any CollisionShape2D node of the parent node so the CollisionShape2D is already inside the SceneTree.");
- }
- }
- Vector2 s = parent_node2d->get_global_scale();
- max_radius *= MAX(s.x, s.y);
-
- if (max_radius > 0.0) {
- return max_radius;
- }
+RID NavigationObstacle2D::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 1.0; // Never a 0 radius
+ return RID();
}
-void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) {
- if (parent_node2d == p_agent_parent) {
+void NavigationObstacle2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
+ if (Math::is_equal_approx(radius, p_radius)) {
return;
}
- if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
- parent_node2d = Object::cast_to<Node2D>(p_agent_parent);
- if (map_override.is_valid()) {
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_override);
- } else {
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
- }
- // Need to register Callback as obstacle requires a valid Callback to be added to avoidance simulation.
- NavigationServer2D::get_singleton()->agent_set_callback(get_rid(), callable_mp(this, &NavigationObstacle2D::_avoidance_done));
- reevaluate_agent_radius();
- } else {
- parent_node2d = nullptr;
- NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID());
- NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable());
+ radius = p_radius;
+
+ NavigationServer2D::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0.0);
+ NavigationServer2D::get_singleton()->agent_set_radius(fake_agent, radius);
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
+ queue_redraw();
}
}
-void NavigationObstacle2D::_avoidance_done(Vector3 p_new_velocity) {
- // Dummy function as obstacle requires a valid Callback to be added to avoidance simulation.
+void NavigationObstacle2D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
+ NavigationServer2D::get_singleton()->agent_set_avoidance_layers(fake_agent, avoidance_layers);
}
-void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
- if (map_override == p_navigation_map) {
- return;
+uint32_t NavigationObstacle2D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationObstacle2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
+ set_avoidance_layers(avoidance_layers_new);
+}
- map_override = p_navigation_map;
+bool NavigationObstacle2D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
- NavigationServer2D::get_singleton()->agent_set_map(agent, map_override);
+void NavigationObstacle2D::set_velocity(const Vector2 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
}
-RID NavigationObstacle2D::get_navigation_map() const {
- if (map_override.is_valid()) {
- return map_override;
- } else if (parent_node2d != nullptr) {
- return parent_node2d->get_world_2d()->get_navigation_map();
+void NavigationObstacle2D::_update_map(RID p_map) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(obstacle, p_map);
+ NavigationServer2D::get_singleton()->agent_set_map(fake_agent, p_map);
+ map_current = p_map;
+}
+
+void NavigationObstacle2D::_update_position(const Vector2 p_position) {
+ if (vertices.size() > 0) {
+ NavigationServer2D::get_singleton()->obstacle_set_position(obstacle, p_position);
+ }
+ if (radius > 0.0) {
+ NavigationServer2D::get_singleton()->agent_set_position(fake_agent, p_position);
}
- return RID();
}
-void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
- if (estimate_radius == p_estimate_radius) {
- return;
+#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);
}
+}
+#endif // DEBUG_ENABLED
- estimate_radius = p_estimate_radius;
+#ifdef DEBUG_ENABLED
+void NavigationObstacle2D::_update_static_obstacle_debug() {
+ if (get_vertices().size() > 2 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
+ bool obstacle_pushes_inward = Geometry2D::is_polygon_clockwise(get_vertices());
- notify_property_list_changed();
- reevaluate_agent_radius();
-}
+ Color debug_static_obstacle_face_color;
-void NavigationObstacle2D::set_radius(real_t p_radius) {
- ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
- if (Math::is_equal_approx(radius, p_radius)) {
- return;
- }
+ if (obstacle_pushes_inward) {
+ debug_static_obstacle_face_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
+ } else {
+ debug_static_obstacle_face_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
+ }
- radius = p_radius;
+ Vector<Vector2> debug_obstacle_polygon_vertices = get_vertices();
+
+ Vector<Color> debug_obstacle_polygon_colors;
+ debug_obstacle_polygon_colors.resize(debug_obstacle_polygon_vertices.size());
+ debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
+
+ RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_obstacle_polygon_vertices, debug_obstacle_polygon_colors);
+
+ Color debug_static_obstacle_edge_color;
+
+ if (obstacle_pushes_inward) {
+ debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
+ } else {
+ debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
+ }
+
+ Vector<Vector2> debug_obstacle_line_vertices = get_vertices();
+ debug_obstacle_line_vertices.push_back(debug_obstacle_line_vertices[0]);
+ debug_obstacle_line_vertices.resize(debug_obstacle_line_vertices.size());
- reevaluate_agent_radius();
+ Vector<Color> debug_obstacle_line_colors;
+ debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
+ debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
+
+ RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
+ }
}
+#endif // DEBUG_ENABLED
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index f856c481b0..d3b0926247 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -32,55 +32,71 @@
#define NAVIGATION_OBSTACLE_2D_H
#include "scene/2d/node_2d.h"
-#include "scene/main/node.h"
-class NavigationObstacle2D : public Node {
- GDCLASS(NavigationObstacle2D, Node);
+class NavigationObstacle2D : public Node2D {
+ GDCLASS(NavigationObstacle2D, Node2D);
- Node2D *parent_node2d = nullptr;
-
- RID agent;
+ RID obstacle;
RID map_before_pause;
RID map_override;
+ RID map_current;
+
+ real_t radius = 0.0;
+
+ Vector<Vector2> vertices;
+
+ RID fake_agent;
+ uint32_t avoidance_layers = 1;
+
+ Transform2D previous_transform;
+
+ Vector2 velocity;
+ Vector2 previous_velocity;
+ bool velocity_submitted = false;
- bool estimate_radius = true;
- real_t radius = 1.0;
+#ifdef DEBUG_ENABLED
+private:
+ void _update_fake_agent_radius_debug();
+ void _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
public:
NavigationObstacle2D();
virtual ~NavigationObstacle2D();
- RID get_rid() const {
- return agent;
- }
-
- void set_agent_parent(Node *p_agent_parent);
+ RID get_obstacle_rid() const { return obstacle; }
+ RID get_agent_rid() const { return fake_agent; }
void set_navigation_map(RID p_navigation_map);
RID get_navigation_map() const;
- void set_estimate_radius(bool p_estimate_radius);
- bool is_radius_estimated() const {
- return estimate_radius;
- }
void set_radius(real_t p_radius);
- real_t get_radius() const {
- return radius;
- }
+ real_t get_radius() const { return radius; }
+
+ void set_vertices(const Vector<Vector2> &p_vertices);
+ const Vector<Vector2> &get_vertices() const { return vertices; };
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
- PackedStringArray get_configuration_warnings() const override;
+ void set_velocity(const Vector2 p_velocity);
+ Vector2 get_velocity() const { return velocity; };
void _avoidance_done(Vector3 p_new_velocity); // Dummy
private:
- void initialize_agent();
- void reevaluate_agent_radius();
- real_t estimate_agent_radius() const;
+ void _update_map(RID p_map);
+ void _update_position(const Vector2 p_position);
};
#endif // NAVIGATION_OBSTACLE_2D_H
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 5dbba313bc..c14fb29353 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -31,6 +31,8 @@
#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"
#include "servers/navigation_server_2d.h"
#include "servers/navigation_server_3d.h"
@@ -55,7 +57,7 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
}
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (Engine::get_singleton()->is_editor_hint() || NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
queue_redraw();
}
#endif // DEBUG_ENABLED
@@ -65,6 +67,20 @@ bool NavigationRegion2D::is_enabled() const {
return enabled;
}
+void NavigationRegion2D::set_use_edge_connections(bool p_enabled) {
+ if (use_edge_connections == p_enabled) {
+ return;
+ }
+
+ use_edge_connections = p_enabled;
+
+ NavigationServer2D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections);
+}
+
+bool NavigationRegion2D::get_use_edge_connections() const {
+ return use_edge_connections;
+}
+
void NavigationRegion2D::set_navigation_layers(uint32_t p_navigation_layers) {
if (navigation_layers == p_navigation_layers) {
return;
@@ -151,11 +167,19 @@ void NavigationRegion2D::_notification(int p_what) {
if (enabled) {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ 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);
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- NavigationServer2D::get_singleton()->region_set_transform(region, get_global_transform());
+ set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -163,6 +187,29 @@ void NavigationRegion2D::_notification(int p_what) {
if (enabled) {
NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
+ 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());
+ }
+ }
+ } 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: {
@@ -177,7 +224,7 @@ void NavigationRegion2D::_notification(int p_what) {
bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
- bool enable_edge_connections = ns2d->get_debug_navigation_enable_edge_connections();
+ bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map());
Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color();
Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color();
@@ -277,6 +324,7 @@ void NavigationRegion2D::_navigation_polygon_changed() {
if (navigation_polygon.is_valid()) {
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon);
}
+ _update_avoidance_constrain();
}
void NavigationRegion2D::_map_changed(RID p_map) {
@@ -306,12 +354,22 @@ 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_use_edge_connections", "enabled"), &NavigationRegion2D::set_use_edge_connections);
+ ClassDB::bind_method(D_METHOD("get_use_edge_connections"), &NavigationRegion2D::get_use_edge_connections);
+
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationRegion2D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationRegion2D::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationRegion2D::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationRegion2D::get_navigation_layer_value);
+ ClassDB::bind_method(D_METHOD("set_constrain_avoidance", "enabled"), &NavigationRegion2D::set_constrain_avoidance);
+ ClassDB::bind_method(D_METHOD("get_constrain_avoidance"), &NavigationRegion2D::get_constrain_avoidance);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationRegion2D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationRegion2D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationRegion2D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationRegion2D::get_avoidance_layer_value);
+
ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion2D::get_region_rid);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationRegion2D::set_enter_cost);
@@ -324,9 +382,12 @@ void NavigationRegion2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
}
#ifndef DISABLE_DEPRECATED
@@ -367,8 +428,123 @@ NavigationRegion2D::~NavigationRegion2D() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(region);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
+ }
+ }
+ constrain_avoidance_obstacles.clear();
+
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
#endif // DEBUG_ENABLED
}
+
+void NavigationRegion2D::_update_avoidance_constrain() {
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
+ constrain_avoidance_obstacles[i] = RID();
+ }
+ }
+ constrain_avoidance_obstacles.clear();
+
+ if (!constrain_avoidance) {
+ return;
+ }
+
+ if (get_navigation_polygon() == nullptr) {
+ return;
+ }
+
+ Ref<NavigationPolygon> _navpoly = get_navigation_polygon();
+ int _outline_count = _navpoly->get_outline_count();
+ if (_outline_count == 0) {
+ return;
+ }
+
+ for (int outline_index(0); outline_index < _outline_count; outline_index++) {
+ const Vector<Vector2> &_outline = _navpoly->get_outline(outline_index);
+
+ const int outline_size = _outline.size();
+ if (outline_size < 3) {
+ ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's");
+ continue;
+ }
+
+ RID obstacle_rid = NavigationServer2D::get_singleton()->obstacle_create();
+ constrain_avoidance_obstacles.push_back(obstacle_rid);
+
+ Vector<Vector2> new_obstacle_outline;
+
+ if (outline_index == 0) {
+ for (int i(0); i < outline_size; i++) {
+ new_obstacle_outline.push_back(_outline[outline_size - i - 1]);
+ }
+ ERR_FAIL_COND_MSG(Geometry2D::is_polygon_clockwise(_outline), "Outer most outline needs to be clockwise to push avoidance agent inside");
+ } else {
+ for (int i(0); i < outline_size; i++) {
+ new_obstacle_outline.push_back(_outline[i]);
+ }
+ }
+ new_obstacle_outline.resize(outline_size);
+
+ 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());
+ NavigationServer2D::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
+ }
+ }
+ constrain_avoidance_obstacles.resize(_outline_count);
+}
+
+void NavigationRegion2D::set_constrain_avoidance(bool p_enabled) {
+ constrain_avoidance = p_enabled;
+ _update_avoidance_constrain();
+ notify_property_list_changed();
+}
+
+bool NavigationRegion2D::get_constrain_avoidance() const {
+ return constrain_avoidance;
+}
+
+void NavigationRegion2D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "avoidance_layers") {
+ if (!constrain_avoidance) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
+}
+
+void NavigationRegion2D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ if (constrain_avoidance_obstacles.size() > 0) {
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers);
+ }
+ }
+}
+
+uint32_t NavigationRegion2D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationRegion2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationRegion2D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 49ff143dd8..39de0a0004 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -37,17 +37,26 @@ class NavigationRegion2D : public Node2D {
GDCLASS(NavigationRegion2D, Node2D);
bool enabled = true;
+ bool use_edge_connections = true;
+
RID region;
uint32_t navigation_layers = 1;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
Ref<NavigationPolygon> navigation_polygon;
+ bool constrain_avoidance = false;
+ LocalVector<RID> constrain_avoidance_obstacles;
+ uint32_t avoidance_layers = 1;
+
+ Transform2D current_global_transform;
+
void _navigation_polygon_changed();
void _map_changed(RID p_RID);
protected:
void _notification(int p_what);
+ void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
@@ -64,6 +73,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
+ void set_use_edge_connections(bool p_enabled);
+ bool get_use_edge_connections() const;
+
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
@@ -81,10 +93,22 @@ public:
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon() const;
+ void set_constrain_avoidance(bool p_enabled);
+ bool get_constrain_avoidance() const;
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
PackedStringArray get_configuration_warnings() const override;
NavigationRegion2D();
~NavigationRegion2D();
+
+private:
+ void _update_avoidance_constrain();
};
#endif // NAVIGATION_REGION_2D_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index c678b4bf02..1b59351b30 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -907,9 +907,7 @@ void RigidBody2D::_notification(int p_what) {
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- }
+ update_configuration_warnings();
} break;
}
#endif
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 19d1b83cab..6d8d60dcaa 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -83,13 +83,9 @@ void CollisionObject3D::_notification(int p_what) {
_update_pickable();
} break;
-#ifdef TOOLS_ENABLED
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- }
+ update_configuration_warnings();
} break;
-#endif
case NOTIFICATION_TRANSFORM_CHANGED: {
if (only_update_transform_changes) {
diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp
index 53a61c1368..9a2ed00274 100644
--- a/scene/3d/collision_polygon_3d.cpp
+++ b/scene/3d/collision_polygon_3d.cpp
@@ -104,11 +104,7 @@ void CollisionPolygon3D::_notification(int p_what) {
if (parent) {
_update_in_shape_owner(true);
}
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- }
-#endif
+ update_configuration_warnings();
} break;
case NOTIFICATION_UNPARENTED: {
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index f1d918ad9b..b7f3b12c25 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -99,11 +99,7 @@ void CollisionShape3D::_notification(int p_what) {
if (parent) {
_update_in_shape_owner(true);
}
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- }
-#endif
+ update_configuration_warnings();
} break;
case NOTIFICATION_UNPARENTED: {
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 16c82bf6d2..18198b566e 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -284,6 +284,9 @@ void Light3D::_update_visibility() {
void Light3D::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ update_configuration_warnings();
+ } break;
case NOTIFICATION_VISIBILITY_CHANGED:
case NOTIFICATION_ENTER_TREE: {
_update_visibility();
diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h
index 018ed9485d..5a390eaede 100644
--- a/scene/3d/lightmapper.h
+++ b/scene/3d/lightmapper.h
@@ -71,15 +71,15 @@ public:
/*! Constructs a ray from origin, direction, and ray segment. Near
* has to be smaller than far. */
- _FORCE_INLINE_ Ray(const Vector3 &org,
- const Vector3 &dir,
- float tnear = 0.0f,
- float tfar = INFINITY) :
- org(org),
- tnear(tnear),
- dir(dir),
+ _FORCE_INLINE_ Ray(const Vector3 &p_org,
+ const Vector3 &p_dir,
+ float p_tnear = 0.0f,
+ float p_tfar = INFINITY) :
+ org(p_org),
+ tnear(p_tnear),
+ dir(p_dir),
time(0.0f),
- tfar(tfar),
+ tfar(p_tfar),
mask(-1),
u(0.0),
v(0.0),
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 7d404ffa07..80b360a2fd 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -48,11 +48,14 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent3D::get_radius);
- ClassDB::bind_method(D_METHOD("set_agent_height_offset", "agent_height_offset"), &NavigationAgent3D::set_agent_height_offset);
- ClassDB::bind_method(D_METHOD("get_agent_height_offset"), &NavigationAgent3D::get_agent_height_offset);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NavigationAgent3D::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &NavigationAgent3D::get_height);
- ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent3D::set_ignore_y);
- ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent3D::get_ignore_y);
+ ClassDB::bind_method(D_METHOD("set_path_height_offset", "path_height_offset"), &NavigationAgent3D::set_path_height_offset);
+ ClassDB::bind_method(D_METHOD("get_path_height_offset"), &NavigationAgent3D::get_path_height_offset);
+
+ ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationAgent3D::set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationAgent3D::get_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("set_neighbor_distance", "neighbor_distance"), &NavigationAgent3D::set_neighbor_distance);
ClassDB::bind_method(D_METHOD("get_neighbor_distance"), &NavigationAgent3D::get_neighbor_distance);
@@ -60,8 +63,11 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent3D::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent3D::get_max_neighbors);
- ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent3D::set_time_horizon);
- ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent3D::get_time_horizon);
+ ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent3D::set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent3D::get_time_horizon_agents);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent3D::set_time_horizon_obstacles);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent3D::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent3D::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent3D::get_max_speed);
@@ -91,9 +97,16 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent3D::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent3D::get_next_path_position);
- ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target);
+
+ ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent3D::set_velocity_forced);
+
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent3D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent3D::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent3D::get_current_navigation_result);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent3D::get_current_navigation_path);
ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent3D::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent3D::is_target_reached);
@@ -103,11 +116,22 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent3D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent3D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent3D::set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent3D::get_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent3D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent3D::get_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent3D::set_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent3D::get_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent3D::set_avoidance_priority);
+ ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent3D::get_avoidance_priority);
+
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater,suffix:m"), "set_path_desired_distance", "get_path_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater,suffix:m"), "set_target_desired_distance", "get_target_desired_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,or_greater,suffix:m"), "set_agent_height_offset", "get_agent_height_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,or_greater,suffix:m"), "set_path_height_offset", "get_path_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,or_greater,suffix:m"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
@@ -116,12 +140,18 @@ void NavigationAgent3D::_bind_methods() {
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_distance", PROPERTY_HINT_RANGE, "0.1,10000,0.01,or_greater,suffix:m"), "set_neighbor_distance", "get_neighbor_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1,or_greater"), "set_max_neighbors", "get_max_neighbors");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_time_horizon", "get_time_horizon");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,1000,0.01,or_greater,suffix:m/s"), "set_max_speed", "get_max_speed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.01,10000,0.01,or_greater,suffix:m/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -146,6 +176,42 @@ void NavigationAgent3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "0,50,0.01,or_greater,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size");
}
+#ifndef DISABLE_DEPRECATED
+// Compatibility with Godot 4.0 beta 10 or below.
+// Functions in block below all renamed or replaced in 4.0 beta 1X avoidance rework.
+bool NavigationAgent3D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "time_horizon") {
+ set_time_horizon_agents(p_value);
+ return true;
+ }
+ if (p_name == "target_location") {
+ set_target_position(p_value);
+ return true;
+ }
+ if (p_name == "agent_height_offset") {
+ set_path_height_offset(p_value);
+ return true;
+ }
+ return false;
+}
+
+bool NavigationAgent3D::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "time_horizon") {
+ r_ret = get_time_horizon_agents();
+ return true;
+ }
+ if (p_name == "target_location") {
+ r_ret = get_target_position();
+ return true;
+ }
+ if (p_name == "agent_height_offset") {
+ r_ret = get_path_height_offset();
+ return true;
+ }
+ return false;
+}
+#endif // DISABLE_DEPRECATED
+
void NavigationAgent3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POST_ENTER_TREE: {
@@ -154,11 +220,16 @@ void NavigationAgent3D::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
+ if (avoidance_enabled) {
+ NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ }
+
#ifdef DEBUG_ENABLED
if (NavigationServer3D::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
}
#endif // DEBUG_ENABLED
+
} break;
case NOTIFICATION_PARENTED: {
@@ -211,10 +282,22 @@ void NavigationAgent3D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent && target_position_submitted) {
- if (avoidance_enabled) {
- // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
- // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
- NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ if (!use_3d_avoidance) {
+ stored_y_velocity = velocity.y;
+ velocity.y = 0.0;
+ }
+ NavigationServer3D::get_singleton()->agent_set_velocity(agent, velocity);
+ }
+ }
+ if (velocity_forced_submitted) {
+ velocity_forced_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer3D::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
+ }
}
_check_distance_to_target();
}
@@ -229,12 +312,14 @@ void NavigationAgent3D::_notification(int p_what) {
NavigationAgent3D::NavigationAgent3D() {
agent = NavigationServer3D::get_singleton()->agent_create();
+
NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance);
NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
- NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer3D::get_singleton()->agent_set_height(agent, height);
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
- NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
// Preallocate query and result objects to improve performance.
navigation_query = Ref<NavigationPathQueryParameters3D>();
@@ -243,6 +328,12 @@ NavigationAgent3D::NavigationAgent3D() {
navigation_result = Ref<NavigationPathQueryResult3D>();
navigation_result.instantiate();
+ set_avoidance_layers(avoidance_layers);
+ set_avoidance_mask(avoidance_mask);
+ set_avoidance_priority(avoidance_priority);
+ set_use_3d_avoidance(use_3d_avoidance);
+ set_avoidance_enabled(avoidance_enabled);
+
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed));
#endif // DEBUG_ENABLED
@@ -274,9 +365,11 @@ void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
+ NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(agent, true);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
} else {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
+ NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(agent, false);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_callback(agent, Callable());
}
}
@@ -290,7 +383,7 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
}
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
- NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
+ NavigationServer3D::get_singleton()->agent_set_avoidance_callback(agent, Callable());
if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
@@ -303,7 +396,7 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) {
// create new avoidance callback if enabled
if (avoidance_enabled) {
- NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
+ NavigationServer3D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done));
}
} else {
agent_parent = nullptr;
@@ -408,31 +501,32 @@ void NavigationAgent3D::set_target_desired_distance(real_t p_target_desired_dist
}
void NavigationAgent3D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
if (Math::is_equal_approx(radius, p_radius)) {
return;
}
-
radius = p_radius;
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
}
-void NavigationAgent3D::set_agent_height_offset(real_t p_agent_height_offset) {
- if (Math::is_equal_approx(navigation_height_offset, p_agent_height_offset)) {
+void NavigationAgent3D::set_height(real_t p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
+ if (Math::is_equal_approx(height, p_height)) {
return;
}
-
- navigation_height_offset = p_agent_height_offset;
+ height = p_height;
+ NavigationServer3D::get_singleton()->agent_set_height(agent, height);
}
-void NavigationAgent3D::set_ignore_y(bool p_ignore_y) {
- if (ignore_y == p_ignore_y) {
- return;
- }
-
- ignore_y = p_ignore_y;
+void NavigationAgent3D::set_path_height_offset(real_t p_path_height_offset) {
+ path_height_offset = p_path_height_offset;
+}
- NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y);
+void NavigationAgent3D::set_use_3d_avoidance(bool p_use_3d_avoidance) {
+ use_3d_avoidance = p_use_3d_avoidance;
+ NavigationServer3D::get_singleton()->agent_set_use_3d_avoidance(agent, use_3d_avoidance);
+ notify_property_list_changed();
}
void NavigationAgent3D::set_neighbor_distance(real_t p_distance) {
@@ -455,21 +549,29 @@ void NavigationAgent3D::set_max_neighbors(int p_count) {
NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
-void NavigationAgent3D::set_time_horizon(real_t p_time) {
- if (Math::is_equal_approx(time_horizon, p_time)) {
+void NavigationAgent3D::set_time_horizon_agents(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
return;
}
+ time_horizon_agents = p_time_horizon;
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+}
- time_horizon = p_time;
-
- NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+void NavigationAgent3D::set_time_horizon_obstacles(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
+ return;
+ }
+ time_horizon_obstacles = p_time_horizon;
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent3D::set_max_speed(real_t p_max_speed) {
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
if (Math::is_equal_approx(max_speed, p_max_speed)) {
return;
}
-
max_speed = p_max_speed;
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed);
@@ -509,7 +611,7 @@ Vector3 NavigationAgent3D::get_next_path_position() {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
return agent_parent->get_global_position();
} else {
- return navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0);
+ return navigation_path[navigation_path_index] - Vector3(0, path_height_offset, 0);
}
}
@@ -541,28 +643,26 @@ Vector3 NavigationAgent3D::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
-void NavigationAgent3D::set_velocity(Vector3 p_velocity) {
+void NavigationAgent3D::set_velocity_forced(Vector3 p_velocity) {
// Intentionally not checking for equality of the parameter.
// We need to always submit the velocity to the navigation server, even when it is the same, in order to run avoidance every frame.
// Revisit later when the navigation server can update avoidance without users resubmitting the velocity.
- target_velocity = p_velocity;
- velocity_submitted = true;
+ velocity_forced = p_velocity;
+ velocity_forced_submitted = true;
+}
- NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, target_velocity);
- NavigationServer3D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+void NavigationAgent3D::set_velocity(const Vector3 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
}
void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) {
- prev_safe_velocity = p_new_velocity;
-
- if (!velocity_submitted) {
- target_velocity = Vector3();
- return;
+ safe_velocity = p_new_velocity;
+ if (!use_3d_avoidance) {
+ safe_velocity.y = stored_y_velocity;
}
- velocity_submitted = false;
-
- emit_signal(SNAME("velocity_computed"), p_new_velocity);
+ emit_signal(SNAME("velocity_computed"), safe_velocity);
}
PackedStringArray NavigationAgent3D::get_configuration_warnings() const {
@@ -585,9 +685,6 @@ void NavigationAgent3D::update_navigation() {
if (!target_position_submitted) {
return;
}
- if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
- return;
- }
update_frame_id = Engine::get_singleton()->get_physics_frames();
@@ -607,8 +704,8 @@ void NavigationAgent3D::update_navigation() {
Vector3 segment[2];
segment[0] = navigation_path[navigation_path_index - 1];
segment[1] = navigation_path[navigation_path_index];
- segment[0].y -= navigation_height_offset;
- segment[1].y -= navigation_height_offset;
+ segment[0].y -= path_height_offset;
+ segment[1].y -= path_height_offset;
Vector3 p = Geometry3D::get_closest_point_to_segment(origin, segment);
if (origin.distance_to(p) >= path_max_distance) {
// To faraway, reload path
@@ -650,7 +747,7 @@ void NavigationAgent3D::update_navigation() {
const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids();
const Vector<int64_t> &navigation_path_owners = navigation_result->get_path_owner_ids();
- while (origin.distance_to(navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) {
+ while (origin.distance_to(navigation_path[navigation_path_index] - Vector3(0, path_height_offset, 0)) < path_desired_distance) {
Dictionary details;
const Vector3 waypoint = navigation_path[navigation_path_index];
@@ -735,6 +832,71 @@ void NavigationAgent3D::_check_distance_to_target() {
}
}
+void NavigationAgent3D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer3D::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
+}
+
+uint32_t NavigationAgent3D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationAgent3D::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ NavigationServer3D::get_singleton()->agent_set_avoidance_mask(get_rid(), avoidance_mask);
+}
+
+uint32_t NavigationAgent3D::get_avoidance_mask() const {
+ return avoidance_mask;
+}
+
+void NavigationAgent3D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationAgent3D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationAgent3D::set_avoidance_mask_value(int p_mask_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_avoidance_mask();
+ if (p_value) {
+ mask |= 1 << (p_mask_number - 1);
+ } else {
+ mask &= ~(1 << (p_mask_number - 1));
+ }
+ set_avoidance_mask(mask);
+}
+
+bool NavigationAgent3D::get_avoidance_mask_value(int p_mask_number) const {
+ ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ return get_avoidance_mask() & (1 << (p_mask_number - 1));
+}
+
+void NavigationAgent3D::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ NavigationServer3D::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
+}
+
+real_t NavigationAgent3D::get_avoidance_priority() const {
+ return avoidance_priority;
+}
+
////////DEBUG////////////////////////////////////////////////////////////
void NavigationAgent3D::set_debug_enabled(bool p_enabled) {
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index 16d702f9a5..b74e816d43 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -47,6 +47,10 @@ class NavigationAgent3D : public Node {
RID map_override;
bool avoidance_enabled = false;
+ bool use_3d_avoidance = false;
+ uint32_t avoidance_layers = 1;
+ uint32_t avoidance_mask = 1;
+ real_t avoidance_priority = 1.0;
uint32_t navigation_layers = 1;
NavigationPathQueryParameters3D::PathfindingAlgorithm pathfinding_algorithm = NavigationPathQueryParameters3D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR;
NavigationPathQueryParameters3D::PathPostProcessing path_postprocessing = NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL;
@@ -54,24 +58,41 @@ class NavigationAgent3D : public Node {
real_t path_desired_distance = 1.0;
real_t target_desired_distance = 1.0;
+ real_t height = 1.0;
real_t radius = 0.5;
- real_t navigation_height_offset = 0.0;
- bool ignore_y = true;
+ real_t path_height_offset = 0.0;
real_t neighbor_distance = 50.0;
int max_neighbors = 10;
- real_t time_horizon = 1.0;
+ real_t time_horizon_agents = 1.0;
+ real_t time_horizon_obstacles = 0.0;
real_t max_speed = 10.0;
real_t path_max_distance = 5.0;
Vector3 target_position;
- bool target_position_submitted = false;
+
Ref<NavigationPathQueryParameters3D> navigation_query;
Ref<NavigationPathQueryResult3D> navigation_result;
int navigation_path_index = 0;
+
+ // the velocity result of the avoidance simulation step
+ Vector3 safe_velocity;
+
+ /// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
+ // this velocity is not guaranteed, the simulation will try to fulfil it if possible
+ // if other agents or obstacles interfere it will be changed accordingly
+ Vector3 velocity;
bool velocity_submitted = false;
- Vector3 prev_safe_velocity;
- /// The submitted target velocity
- Vector3 target_velocity;
+
+ /// The submitted forced velocity, overrides the rvo agent velocity on the next update
+ // should only be used very intentionally and not every frame as it interferes with the simulation stability
+ Vector3 velocity_forced;
+ bool velocity_forced_submitted = false;
+
+ // 2D avoidance has no y-axis. This stores and reapplies the y-axis velocity to the agent before and after the avoidance step.
+ // While not perfect it at least looks way better than agent's that clip through everything that is not a flat surface
+ float stored_y_velocity = 0.0;
+
+ bool target_position_submitted = false;
bool target_reached = false;
bool navigation_finished = true;
// No initialized on purpose
@@ -89,23 +110,22 @@ class NavigationAgent3D : public Node {
Ref<ArrayMesh> debug_path_mesh;
Ref<StandardMaterial3D> debug_agent_path_line_custom_material;
Ref<StandardMaterial3D> debug_agent_path_point_custom_material;
-
-private:
- void _navigation_debug_changed();
- void _update_debug_path();
#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
void _notification(int p_what);
+#ifndef DISABLE_DEPRECATED
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+#endif // DISABLE_DEPRECATED
+
public:
NavigationAgent3D();
virtual ~NavigationAgent3D();
- RID get_rid() const {
- return agent;
- }
+ RID get_rid() const { return agent; }
void set_avoidance_enabled(bool p_enabled);
bool get_avoidance_enabled() const;
@@ -137,49 +157,37 @@ public:
RID get_navigation_map() const;
void set_path_desired_distance(real_t p_dd);
- real_t get_path_desired_distance() const {
- return path_desired_distance;
- }
+ real_t get_path_desired_distance() const { return path_desired_distance; }
void set_target_desired_distance(real_t p_dd);
- real_t get_target_desired_distance() const {
- return target_desired_distance;
- }
+ real_t get_target_desired_distance() const { return target_desired_distance; }
void set_radius(real_t p_radius);
- real_t get_radius() const {
- return radius;
- }
+ real_t get_radius() const { return radius; }
- void set_agent_height_offset(real_t p_hh);
- real_t get_agent_height_offset() const {
- return navigation_height_offset;
- }
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
- void set_ignore_y(bool p_ignore_y);
- bool get_ignore_y() const {
- return ignore_y;
- }
+ void set_path_height_offset(real_t p_path_height_offset);
+ real_t get_path_height_offset() const { return path_height_offset; }
+
+ void set_use_3d_avoidance(bool p_use_3d_avoidance);
+ bool get_use_3d_avoidance() const { return use_3d_avoidance; }
void set_neighbor_distance(real_t p_distance);
- real_t get_neighbor_distance() const {
- return neighbor_distance;
- }
+ real_t get_neighbor_distance() const { return neighbor_distance; }
void set_max_neighbors(int p_count);
- int get_max_neighbors() const {
- return max_neighbors;
- }
+ int get_max_neighbors() const { return max_neighbors; }
- void set_time_horizon(real_t p_time);
- real_t get_time_horizon() const {
- return time_horizon;
- }
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
- real_t get_max_speed() const {
- return max_speed;
- }
+ real_t get_max_speed() const { return max_speed; }
void set_path_max_distance(real_t p_pmd);
real_t get_path_max_distance();
@@ -189,15 +197,11 @@ public:
Vector3 get_next_path_position();
- Ref<NavigationPathQueryResult3D> get_current_navigation_result() const {
- return navigation_result;
- }
- const Vector<Vector3> &get_current_navigation_path() const {
- return navigation_result->get_path();
- }
- int get_current_navigation_path_index() const {
- return navigation_path_index;
- }
+ Ref<NavigationPathQueryResult3D> get_current_navigation_result() const { return navigation_result; }
+
+ const Vector<Vector3> &get_current_navigation_path() const { return navigation_result->get_path(); }
+
+ int get_current_navigation_path_index() const { return navigation_path_index; }
real_t distance_to_target() const;
bool is_target_reached() const;
@@ -205,11 +209,30 @@ public:
bool is_navigation_finished();
Vector3 get_final_position();
- void set_velocity(Vector3 p_velocity);
+ void set_velocity(const Vector3 p_velocity);
+ Vector3 get_velocity() { return velocity; }
+
+ void set_velocity_forced(const Vector3 p_velocity);
+
void _avoidance_done(Vector3 p_new_velocity);
PackedStringArray get_configuration_warnings() const override;
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_avoidance_mask_value(int p_mask_number, bool p_value);
+ bool get_avoidance_mask_value(int p_mask_number) const;
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const;
+
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
@@ -226,6 +249,11 @@ private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
+
+#ifdef DEBUG_ENABLED
+ void _navigation_debug_changed();
+ void _update_debug_path();
+#endif // DEBUG_ENABLED
};
#endif // NAVIGATION_AGENT_3D_H
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index 9c4b8e7905..2263d38d6c 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -45,7 +45,7 @@ void NavigationLink3D::_update_debug_mesh() {
return;
}
- if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
@@ -141,6 +141,8 @@ void NavigationLink3D::_update_debug_mesh() {
} else {
RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid());
}
+
+ RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
}
#endif // DEBUG_ENABLED
@@ -215,29 +217,37 @@ void NavigationLink3D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled) {
NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
-
- // Update global positions for the link.
- Transform3D gt = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
}
+ current_global_transform = get_global_transform();
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
#ifdef DEBUG_ENABLED
_update_debug_mesh();
#endif // DEBUG_ENABLED
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
- // Update global positions for the link.
- Transform3D gt = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
+ set_physics_process_internal(true);
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ set_physics_process_internal(false);
+ if (is_inside_tree()) {
+ Transform3D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
- }
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
+ }
#endif // DEBUG_ENABLED
+ }
+ }
} break;
+
case NOTIFICATION_EXIT_TREE: {
NavigationServer3D::get_singleton()->link_set_map(link, RID());
@@ -359,8 +369,7 @@ void NavigationLink3D::set_start_position(Vector3 p_position) {
return;
}
- Transform3D gt = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
#ifdef DEBUG_ENABLED
_update_debug_mesh();
@@ -381,8 +390,7 @@ void NavigationLink3D::set_end_position(Vector3 p_position) {
return;
}
- Transform3D gt = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
#ifdef DEBUG_ENABLED
_update_debug_mesh();
diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h
index 991f45c85d..ec92fb9dd9 100644
--- a/scene/3d/navigation_link_3d.h
+++ b/scene/3d/navigation_link_3d.h
@@ -45,6 +45,8 @@ class NavigationLink3D : public Node3D {
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
+ Transform3D current_global_transform;
+
#ifdef DEBUG_ENABLED
RID debug_instance;
Ref<ArrayMesh> debug_mesh;
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 14d93fb0e0..b44ebf837a 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -30,230 +30,524 @@
#include "navigation_obstacle_3d.h"
+#include "core/math/geometry_2d.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "servers/navigation_server_3d.h"
void NavigationObstacle3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid);
+ ClassDB::bind_method(D_METHOD("get_obstacle_rid"), &NavigationObstacle3D::get_obstacle_rid);
+ ClassDB::bind_method(D_METHOD("get_agent_rid"), &NavigationObstacle3D::get_agent_rid);
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle3D::set_navigation_map);
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle3D::get_navigation_map);
- ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius);
- ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle3D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_radius", "get_radius");
-}
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NavigationObstacle3D::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &NavigationObstacle3D::get_height);
-void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "radius") {
- if (estimate_radius) {
- p_property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
- }
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle3D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle3D::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle3D::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle3D::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle3D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle3D::get_avoidance_layers);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle3D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle3D::get_avoidance_layer_value);
+
+ ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationObstacle3D::set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationObstacle3D::get_use_3d_avoidance);
+
+ ADD_GROUP("Avoidance", "avoidance_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices"), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
}
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POST_ENTER_TREE: {
- set_agent_parent(get_parent());
+ if (map_override.is_valid()) {
+ _update_map(map_override);
+ } else if (is_inside_tree()) {
+ _update_map(get_world_3d()->get_navigation_map());
+ } else {
+ _update_map(RID());
+ }
+ 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
+ NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+ _update_position(get_global_transform().origin);
set_physics_process_internal(true);
+#ifdef DEBUG_ENABLED
+ if ((NavigationServer3D::get_singleton()->get_debug_avoidance_enabled()) &&
+ (NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius())) {
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_EXIT_TREE: {
- set_agent_parent(nullptr);
set_physics_process_internal(false);
- } break;
-
- case NOTIFICATION_PARENTED: {
- if (is_inside_tree() && (get_parent() != parent_node3d)) {
- set_agent_parent(get_parent());
- set_physics_process_internal(true);
+ _update_map(RID());
+#ifdef DEBUG_ENABLED
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
}
- } break;
-
- case NOTIFICATION_UNPARENTED: {
- set_agent_parent(nullptr);
- set_physics_process_internal(false);
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_PAUSED: {
- if (parent_node3d && !parent_node3d->can_process()) {
- map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid());
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) {
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_UNPAUSED: {
- if (parent_node3d && !parent_node3d->can_process()) {
- map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid());
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) {
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node3d && parent_node3d->is_inside_tree()) {
- NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin);
-
- PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent());
- if (rigid) {
- Vector3 v = rigid->get_linear_velocity();
- NavigationServer3D::get_singleton()->agent_set_velocity(agent, v);
- NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, v);
+ if (is_inside_tree()) {
+ _update_position(get_global_transform().origin);
+
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ // only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
+ if (!previous_velocity.is_equal_approx(velocity)) {
+ NavigationServer3D::get_singleton()->agent_set_velocity(fake_agent, velocity);
+ }
+ previous_velocity = velocity;
+ }
+#ifdef DEBUG_ENABLED
+ if (fake_agent_radius_debug_instance.is_valid() && radius > 0.0) {
+ RS::get_singleton()->instance_set_transform(fake_agent_radius_debug_instance, get_global_transform());
}
+ if (static_obstacle_debug_instance.is_valid() && get_vertices().size() > 0) {
+ RS::get_singleton()->instance_set_transform(static_obstacle_debug_instance, get_global_transform());
+ }
+#endif // DEBUG_ENABLED
}
} break;
}
}
NavigationObstacle3D::NavigationObstacle3D() {
- agent = NavigationServer3D::get_singleton()->agent_create();
- initialize_agent();
+ obstacle = NavigationServer3D::get_singleton()->obstacle_create();
+ fake_agent = NavigationServer3D::get_singleton()->agent_create();
+
+ // change properties of the fake agent so it can act as fake obstacle with a radius
+ NavigationServer3D::get_singleton()->agent_set_neighbor_distance(fake_agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_max_neighbors(fake_agent, 0);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_agents(fake_agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_time_horizon_obstacles(fake_agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_max_speed(fake_agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_mask(fake_agent, 1);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_priority(fake_agent, 1.0);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+
+ set_radius(radius);
+ set_height(height);
+ set_vertices(vertices);
+ set_avoidance_layers(avoidance_layers);
+ set_use_3d_avoidance(use_3d_avoidance);
+
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
+ NavigationServer3D::get_singleton()->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
}
NavigationObstacle3D::~NavigationObstacle3D() {
ERR_FAIL_NULL(NavigationServer3D::get_singleton());
- NavigationServer3D::get_singleton()->free(agent);
- agent = RID(); // Pointless
-}
-PackedStringArray NavigationObstacle3D::get_configuration_warnings() const {
- PackedStringArray warnings = Node::get_configuration_warnings();
+ NavigationServer3D::get_singleton()->free(obstacle);
+ obstacle = RID();
+
+ NavigationServer3D::get_singleton()->free(fake_agent);
+ fake_agent = RID();
- if (!Object::cast_to<Node3D>(get_parent())) {
- warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object."));
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug));
+ NavigationServer3D::get_singleton()->disconnect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_static_obstacle_debug));
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(fake_agent_radius_debug_instance);
+ }
+ if (fake_agent_radius_debug_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(fake_agent_radius_debug_mesh->get_rid());
}
- if (Object::cast_to<StaticBody3D>(get_parent())) {
- warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
- "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail"));
+ if (static_obstacle_debug_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(static_obstacle_debug_instance);
+ }
+ if (static_obstacle_debug_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(static_obstacle_debug_mesh->get_rid());
}
+#endif // DEBUG_ENABLED
+}
- return warnings;
+void NavigationObstacle3D::set_vertices(const Vector<Vector3> &p_vertices) {
+ vertices = p_vertices;
+ NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
+#ifdef DEBUG_ENABLED
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
}
-void NavigationObstacle3D::initialize_agent() {
- NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, 0.0);
- NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0);
- NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0);
- NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0);
+void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+ map_override = p_navigation_map;
+ _update_map(map_override);
}
-void NavigationObstacle3D::reevaluate_agent_radius() {
- if (!estimate_radius) {
- NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node3d && parent_node3d->is_inside_tree()) {
- NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+RID NavigationObstacle3D::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();
}
-real_t NavigationObstacle3D::estimate_agent_radius() const {
- if (parent_node3d && parent_node3d->is_inside_tree()) {
- // Estimate the radius of this physics body
- real_t max_radius = 0.0;
- for (int i(0); i < parent_node3d->get_child_count(); i++) {
- // For each collision shape
- CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i));
- if (cs && cs->is_inside_tree()) {
- // Take the distance between the Body center to the shape center
- real_t r = cs->get_transform().origin.length();
- if (cs->get_shape().is_valid()) {
- // and add the enclosing shape radius
- r += cs->get_shape()->get_enclosing_radius();
- }
- Vector3 s = cs->get_global_transform().basis.get_scale();
- r *= MAX(s.x, MAX(s.y, s.z));
- // Takes the biggest radius
- max_radius = MAX(max_radius, r);
- } else if (cs && !cs->is_inside_tree()) {
- WARN_PRINT("A CollisionShape3D of the NavigationObstacle3D parent node was not inside the SceneTree when estimating the obstacle radius."
- "\nMove the NavigationObstacle3D to a child position below any CollisionShape3D node of the parent node so the CollisionShape3D is already inside the SceneTree.");
- }
- }
+void NavigationObstacle3D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
- Vector3 s = parent_node3d->get_global_transform().basis.get_scale();
- max_radius *= MAX(s.x, MAX(s.y, s.z));
+ radius = p_radius;
+ NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0.0);
+ NavigationServer3D::get_singleton()->agent_set_radius(fake_agent, radius);
- if (max_radius > 0.0) {
- return max_radius;
- }
- }
- return 1.0; // Never a 0 radius
+#ifdef DEBUG_ENABLED
+ _update_fake_agent_radius_debug();
+#endif // DEBUG_ENABLED
}
-void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) {
- if (parent_node3d == p_agent_parent) {
+void NavigationObstacle3D::set_height(real_t p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
+ if (Math::is_equal_approx(height, p_height)) {
return;
}
- if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
- parent_node3d = Object::cast_to<Node3D>(p_agent_parent);
- if (map_override.is_valid()) {
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_override);
- } else {
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
- }
- // Need to register Callback as obstacle requires a valid Callback to be added to avoidance simulation.
- NavigationServer3D::get_singleton()->agent_set_callback(get_rid(), callable_mp(this, &NavigationObstacle3D::_avoidance_done));
- reevaluate_agent_radius();
+ height = p_height;
+ NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, height);
+ NavigationServer3D::get_singleton()->agent_set_height(fake_agent, height);
+#ifdef DEBUG_ENABLED
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
+}
+
+void NavigationObstacle3D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer3D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
+ NavigationServer3D::get_singleton()->agent_set_avoidance_layers(fake_agent, avoidance_layers);
+}
+
+uint32_t NavigationObstacle3D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationObstacle3D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
} else {
- parent_node3d = nullptr;
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
- NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable());
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
+ set_avoidance_layers(avoidance_layers_new);
}
-void NavigationObstacle3D::_avoidance_done(Vector3 p_new_velocity) {
- // Dummy function as obstacle requires a valid Callback to be added to avoidance simulation.
+bool NavigationObstacle3D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
-void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) {
- if (map_override == p_navigation_map) {
- return;
- }
+void NavigationObstacle3D::set_use_3d_avoidance(bool p_use_3d_avoidance) {
+ use_3d_avoidance = p_use_3d_avoidance;
+ _update_use_3d_avoidance(use_3d_avoidance);
+ notify_property_list_changed();
+}
- map_override = p_navigation_map;
+void NavigationObstacle3D::set_velocity(const Vector3 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
+}
- NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
+void NavigationObstacle3D::_update_map(RID p_map) {
+ // avoidance obstacles are 2D only, no point keeping them on the map while 3D is used
+ if (use_3d_avoidance) {
+ NavigationServer3D::get_singleton()->obstacle_set_map(obstacle, RID());
+ } else {
+ NavigationServer3D::get_singleton()->obstacle_set_map(obstacle, p_map);
+ }
+ NavigationServer3D::get_singleton()->agent_set_map(fake_agent, p_map);
+ map_current = p_map;
}
-RID NavigationObstacle3D::get_navigation_map() const {
- if (map_override.is_valid()) {
- return map_override;
- } else if (parent_node3d != nullptr) {
- return parent_node3d->get_world_3d()->get_navigation_map();
+void NavigationObstacle3D::_update_position(const Vector3 p_position) {
+ if (vertices.size() > 0) {
+ NavigationServer3D::get_singleton()->obstacle_set_position(obstacle, p_position);
}
- return RID();
+ if (radius > 0.0) {
+ NavigationServer3D::get_singleton()->agent_set_position(fake_agent, p_position);
+ }
+}
+
+void NavigationObstacle3D::_update_use_3d_avoidance(bool p_use_3d_avoidance) {
+ NavigationServer3D::get_singleton()->agent_set_use_3d_avoidance(fake_agent, use_3d_avoidance);
+ _update_map(map_current);
}
-void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
- if (estimate_radius == p_estimate_radius) {
+#ifdef DEBUG_ENABLED
+void NavigationObstacle3D::_update_fake_agent_radius_debug() {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (NavigationServer3D::get_singleton()->get_debug_enabled() &&
+ NavigationServer3D::get_singleton()->get_debug_avoidance_enabled() &&
+ NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
+ is_debug_enabled = true;
+ }
+
+ if (is_debug_enabled == false) {
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
+ }
return;
}
- estimate_radius = p_estimate_radius;
+ if (!fake_agent_radius_debug_instance.is_valid()) {
+ fake_agent_radius_debug_instance = RenderingServer::get_singleton()->instance_create();
+ }
+ if (!fake_agent_radius_debug_mesh.is_valid()) {
+ fake_agent_radius_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+ fake_agent_radius_debug_mesh->clear_surfaces();
- notify_property_list_changed();
- reevaluate_agent_radius();
+ Vector<Vector3> face_vertex_array;
+ Vector<int> face_indices_array;
+
+ int i, j, prevrow, thisrow, point;
+ float x, y, z;
+
+ int rings = 16;
+ int radial_segments = 32;
+
+ point = 0;
+
+ thisrow = 0;
+ prevrow = 0;
+ for (j = 0; j <= (rings + 1); j++) {
+ float v = j;
+ float w;
+
+ v /= (rings + 1);
+ w = sin(Math_PI * v);
+ y = (radius)*cos(Math_PI * v);
+
+ for (i = 0; i <= radial_segments; i++) {
+ float u = i;
+ u /= radial_segments;
+
+ x = sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
+
+ Vector3 p = Vector3(x * radius * w, y, z * radius * w);
+ face_vertex_array.push_back(p);
+
+ point++;
+
+ if (i > 0 && j > 0) {
+ face_indices_array.push_back(prevrow + i - 1);
+ face_indices_array.push_back(prevrow + i);
+ face_indices_array.push_back(thisrow + i - 1);
+
+ face_indices_array.push_back(prevrow + i);
+ face_indices_array.push_back(thisrow + i);
+ face_indices_array.push_back(thisrow + i - 1);
+ };
+ };
+
+ prevrow = thisrow;
+ thisrow = point;
+ };
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
+
+ fake_agent_radius_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+ Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_material();
+ fake_agent_radius_debug_mesh->surface_set_material(0, face_material);
+
+ RS::get_singleton()->instance_set_base(fake_agent_radius_debug_instance, fake_agent_radius_debug_mesh->get_rid());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(fake_agent_radius_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, is_visible_in_tree());
+ }
}
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationObstacle3D::_update_static_obstacle_debug() {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (NavigationServer3D::get_singleton()->get_debug_enabled() &&
+ NavigationServer3D::get_singleton()->get_debug_avoidance_enabled() &&
+ NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
+ is_debug_enabled = true;
+ }
-void NavigationObstacle3D::set_radius(real_t p_radius) {
- ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
- if (Math::is_equal_approx(radius, p_radius)) {
+ if (is_debug_enabled == false) {
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
return;
}
- radius = p_radius;
+ if (vertices.size() < 3) {
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
+ return;
+ }
+
+ if (!static_obstacle_debug_instance.is_valid()) {
+ static_obstacle_debug_instance = RenderingServer::get_singleton()->instance_create();
+ }
+ if (!static_obstacle_debug_mesh.is_valid()) {
+ static_obstacle_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+ static_obstacle_debug_mesh->clear_surfaces();
+
+ Vector<Vector2> polygon_2d_vertices;
+ polygon_2d_vertices.resize(vertices.size());
+ Vector2 *polygon_2d_vertices_ptr = polygon_2d_vertices.ptrw();
- reevaluate_agent_radius();
+ for (int i = 0; i < vertices.size(); ++i) {
+ Vector3 obstacle_vertex = vertices[i];
+ Vector2 obstacle_vertex_2d = Vector2(obstacle_vertex.x, obstacle_vertex.z);
+ polygon_2d_vertices_ptr[i] = obstacle_vertex_2d;
+ }
+
+ Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(polygon_2d_vertices);
+
+ if (triangulated_polygon_2d_indices.is_empty()) {
+ // failed triangulation
+ return;
+ }
+
+ bool obstacle_pushes_inward = Geometry2D::is_polygon_clockwise(polygon_2d_vertices);
+
+ Vector<Vector3> face_vertex_array;
+ Vector<int> face_indices_array;
+
+ face_vertex_array.resize(polygon_2d_vertices.size());
+ face_indices_array.resize(triangulated_polygon_2d_indices.size());
+
+ Vector3 *face_vertex_array_ptr = face_vertex_array.ptrw();
+ int *face_indices_array_ptr = face_indices_array.ptrw();
+
+ for (int i = 0; i < triangulated_polygon_2d_indices.size(); ++i) {
+ int vertex_index = triangulated_polygon_2d_indices[i];
+ const Vector2 &vertex_2d = polygon_2d_vertices[vertex_index];
+ Vector3 vertex_3d = Vector3(vertex_2d.x, 0.0, vertex_2d.y);
+ face_vertex_array_ptr[vertex_index] = vertex_3d;
+ face_indices_array_ptr[i] = vertex_index;
+ }
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
+
+ static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+
+ Vector<Vector3> edge_vertex_array;
+
+ for (int i = 0; i < polygon_2d_vertices.size(); ++i) {
+ int from_index = i - 1;
+ int to_index = i;
+
+ if (i == 0) {
+ from_index = polygon_2d_vertices.size() - 1;
+ }
+
+ const Vector2 &vertex_2d_from = polygon_2d_vertices[from_index];
+ const Vector2 &vertex_2d_to = polygon_2d_vertices[to_index];
+
+ Vector3 vertex_3d_ground_from = Vector3(vertex_2d_from.x, 0.0, vertex_2d_from.y);
+ Vector3 vertex_3d_ground_to = Vector3(vertex_2d_to.x, 0.0, vertex_2d_to.y);
+
+ edge_vertex_array.push_back(vertex_3d_ground_from);
+ edge_vertex_array.push_back(vertex_3d_ground_to);
+
+ Vector3 vertex_3d_height_from = Vector3(vertex_2d_from.x, height, vertex_2d_from.y);
+ Vector3 vertex_3d_height_to = Vector3(vertex_2d_to.x, height, vertex_2d_to.y);
+
+ edge_vertex_array.push_back(vertex_3d_height_from);
+ edge_vertex_array.push_back(vertex_3d_height_to);
+
+ edge_vertex_array.push_back(vertex_3d_ground_from);
+ edge_vertex_array.push_back(vertex_3d_height_from);
+ }
+
+ Array edge_mesh_array;
+ edge_mesh_array.resize(Mesh::ARRAY_MAX);
+ edge_mesh_array[Mesh::ARRAY_VERTEX] = edge_vertex_array;
+
+ static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, edge_mesh_array);
+
+ Ref<StandardMaterial3D> face_material;
+ Ref<StandardMaterial3D> edge_material;
+
+ if (obstacle_pushes_inward) {
+ face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
+ edge_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
+ } else {
+ face_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
+ edge_material = NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
+ }
+
+ static_obstacle_debug_mesh->surface_set_material(0, face_material);
+ static_obstacle_debug_mesh->surface_set_material(1, edge_material);
+
+ RS::get_singleton()->instance_set_base(static_obstacle_debug_instance, static_obstacle_debug_mesh->get_rid());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(static_obstacle_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, is_visible_in_tree());
+ }
}
+#endif // DEBUG_ENABLED
diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h
index c5a9d737f6..603d22cf5e 100644
--- a/scene/3d/navigation_obstacle_3d.h
+++ b/scene/3d/navigation_obstacle_3d.h
@@ -33,53 +33,83 @@
#include "scene/3d/node_3d.h"
-class NavigationObstacle3D : public Node {
- GDCLASS(NavigationObstacle3D, Node);
+class NavigationObstacle3D : public Node3D {
+ GDCLASS(NavigationObstacle3D, Node3D);
- Node3D *parent_node3d = nullptr;
-
- RID agent;
+ RID obstacle;
RID map_before_pause;
RID map_override;
+ RID map_current;
+
+ real_t height = 1.0;
+ real_t radius = 0.0;
+
+ Vector<Vector3> vertices;
+
+ RID fake_agent;
+ uint32_t avoidance_layers = 1;
+
+ bool use_3d_avoidance = false;
+
+ Transform3D previous_transform;
+
+ Vector3 velocity;
+ Vector3 previous_velocity;
+ bool velocity_submitted = false;
+
+#ifdef DEBUG_ENABLED
+ RID fake_agent_radius_debug_instance;
+ Ref<ArrayMesh> fake_agent_radius_debug_mesh;
- bool estimate_radius = true;
- real_t radius = 1.0;
+ RID static_obstacle_debug_instance;
+ Ref<ArrayMesh> static_obstacle_debug_mesh;
+
+private:
+ void _update_fake_agent_radius_debug();
+ void _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
public:
NavigationObstacle3D();
virtual ~NavigationObstacle3D();
- RID get_rid() const {
- return agent;
- }
-
- void set_agent_parent(Node *p_agent_parent);
+ RID get_obstacle_rid() const { return obstacle; }
+ RID get_agent_rid() const { return fake_agent; }
void set_navigation_map(RID p_navigation_map);
RID get_navigation_map() const;
- void set_estimate_radius(bool p_estimate_radius);
- bool is_radius_estimated() const {
- return estimate_radius;
- }
void set_radius(real_t p_radius);
- real_t get_radius() const {
- return radius;
- }
+ real_t get_radius() const { return radius; }
+
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_vertices(const Vector<Vector3> &p_vertices);
+ const Vector<Vector3> &get_vertices() const { return vertices; };
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_use_3d_avoidance(bool p_use_3d_avoidance);
+ bool get_use_3d_avoidance() const { return use_3d_avoidance; }
- PackedStringArray get_configuration_warnings() const override;
+ void set_velocity(const Vector3 p_velocity);
+ Vector3 get_velocity() const { return velocity; };
void _avoidance_done(Vector3 p_new_velocity); // Dummy
private:
- void initialize_agent();
- void reevaluate_agent_radius();
- real_t estimate_agent_radius() const;
+ void _update_map(RID p_map);
+ void _update_position(const Vector3 p_position);
+ void _update_use_3d_avoidance(bool p_use_3d_avoidance);
};
#endif // NAVIGATION_OBSTACLE_3D_H
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 6af8999b89..165d44436c 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -81,6 +81,20 @@ bool NavigationRegion3D::is_enabled() const {
return enabled;
}
+void NavigationRegion3D::set_use_edge_connections(bool p_enabled) {
+ if (use_edge_connections == p_enabled) {
+ return;
+ }
+
+ use_edge_connections = p_enabled;
+
+ NavigationServer3D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections);
+}
+
+bool NavigationRegion3D::get_use_edge_connections() const {
+ return use_edge_connections;
+}
+
void NavigationRegion3D::set_navigation_layers(uint32_t p_navigation_layers) {
if (navigation_layers == p_navigation_layers) {
return;
@@ -157,9 +171,11 @@ void NavigationRegion3D::_notification(int p_what) {
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_enabled()) {
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
_update_debug_mesh();
}
#endif // DEBUG_ENABLED
@@ -167,14 +183,24 @@ void NavigationRegion3D::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform());
+ set_physics_process_internal(true);
+
+ } break;
+ 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 (is_inside_tree() && debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
- }
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
+ }
#endif // DEBUG_ENABLED
-
+ }
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -210,7 +236,7 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_naviga
NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh);
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (navigation_mesh.is_valid()) {
_update_debug_mesh();
_update_debug_edge_connections_mesh();
@@ -270,7 +296,9 @@ void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
set_navigation_mesh(p_nav_mesh);
- bake_thread.wait_to_finish();
+ if (bake_thread.is_started()) {
+ bake_thread.wait_to_finish();
+ }
emit_signal(SNAME("bake_finished"));
}
@@ -293,6 +321,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_use_edge_connections", "enabled"), &NavigationRegion3D::set_use_edge_connections);
+ ClassDB::bind_method(D_METHOD("get_use_edge_connections"), &NavigationRegion3D::get_use_edge_connections);
+
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationRegion3D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationRegion3D::get_navigation_layers);
@@ -312,6 +343,7 @@ void NavigationRegion3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_mesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
@@ -372,6 +404,10 @@ NavigationRegion3D::NavigationRegion3D() {
}
NavigationRegion3D::~NavigationRegion3D() {
+ if (bake_thread.is_started()) {
+ bake_thread.wait_to_finish();
+ }
+
if (navigation_mesh.is_valid()) {
navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed));
}
@@ -407,7 +443,7 @@ void NavigationRegion3D::_update_debug_mesh() {
return;
}
- if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
@@ -546,6 +582,7 @@ void NavigationRegion3D::_update_debug_mesh() {
RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid());
if (is_inside_tree()) {
RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree());
}
if (!is_enabled()) {
@@ -572,7 +609,7 @@ void NavigationRegion3D::_update_debug_mesh() {
#ifdef DEBUG_ENABLED
void NavigationRegion3D::_update_debug_edge_connections_mesh() {
- if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
@@ -583,6 +620,13 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
return;
}
+ if (!use_edge_connections || !NavigationServer3D::get_singleton()->map_get_use_edge_connections(get_world_3d()->get_navigation_map())) {
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
+ return;
+ }
+
if (!navigation_mesh.is_valid()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index d4045ae1ac..fd6deebd63 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -38,12 +38,16 @@ class NavigationRegion3D : public Node3D {
GDCLASS(NavigationRegion3D, Node3D);
bool enabled = true;
+ bool use_edge_connections = true;
+
RID region;
uint32_t navigation_layers = 1;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
Ref<NavigationMesh> navigation_mesh;
+ Transform3D current_global_transform;
+
Thread bake_thread;
void _navigation_changed();
@@ -73,6 +77,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
+ void set_use_edge_connections(bool p_enabled);
+ bool get_use_edge_connections() const;
+
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 04ac01a085..f02826b1c4 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -40,11 +40,12 @@ Path3D::Path3D() {
}
Path3D::~Path3D() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_instance.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(debug_instance);
}
if (debug_mesh.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(debug_mesh->get_rid());
}
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index b7d63258db..4be695d189 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -599,9 +599,7 @@ void RigidBody3D::_notification(int p_what) {
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- }
+ update_configuration_warnings();
} break;
}
#endif
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 1b46879079..61e61ead5e 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -226,6 +226,11 @@ void Skeleton3D::_update_process_order() {
void Skeleton3D::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (dirty) {
+ notification(NOTIFICATION_UPDATE_SKELETON);
+ }
+ } break;
case NOTIFICATION_UPDATE_SKELETON: {
RenderingServer *rs = RenderingServer::get_singleton();
Bone *bonesptr = bones.ptrw();
@@ -629,7 +634,9 @@ void Skeleton3D::_make_dirty() {
return;
}
- MessageQueue::get_singleton()->push_notification(this, NOTIFICATION_UPDATE_SKELETON);
+ if (is_inside_tree()) {
+ notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON);
+ }
dirty = true;
}
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 59e4a0b718..9f7ff05924 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1095,13 +1095,13 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
frames->get_animation_list(&al);
if (al.size() == 0) {
set_animation(StringName());
- set_autoplay(String());
+ autoplay = String();
} else {
if (!frames->has_animation(animation)) {
set_animation(al[0]);
}
if (!frames->has_animation(autoplay)) {
- set_autoplay(String());
+ autoplay = String();
}
}
}
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 09eb2beba2..5936fbc2c0 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -232,19 +232,20 @@ AnimationNodeSync::AnimationNodeSync() {
}
////////////////////////////////////////////////////////
-
void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
- r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort"));
+ r_list->push_back(PropertyInfo(Variant::BOOL, internal_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
+ r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort,Fade Out"));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, fade_out_remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
if (p_parameter == request) {
return ONE_SHOT_REQUEST_NONE;
- } else if (p_parameter == active) {
+ } else if (p_parameter == active || p_parameter == internal_active) {
return false;
} else if (p_parameter == time_to_restart) {
return -1;
@@ -254,50 +255,66 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
}
bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
- if (p_parameter == active) {
+ if (p_parameter == active || p_parameter == internal_active) {
return true;
}
return false;
}
-void AnimationNodeOneShot::set_fadein_time(double p_time) {
+void AnimationNodeOneShot::set_fade_in_time(double p_time) {
fade_in = p_time;
}
-void AnimationNodeOneShot::set_fadeout_time(double p_time) {
- fade_out = p_time;
+double AnimationNodeOneShot::get_fade_in_time() const {
+ return fade_in;
}
-double AnimationNodeOneShot::get_fadein_time() const {
- return fade_in;
+void AnimationNodeOneShot::set_fade_out_time(double p_time) {
+ fade_out = p_time;
}
-double AnimationNodeOneShot::get_fadeout_time() const {
+double AnimationNodeOneShot::get_fade_out_time() const {
return fade_out;
}
-void AnimationNodeOneShot::set_autorestart(bool p_active) {
- autorestart = p_active;
+void AnimationNodeOneShot::set_fade_in_curve(const Ref<Curve> &p_curve) {
+ fade_in_curve = p_curve;
+}
+
+Ref<Curve> AnimationNodeOneShot::get_fade_in_curve() const {
+ return fade_in_curve;
+}
+
+void AnimationNodeOneShot::set_fade_out_curve(const Ref<Curve> &p_curve) {
+ fade_out_curve = p_curve;
+}
+
+Ref<Curve> AnimationNodeOneShot::get_fade_out_curve() const {
+ return fade_out_curve;
}
-void AnimationNodeOneShot::set_autorestart_delay(double p_time) {
- autorestart_delay = p_time;
+void AnimationNodeOneShot::set_auto_restart_enabled(bool p_enabled) {
+ auto_restart = p_enabled;
}
-void AnimationNodeOneShot::set_autorestart_random_delay(double p_time) {
- autorestart_random_delay = p_time;
+void AnimationNodeOneShot::set_auto_restart_delay(double p_time) {
+ auto_restart_delay = p_time;
}
-bool AnimationNodeOneShot::has_autorestart() const {
- return autorestart;
+void AnimationNodeOneShot::set_auto_restart_random_delay(double p_time) {
+ auto_restart_random_delay = p_time;
}
-double AnimationNodeOneShot::get_autorestart_delay() const {
- return autorestart_delay;
+bool AnimationNodeOneShot::is_auto_restart_enabled() const {
+ return auto_restart;
}
-double AnimationNodeOneShot::get_autorestart_random_delay() const {
- return autorestart_random_delay;
+double AnimationNodeOneShot::get_auto_restart_delay() const {
+ return auto_restart_delay;
+}
+
+double AnimationNodeOneShot::get_auto_restart_random_delay() const {
+ return auto_restart_random_delay;
}
void AnimationNodeOneShot::set_mix_mode(MixMode p_mix) {
@@ -319,17 +336,39 @@ bool AnimationNodeOneShot::has_filter() const {
double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_external_seeking, bool p_test_only) {
OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
bool cur_active = get_parameter(active);
+ bool cur_internal_active = get_parameter(internal_active);
double cur_time = get_parameter(time);
double cur_remaining = get_parameter(remaining);
+ double cur_fade_out_remaining = get_parameter(fade_out_remaining);
double cur_time_to_restart = get_parameter(time_to_restart);
set_parameter(request, ONE_SHOT_REQUEST_NONE);
+ bool is_shooting = true;
+ bool clear_remaining_fade = false;
+ bool is_fading_out = cur_active == true && cur_internal_active == false;
+
+ if (p_time == 0 && p_seek && !p_is_external_seeking) {
+ clear_remaining_fade = true; // Reset occurs.
+ }
+
bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
if (cur_request == ONE_SHOT_REQUEST_ABORT) {
+ set_parameter(internal_active, false);
set_parameter(active, false);
set_parameter(time_to_restart, -1);
- return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
+ is_shooting = false;
+ } else if (cur_request == ONE_SHOT_REQUEST_FADE_OUT && !is_fading_out) { // If fading, keep current fade.
+ if (cur_active) {
+ // Request fading.
+ is_fading_out = true;
+ cur_fade_out_remaining = fade_out;
+ } else {
+ // Shot is ended, do nothing.
+ is_shooting = false;
+ }
+ set_parameter(internal_active, false);
+ set_parameter(time_to_restart, -1);
} else if (!do_start && !cur_active) {
if (cur_time_to_restart >= 0.0 && !p_seek) {
cur_time_to_restart -= p_time;
@@ -339,19 +378,32 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
set_parameter(time_to_restart, cur_time_to_restart);
}
if (!do_start) {
- return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
+ is_shooting = false;
}
}
bool os_seek = p_seek;
- if (p_seek) {
- cur_time = p_time;
+
+ if (clear_remaining_fade) {
+ os_seek = false;
+ cur_fade_out_remaining = 0;
+ set_parameter(fade_out_remaining, 0);
+ if (is_fading_out) {
+ is_fading_out = false;
+ set_parameter(internal_active, false);
+ set_parameter(active, false);
+ }
+ }
+
+ if (!is_shooting) {
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync, p_test_only);
}
if (do_start) {
cur_time = 0;
os_seek = true;
set_parameter(request, ONE_SHOT_REQUEST_NONE);
+ set_parameter(internal_active, true);
set_parameter(active, true);
}
@@ -361,13 +413,25 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
if (fade_in > 0) {
use_blend = true;
blend = cur_time / fade_in;
+ if (fade_in_curve.is_valid()) {
+ blend = fade_in_curve->sample(blend);
+ }
} else {
blend = 0; // Should not happen.
}
- } else if (!do_start && cur_remaining <= fade_out) {
+ } else if (!do_start && !is_fading_out && cur_remaining <= fade_out) {
+ is_fading_out = true;
+ cur_fade_out_remaining = cur_remaining;
+ set_parameter(internal_active, false);
+ }
+
+ if (is_fading_out) {
use_blend = true;
if (fade_out > 0) {
- blend = (cur_remaining / fade_out);
+ blend = cur_fade_out_remaining / fade_out;
+ if (fade_out_curve.is_valid()) {
+ blend = 1.0 - fade_out_curve->sample(1.0 - blend);
+ }
} else {
blend = 0;
}
@@ -385,13 +449,17 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
cur_remaining = os_rem;
}
- if (!p_seek) {
+ if (p_seek) {
+ cur_time = p_time;
+ } else {
cur_time += p_time;
cur_remaining = os_rem;
- if (cur_remaining <= 0) {
+ cur_fade_out_remaining -= p_time;
+ if (cur_remaining <= 0 || (is_fading_out && cur_fade_out_remaining <= 0)) {
+ set_parameter(internal_active, false);
set_parameter(active, false);
- if (autorestart) {
- double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay;
+ if (auto_restart) {
+ double restart_sec = auto_restart_delay + Math::randd() * auto_restart_random_delay;
set_parameter(time_to_restart, restart_sec);
}
}
@@ -399,25 +467,32 @@ double AnimationNodeOneShot::_process(double p_time, bool p_seek, bool p_is_exte
set_parameter(time, cur_time);
set_parameter(remaining, cur_remaining);
+ set_parameter(fade_out_remaining, cur_fade_out_remaining);
return MAX(main_rem, cur_remaining);
}
void AnimationNodeOneShot::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fadein_time);
- ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fadein_time);
+ ClassDB::bind_method(D_METHOD("set_fadein_time", "time"), &AnimationNodeOneShot::set_fade_in_time);
+ ClassDB::bind_method(D_METHOD("get_fadein_time"), &AnimationNodeOneShot::get_fade_in_time);
+
+ ClassDB::bind_method(D_METHOD("set_fadein_curve", "curve"), &AnimationNodeOneShot::set_fade_in_curve);
+ ClassDB::bind_method(D_METHOD("get_fadein_curve"), &AnimationNodeOneShot::get_fade_in_curve);
+
+ ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fade_out_time);
+ ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fade_out_time);
- ClassDB::bind_method(D_METHOD("set_fadeout_time", "time"), &AnimationNodeOneShot::set_fadeout_time);
- ClassDB::bind_method(D_METHOD("get_fadeout_time"), &AnimationNodeOneShot::get_fadeout_time);
+ ClassDB::bind_method(D_METHOD("set_fadeout_curve", "curve"), &AnimationNodeOneShot::set_fade_out_curve);
+ ClassDB::bind_method(D_METHOD("get_fadeout_curve"), &AnimationNodeOneShot::get_fade_out_curve);
- ClassDB::bind_method(D_METHOD("set_autorestart", "enable"), &AnimationNodeOneShot::set_autorestart);
- ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::has_autorestart);
+ ClassDB::bind_method(D_METHOD("set_autorestart", "active"), &AnimationNodeOneShot::set_auto_restart_enabled);
+ ClassDB::bind_method(D_METHOD("has_autorestart"), &AnimationNodeOneShot::is_auto_restart_enabled);
- ClassDB::bind_method(D_METHOD("set_autorestart_delay", "enable"), &AnimationNodeOneShot::set_autorestart_delay);
- ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_autorestart_delay);
+ ClassDB::bind_method(D_METHOD("set_autorestart_delay", "time"), &AnimationNodeOneShot::set_auto_restart_delay);
+ ClassDB::bind_method(D_METHOD("get_autorestart_delay"), &AnimationNodeOneShot::get_auto_restart_delay);
- ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "enable"), &AnimationNodeOneShot::set_autorestart_random_delay);
- ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_autorestart_random_delay);
+ ClassDB::bind_method(D_METHOD("set_autorestart_random_delay", "time"), &AnimationNodeOneShot::set_auto_restart_random_delay);
+ ClassDB::bind_method(D_METHOD("get_autorestart_random_delay"), &AnimationNodeOneShot::get_auto_restart_random_delay);
ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode);
ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode);
@@ -425,7 +500,9 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadein_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadein_curve", "get_fadein_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fadeout_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_fadeout_curve", "get_fadeout_curve");
ADD_GROUP("Auto Restart", "autorestart_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
@@ -436,6 +513,7 @@ void AnimationNodeOneShot::_bind_methods() {
BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE);
BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE);
BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT);
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FADE_OUT);
BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
BIND_ENUM_CONSTANT(MIX_MODE_ADD);
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index e1cba60d7b..25b001e973 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -100,6 +100,7 @@ public:
ONE_SHOT_REQUEST_NONE,
ONE_SHOT_REQUEST_FIRE,
ONE_SHOT_REQUEST_ABORT,
+ ONE_SHOT_REQUEST_FADE_OUT,
};
enum MixMode {
@@ -109,17 +110,21 @@ public:
private:
double fade_in = 0.0;
+ Ref<Curve> fade_in_curve;
double fade_out = 0.0;
+ Ref<Curve> fade_out_curve;
- bool autorestart = false;
- double autorestart_delay = 1.0;
- double autorestart_random_delay = 0.0;
+ bool auto_restart = false;
+ double auto_restart_delay = 1.0;
+ double auto_restart_random_delay = 0.0;
MixMode mix = MIX_MODE_BLEND;
StringName request = PNAME("request");
StringName active = PNAME("active");
+ StringName internal_active = PNAME("internal_active");
StringName time = "time";
StringName remaining = "remaining";
+ StringName fade_out_remaining = "fade_out_remaining";
StringName time_to_restart = "time_to_restart";
protected:
@@ -132,19 +137,25 @@ public:
virtual String get_caption() const override;
- void set_fadein_time(double p_time);
- void set_fadeout_time(double p_time);
+ void set_fade_in_time(double p_time);
+ double get_fade_in_time() const;
- double get_fadein_time() const;
- double get_fadeout_time() const;
+ void set_fade_in_curve(const Ref<Curve> &p_curve);
+ Ref<Curve> get_fade_in_curve() const;
- void set_autorestart(bool p_active);
- void set_autorestart_delay(double p_time);
- void set_autorestart_random_delay(double p_time);
+ void set_fade_out_time(double p_time);
+ double get_fade_out_time() const;
- bool has_autorestart() const;
- double get_autorestart_delay() const;
- double get_autorestart_random_delay() const;
+ void set_fade_out_curve(const Ref<Curve> &p_curve);
+ Ref<Curve> get_fade_out_curve() const;
+
+ void set_auto_restart_enabled(bool p_enabled);
+ void set_auto_restart_delay(double p_time);
+ void set_auto_restart_random_delay(double p_time);
+
+ bool is_auto_restart_enabled() const;
+ double get_auto_restart_delay() const;
+ double get_auto_restart_random_delay() const;
void set_mix_mode(MixMode p_mix);
MixMode get_mix_mode() const;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 5ba861dc06..071e2e7f37 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -305,6 +305,10 @@ Vector<StringName> AnimationNodeStateMachinePlayback::get_travel_path() const {
return path;
}
+TypedArray<StringName> AnimationNodeStateMachinePlayback::_get_travel_path() const {
+ return Variant(get_travel_path()).operator Array();
+}
+
float AnimationNodeStateMachinePlayback::get_current_play_pos() const {
return pos_current;
}
@@ -1180,7 +1184,7 @@ void AnimationNodeStateMachinePlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_play_position"), &AnimationNodeStateMachinePlayback::get_current_play_pos);
ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length);
ClassDB::bind_method(D_METHOD("get_fading_from_node"), &AnimationNodeStateMachinePlayback::get_fading_from_node);
- ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path);
+ ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::_get_travel_path);
}
AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() {
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 757c6ddc68..e45aae9e2b 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -314,6 +314,8 @@ class AnimationNodeStateMachinePlayback : public Resource {
Ref<AnimationNodeStateMachineTransition> _get_group_start_transition() const;
Ref<AnimationNodeStateMachineTransition> _get_group_end_transition() const;
+ TypedArray<StringName> _get_travel_path() const;
+
protected:
static void _bind_methods();
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 70d87e221c..46ac8187c4 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -233,13 +233,13 @@ void Button::_notification(int p_what) {
}
Rect2 icon_region;
- HorizontalAlignment icon_align_rtl_checked = icon_alignment;
+ HorizontalAlignment icon_align_rtl_checked = horizontal_icon_alignment;
HorizontalAlignment align_rtl_checked = alignment;
// Swap icon and text alignment sides if right-to-left layout is set.
if (rtl) {
- if (icon_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
+ if (horizontal_icon_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
- } else if (icon_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
+ } else if (horizontal_icon_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
}
if (alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
@@ -251,6 +251,14 @@ void Button::_notification(int p_what) {
if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y;
+ int voffset = 0;
+ Size2 icon_size = _icon->get_size();
+
+ // Fix vertical size.
+ if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
+ valign -= text_buf->get_size().height;
+ }
+
float icon_ofs_region = 0.0;
Point2 style_offset;
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
@@ -268,7 +276,6 @@ void Button::_notification(int p_what) {
}
style_offset.y = style->get_margin(SIDE_TOP);
- Size2 icon_size = _icon->get_size();
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
@@ -276,6 +283,9 @@ void Button::_notification(int p_what) {
if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
+ if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
+ _size.height -= text_buf->get_size().height;
+ }
float icon_width = _icon->get_width() * _size.height / _icon->get_height();
float icon_height = _size.height;
@@ -288,12 +298,19 @@ void Button::_notification(int p_what) {
}
icon_size = _fit_icon_size(icon_size);
+ if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) {
+ voffset = -(valign - icon_size.y) / 2;
+ }
+ if (vertical_icon_alignment == VERTICAL_ALIGNMENT_BOTTOM) {
+ voffset = (valign - icon_size.y) / 2 + text_buf->get_size().y;
+ }
+
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
- icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
+ icon_region = Rect2(style_offset + Point2(icon_ofs_region, voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
} else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
- icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), Math::floor((valign - icon_size.y) * 0.5)), icon_size);
+ icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
} else {
- icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
+ icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
}
if (icon_region.size.width > 0) {
@@ -320,6 +337,13 @@ void Button::_notification(int p_what) {
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) {
+ text_ofs.y += icon_region.size.height / 2;
+ }
+ if (vertical_icon_alignment == VERTICAL_ALIGNMENT_BOTTOM) {
+ text_ofs.y -= icon_region.size.height / 2;
+ }
+
text_buf->set_alignment(align_rtl_checked);
text_buf->set_width(text_width);
switch (align_rtl_checked) {
@@ -395,9 +419,13 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
if (!expand_icon && p_icon.is_valid()) {
Size2 icon_size = _fit_icon_size(p_icon->get_size());
- minsize.height = MAX(minsize.height, icon_size.height);
+ if (vertical_icon_alignment == VERTICAL_ALIGNMENT_CENTER) {
+ minsize.height = MAX(minsize.height, icon_size.height);
+ } else {
+ minsize.height += icon_size.height;
+ }
- if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
+ if (horizontal_icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
minsize.width += icon_size.width;
if (!xl_text.is_empty() || !p_text.is_empty()) {
minsize.width += MAX(0, theme_cache.h_separation);
@@ -410,7 +438,11 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
if (!xl_text.is_empty() || !p_text.is_empty()) {
Ref<Font> font = theme_cache.font;
float font_height = font->get_height(theme_cache.font_size);
- minsize.height = MAX(font_height, minsize.height);
+ if (vertical_icon_alignment == VERTICAL_ALIGNMENT_CENTER) {
+ minsize.height = MAX(font_height, minsize.height);
+ } else {
+ minsize.height += font_height;
+ }
}
return theme_cache.normal->get_minimum_size() + minsize;
@@ -556,13 +588,23 @@ HorizontalAlignment Button::get_text_alignment() const {
}
void Button::set_icon_alignment(HorizontalAlignment p_alignment) {
- icon_alignment = p_alignment;
+ horizontal_icon_alignment = p_alignment;
+ update_minimum_size();
+ queue_redraw();
+}
+
+void Button::set_vertical_icon_alignment(VerticalAlignment p_alignment) {
+ vertical_icon_alignment = p_alignment;
update_minimum_size();
queue_redraw();
}
HorizontalAlignment Button::get_icon_alignment() const {
- return icon_alignment;
+ return horizontal_icon_alignment;
+}
+
+VerticalAlignment Button::get_vertical_icon_alignment() const {
+ return vertical_icon_alignment;
}
void Button::_bind_methods() {
@@ -584,6 +626,8 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_alignment"), &Button::get_text_alignment);
ClassDB::bind_method(D_METHOD("set_icon_alignment", "icon_alignment"), &Button::set_icon_alignment);
ClassDB::bind_method(D_METHOD("get_icon_alignment"), &Button::get_icon_alignment);
+ ClassDB::bind_method(D_METHOD("set_vertical_icon_alignment", "vertical_icon_alignment"), &Button::set_vertical_icon_alignment);
+ ClassDB::bind_method(D_METHOD("get_vertical_icon_alignment"), &Button::get_vertical_icon_alignment);
ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon);
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
@@ -598,6 +642,7 @@ void Button::_bind_methods() {
ADD_GROUP("Icon Behavior", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_icon_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_icon_alignment", "get_vertical_icon_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
ADD_GROUP("BiDi", "");
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 3634b5344c..733f40c84e 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -51,7 +51,8 @@ private:
bool expand_icon = false;
bool clip_text = false;
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_CENTER;
- HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ HorizontalAlignment horizontal_icon_alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ VerticalAlignment vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER;
float _internal_margin[4] = {};
struct ThemeCache {
@@ -135,7 +136,9 @@ public:
HorizontalAlignment get_text_alignment() const;
void set_icon_alignment(HorizontalAlignment p_alignment);
+ void set_vertical_icon_alignment(VerticalAlignment p_alignment);
HorizontalAlignment get_icon_alignment() const;
+ VerticalAlignment get_vertical_icon_alignment() const;
Button(const String &p_text = String());
~Button();
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 92407e75df..d83182c775 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -1007,6 +1007,116 @@ void CodeEdit::unindent_lines() {
queue_redraw();
}
+void CodeEdit::convert_indent(int p_from_line, int p_to_line) {
+ if (!is_editable()) {
+ return;
+ }
+
+ // Check line range.
+ p_from_line = (p_from_line < 0) ? 0 : p_from_line;
+ p_to_line = (p_to_line < 0) ? get_line_count() - 1 : p_to_line;
+
+ ERR_FAIL_COND(p_from_line >= get_line_count());
+ ERR_FAIL_COND(p_to_line >= get_line_count());
+ ERR_FAIL_COND(p_to_line < p_from_line);
+
+ // Store caret states.
+ Vector<int> caret_columns;
+ Vector<Pair<int, int>> from_selections;
+ Vector<Pair<int, int>> to_selections;
+ caret_columns.resize(get_caret_count());
+ from_selections.resize(get_caret_count());
+ to_selections.resize(get_caret_count());
+ for (int c = 0; c < get_caret_count(); c++) {
+ caret_columns.write[c] = get_caret_column(c);
+
+ // Set "selection_from_line" to -1 to allow checking if there was a selection later.
+ if (!has_selection(c)) {
+ from_selections.write[c].first = -1;
+ continue;
+ }
+ from_selections.write[c].first = get_selection_from_line(c);
+ from_selections.write[c].second = get_selection_from_column(c);
+ to_selections.write[c].first = get_selection_to_line(c);
+ to_selections.write[c].second = get_selection_to_column(c);
+ }
+
+ // Check lines within range.
+ const char32_t from_indent_char = indent_using_spaces ? '\t' : ' ';
+ int size_diff = indent_using_spaces ? indent_size - 1 : -(indent_size - 1);
+ bool changed_indentation = false;
+ for (int i = p_from_line; i <= p_to_line; i++) {
+ String line = get_line(i);
+
+ if (line.length() <= 0) {
+ continue;
+ }
+
+ // Check chars in the line.
+ int j = 0;
+ int space_count = 0;
+ bool line_changed = false;
+ while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) {
+ if (line[j] != from_indent_char) {
+ space_count = 0;
+ j++;
+ continue;
+ }
+ space_count++;
+
+ if (!indent_using_spaces && space_count != indent_size) {
+ j++;
+ continue;
+ }
+
+ line_changed = true;
+ if (!changed_indentation) {
+ begin_complex_operation();
+ changed_indentation = true;
+ }
+
+ // Calculate new caret state.
+ for (int c = 0; c < get_caret_count(); c++) {
+ if (get_caret_line(c) != i || caret_columns[c] <= j) {
+ continue;
+ }
+ caret_columns.write[c] += size_diff;
+
+ if (from_selections.write[c].first == -1) {
+ continue;
+ }
+ from_selections.write[c].second = from_selections[c].first == i ? from_selections[c].second + size_diff : from_selections[c].second;
+ to_selections.write[c].second = to_selections[c].first == i ? to_selections[c].second + size_diff : to_selections[c].second;
+ }
+
+ // Calculate new line.
+ line = line.left(j + ((size_diff < 0) ? size_diff : 0)) + indent_text + line.substr(j + 1);
+
+ space_count = 0;
+ j += size_diff;
+ }
+
+ if (line_changed) {
+ set_line(i, line);
+ }
+ }
+
+ if (!changed_indentation) {
+ return;
+ }
+
+ // Restore caret states.
+ for (int c = 0; c < get_caret_count(); c++) {
+ set_caret_column(caret_columns[c], c == 0, c);
+ if (from_selections.write[c].first != -1) {
+ select(from_selections.write[c].first, from_selections.write[c].second, to_selections.write[c].first, to_selections.write[c].second, c);
+ }
+ }
+ merge_overlapping_carets();
+ end_complex_operation();
+ queue_redraw();
+}
+
int CodeEdit::_calculate_spaces_till_next_left_indent(int p_column) const {
int spaces_till_indent = p_column % indent_size;
if (spaces_till_indent == 0) {
@@ -2197,6 +2307,8 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines);
ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines);
+ ClassDB::bind_method(D_METHOD("convert_indent", "from_line", "to_line"), &CodeEdit::convert_indent, DEFVAL(-1), DEFVAL(-1));
+
/* Auto brace completion */
ClassDB::bind_method(D_METHOD("set_auto_brace_completion_enabled", "enable"), &CodeEdit::set_auto_brace_completion_enabled);
ClassDB::bind_method(D_METHOD("is_auto_brace_completion_enabled"), &CodeEdit::is_auto_brace_completion_enabled);
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index ee73758bef..a086707745 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -314,6 +314,8 @@ public:
void indent_lines();
void unindent_lines();
+ void convert_indent(int p_from_line = -1, int p_to_line = -1);
+
/* Auto brace completion */
void set_auto_brace_completion_enabled(bool p_enabled);
bool is_auto_brace_completion_enabled() const;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index cad09a1d9f..5e861ba45d 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -67,9 +67,11 @@ void ColorPicker::_notification(int p_what) {
for (int i = 0; i < SLIDER_COUNT; i++) {
labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
+ sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
set_offset((Side)i, get_offset((Side)i) + theme_cache.content_margin);
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
+ alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
set_offset((Side)0, get_offset((Side)0) + theme_cache.content_margin);
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
@@ -122,6 +124,8 @@ void ColorPicker::_update_theme_item_cache() {
theme_cache.sv_height = get_theme_constant(SNAME("sv_height"));
theme_cache.h_width = get_theme_constant(SNAME("h_width"));
+ theme_cache.center_slider_grabbers = get_theme_constant(SNAME("center_slider_grabbers"));
+
theme_cache.screen_picker = get_theme_icon(SNAME("screen_picker"));
theme_cache.expanded_arrow = get_theme_icon(SNAME("expanded_arrow"));
theme_cache.folded_arrow = get_theme_icon(SNAME("folded_arrow"));
@@ -1909,34 +1913,23 @@ void ColorPickerButton::_modal_closed() {
void ColorPickerButton::pressed() {
_update_picker();
- Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
+ Size2 minsize = popup->get_contents_minimum_size();
+ float viewport_height = get_viewport_rect().size.y;
popup->reset_size();
picker->_update_presets();
picker->_update_recent_presets();
- Rect2i usable_rect = popup->get_usable_parent_rect();
- //let's try different positions to see which one we can use
-
- Rect2i cp_rect(Point2i(), popup->get_size());
- for (int i = 0; i < 4; i++) {
- if (i > 1) {
- cp_rect.position.y = get_screen_position().y - cp_rect.size.y;
- } else {
- cp_rect.position.y = get_screen_position().y + size.height;
- }
-
- if (i & 1) {
- cp_rect.position.x = get_screen_position().x;
- } else {
- cp_rect.position.x = get_screen_position().x - MAX(0, (cp_rect.size.x - size.x));
- }
-
- if (usable_rect.encloses(cp_rect)) {
- break;
- }
+ // Determine in which direction to show the popup. By default popup horizontally centered below the button.
+ // But if the popup doesn't fit below and the button is in the bottom half of the viewport, show above.
+ bool show_above = false;
+ if (get_global_position().y + get_size().y + minsize.y > viewport_height && get_global_position().y * 2 + get_size().y > viewport_height) {
+ show_above = true;
}
- popup->set_position(cp_rect.position);
+
+ float h_offset = (get_size().x - minsize.x) / 2;
+ float v_offset = show_above ? -minsize.y : get_size().y;
+ popup->set_position(get_screen_position() + Vector2(h_offset, v_offset));
popup->popup();
picker->set_focus_on_line_edit();
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 6e894efb00..018ae10955 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -213,6 +213,8 @@ private:
int sv_width = 0;
int h_width = 0;
+ bool center_slider_grabbers = true;
+
Ref<Texture2D> screen_picker;
Ref<Texture2D> expanded_arrow;
Ref<Texture2D> folded_arrow;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 946e8a2ad5..f2f98b1889 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -62,6 +62,15 @@ GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
is_resizing = false;
}
+Control::CursorShape GraphEditMinimap::get_cursor_shape(const Point2 &p_pos) const {
+ Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
+ if (is_resizing || (p_pos.x < resizer->get_width() && p_pos.y < resizer->get_height())) {
+ return CURSOR_FDIAGSIZE;
+ }
+
+ return Control::get_cursor_shape(p_pos);
+}
+
void GraphEditMinimap::update_minimap() {
Vector2 graph_offset = _get_graph_offset();
Vector2 graph_size = _get_graph_size();
@@ -190,6 +199,14 @@ void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2);
}
+Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const {
+ if (moving_selection) {
+ return CURSOR_MOVE;
+ }
+
+ return Control::get_cursor_shape(p_pos);
+}
+
PackedStringArray GraphEdit::get_configuration_warnings() const {
PackedStringArray warnings = Control::get_configuration_warnings();
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index dfe6b94906..8b02fbfddc 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -64,6 +64,8 @@ protected:
public:
GraphEditMinimap(GraphEdit *p_edit);
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+
void update_minimap();
Rect2 get_camera_rect();
@@ -286,6 +288,8 @@ protected:
GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int);
public:
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+
PackedStringArray get_configuration_warnings() const override;
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 8b2d462d85..6031730b81 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -990,7 +990,6 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseMotion> mm = p_ev;
if (resizing && mm.is_valid()) {
Vector2 mpos = mm->get_position();
-
Vector2 diff = mpos - resizing_from;
emit_signal(SNAME("resize_request"), resizing_from_size + diff);
@@ -1055,6 +1054,18 @@ bool GraphNode::is_selectable() {
return selectable;
}
+Control::CursorShape GraphNode::get_cursor_shape(const Point2 &p_pos) const {
+ if (resizable) {
+ Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
+
+ if (resizing || (p_pos.x > get_size().x - resizer->get_width() && p_pos.y > get_size().y - resizer->get_height())) {
+ return CURSOR_FDIAGSIZE;
+ }
+ }
+
+ return Control::get_cursor_shape(p_pos);
+}
+
Vector<int> GraphNode::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index a118efb37a..e6ecc3d89b 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -197,6 +197,8 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 30f161d27a..594276a428 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -294,6 +294,18 @@ Color ItemList::get_item_custom_fg_color(int p_idx) const {
return items[p_idx].custom_fg;
}
+Rect2 ItemList::get_item_rect(int p_idx, bool p_expand) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Rect2());
+
+ Rect2 ret = items[p_idx].rect_cache;
+ ret.position += theme_cache.panel_style->get_offset();
+
+ if (p_expand && p_idx % current_columns == current_columns - 1) {
+ ret.size.width = get_size().width - ret.position.x;
+ }
+ return ret;
+}
+
void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) {
if (p_idx < 0) {
p_idx += get_item_count();
@@ -597,6 +609,7 @@ void ItemList::set_fixed_icon_size(const Size2i &p_size) {
fixed_icon_size = p_size;
queue_redraw();
+ shape_changed = true;
}
Size2i ItemList::get_fixed_icon_size() const {
@@ -656,13 +669,6 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->is_pressed()) {
search_string = ""; //any mousepress cancels
- Vector2 pos = mb->get_position();
- pos -= theme_cache.panel_style->get_offset();
- pos.y += scroll_bar->get_value();
-
- if (is_layout_rtl()) {
- pos.x = get_size().width - pos.x;
- }
int closest = get_item_at_position(mb->get_position(), true);
@@ -1784,6 +1790,8 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_custom_fg_color", "idx", "custom_fg_color"), &ItemList::set_item_custom_fg_color);
ClassDB::bind_method(D_METHOD("get_item_custom_fg_color", "idx"), &ItemList::get_item_custom_fg_color);
+ ClassDB::bind_method(D_METHOD("get_item_rect", "idx", "expand"), &ItemList::get_item_rect, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("set_item_tooltip_enabled", "idx", "enable"), &ItemList::set_item_tooltip_enabled);
ClassDB::bind_method(D_METHOD("is_item_tooltip_enabled", "idx"), &ItemList::is_item_tooltip_enabled);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index cd91f97410..ced04f8718 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -211,6 +211,8 @@ public:
void set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_color);
Color get_item_custom_fg_color(int p_idx) const;
+ Rect2 get_item_rect(int p_idx, bool p_expand = true) const;
+
void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior);
TextServer::OverrunBehavior get_text_overrun_behavior() const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 03860c7449..2e6c6dc4bd 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -635,6 +635,8 @@ void PopupMenu::_draw_items() {
Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1);
+ icon_color *= items[i].icon_modulate;
+
// For non-separator items, add some padding for the content.
item_ofs.x += theme_cache.item_start_padding;
@@ -1249,6 +1251,20 @@ void PopupMenu::set_item_icon_max_width(int p_idx, int p_width) {
_menu_changed();
}
+void PopupMenu::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ if (items[p_idx].icon_modulate == p_modulate) {
+ return;
+ }
+
+ items.write[p_idx].icon_modulate = p_modulate;
+ control->queue_redraw();
+}
+
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
if (p_idx < 0) {
p_idx += get_item_count();
@@ -1392,6 +1408,11 @@ int PopupMenu::get_item_icon_max_width(int p_idx) const {
return items[p_idx].icon_max_width;
}
+Color PopupMenu::get_item_icon_modulate(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Color());
+ return items[p_idx].icon_modulate;
+}
+
Key PopupMenu::get_item_accelerator(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
return items[p_idx].accel;
@@ -2102,6 +2123,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
ClassDB::bind_method(D_METHOD("set_item_icon_max_width", "index", "width"), &PopupMenu::set_item_icon_max_width);
+ ClassDB::bind_method(D_METHOD("set_item_icon_modulate", "index", "modulate"), &PopupMenu::set_item_icon_modulate);
ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id);
ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator);
@@ -2125,6 +2147,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
ClassDB::bind_method(D_METHOD("get_item_icon_max_width", "index"), &PopupMenu::get_item_icon_max_width);
+ ClassDB::bind_method(D_METHOD("get_item_icon_modulate", "index"), &PopupMenu::get_item_icon_modulate);
ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 74b739ac0f..0926ee7e55 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -43,6 +43,7 @@ class PopupMenu : public Popup {
struct Item {
Ref<Texture2D> icon;
int icon_max_width = 0;
+ Color icon_modulate = Color(1, 1, 1, 1);
String text;
String xl_text;
Ref<TextLine> text_buf;
@@ -226,6 +227,7 @@ public:
void set_item_language(int p_idx, const String &p_language);
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
void set_item_icon_max_width(int p_idx, int p_width);
+ void set_item_icon_modulate(int p_idx, const Color &p_modulate);
void set_item_checked(int p_idx, bool p_checked);
void set_item_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, Key p_accel);
@@ -250,6 +252,7 @@ public:
int get_item_idx_from_text(const String &text) const;
Ref<Texture2D> get_item_icon(int p_idx) const;
int get_item_icon_max_width(int p_idx) const;
+ Color get_item_icon_modulate(int p_idx) const;
bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const;
int get_item_index(int p_id) const;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index b678f46091..85dd73beeb 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -482,6 +482,22 @@ int ScrollContainer::get_v_scroll() const {
return v_scroll->get_value();
}
+void ScrollContainer::set_horizontal_custom_step(float p_custom_step) {
+ h_scroll->set_custom_step(p_custom_step);
+}
+
+float ScrollContainer::get_horizontal_custom_step() const {
+ return h_scroll->get_custom_step();
+}
+
+void ScrollContainer::set_vertical_custom_step(float p_custom_step) {
+ v_scroll->set_custom_step(p_custom_step);
+}
+
+float ScrollContainer::get_vertical_custom_step() const {
+ return v_scroll->get_custom_step();
+}
+
void ScrollContainer::set_horizontal_scroll_mode(ScrollMode p_mode) {
if (horizontal_scroll_mode == p_mode) {
return;
@@ -570,6 +586,12 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("set_horizontal_custom_step", "value"), &ScrollContainer::set_horizontal_custom_step);
+ ClassDB::bind_method(D_METHOD("get_horizontal_custom_step"), &ScrollContainer::get_horizontal_custom_step);
+
+ ClassDB::bind_method(D_METHOD("set_vertical_custom_step", "value"), &ScrollContainer::set_vertical_custom_step);
+ ClassDB::bind_method(D_METHOD("get_vertical_custom_step"), &ScrollContainer::get_vertical_custom_step);
+
ClassDB::bind_method(D_METHOD("set_horizontal_scroll_mode", "enable"), &ScrollContainer::set_horizontal_scroll_mode);
ClassDB::bind_method(D_METHOD("get_horizontal_scroll_mode"), &ScrollContainer::get_horizontal_scroll_mode);
@@ -594,6 +616,8 @@ void ScrollContainer::_bind_methods() {
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_horizontal_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_horizontal_custom_step", "get_horizontal_custom_step");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_vertical_custom_step", "get_vertical_custom_step");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 56bc3f8381..dbd1b3904a 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -98,6 +98,12 @@ public:
void set_v_scroll(int p_pos);
int get_v_scroll() const;
+ void set_horizontal_custom_step(float p_custom_step);
+ float get_horizontal_custom_step() const;
+
+ void set_vertical_custom_step(float p_custom_step);
+ float get_vertical_custom_step() const;
+
void set_horizontal_scroll_mode(ScrollMode p_mode);
ScrollMode get_horizontal_scroll_mode() const;
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 292a4cfea2..b9efa449cb 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -64,8 +64,8 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x;
- double grab_width = (double)grabber->get_size().width;
- double grab_height = (double)grabber->get_size().height;
+ double grab_width = (double)grabber->get_width();
+ double grab_height = (double)grabber->get_height();
double max = orientation == VERTICAL ? get_size().height - grab_height : get_size().width - grab_width;
if (orientation == VERTICAL) {
set_as_ratio(1 - (((double)grab.pos - (grab_height / 2.0)) / max));
@@ -103,7 +103,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
if (orientation == VERTICAL) {
motion = -motion;
}
- double areasize = orientation == VERTICAL ? size.height - grabber->get_size().height : size.width - grabber->get_size().width;
+ double areasize = orientation == VERTICAL ? size.height - grabber->get_height() : size.width - grabber->get_width();
if (areasize <= 0) {
return;
}
@@ -159,6 +159,9 @@ void Slider::_update_theme_item_cache() {
theme_cache.grabber_hl_icon = get_theme_icon(SNAME("grabber_highlight"));
theme_cache.grabber_disabled_icon = get_theme_icon(SNAME("grabber_disabled"));
theme_cache.tick_icon = get_theme_icon(SNAME("tick"));
+
+ theme_cache.center_grabber = get_theme_constant(SNAME("center_grabber"));
+ theme_cache.grabber_offset = get_theme_constant(SNAME("grabber_offset"));
}
void Slider::_notification(int p_what) {
@@ -213,39 +216,41 @@ void Slider::_notification(int p_what) {
if (orientation == VERTICAL) {
int widget_width = style->get_minimum_size().width;
- double areasize = size.height - grabber->get_size().height;
+ double areasize = size.height - (theme_cache.center_grabber ? 0 : grabber->get_height());
+ int grabber_shift = theme_cache.center_grabber ? grabber->get_height() / 2 : 0;
style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height)));
- grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2)));
+ grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_height() / 2 + grabber_shift), Size2i(widget_width, areasize * ratio + grabber->get_height() / 2 - grabber_shift)));
if (ticks > 1) {
- int grabber_offset = (grabber->get_size().height / 2 - tick->get_height() / 2);
+ int grabber_offset = (grabber->get_height() / 2 - tick->get_height() / 2);
for (int i = 0; i < ticks; i++) {
if (!ticks_on_borders && (i == 0 || i + 1 == ticks)) {
continue;
}
- int ofs = (i * areasize / (ticks - 1)) + grabber_offset;
+ int ofs = (i * areasize / (ticks - 1)) + grabber_offset - grabber_shift;
tick->draw(ci, Point2i((size.width - widget_width) / 2, ofs));
}
}
- grabber->draw(ci, Point2i(size.width / 2 - grabber->get_size().width / 2 + get_theme_constant(SNAME("grabber_offset")), size.height - ratio * areasize - grabber->get_size().height));
+ grabber->draw(ci, Point2i(size.width / 2 - grabber->get_width() / 2 + theme_cache.grabber_offset, size.height - ratio * areasize - grabber->get_height() + grabber_shift));
} else {
int widget_height = style->get_minimum_size().height;
- double areasize = size.width - grabber->get_size().width;
+ double areasize = size.width - (theme_cache.center_grabber ? 0 : grabber->get_size().width);
+ int grabber_shift = theme_cache.center_grabber ? -grabber->get_width() / 2 : 0;
style->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(size.width, widget_height)));
- grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * ratio + grabber->get_size().width / 2, widget_height)));
+ grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * ratio + grabber->get_width() / 2 + grabber_shift, widget_height)));
if (ticks > 1) {
- int grabber_offset = (grabber->get_size().width / 2 - tick->get_width() / 2);
+ int grabber_offset = (grabber->get_width() / 2 - tick->get_width() / 2);
for (int i = 0; i < ticks; i++) {
if ((!ticks_on_borders) && ((i == 0) || ((i + 1) == ticks))) {
continue;
}
- int ofs = (i * areasize / (ticks - 1)) + grabber_offset;
+ int ofs = (i * areasize / (ticks - 1)) + grabber_offset + grabber_shift;
tick->draw(ci, Point2i(ofs, (size.height - widget_height) / 2));
}
}
- grabber->draw(ci, Point2i(ratio * areasize, size.height / 2 - grabber->get_size().height / 2 + get_theme_constant(SNAME("grabber_offset"))));
+ grabber->draw(ci, Point2i(ratio * areasize + grabber_shift, size.height / 2 - grabber->get_height() / 2 + theme_cache.grabber_offset));
}
} break;
}
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index 42778684af..684445f2b3 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -58,6 +58,9 @@ class Slider : public Range {
Ref<Texture2D> grabber_hl_icon;
Ref<Texture2D> grabber_disabled_icon;
Ref<Texture2D> tick_icon;
+
+ bool center_grabber = false;
+ int grabber_offset = 0;
} theme_cache;
protected:
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index f9e96a44ed..9105837486 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -221,33 +221,6 @@ bool SubViewportContainer::_is_propagated_in_gui_input(const Ref<InputEvent> &p_
return false;
}
-void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND(p_event.is_null());
-
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-
- Transform2D xform = get_global_transform_with_canvas();
-
- if (stretch) {
- Transform2D scale_xf;
- scale_xf.scale(Vector2(shrink, shrink));
- xform *= scale_xf;
- }
-
- Ref<InputEvent> ev = p_event->xformed_by(xform.affine_inverse());
-
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c || c->is_input_disabled()) {
- continue;
- }
-
- c->push_unhandled_input(ev);
- }
-}
-
void SubViewportContainer::add_child_notify(Node *p_child) {
if (Object::cast_to<SubViewport>(p_child)) {
queue_redraw();
@@ -290,5 +263,4 @@ void SubViewportContainer::_bind_methods() {
SubViewportContainer::SubViewportContainer() {
set_process_input(true);
- set_process_unhandled_input(true);
}
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index c1a74e5b98..8e5f5d157d 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -55,7 +55,6 @@ public:
virtual void input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
- virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
void recalc_force_viewport_sizes();
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 818e19ab29..1b148defc1 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -564,12 +564,12 @@ void TabContainer::add_child_notify(Node *p_child) {
}
void TabContainer::move_child_notify(Node *p_child) {
+ Container::move_child_notify(p_child);
+
if (p_child == tab_bar) {
return;
}
- Container::move_child_notify(p_child);
-
Control *c = Object::cast_to<Control>(p_child);
if (c && !c->is_set_as_top_level()) {
int old_idx = -1;
@@ -588,12 +588,12 @@ void TabContainer::move_child_notify(Node *p_child) {
}
void TabContainer::remove_child_notify(Node *p_child) {
+ Container::remove_child_notify(p_child);
+
if (p_child == tab_bar) {
return;
}
- Container::remove_child_notify(p_child);
-
Control *c = Object::cast_to<Control>(p_child);
if (!c || c->is_set_as_top_level()) {
return;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 1564f9bc62..e29f044537 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1851,23 +1851,28 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
Point2i pos = get_line_column_at_pos(mpos);
int row = pos.y;
int col = pos.x;
- int caret = carets.size() - 1;
+ bool selection_clicked = false;
if (is_move_caret_on_right_click_enabled()) {
- if (has_selection(caret)) {
- int from_line = get_selection_from_line(caret);
- int to_line = get_selection_to_line(caret);
- int from_column = get_selection_from_column(caret);
- int to_column = get_selection_to_column(caret);
-
- if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
- // Right click is outside the selected text.
- deselect(caret);
+ if (has_selection()) {
+ for (int i = 0; i < get_caret_count(); i++) {
+ int from_line = get_selection_from_line(i);
+ int to_line = get_selection_to_line(i);
+ int from_column = get_selection_from_column(i);
+ int to_column = get_selection_to_column(i);
+
+ if (row >= from_line && row <= to_line && (row != from_line || col >= from_column) && (row != to_line || col <= to_column)) {
+ // Right click in one of the selected text
+ selection_clicked = true;
+ break;
+ }
}
}
- if (!has_selection(caret)) {
- set_caret_line(row, true, false, 0, caret);
- set_caret_column(col, true, caret);
+ if (!selection_clicked) {
+ deselect();
+ remove_secondary_carets();
+ set_caret_line(row, false, false);
+ set_caret_column(col);
}
merge_overlapping_carets();
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 434daa484f..b8fc8004c9 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -38,6 +38,7 @@
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/text_edit.h"
#include "scene/main/window.h"
#include <limits.h>
@@ -166,6 +167,18 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
return cells[p_column].mode;
}
+/* multiline editable */
+void TreeItem::set_edit_multiline(int p_column, bool p_multiline) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+ cells.write[p_column].edit_multiline = p_multiline;
+ _changed_notify(p_column);
+}
+
+bool TreeItem::is_edit_multiline(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), false);
+ return cells[p_column].edit_multiline;
+}
+
/* check mode */
void TreeItem::set_checked(int p_column, bool p_checked) {
ERR_FAIL_INDEX(p_column, cells.size());
@@ -735,7 +748,7 @@ TreeItem *TreeItem::get_first_child() const {
return first_child;
}
-TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
+TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
TreeItem *current = this;
TreeItem *prev_item = current->get_prev();
@@ -758,7 +771,7 @@ TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
}
} else {
current = prev_item;
- while (!current->collapsed && current->first_child) {
+ while ((!current->collapsed || p_include_invisible) && current->first_child) {
//go to the very end
current = current->first_child;
@@ -773,9 +786,9 @@ TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *loop = this;
- TreeItem *prev_item = this->_get_prev_visible(p_wrap);
+ TreeItem *prev_item = this->_get_prev_in_tree(p_wrap);
while (prev_item && !prev_item->is_visible()) {
- prev_item = prev_item->_get_prev_visible(p_wrap);
+ prev_item = prev_item->_get_prev_in_tree(p_wrap);
if (prev_item == loop) {
// Check that we haven't looped all the way around to the start.
prev_item = nullptr;
@@ -785,10 +798,10 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
return prev_item;
}
-TreeItem *TreeItem::_get_next_visible(bool p_wrap) {
+TreeItem *TreeItem::_get_next_in_tree(bool p_wrap, bool p_include_invisible) {
TreeItem *current = this;
- if (!current->collapsed && current->first_child) {
+ if ((!current->collapsed || p_include_invisible) && current->first_child) {
current = current->first_child;
} else if (current->next) {
@@ -814,9 +827,9 @@ TreeItem *TreeItem::_get_next_visible(bool p_wrap) {
TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *loop = this;
- TreeItem *next_item = this->_get_next_visible(p_wrap);
+ TreeItem *next_item = this->_get_next_in_tree(p_wrap);
while (next_item && !next_item->is_visible()) {
- next_item = next_item->_get_next_visible(p_wrap);
+ next_item = next_item->_get_next_in_tree(p_wrap);
if (next_item == loop) {
// Check that we haven't looped all the way around to the start.
next_item = nullptr;
@@ -826,6 +839,16 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) {
return next_item;
}
+TreeItem *TreeItem::get_prev_in_tree(bool p_wrap) {
+ TreeItem *prev_item = this->_get_prev_in_tree(p_wrap, true);
+ return prev_item;
+}
+
+TreeItem *TreeItem::get_next_in_tree(bool p_wrap) {
+ TreeItem *next_item = this->_get_next_in_tree(p_wrap, true);
+ return next_item;
+}
+
TreeItem *TreeItem::get_child(int p_index) {
_create_children_cache();
@@ -1404,6 +1427,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_mode", "column", "mode"), &TreeItem::set_cell_mode);
ClassDB::bind_method(D_METHOD("get_cell_mode", "column"), &TreeItem::get_cell_mode);
+ ClassDB::bind_method(D_METHOD("set_edit_multiline", "column", "multiline"), &TreeItem::set_edit_multiline);
+ ClassDB::bind_method(D_METHOD("is_edit_multiline", "column"), &TreeItem::is_edit_multiline);
+
ClassDB::bind_method(D_METHOD("set_checked", "column", "checked"), &TreeItem::set_checked);
ClassDB::bind_method(D_METHOD("set_indeterminate", "column", "indeterminate"), &TreeItem::set_indeterminate);
ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
@@ -1523,6 +1549,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child);
+ ClassDB::bind_method(D_METHOD("get_next_in_tree", "wrap"), &TreeItem::get_next_in_tree, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_in_tree", "wrap"), &TreeItem::get_prev_in_tree, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
@@ -1726,7 +1755,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
}
}
}
- int item_min_height = p_item->get_custom_minimum_height();
+ int item_min_height = MAX(theme_cache.font->get_height(theme_cache.font_size), p_item->get_custom_minimum_height());
if (height < item_min_height) {
height = item_min_height;
}
@@ -1795,8 +1824,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
if (rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
- draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(rect.size.width);
+ draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -1815,8 +1843,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
if (!rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
- draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(rect.size.width);
+ draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -1891,6 +1918,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
font_size = theme_cache.font_size;
}
p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
+ p_item->cells.write[p_col].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE);
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
@@ -1907,7 +1935,7 @@ void Tree::update_item_cache(TreeItem *p_item) {
}
}
-int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item) {
+int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height) {
if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) {
return -1; //draw no more!
}
@@ -1920,17 +1948,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int htotal = 0;
- int label_h = compute_item_height(p_item);
+ int label_h = 0;
bool rtl = cache.rtl;
- /* Calculate height of the label part */
- label_h += theme_cache.v_separation;
-
/* Draw label, if height fits */
bool skip = (p_item == root && hide_root);
- if (!skip && (p_pos.y + label_h - theme_cache.offset.y) > 0) {
+ if (!skip) {
// Draw separation.
ERR_FAIL_COND_V(theme_cache.font.is_null(), -1);
@@ -1990,6 +2015,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
buttons_width += button_size.width + theme_cache.button_margin;
}
+ p_item->cells.write[i].text_buf->set_width(item_width);
+
+ label_h = compute_item_height(p_item);
+ if (r_self_height != nullptr) {
+ *r_self_height = label_h;
+ }
+ label_h += theme_cache.v_separation;
+
Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(item_width, label_h));
Rect2i cell_rect = item_rect;
if (i != 0) {
@@ -2108,7 +2141,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
Point2i text_pos = item_rect.position;
- text_pos.y += Math::floor((item_rect.size.y - p_item->cells[i].text_buf->get_size().y) / 2);
+ text_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height();
int text_width = p_item->cells[i].text_buf->get_size().x;
switch (p_item->cells[i].mode) {
@@ -2149,7 +2182,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
Ref<Texture2D> downarrow = theme_cache.select_arrow;
int cell_width = item_rect.size.x - downarrow->get_width();
- p_item->cells.write[i].text_buf->set_width(cell_width);
if (rtl) {
if (outline_size > 0 && font_outline_color.a > 0) {
p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
@@ -2336,15 +2368,17 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
while (c) {
int child_h = -1;
+ int child_self_height = 0;
if (htotal >= 0) {
- child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c);
+ child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, &child_self_height);
+ child_self_height += theme_cache.v_separation;
}
// Draw relationship lines.
if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) {
int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
int parent_ofs = p_pos.x + theme_cache.item_margin;
- Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - theme_cache.offset + p_draw_ofs;
+ Point2i root_pos = Point2i(root_ofs, children_pos.y + child_self_height / 2) - theme_cache.offset + p_draw_ofs;
if (c->get_visible_child_count() > 0) {
root_pos -= Point2i(theme_cache.arrow->get_width(), 0);
@@ -2975,7 +3009,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
return item_h; // nothing found
}
-void Tree::_text_editor_modal_close() {
+void Tree::_text_editor_popup_modal_close() {
if (Input::get_singleton()->is_key_pressed(Key::ESCAPE) ||
Input::get_singleton()->is_key_pressed(Key::KP_ENTER) ||
Input::get_singleton()->is_key_pressed(Key::ENTER)) {
@@ -2986,10 +3020,51 @@ void Tree::_text_editor_modal_close() {
return;
}
- _text_editor_submit(text_editor->get_text());
+ if (!popup_edited_item) {
+ return;
+ }
+
+ if (popup_edited_item->is_edit_multiline(popup_edited_item_col) && popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_STRING) {
+ _apply_multiline_edit();
+ } else {
+ _line_editor_submit(line_editor->get_text());
+ }
+}
+
+void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
+ if (p_event->is_action_pressed("ui_text_newline_blank", true)) {
+ accept_event();
+ } else if (p_event->is_action_pressed("ui_text_newline")) {
+ popup_editor->hide();
+ _apply_multiline_edit();
+ accept_event();
+ }
}
-void Tree::_text_editor_submit(String p_text) {
+void Tree::_apply_multiline_edit() {
+ if (!popup_edited_item) {
+ return;
+ }
+
+ if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) {
+ return;
+ }
+
+ TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col];
+ switch (c.mode) {
+ case TreeItem::CELL_MODE_STRING: {
+ c.text = text_editor->get_text();
+ } break;
+ default: {
+ ERR_FAIL();
+ }
+ }
+
+ item_edited(popup_edited_item_col, popup_edited_item);
+ queue_redraw();
+}
+
+void Tree::_line_editor_submit(String p_text) {
popup_editor->hide();
if (!popup_edited_item) {
@@ -3789,14 +3864,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
}
-bool Tree::edit_selected() {
+bool Tree::edit_selected(bool p_force_edit) {
TreeItem *s = get_selected();
ERR_FAIL_COND_V_MSG(!s, false, "No item selected.");
ensure_cursor_is_visible();
int col = get_selected_column();
ERR_FAIL_INDEX_V_MSG(col, columns.size(), false, "No item column selected.");
- if (!s->cells[col].editable) {
+ if (!s->cells[col].editable && !p_force_edit) {
return false;
}
@@ -3830,18 +3905,16 @@ bool Tree::edit_selected() {
popup_menu->popup();
popup_edited_item = s;
popup_edited_item_col = col;
- return true;
- } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE) {
+ return true;
+ } else if ((c.mode == TreeItem::CELL_MODE_STRING && !c.edit_multiline) || c.mode == TreeItem::CELL_MODE_RANGE) {
Rect2 popup_rect;
int value_editor_height = c.mode == TreeItem::CELL_MODE_RANGE ? value_editor->get_minimum_size().height : 0;
// "floor()" centers vertically.
- Vector2 ofs(0, Math::floor((MAX(text_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
+ Vector2 ofs(0, Math::floor((MAX(line_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
- Point2i textedpos = get_screen_position() + rect.position - ofs;
- cache.text_editor_position = textedpos;
- popup_rect.position = textedpos;
+ popup_rect.position = get_screen_position() + rect.position - ofs;
popup_rect.size = rect.size;
// Account for icon.
@@ -3849,9 +3922,12 @@ bool Tree::edit_selected() {
popup_rect.position.x += icon_size.x;
popup_rect.size.x -= icon_size.x;
- text_editor->clear();
- text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
- text_editor->select_all();
+ line_editor->clear();
+ line_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
+ line_editor->select_all();
+ line_editor->show();
+
+ text_editor->hide();
if (c.mode == TreeItem::CELL_MODE_RANGE) {
popup_rect.size.y += value_editor_height;
@@ -3873,6 +3949,22 @@ bool Tree::edit_selected() {
popup_editor->popup();
popup_editor->child_controls_changed();
+ line_editor->grab_focus();
+
+ return true;
+ } else if (c.mode == TreeItem::CELL_MODE_STRING && c.edit_multiline) {
+ line_editor->hide();
+
+ text_editor->clear();
+ text_editor->set_text(c.text);
+ text_editor->select_all();
+ text_editor->show();
+
+ popup_editor->set_position(get_screen_position() + rect.position);
+ popup_editor->set_size(rect.size);
+ popup_editor->popup();
+ popup_editor->child_controls_changed();
+
text_editor->grab_focus();
return true;
@@ -3885,6 +3977,14 @@ bool Tree::is_editing() {
return popup_editor->is_visible();
}
+void Tree::set_editor_selection(int p_from_line, int p_to_line, int p_from_column, int p_to_column, int p_caret) {
+ if (p_from_column == -1 || p_to_column == -1) {
+ line_editor->select(p_from_line, p_to_line);
+ } else {
+ text_editor->select(p_from_line, p_from_column, p_to_line, p_to_column, p_caret);
+ }
+}
+
Size2 Tree::get_internal_min_size() const {
Size2i size;
if (root) {
@@ -4144,14 +4244,10 @@ void Tree::_notification(int p_what) {
case NOTIFICATION_TRANSFORM_CHANGED: {
if (popup_edited_item != nullptr) {
Rect2 rect = popup_edited_item->get_meta("__focus_rect");
- Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
- Point2i textedpos = get_global_position() + rect.position - ofs;
- if (cache.text_editor_position != textedpos) {
- cache.text_editor_position = textedpos;
- text_editor->set_position(textedpos);
- value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
- }
+ popup_editor->set_position(get_global_position() + rect.position);
+ popup_editor->set_size(rect.size);
+ popup_editor->child_controls_changed();
}
} break;
}
@@ -4920,6 +5016,26 @@ TreeItem *Tree::get_item_with_text(const String &p_find) const {
return nullptr;
}
+TreeItem *Tree::get_item_with_metadata(const Variant &p_find, int p_column) const {
+ if (p_column < 0) {
+ for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
+ for (int i = 0; i < columns.size(); i++) {
+ if (current->get_metadata(i) == p_find) {
+ return current;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
+ if (current->get_metadata(p_column) == p_find) {
+ return current;
+ }
+ }
+ return nullptr;
+}
+
void Tree::_do_incr_search(const String &p_add) {
uint64_t time = OS::get_singleton()->get_ticks_usec() / 1000; // convert to msec
uint64_t diff = time - last_keypress;
@@ -5285,7 +5401,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited"), &Tree::get_edited);
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
- ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
+ ClassDB::bind_method(D_METHOD("edit_selected", "force_edit"), &Tree::edit_selected, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column", "button_index"), &Tree::get_item_rect, DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
@@ -5386,17 +5502,26 @@ Tree::Tree() {
popup_editor = memnew(Popup);
add_child(popup_editor, false, INTERNAL_MODE_FRONT);
+
popup_editor_vb = memnew(VBoxContainer);
- popup_editor->add_child(popup_editor_vb);
popup_editor_vb->add_theme_constant_override("separation", 0);
popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
- text_editor = memnew(LineEdit);
- popup_editor_vb->add_child(text_editor);
+ popup_editor->add_child(popup_editor_vb);
+
+ line_editor = memnew(LineEdit);
+ line_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ line_editor->hide();
+ popup_editor_vb->add_child(line_editor);
+
+ text_editor = memnew(TextEdit);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ text_editor->hide();
+ popup_editor_vb->add_child(text_editor);
+
value_editor = memnew(HSlider);
- popup_editor_vb->add_child(value_editor);
value_editor->set_v_size_flags(SIZE_EXPAND_FILL);
value_editor->hide();
+ popup_editor_vb->add_child(value_editor);
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
@@ -5410,8 +5535,9 @@ Tree::Tree() {
h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
- text_editor->connect("text_submitted", callable_mp(this, &Tree::_text_editor_submit));
- popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_modal_close));
+ line_editor->connect("text_submitted", callable_mp(this, &Tree::_line_editor_submit));
+ text_editor->connect("gui_input", callable_mp(this, &Tree::_text_editor_gui_input));
+ popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_popup_modal_close));
popup_menu->connect("id_pressed", callable_mp(this, &Tree::popup_select));
value_editor->connect("value_changed", callable_mp(this, &Tree::value_editor_changed));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index fcfeb26f95..24b649b040 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -36,8 +36,9 @@
#include "scene/gui/popup_menu.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
-#include "scene/resources/text_line.h"
+#include "scene/resources/text_paragraph.h"
+class TextEdit;
class Tree;
class TreeItem : public Object {
@@ -61,8 +62,9 @@ private:
Ref<Texture2D> icon;
Rect2i icon_region;
String text;
+ bool edit_multiline = false;
String suffix;
- Ref<TextLine> text_buf;
+ Ref<TextParagraph> text_buf;
String language;
TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
@@ -198,6 +200,10 @@ public:
void set_cell_mode(int p_column, TreeCellMode p_mode);
TreeCellMode get_cell_mode(int p_column) const;
+ /* multiline editable */
+ void set_edit_multiline(int p_column, bool p_multiline);
+ bool is_edit_multiline(int p_column) const;
+
/* check mode */
void set_checked(int p_column, bool p_checked);
void set_indeterminate(int p_column, bool p_indeterminate);
@@ -211,8 +217,8 @@ private:
void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
void _propagate_check_through_parents(int p_column, bool p_emit_signal);
- TreeItem *_get_prev_visible(bool p_wrap = false);
- TreeItem *_get_next_visible(bool p_wrap = false);
+ TreeItem *_get_prev_in_tree(bool p_wrap = false, bool p_include_invisible = false);
+ TreeItem *_get_next_in_tree(bool p_wrap = false, bool p_include_invisible = false);
public:
void set_text(int p_column, String p_text);
@@ -338,6 +344,9 @@ public:
TreeItem *get_parent() const;
TreeItem *get_first_child() const;
+ TreeItem *get_prev_in_tree(bool p_wrap = false);
+ TreeItem *get_next_in_tree(bool p_wrap = false);
+
TreeItem *get_prev_visible(bool p_wrap = false);
TreeItem *get_next_visible(bool p_wrap = false);
@@ -436,7 +445,7 @@ private:
bool clip_content = false;
String title;
HorizontalAlignment title_alignment = HORIZONTAL_ALIGNMENT_CENTER;
- Ref<TextLine> text_buf;
+ Ref<TextParagraph> text_buf;
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
ColumnInfo() {
@@ -449,7 +458,8 @@ private:
VBoxContainer *popup_editor_vb = nullptr;
Popup *popup_editor = nullptr;
- LineEdit *text_editor = nullptr;
+ LineEdit *line_editor = nullptr;
+ TextEdit *text_editor = nullptr;
HSlider *value_editor = nullptr;
bool updating_value_editor = false;
uint64_t focus_in_id = 0;
@@ -470,11 +480,13 @@ private:
void update_item_cache(TreeItem *p_item);
//void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color);
void 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);
- int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item);
+ int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height = nullptr);
void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod);
- void _text_editor_submit(String p_text);
- void _text_editor_modal_close();
+ void _line_editor_submit(String p_text);
+ void _apply_multiline_edit();
+ void _text_editor_popup_modal_close();
+ void _text_editor_gui_input(const Ref<InputEvent> &p_event);
void value_editor_changed(double p_value);
void popup_select(int p_option);
@@ -578,8 +590,6 @@ private:
TreeItem *hover_item = nullptr;
int hover_cell = -1;
- Point2i text_editor_position;
-
bool rtl = false;
} cache;
@@ -717,13 +727,15 @@ public:
int get_item_offset(TreeItem *p_item) const;
Rect2 get_item_rect(TreeItem *p_item, int p_column = -1, int p_button = -1) const;
- bool edit_selected();
+ bool edit_selected(bool p_force_edit = false);
bool is_editing();
+ void set_editor_selection(int p_from_line, int p_to_line, int p_from_column = -1, int p_to_column = -1, int p_caret = 0);
// First item that starts with the text, from the current focused item down and wraps around.
TreeItem *search_item_text(const String &p_find, int *r_col = nullptr, bool p_selectable = false);
// First item that matches the whole text, from the first item down.
TreeItem *get_item_with_text(const String &p_find) const;
+ TreeItem *get_item_with_metadata(const Variant &p_find, int p_column = -1) const;
Point2 get_scroll() const;
void scroll_to_item(TreeItem *p_item, bool p_center_on_item = false);
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 71602d6243..cb115a6d94 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -338,6 +338,10 @@ void CanvasItem::_notification(int p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
emit_signal(SceneStringNames::get_singleton()->visibility_changed);
} break;
+ case NOTIFICATION_WORLD_2D_CHANGED: {
+ _exit_canvas();
+ _enter_canvas();
+ }
}
}
@@ -569,8 +573,7 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color
void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width, bool p_antialiased) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- Vector<Color> colors;
- colors.push_back(p_color);
+ Vector<Color> colors = { p_color };
RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width, p_antialiased);
}
@@ -598,8 +601,7 @@ void CanvasItem::draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_sta
void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- Vector<Color> colors;
- colors.push_back(p_color);
+ Vector<Color> colors = { p_color };
RenderingServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width);
}
@@ -727,8 +729,7 @@ void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color
void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- Vector<Color> colors;
- colors.push_back(p_color);
+ Vector<Color> colors = { p_color };
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid);
}
@@ -1123,6 +1124,7 @@ void CanvasItem::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
BIND_CONSTANT(NOTIFICATION_ENTER_CANVAS);
BIND_CONSTANT(NOTIFICATION_EXIT_CANVAS);
+ BIND_CONSTANT(NOTIFICATION_WORLD_2D_CHANGED);
BIND_ENUM_CONSTANT(TEXTURE_FILTER_PARENT_NODE);
BIND_ENUM_CONSTANT(TEXTURE_FILTER_NEAREST);
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 0d53f740db..ddc694f894 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -190,7 +190,9 @@ void HTTPRequest::cancel_request() {
set_process_internal(false);
} else {
thread_request_quit.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
}
file.unref();
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index b947526e96..11f2a6af66 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -47,10 +47,14 @@
#include <stdint.h>
VARIANT_ENUM_CAST(Node::ProcessMode);
+VARIANT_ENUM_CAST(Node::ProcessThreadGroup);
+VARIANT_BITFIELD_CAST(Node::ProcessThreadMessages);
VARIANT_ENUM_CAST(Node::InternalMode);
int Node::orphan_node_count = 0;
+thread_local Node *Node::current_process_thread_group = nullptr;
+
void Node::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_PROCESS: {
@@ -65,6 +69,7 @@ void Node::_notification(int p_notification) {
ERR_FAIL_COND(!get_viewport());
ERR_FAIL_COND(!get_tree());
+ // Update process mode.
if (data.process_mode == PROCESS_MODE_INHERIT) {
if (data.parent) {
data.process_owner = data.parent->data.process_owner;
@@ -77,6 +82,27 @@ void Node::_notification(int p_notification) {
data.process_owner = this;
}
+ { // Update threaded process mode.
+ if (data.process_thread_group == PROCESS_THREAD_GROUP_INHERIT) {
+ if (data.parent) {
+ data.process_thread_group_owner = data.parent->data.process_thread_group_owner;
+ }
+
+ if (data.process_thread_group_owner) {
+ data.process_group = data.process_thread_group_owner->data.process_group;
+ } else {
+ data.process_group = &data.tree->default_process_group;
+ }
+ } else {
+ data.process_thread_group_owner = this;
+ _add_process_group();
+ }
+
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
+ }
+ }
+
if (data.input) {
add_to_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
@@ -90,7 +116,7 @@ void Node::_notification(int p_notification) {
add_to_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
}
- get_tree()->node_count++;
+ get_tree()->nodes_in_tree_count++;
orphan_node_count--;
} break;
@@ -98,7 +124,7 @@ void Node::_notification(int p_notification) {
ERR_FAIL_COND(!get_viewport());
ERR_FAIL_COND(!get_tree());
- get_tree()->node_count--;
+ get_tree()->nodes_in_tree_count--;
orphan_node_count++;
if (data.input) {
@@ -114,7 +140,17 @@ void Node::_notification(int p_notification) {
remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
}
+ // Remove from processing first
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ }
+ // Remove the process group
+ if (data.process_thread_group_owner == this) {
+ _remove_process_group();
+ }
+ data.process_thread_group_owner = nullptr;
data.process_owner = nullptr;
+
if (data.path_cache) {
memdelete(data.path_cache);
data.path_cache = nullptr;
@@ -160,6 +196,16 @@ void Node::_notification(int p_notification) {
} break;
case NOTIFICATION_PREDELETE: {
+ if (data.inside_tree && !Thread::is_main_thread()) {
+ cancel_free();
+ ERR_PRINT("Attempted to free a node that is currently added to the SceneTree from a thread. This is not permitted, use queue_free() instead. Node has not been freed.");
+ return;
+ }
+
+ if (data.owner) {
+ _clean_up_owner();
+ }
+
if (data.parent) {
data.parent->remove_child(this);
}
@@ -259,11 +305,7 @@ void Node::_propagate_after_exit_tree() {
}
if (!found) {
- if (data.unique_name_in_owner) {
- _release_unique_name_in_owner();
- }
- data.owner->data.owned.erase(data.OW);
- data.owner = nullptr;
+ _clean_up_owner();
}
}
@@ -329,6 +371,7 @@ void Node::_propagate_exit_tree() {
}
void Node::move_child(Node *p_child, int p_index) {
+ ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index).");
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
@@ -440,16 +483,24 @@ void Node::owner_changed_notify() {
}
void Node::set_physics_process(bool p_process) {
+ ERR_THREAD_GUARD
if (data.physics_process == p_process) {
return;
}
+ if (!is_inside_tree()) {
+ data.physics_process = p_process;
+ return;
+ }
+
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ }
+
data.physics_process = p_process;
- if (data.physics_process) {
- add_to_group(SNAME("_physics_process"), false);
- } else {
- remove_from_group(SNAME("_physics_process"));
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
}
}
@@ -458,16 +509,24 @@ bool Node::is_physics_processing() const {
}
void Node::set_physics_process_internal(bool p_process_internal) {
+ ERR_THREAD_GUARD
if (data.physics_process_internal == p_process_internal) {
return;
}
+ if (!is_inside_tree()) {
+ data.physics_process_internal = p_process_internal;
+ return;
+ }
+
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ }
+
data.physics_process_internal = p_process_internal;
- if (data.physics_process_internal) {
- add_to_group(SNAME("_physics_process_internal"), false);
- } else {
- remove_from_group(SNAME("_physics_process_internal"));
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
}
}
@@ -476,6 +535,7 @@ bool Node::is_physics_processing_internal() const {
}
void Node::set_process_mode(ProcessMode p_mode) {
+ ERR_THREAD_GUARD
if (data.process_mode == p_mode) {
return;
}
@@ -569,6 +629,7 @@ void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int
}
void Node::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
+ ERR_THREAD_GUARD
data.multiplayer_authority = p_peer_id;
if (p_recursive) {
@@ -591,6 +652,7 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
+ ERR_THREAD_GUARD
if (data.rpc_config.get_type() != Variant::DICTIONARY) {
data.rpc_config = Dictionary();
}
@@ -762,16 +824,24 @@ double Node::get_process_delta_time() const {
}
void Node::set_process(bool p_process) {
+ ERR_THREAD_GUARD
if (data.process == p_process) {
return;
}
+ if (!is_inside_tree()) {
+ data.process = p_process;
+ return;
+ }
+
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ }
+
data.process = p_process;
- if (data.process) {
- add_to_group(SNAME("_process"), false);
- } else {
- remove_from_group(SNAME("_process"));
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
}
}
@@ -780,53 +850,201 @@ bool Node::is_processing() const {
}
void Node::set_process_internal(bool p_process_internal) {
+ ERR_THREAD_GUARD
if (data.process_internal == p_process_internal) {
return;
}
+ if (!is_inside_tree()) {
+ data.process_internal = p_process_internal;
+ return;
+ }
+
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ }
+
data.process_internal = p_process_internal;
- if (data.process_internal) {
- add_to_group(SNAME("_process_internal"), false);
- } else {
- remove_from_group(SNAME("_process_internal"));
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
+ }
+}
+
+void Node::_add_process_group() {
+ get_tree()->_add_process_group(this);
+}
+
+void Node::_remove_process_group() {
+ get_tree()->_remove_process_group(this);
+}
+
+void Node::_remove_from_process_thread_group() {
+ get_tree()->_remove_node_from_process_group(this, data.process_thread_group_owner);
+}
+
+void Node::_add_to_process_thread_group() {
+ get_tree()->_add_node_to_process_group(this, data.process_thread_group_owner);
+}
+
+void Node::_remove_tree_from_process_thread_group() {
+ if (!is_inside_tree()) {
+ return; // May not be initialized yet.
+ }
+
+ for (KeyValue<StringName, Node *> &K : data.children) {
+ if (K.value->data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) {
+ continue;
+ }
+
+ K.value->_remove_tree_from_process_thread_group();
+ }
+
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
}
}
+void Node::_add_tree_to_process_thread_group(Node *p_owner) {
+ if (_is_any_processing()) {
+ _add_to_process_thread_group();
+ }
+
+ data.process_thread_group_owner = p_owner;
+ if (p_owner != nullptr) {
+ data.process_group = p_owner->data.process_group;
+ } else {
+ data.process_group = &data.tree->default_process_group;
+ }
+
+ for (KeyValue<StringName, Node *> &K : data.children) {
+ if (K.value->data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) {
+ continue;
+ }
+
+ K.value->_add_to_process_thread_group();
+ }
+}
bool Node::is_processing_internal() const {
return data.process_internal;
}
+void Node::set_process_thread_group_order(int p_order) {
+ ERR_THREAD_GUARD
+ if (data.process_thread_group_order == p_order) {
+ return;
+ }
+ // Make sure we are in SceneTree and an actual process owner
+ if (!is_inside_tree() || data.process_thread_group_owner != this) {
+ data.process_thread_group_order = p_order;
+ return;
+ }
+
+ get_tree()->process_groups_dirty = true;
+}
+
+int Node::get_process_thread_group_order() const {
+ return data.process_thread_group_order;
+}
+
void Node::set_process_priority(int p_priority) {
- data.process_priority = p_priority;
+ ERR_THREAD_GUARD
+ if (data.process_priority == p_priority) {
+ return;
+ }
+ // Make sure we are in SceneTree and an actual process owner
+ if (!is_inside_tree()) {
+ data.process_priority = p_priority;
+ return;
+ }
- // Make sure we are in SceneTree.
- if (data.tree == nullptr) {
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ data.process_priority = p_priority;
+ _add_to_process_thread_group();
+ }
+}
+
+int Node::get_process_priority() const {
+ return data.process_priority;
+}
+
+void Node::set_physics_process_priority(int p_priority) {
+ ERR_THREAD_GUARD
+ if (data.physics_process_priority == p_priority) {
+ return;
+ }
+ // Make sure we are in SceneTree and an actual physics_process owner
+ if (!is_inside_tree()) {
+ data.physics_process_priority = p_priority;
return;
}
- if (is_processing()) {
- data.tree->make_group_changed(SNAME("_process"));
+ if (_is_any_processing()) {
+ _remove_from_process_thread_group();
+ data.physics_process_priority = p_priority;
+ _add_to_process_thread_group();
}
+}
+
+int Node::get_physics_process_priority() const {
+ return data.physics_process_priority;
+}
- if (is_processing_internal()) {
- data.tree->make_group_changed(SNAME("_process_internal"));
+void Node::set_process_thread_group(ProcessThreadGroup p_mode) {
+ ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the process thread group can only be done from the main thread. Use call_deferred(\"set_process_thread_group\",mode).");
+ if (data.process_thread_group == p_mode) {
+ return;
}
- if (is_physics_processing()) {
- data.tree->make_group_changed(SNAME("_physics_process"));
+ if (!is_inside_tree()) {
+ data.process_thread_group = p_mode;
+ return;
}
- if (is_physics_processing_internal()) {
- data.tree->make_group_changed(SNAME("_physics_process_internal"));
+ // Mode changed, must update everything.
+ _remove_tree_from_process_thread_group();
+ if (data.process_thread_group != PROCESS_THREAD_GROUP_INHERIT) {
+ _remove_process_group();
}
+
+ data.process_thread_group = p_mode;
+
+ if (p_mode == PROCESS_THREAD_GROUP_INHERIT) {
+ if (data.parent) {
+ data.process_thread_group_owner = data.parent->data.process_thread_group_owner;
+ } else {
+ data.process_thread_group_owner = nullptr;
+ }
+ } else {
+ data.process_thread_group_owner = this;
+ _add_process_group();
+ }
+
+ _add_tree_to_process_thread_group(data.process_thread_group_owner);
+
+ notify_property_list_changed();
}
-int Node::get_process_priority() const {
- return data.process_priority;
+Node::ProcessThreadGroup Node::get_process_thread_group() const {
+ return data.process_thread_group;
+}
+
+void Node::set_process_thread_messages(BitField<ProcessThreadMessages> p_flags) {
+ ERR_THREAD_GUARD
+ if (data.process_thread_group_order == p_flags) {
+ return;
+ }
+
+ data.process_thread_messages = p_flags;
+}
+
+BitField<Node::ProcessThreadMessages> Node::get_process_thread_messages() const {
+ return data.process_thread_messages;
}
void Node::set_process_input(bool p_enable) {
+ ERR_THREAD_GUARD
if (p_enable == data.input) {
return;
}
@@ -848,6 +1066,7 @@ bool Node::is_processing_input() const {
}
void Node::set_process_shortcut_input(bool p_enable) {
+ ERR_THREAD_GUARD
if (p_enable == data.shortcut_input) {
return;
}
@@ -868,6 +1087,7 @@ bool Node::is_processing_shortcut_input() const {
}
void Node::set_process_unhandled_input(bool p_enable) {
+ ERR_THREAD_GUARD
if (p_enable == data.unhandled_input) {
return;
}
@@ -888,6 +1108,7 @@ bool Node::is_processing_unhandled_input() const {
}
void Node::set_process_unhandled_key_input(bool p_enable) {
+ ERR_THREAD_GUARD
if (p_enable == data.unhandled_key_input) {
return;
}
@@ -916,6 +1137,7 @@ 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 call_deferred(\"set_name\",new_name).");
String name = p_name.validate_node_name();
ERR_FAIL_COND(name.is_empty());
@@ -1147,6 +1369,9 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalM
}
void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) {
+ ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding children to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_child\",node).");
+
+ ERR_THREAD_GUARD
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent
@@ -1160,6 +1385,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i
}
void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) {
+ ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding a sibling to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_sibling\",node).");
ERR_FAIL_NULL(p_sibling);
ERR_FAIL_NULL(data.parent);
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
@@ -1171,6 +1397,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) {
}
void Node::remove_child(Node *p_child) {
+ ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Removing children from a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"remove_child\",node).");
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy adding/removing children, `remove_child()` can't be called at this time. Consider using `remove_child.call_deferred(child)` instead.");
ERR_FAIL_COND(p_child->data.parent != this);
@@ -1241,6 +1468,7 @@ void Node::_update_children_cache_impl() const {
}
int Node::get_child_count(bool p_include_internal) const {
+ ERR_THREAD_GUARD_V(0);
_update_children_cache();
if (p_include_internal) {
@@ -1251,6 +1479,7 @@ int Node::get_child_count(bool p_include_internal) const {
}
Node *Node::get_child(int p_index, bool p_include_internal) const {
+ ERR_THREAD_GUARD_V(nullptr);
_update_children_cache();
if (p_include_internal) {
@@ -1270,6 +1499,7 @@ Node *Node::get_child(int p_index, bool p_include_internal) const {
}
TypedArray<Node> Node::get_children(bool p_include_internal) const {
+ ERR_THREAD_GUARD_V(TypedArray<Node>());
TypedArray<Node> arr;
int cc = get_child_count(p_include_internal);
arr.resize(cc);
@@ -1290,6 +1520,7 @@ Node *Node::_get_child_by_name(const StringName &p_name) const {
}
Node *Node::get_node_or_null(const NodePath &p_path) const {
+ ERR_THREAD_GUARD_V(nullptr);
if (p_path.is_empty()) {
return nullptr;
}
@@ -1395,6 +1626,7 @@ bool Node::has_node(const NodePath &p_path) const {
// Finds the first child node (in tree order) whose name matches the given pattern.
// Can be recursive or not, and limited to owned nodes.
Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const {
+ ERR_THREAD_GUARD_V(nullptr);
ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr);
_update_children_cache();
Node *const *cptr = data.children_cache.ptr();
@@ -1423,6 +1655,7 @@ Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned)
// or both (either pattern or type can be left empty).
// Can be recursive or not, and limited to owned nodes.
TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
+ ERR_THREAD_GUARD_V(TypedArray<Node>());
TypedArray<Node> ret;
ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret);
_update_children_cache();
@@ -1464,6 +1697,7 @@ TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_ty
}
void Node::reparent(Node *p_parent, bool p_keep_global_transform) {
+ ERR_THREAD_GUARD
ERR_FAIL_NULL(p_parent);
ERR_FAIL_NULL_MSG(data.parent, "Node needs a parent to be reparented.");
@@ -1480,6 +1714,7 @@ Node *Node::get_parent() const {
}
Node *Node::find_parent(const String &p_pattern) const {
+ ERR_THREAD_GUARD_V(nullptr);
Node *p = data.parent;
while (p) {
if (p->data.name.operator String().match(p_pattern)) {
@@ -1492,6 +1727,7 @@ Node *Node::find_parent(const String &p_pattern) const {
}
Window *Node::get_window() const {
+ ERR_THREAD_GUARD_V(nullptr);
Viewport *vp = get_viewport();
if (vp) {
return vp->get_base_window();
@@ -1616,6 +1852,7 @@ void Node::_acquire_unique_name_in_owner() {
}
void Node::set_unique_name_in_owner(bool p_enabled) {
+ ERR_MAIN_THREAD_GUARD
if (data.unique_name_in_owner == p_enabled) {
return;
}
@@ -1637,13 +1874,9 @@ bool Node::is_unique_name_in_owner() const {
}
void Node::set_owner(Node *p_owner) {
+ ERR_MAIN_THREAD_GUARD
if (data.owner) {
- if (data.unique_name_in_owner) {
- _release_unique_name_in_owner();
- }
- data.owner->data.owned.erase(data.OW);
- data.OW = nullptr;
- data.owner = nullptr;
+ _clean_up_owner();
}
ERR_FAIL_COND(p_owner == this);
@@ -1677,6 +1910,17 @@ Node *Node::get_owner() const {
return data.owner;
}
+void Node::_clean_up_owner() {
+ ERR_FAIL_NULL(data.owner); // Sanity check.
+
+ if (data.unique_name_in_owner) {
+ _release_unique_name_in_owner();
+ }
+ data.owner->data.owned.erase(data.OW);
+ data.owner = nullptr;
+ data.OW = nullptr;
+}
+
Node *Node::find_common_parent_with(const Node *p_node) const {
if (this == p_node) {
return const_cast<Node *>(p_node);
@@ -1820,10 +2064,12 @@ NodePath Node::get_path() const {
}
bool Node::is_in_group(const StringName &p_identifier) const {
+ ERR_THREAD_GUARD_V(false);
return data.grouped.has(p_identifier);
}
void Node::add_to_group(const StringName &p_identifier, bool p_persistent) {
+ ERR_THREAD_GUARD
ERR_FAIL_COND(!p_identifier.operator String().length());
if (data.grouped.has(p_identifier)) {
@@ -1844,6 +2090,7 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) {
}
void Node::remove_from_group(const StringName &p_identifier) {
+ ERR_THREAD_GUARD
HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier);
if (!E) {
@@ -1869,6 +2116,7 @@ TypedArray<StringName> Node::_get_groups() const {
}
void Node::get_groups(List<GroupInfo> *p_groups) const {
+ ERR_THREAD_GUARD
for (const KeyValue<StringName, GroupData> &E : data.grouped) {
GroupInfo gi;
gi.name = E.key;
@@ -1878,6 +2126,7 @@ void Node::get_groups(List<GroupInfo> *p_groups) const {
}
int Node::get_persistent_group_count() const {
+ ERR_THREAD_GUARD_V(0);
int count = 0;
for (const KeyValue<StringName, GroupData> &E : data.grouped) {
@@ -1947,6 +2196,7 @@ void Node::_propagate_deferred_notification(int p_notification, bool p_reverse)
}
void Node::propagate_notification(int p_notification) {
+ ERR_THREAD_GUARD
data.blocked++;
notification(p_notification);
@@ -1957,6 +2207,7 @@ void Node::propagate_notification(int p_notification) {
}
void Node::propagate_call(const StringName &p_method, const Array &p_args, const bool p_parent_first) {
+ ERR_THREAD_GUARD
data.blocked++;
if (p_parent_first && has_method(p_method)) {
@@ -1987,6 +2238,7 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
}
Ref<Tween> Node::create_tween() {
+ ERR_THREAD_GUARD_V(Ref<Tween>());
ERR_FAIL_COND_V_MSG(!data.tree, nullptr, "Can't create Tween when not inside scene tree.");
Ref<Tween> tween = get_tree()->create_tween();
tween->bind_node(this);
@@ -1994,6 +2246,7 @@ Ref<Tween> Node::create_tween() {
}
void Node::set_scene_file_path(const String &p_scene_file_path) {
+ ERR_THREAD_GUARD
data.scene_file_path = p_scene_file_path;
}
@@ -2002,6 +2255,7 @@ String Node::get_scene_file_path() const {
}
void Node::set_editor_description(const String &p_editor_description) {
+ ERR_THREAD_GUARD
if (data.editor_description == p_editor_description) {
return;
}
@@ -2019,6 +2273,7 @@ String Node::get_editor_description() const {
}
void Node::set_editable_instance(Node *p_node, bool p_editable) {
+ ERR_THREAD_GUARD
ERR_FAIL_NULL(p_node);
ERR_FAIL_COND(!is_ancestor_of(p_node));
if (!p_editable) {
@@ -2040,6 +2295,7 @@ bool Node::is_editable_instance(const Node *p_node) const {
}
Node *Node::get_deepest_editable_node(Node *p_start_node) const {
+ ERR_THREAD_GUARD_V(nullptr);
ERR_FAIL_NULL_V(p_start_node, nullptr);
ERR_FAIL_COND_V(!is_ancestor_of(p_start_node), p_start_node);
@@ -2059,6 +2315,7 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const {
#ifdef TOOLS_ENABLED
void Node::set_property_pinned(const String &p_property, bool p_pinned) {
+ ERR_THREAD_GUARD
bool current_pinned = false;
Array pinned = get_meta("_edit_pinned_properties_", Array());
StringName psa = get_property_store_alias(p_property);
@@ -2096,6 +2353,7 @@ bool Node::is_part_of_edited_scene() const {
#endif
void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) const {
+ ERR_THREAD_GUARD
List<PropertyInfo> pi;
get_property_list(&pi);
for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
@@ -2106,6 +2364,7 @@ void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) c
}
String Node::to_string() {
+ ERR_THREAD_GUARD_V(String());
if (get_script_instance()) {
bool valid;
String ret = get_script_instance()->to_string(&valid);
@@ -2118,6 +2377,7 @@ String Node::to_string() {
}
void Node::set_scene_instance_state(const Ref<SceneState> &p_state) {
+ ERR_THREAD_GUARD
data.instance_state = p_state;
}
@@ -2126,6 +2386,7 @@ Ref<SceneState> Node::get_scene_instance_state() const {
}
void Node::set_scene_inherited_state(const Ref<SceneState> &p_state) {
+ ERR_THREAD_GUARD
data.inherited_state = p_state;
}
@@ -2142,6 +2403,7 @@ bool Node::get_scene_instance_load_placeholder() const {
}
Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) const {
+ ERR_THREAD_GUARD_V(nullptr);
Node *node = nullptr;
bool instantiated = false;
@@ -2323,6 +2585,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c
}
Node *Node::duplicate(int p_flags) const {
+ ERR_THREAD_GUARD_V(nullptr);
Node *dupe = _duplicate(p_flags);
if (dupe && (p_flags & DUPLICATE_SIGNALS)) {
@@ -2467,6 +2730,7 @@ static void find_owned_by(Node *p_by, Node *p_node, List<Node *> *p_owned) {
}
void Node::replace_by(Node *p_node, bool p_keep_groups) {
+ ERR_THREAD_GUARD
ERR_FAIL_NULL(p_node);
ERR_FAIL_COND(p_node->data.parent);
@@ -2489,6 +2753,8 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
for (int i = 0; i < get_child_count(); i++) {
find_owned_by(data.owner, get_child(i), &owned_by_owner);
}
+
+ _clean_up_owner();
}
Node *parent = data.parent;
@@ -2536,6 +2802,7 @@ void Node::_replace_connections_target(Node *p_new_target) {
}
bool Node::has_node_and_resource(const NodePath &p_path) const {
+ ERR_THREAD_GUARD_V(false);
if (!has_node(p_path)) {
return false;
}
@@ -2570,6 +2837,7 @@ Array Node::_get_node_and_resource(const NodePath &p_path) {
}
Node *Node::get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const {
+ ERR_THREAD_GUARD_V(nullptr);
Node *node = get_node(p_path);
r_res = Ref<Resource>();
r_leftover_subpath = Vector<StringName>();
@@ -2739,6 +3007,7 @@ void Node::clear_internal_tree_resource_paths() {
}
PackedStringArray Node::get_configuration_warnings() const {
+ ERR_THREAD_GUARD_V(PackedStringArray());
PackedStringArray ret;
Vector<String> warnings;
@@ -2764,6 +3033,7 @@ String Node::get_configuration_warnings_as_string() const {
}
void Node::update_configuration_warnings() {
+ ERR_THREAD_GUARD
#ifdef TOOLS_ENABLED
if (!is_inside_tree()) {
return;
@@ -2779,6 +3049,7 @@ bool Node::is_owned_by_parent() const {
}
void Node::set_display_folded(bool p_folded) {
+ ERR_THREAD_GUARD
data.display_folded = p_folded;
}
@@ -2786,7 +3057,12 @@ bool Node::is_displayed_folded() const {
return data.display_folded;
}
+bool Node::is_ready() const {
+ return !data.ready_first;
+}
+
void Node::request_ready() {
+ ERR_THREAD_GUARD
data.ready_first = true;
}
@@ -2830,6 +3106,12 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) {
unhandled_key_input(p_event);
}
+void Node::_validate_property(PropertyInfo &p_property) const {
+ if ((p_property.name == "process_thread_group_order" || p_property.name == "process_thread_messages") && data.process_thread_group == PROCESS_THREAD_GROUP_INHERIT) {
+ p_property.usage = 0;
+ }
+}
+
void Node::input(const Ref<InputEvent> &p_event) {
}
@@ -2842,6 +3124,94 @@ void Node::unhandled_input(const Ref<InputEvent> &p_event) {
void Node::unhandled_key_input(const Ref<InputEvent> &p_key_event) {
}
+Variant Node::_call_deferred_thread_group_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ StringName method = *p_args[0];
+
+ call_deferred_thread_groupp(method, &p_args[1], p_argcount - 1, true);
+
+ return Variant();
+}
+
+Variant Node::_call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (p_argcount < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 0;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING_NAME;
+ return Variant();
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ StringName method = *p_args[0];
+
+ call_thread_safep(method, &p_args[1], p_argcount - 1, true);
+
+ return Variant();
+}
+
+void Node::call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
+ ERR_FAIL_COND(!is_inside_tree());
+ SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group;
+ pg->call_queue.push_callp(this, p_method, p_args, p_argcount, p_show_error);
+}
+void Node::set_deferred_thread_group(const StringName &p_property, const Variant &p_value) {
+ ERR_FAIL_COND(!is_inside_tree());
+ SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group;
+ pg->call_queue.push_set(this, p_property, p_value);
+}
+void Node::notify_deferred_thread_group(int p_notification) {
+ ERR_FAIL_COND(!is_inside_tree());
+ SceneTree::ProcessGroup *pg = (SceneTree::ProcessGroup *)data.process_group;
+ pg->call_queue.push_notification(this, p_notification);
+}
+
+void Node::call_thread_safep(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
+ if (is_accessible_from_caller_thread()) {
+ Callable::CallError ce;
+ callp(p_method, p_args, p_argcount, ce);
+ if (p_show_error && ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_MSG("Error calling method from 'call_threadp': " + Variant::get_call_error_text(this, p_method, p_args, p_argcount, ce) + ".");
+ }
+ } else {
+ call_deferred_thread_groupp(p_method, p_args, p_argcount, p_show_error);
+ }
+}
+void Node::set_thread_safe(const StringName &p_property, const Variant &p_value) {
+ if (is_accessible_from_caller_thread()) {
+ set(p_property, p_value);
+ } else {
+ set_deferred_thread_group(p_property, p_value);
+ }
+}
+void Node::notify_thread_safe(int p_notification) {
+ if (is_accessible_from_caller_thread()) {
+ notification(p_notification);
+ } else {
+ notify_deferred_thread_group(p_notification);
+ }
+}
+
void Node::_bind_methods() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/node_name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"), NAME_CASING_PASCAL_CASE);
@@ -2893,6 +3263,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process);
ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority);
ClassDB::bind_method(D_METHOD("get_process_priority"), &Node::get_process_priority);
+ ClassDB::bind_method(D_METHOD("set_physics_process_priority", "priority"), &Node::set_physics_process_priority);
+ ClassDB::bind_method(D_METHOD("get_physics_process_priority"), &Node::get_physics_process_priority);
ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing);
ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input);
ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input);
@@ -2906,6 +3278,15 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode);
ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process);
+ ClassDB::bind_method(D_METHOD("set_process_thread_group", "mode"), &Node::set_process_thread_group);
+ ClassDB::bind_method(D_METHOD("get_process_thread_group"), &Node::get_process_thread_group);
+
+ ClassDB::bind_method(D_METHOD("set_process_thread_messages", "flags"), &Node::set_process_thread_messages);
+ ClassDB::bind_method(D_METHOD("get_process_thread_messages"), &Node::get_process_thread_messages);
+
+ ClassDB::bind_method(D_METHOD("set_process_thread_group_order", "order"), &Node::set_process_thread_group_order);
+ ClassDB::bind_method(D_METHOD("get_process_thread_group_order"), &Node::get_process_thread_group_order);
+
ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded);
ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded);
@@ -2932,6 +3313,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_free"), &Node::queue_free);
ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready);
+ ClassDB::bind_method(D_METHOD("is_node_ready"), &Node::is_ready);
ClassDB::bind_method(D_METHOD("set_multiplayer_authority", "id", "recursive"), &Node::set_multiplayer_authority, DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_multiplayer_authority"), &Node::get_multiplayer_authority);
@@ -2972,6 +3354,26 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("update_configuration_warnings"), &Node::update_configuration_warnings);
+ {
+ MethodInfo mi;
+ mi.name = "call_deferred_thread_group";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred_thread_group", &Node::_call_deferred_thread_group_bind, mi, varray(), false);
+ }
+ ClassDB::bind_method(D_METHOD("set_deferred_thread_group", "property", "value"), &Node::set_deferred_thread_group);
+ ClassDB::bind_method(D_METHOD("notify_deferred_thread_group", "what"), &Node::notify_deferred_thread_group);
+
+ {
+ MethodInfo mi;
+ mi.name = "call_thread_safe";
+ mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_thread_safe", &Node::_call_thread_safe_bind, mi, varray(), false);
+ }
+ ClassDB::bind_method(D_METHOD("set_thread_safe", "property", "value"), &Node::set_thread_safe);
+ ClassDB::bind_method(D_METHOD("notify_thread_safe", "what"), &Node::notify_thread_safe);
+
BIND_CONSTANT(NOTIFICATION_ENTER_TREE);
BIND_CONSTANT(NOTIFICATION_EXIT_TREE);
BIND_CONSTANT(NOTIFICATION_MOVED_IN_PARENT);
@@ -3024,6 +3426,14 @@ void Node::_bind_methods() {
BIND_ENUM_CONSTANT(PROCESS_MODE_ALWAYS);
BIND_ENUM_CONSTANT(PROCESS_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_INHERIT);
+ BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_MAIN_THREAD);
+ BIND_ENUM_CONSTANT(PROCESS_THREAD_GROUP_SUB_THREAD);
+
+ BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES);
+ BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES_PHYSICS);
+ BIND_ENUM_CONSTANT(FLAG_PROCESS_THREAD_MESSAGES_ALL);
+
BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS);
BIND_ENUM_CONSTANT(DUPLICATE_GROUPS);
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
@@ -3051,6 +3461,11 @@ void Node::_bind_methods() {
ADD_GROUP("Process", "process_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_physics_priority"), "set_physics_process_priority", "get_physics_process_priority");
+ ADD_SUBGROUP("Thread Group", "process_thread");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group", PROPERTY_HINT_ENUM, "Inherit,Main Thread,Sub Thread"), "set_process_thread_group", "get_process_thread_group");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group_order"), "set_process_thread_group_order", "get_process_thread_group_order");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_messages", PROPERTY_HINT_FLAGS, "Process,Physics Process"), "set_process_thread_messages", "get_process_thread_messages");
ADD_GROUP("Editor Description", "editor_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT), "set_editor_description", "get_editor_description");
diff --git a/scene/main/node.h b/scene/main/node.h
index b355c27b04..ad19a1b694 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -54,6 +54,18 @@ public:
PROCESS_MODE_DISABLED, // never process
};
+ enum ProcessThreadGroup {
+ PROCESS_THREAD_GROUP_INHERIT,
+ PROCESS_THREAD_GROUP_MAIN_THREAD,
+ PROCESS_THREAD_GROUP_SUB_THREAD,
+ };
+
+ enum ProcessThreadMessages {
+ FLAG_PROCESS_THREAD_MESSAGES = 1,
+ FLAG_PROCESS_THREAD_MESSAGES_PHYSICS = 2,
+ FLAG_PROCESS_THREAD_MESSAGES_ALL = 3,
+ };
+
enum DuplicateFlags {
DUPLICATE_SIGNALS = 1,
DUPLICATE_GROUPS = 2,
@@ -80,12 +92,10 @@ public:
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
- struct ComparatorWithPriority {
- bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
- };
-
static int orphan_node_count;
+ void _update_process(bool p_enable, bool p_for_children);
+
private:
struct GroupData {
bool persistent = false;
@@ -104,6 +114,14 @@ private:
}
};
+ struct ComparatorWithPriority {
+ bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
+ };
+
+ struct ComparatorWithPhysicsPriority {
+ bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.physics_process_priority == p_a->data.physics_process_priority ? p_b->is_greater_than(p_a) : p_b->data.physics_process_priority > p_a->data.physics_process_priority; }
+ };
+
// This Data struct is to avoid namespace pollution in derived classes.
struct Data {
String scene_file_path;
@@ -142,6 +160,11 @@ private:
ProcessMode process_mode = PROCESS_MODE_INHERIT;
Node *process_owner = nullptr;
+ ProcessThreadGroup process_thread_group = PROCESS_THREAD_GROUP_INHERIT;
+ Node *process_thread_group_owner = nullptr;
+ int process_thread_group_order = 0;
+ BitField<ProcessThreadMessages> process_thread_messages;
+ void *process_group = nullptr; // to avoid cyclic dependency
int multiplayer_authority = 1; // Server by default.
Variant rpc_config;
@@ -151,6 +174,7 @@ private:
bool physics_process = false;
bool process = false;
int process_priority = 0;
+ int physics_process_priority = 0;
bool physics_process_internal = false;
bool process_internal = false;
@@ -212,6 +236,8 @@ private:
void _release_unique_name_in_owner();
void _acquire_unique_name_in_owner();
+ void _clean_up_owner();
+
_FORCE_INLINE_ void _update_children_cache() const {
if (unlikely(data.children_cache_dirty)) {
_update_children_cache_impl();
@@ -220,6 +246,19 @@ private:
void _update_children_cache_impl() const;
+ // Process group management
+ void _add_process_group();
+ void _remove_process_group();
+ void _add_to_process_thread_group();
+ void _remove_from_process_thread_group();
+ void _remove_tree_from_process_thread_group();
+ void _add_tree_to_process_thread_group(Node *p_owner);
+
+ static thread_local Node *current_process_thread_group;
+
+ Variant _call_deferred_thread_group_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Variant _call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@@ -248,6 +287,8 @@ protected:
void _call_unhandled_input(const Ref<InputEvent> &p_event);
void _call_unhandled_key_input(const Ref<InputEvent> &p_event);
+ void _validate_property(PropertyInfo &p_property) const;
+
protected:
virtual void input(const Ref<InputEvent> &p_event);
virtual void shortcut_input(const Ref<InputEvent> &p_key_event);
@@ -456,6 +497,12 @@ public:
void set_process_priority(int p_priority);
int get_process_priority() const;
+ void set_process_thread_group_order(int p_order);
+ int get_process_thread_group_order() const;
+
+ void set_physics_process_priority(int p_priority);
+ int get_physics_process_priority() const;
+
void set_process_input(bool p_enable);
bool is_processing_input() const;
@@ -468,6 +515,23 @@ public:
void set_process_unhandled_key_input(bool p_enable);
bool is_processing_unhandled_key_input() const;
+ _FORCE_INLINE_ bool _is_any_processing() const {
+ return data.process || data.process_internal || data.physics_process || data.physics_process_internal;
+ }
+ _FORCE_INLINE_ bool is_accessible_from_caller_thread() const {
+ if (current_process_thread_group == nullptr) {
+ // Not thread processing. Only accessible if node is outside the scene tree,
+ // or if accessing from the main thread.
+ return !data.inside_tree || Thread::is_main_thread();
+ } else {
+ // Thread processing
+ return current_process_thread_group == data.process_thread_group_owner;
+ }
+ }
+
+ void set_process_thread_messages(BitField<ProcessThreadMessages> p_flags);
+ BitField<ProcessThreadMessages> get_process_thread_messages() const;
+
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;
#ifdef TOOLS_ENABLED
Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const;
@@ -499,9 +563,13 @@ public:
bool can_process() const;
bool can_process_notification(int p_what) const;
bool is_enabled() const;
+ bool is_ready() const;
void request_ready();
+ void set_process_thread_group(ProcessThreadGroup p_mode);
+ ProcessThreadGroup get_process_thread_group() const;
+
static void print_orphan_nodes();
#ifdef TOOLS_ENABLED
@@ -553,6 +621,32 @@ public:
Ref<MultiplayerAPI> get_multiplayer() const;
+ void call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ template <typename... VarArgs>
+ void call_deferred_thread_group(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+ void set_deferred_thread_group(const StringName &p_property, const Variant &p_value);
+ void notify_deferred_thread_group(int p_notification);
+
+ void call_thread_safep(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ template <typename... VarArgs>
+ void call_thread_safe(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+ void set_thread_safe(const StringName &p_property, const Variant &p_value);
+ void notify_thread_safe(int p_notification);
+
Node();
~Node();
};
@@ -579,6 +673,18 @@ Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args)
return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
+#ifdef DEBUG_ENABLED
+#define ERR_THREAD_GUARD ERR_FAIL_COND_MSG(!is_accessible_from_caller_thread(), "Caller thread can't call this function in this node. Use call_deferred() or call_thread_group() instead.");
+#define ERR_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_accessible_from_caller_thread(), (m_ret), "Caller thread can't call this function in this node. Use call_deferred() or call_thread_group() instead.")
+#define ERR_MAIN_THREAD_GUARD ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.");
+#define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !Thread::is_main_thread(), (m_ret), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.")
+#else
+#define ERR_THREAD_GUARD
+#define ERR_THREAD_GUARD_V(m_ret)
+#define ERR_MAIN_THREAD_GUARD
+#define ERR_MAIN_THREAD_GUARD_V(m_ret)
+#endif
+
// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector.
#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration."));
#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions."));
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index b6b694ff55..892cbb313b 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -38,6 +38,7 @@
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
#include "core/object/message_queue.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -60,7 +61,6 @@
#include "servers/physics_server_2d.h"
#include "servers/physics_server_3d.h"
#include "window.h"
-
#include <stdio.h>
#include <stdlib.h>
@@ -126,12 +126,13 @@ void SceneTree::node_added(Node *p_node) {
}
void SceneTree::node_removed(Node *p_node) {
+ // Nodes can only be removed from the main thread.
if (current_scene == p_node) {
current_scene = nullptr;
}
emit_signal(node_removed_name, p_node);
- if (call_lock > 0) {
- call_skip.insert(p_node);
+ if (nodes_removed_on_group_call_lock) {
+ nodes_removed_on_group_call.insert(p_node);
}
}
@@ -140,6 +141,8 @@ void SceneTree::node_renamed(Node *p_node) {
}
SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) {
+ _THREAD_SAFE_METHOD_
+
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
E = group_map.insert(p_group, Group());
@@ -153,6 +156,8 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod
}
void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
+ _THREAD_SAFE_METHOD_
+
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
ERR_FAIL_COND(!E);
@@ -163,6 +168,7 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) {
}
void SceneTree::make_group_changed(const StringName &p_group) {
+ _THREAD_SAFE_METHOD_
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (E) {
E->value.changed = true;
@@ -170,6 +176,8 @@ void SceneTree::make_group_changed(const StringName &p_group) {
}
void SceneTree::flush_transform_notifications() {
+ _THREAD_SAFE_METHOD_
+
SelfList<Node> *n = xform_change_list.first();
while (n) {
Node *node = n->self();
@@ -200,7 +208,7 @@ void SceneTree::_flush_ugc() {
ugc_locked = false;
}
-void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
+void SceneTree::_update_group_order(Group &g) {
if (!g.changed) {
return;
}
@@ -211,57 +219,62 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
Node **gr_nodes = g.nodes.ptrw();
int gr_node_count = g.nodes.size();
- if (p_use_priority) {
- SortArray<Node *, Node::ComparatorWithPriority> node_sort;
- node_sort.sort(gr_nodes, gr_node_count);
- } else {
- SortArray<Node *, Node::Comparator> node_sort;
- node_sort.sort(gr_nodes, gr_node_count);
- }
+ SortArray<Node *, Node::Comparator> node_sort;
+ node_sort.sort(gr_nodes, gr_node_count);
+
g.changed = false;
}
void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount) {
- HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
- if (!E) {
- return;
- }
- Group &g = E->value;
- if (g.nodes.is_empty()) {
- return;
- }
-
- if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) {
- ERR_FAIL_COND(ugc_locked);
+ Vector<Node *> nodes_copy;
- UGCall ug;
- ug.call = p_function;
- ug.group = p_group;
+ {
+ _THREAD_SAFE_METHOD_
- if (unique_group_calls.has(ug)) {
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
+ if (!E) {
return;
}
+ Group &g = E->value;
+ if (g.nodes.is_empty()) {
+ return;
+ }
+
+ if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) {
+ ERR_FAIL_COND(ugc_locked);
+
+ UGCall ug;
+ ug.call = p_function;
+ ug.group = p_group;
+
+ if (unique_group_calls.has(ug)) {
+ return;
+ }
- Vector<Variant> args;
- for (int i = 0; i < p_argcount; i++) {
- args.push_back(*p_args[i]);
+ Vector<Variant> args;
+ for (int i = 0; i < p_argcount; i++) {
+ args.push_back(*p_args[i]);
+ }
+
+ unique_group_calls[ug] = args;
+ return;
}
- unique_group_calls[ug] = args;
- return;
+ _update_group_order(g);
+ nodes_copy = g.nodes;
}
- _update_group_order(g);
-
- Vector<Node *> nodes_copy = g.nodes;
Node **gr_nodes = nodes_copy.ptrw();
int gr_node_count = nodes_copy.size();
- call_lock++;
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock++;
+ }
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call_lock && nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -275,7 +288,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call_lock && nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -288,33 +301,44 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
}
}
- call_lock--;
- if (call_lock == 0) {
- call_skip.clear();
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock--;
+ if (nodes_removed_on_group_call_lock == 0) {
+ nodes_removed_on_group_call.clear();
+ }
}
}
void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification) {
- HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
- if (!E) {
- return;
- }
- Group &g = E->value;
- if (g.nodes.is_empty()) {
- return;
- }
+ Vector<Node *> nodes_copy;
+ {
+ _THREAD_SAFE_METHOD_
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
+ if (!E) {
+ return;
+ }
+ Group &g = E->value;
+ if (g.nodes.is_empty()) {
+ return;
+ }
- _update_group_order(g);
+ _update_group_order(g);
+
+ nodes_copy = g.nodes;
+ }
- Vector<Node *> nodes_copy = g.nodes;
Node **gr_nodes = nodes_copy.ptrw();
int gr_node_count = nodes_copy.size();
- call_lock++;
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock++;
+ }
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -327,7 +351,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -339,33 +363,44 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
}
}
- call_lock--;
- if (call_lock == 0) {
- call_skip.clear();
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock--;
+ if (nodes_removed_on_group_call_lock == 0) {
+ nodes_removed_on_group_call.clear();
+ }
}
}
void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value) {
- HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
- if (!E) {
- return;
- }
- Group &g = E->value;
- if (g.nodes.is_empty()) {
- return;
- }
+ Vector<Node *> nodes_copy;
+ {
+ _THREAD_SAFE_METHOD_
- _update_group_order(g);
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
+ if (!E) {
+ return;
+ }
+ Group &g = E->value;
+ if (g.nodes.is_empty()) {
+ return;
+ }
+
+ _update_group_order(g);
- Vector<Node *> nodes_copy = g.nodes;
+ nodes_copy = g.nodes;
+ }
Node **gr_nodes = nodes_copy.ptrw();
int gr_node_count = nodes_copy.size();
- call_lock++;
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock++;
+ }
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -378,7 +413,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (nodes_removed_on_group_call.has(gr_nodes[i])) {
continue;
}
@@ -390,9 +425,12 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
}
}
- call_lock--;
- if (call_lock == 0) {
- call_skip.clear();
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock--;
+ if (nodes_removed_on_group_call_lock == 0) {
+ nodes_removed_on_group_call.clear();
+ }
}
}
@@ -423,9 +461,10 @@ bool SceneTree::physics_process(double p_time) {
emit_signal(SNAME("physics_frame"));
- _notify_group_pause(SNAME("_physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
call_group(SNAME("_picking_viewports"), SNAME("_process_picking"));
- _notify_group_pause(SNAME("_physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS);
+
+ _process(true);
+
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack
@@ -462,8 +501,7 @@ bool SceneTree::process(double p_time) {
flush_transform_notifications();
- _notify_group_pause(SNAME("_process_internal"), Node::NOTIFICATION_INTERNAL_PROCESS);
- _notify_group_pause(SNAME("_process"), Node::NOTIFICATION_PROCESS);
+ _process(false);
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack
@@ -512,6 +550,7 @@ bool SceneTree::process(double p_time) {
}
void SceneTree::process_timers(double p_delta, bool p_physics_frame) {
+ _THREAD_SAFE_METHOD_
List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element
for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) {
@@ -544,6 +583,7 @@ void SceneTree::process_timers(double p_delta, bool p_physics_frame) {
}
void SceneTree::process_tweens(double p_delta, bool p_physics) {
+ _THREAD_SAFE_METHOD_
// This methods works similarly to how SceneTreeTimers are handled.
List<Ref<Tween>>::Element *L = tweens.back();
@@ -603,6 +643,8 @@ void SceneTree::finalize() {
}
void SceneTree::quit(int p_exit_code) {
+ _THREAD_SAFE_METHOD_
+
OS::get_singleton()->set_exit_code(p_exit_code);
_quit = true;
}
@@ -695,6 +737,14 @@ void SceneTree::set_debug_navigation_hint(bool p_enabled) {
bool SceneTree::is_debugging_navigation_hint() const {
return debug_navigation_hint;
}
+
+void SceneTree::set_debug_avoidance_hint(bool p_enabled) {
+ debug_avoidance_hint = p_enabled;
+}
+
+bool SceneTree::is_debugging_avoidance_hint() const {
+ return debug_avoidance_hint;
+}
#endif
void SceneTree::set_debug_collisions_color(const Color &p_color) {
@@ -730,6 +780,8 @@ float SceneTree::get_debug_paths_width() const {
}
Ref<Material> SceneTree::get_debug_paths_material() {
+ _THREAD_SAFE_METHOD_
+
if (debug_paths_material.is_valid()) {
return debug_paths_material;
}
@@ -747,6 +799,8 @@ Ref<Material> SceneTree::get_debug_paths_material() {
}
Ref<Material> SceneTree::get_debug_collision_material() {
+ _THREAD_SAFE_METHOD_
+
if (collision_material.is_valid()) {
return collision_material;
}
@@ -764,6 +818,8 @@ Ref<Material> SceneTree::get_debug_collision_material() {
}
Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
+ _THREAD_SAFE_METHOD_
+
if (debug_contact_mesh.is_valid()) {
return debug_contact_mesh;
}
@@ -821,6 +877,8 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
}
void SceneTree::set_pause(bool p_enabled) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread.");
+
if (p_enabled == paused) {
return;
}
@@ -836,70 +894,282 @@ bool SceneTree::is_paused() const {
return paused;
}
-void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) {
- HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
- if (!E) {
- return;
- }
- Group &g = E->value;
- if (g.nodes.is_empty()) {
+void SceneTree::_process_group(ProcessGroup *p_group, bool p_physics) {
+ // When reading this function, keep in mind that this code must work in a way where
+ // if any node is removed, this needs to continue working.
+
+ p_group->call_queue.flush(); // Flush messages before processing.
+
+ Vector<Node *> &nodes = p_physics ? p_group->physics_nodes : p_group->nodes;
+ if (nodes.is_empty()) {
return;
}
- _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
+ bool &node_order_dirty = p_physics ? p_group->physics_node_order_dirty : p_group->node_order_dirty;
- //copy, so copy on write happens in case something is removed from process while being called
- //performance is not lost because only if something is added/removed the vector is copied.
- Vector<Node *> nodes_copy = g.nodes;
+ if (node_order_dirty) {
+ nodes.sort_custom<Node::ComparatorWithPhysicsPriority>();
+ node_order_dirty = false;
+ }
- int gr_node_count = nodes_copy.size();
- Node **gr_nodes = nodes_copy.ptrw();
+ // Make a copy, so if nodes are added/removed from process, this does not break
+ Vector<Node *> nodes_copy = nodes;
- call_lock++;
+ uint32_t node_count = nodes_copy.size();
+ Node **nodes_ptr = (Node **)nodes_copy.ptr(); // Force cast, pointer will not change.
- for (int i = 0; i < gr_node_count; i++) {
- Node *n = gr_nodes[i];
- if (call_lock && call_skip.has(n)) {
+ for (uint32_t i = 0; i < node_count; i++) {
+ Node *n = nodes_ptr[i];
+ if (nodes_removed_on_group_call.has(n)) {
+ // Node may have been removed during process, skip it.
+ // Keep in mind removals can only happen on the main thread.
continue;
}
- if (!n->can_process()) {
+ if (!n->can_process() || !n->is_inside_tree()) {
continue;
}
- if (!n->can_process_notification(p_notification)) {
+
+ if (p_physics) {
+ if (n->is_physics_processing()) {
+ n->notification(Node::NOTIFICATION_PHYSICS_PROCESS);
+ }
+ if (n->is_physics_processing_internal()) {
+ n->notification(Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
+ }
+ } else {
+ if (n->is_processing()) {
+ n->notification(Node::NOTIFICATION_PROCESS);
+ }
+ if (n->is_processing_internal()) {
+ n->notification(Node::NOTIFICATION_INTERNAL_PROCESS);
+ }
+ }
+ }
+
+ p_group->call_queue.flush(); // Flush messages also after processing (for potential deferred calls).
+}
+
+void SceneTree::_process_groups_thread(uint32_t p_index, bool p_physics) {
+ Node::current_process_thread_group = local_process_group_cache[p_index]->owner;
+ _process_group(local_process_group_cache[p_index], p_physics);
+ Node::current_process_thread_group = nullptr;
+}
+
+void SceneTree::_process(bool p_physics) {
+ if (process_groups_dirty) {
+ {
+ // First, remove dirty groups.
+ // This needs to be done when not processing to avoid problems.
+ ProcessGroup **pg_ptr = (ProcessGroup **)process_groups.ptr(); // discard constness.
+ uint32_t pg_count = process_groups.size();
+
+ for (uint32_t i = 0; i < pg_count; i++) {
+ if (pg_ptr[i]->removed) {
+ // Replace removed with last.
+ pg_ptr[i] = pg_ptr[pg_count - 1];
+ // Retry
+ i--;
+ pg_count--;
+ }
+ }
+ if (pg_count != process_groups.size()) {
+ process_groups.resize(pg_count);
+ }
+ }
+ {
+ // Then, re-sort groups.
+ process_groups.sort_custom<ProcessGroupSort>();
+ }
+
+ process_groups_dirty = false;
+ }
+
+ // Cache the group count, because during processing new groups may be added.
+ // They will be added at the end, hence for consistency they will be ignored by this process loop.
+ // No group will be removed from the array during processing (this is done earlier in this function by marking the groups dirty).
+ uint32_t group_count = process_groups.size();
+
+ if (group_count == 0) {
+ return;
+ }
+
+ process_last_pass++; // Increment pass
+ uint32_t from = 0;
+ uint32_t process_count = 0;
+ nodes_removed_on_group_call_lock++;
+
+ int current_order = process_groups[0]->owner ? process_groups[0]->owner->data.process_thread_group_order : 0;
+ bool current_threaded = process_groups[0]->owner ? process_groups[0]->owner->data.process_thread_group == Node::PROCESS_THREAD_GROUP_SUB_THREAD : false;
+
+ for (uint32_t i = 0; i <= group_count; i++) {
+ int order = i < group_count && process_groups[i]->owner ? process_groups[i]->owner->data.process_thread_group_order : 0;
+ bool threaded = i < group_count && process_groups[i]->owner ? process_groups[i]->owner->data.process_thread_group == Node::PROCESS_THREAD_GROUP_SUB_THREAD : false;
+
+ if (i == group_count || current_order != order || current_threaded != threaded) {
+ if (process_count > 0) {
+ // Proceed to process the group.
+ bool using_threads = process_groups[from]->owner && process_groups[from]->owner->data.process_thread_group == Node::PROCESS_THREAD_GROUP_SUB_THREAD && !node_threading_disabled;
+
+ if (using_threads) {
+ local_process_group_cache.clear();
+ }
+ for (uint32_t j = from; j < i; j++) {
+ if (process_groups[j]->last_pass == process_last_pass) {
+ if (using_threads) {
+ local_process_group_cache.push_back(process_groups[j]);
+ } else {
+ _process_group(process_groups[j], p_physics);
+ }
+ }
+ }
+
+ if (using_threads) {
+ WorkerThreadPool::GroupID id = WorkerThreadPool::get_singleton()->add_template_group_task(this, &SceneTree::_process_groups_thread, p_physics, local_process_group_cache.size(), -1, true);
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(id);
+ }
+ }
+
+ if (i == group_count) {
+ // This one is invalid, no longer process
+ break;
+ }
+
+ from = i;
+ current_threaded = threaded;
+ current_order = order;
+ }
+
+ if (process_groups[i]->removed) {
continue;
}
- n->notification(p_notification);
- //ERR_FAIL_COND(gr_node_count != g.nodes.size());
+ ProcessGroup *pg = process_groups[i];
+
+ // Validate group for processing
+ bool process_valid = false;
+ if (p_physics) {
+ if (!pg->physics_nodes.is_empty()) {
+ process_valid = true;
+ } else if (pg->owner != nullptr && pg->owner->data.process_thread_messages.has_flag(Node::FLAG_PROCESS_THREAD_MESSAGES_PHYSICS) && pg->call_queue.has_messages()) {
+ process_valid = true;
+ }
+ } else {
+ if (!pg->nodes.is_empty()) {
+ process_valid = true;
+ } else if (pg->owner != nullptr && pg->owner->data.process_thread_messages.has_flag(Node::FLAG_PROCESS_THREAD_MESSAGES) && pg->call_queue.has_messages()) {
+ process_valid = true;
+ }
+ }
+
+ if (process_valid) {
+ pg->last_pass = process_last_pass; // Enable for processing
+ process_count++;
+ }
}
- call_lock--;
- if (call_lock == 0) {
- call_skip.clear();
+ nodes_removed_on_group_call_lock--;
+ if (nodes_removed_on_group_call_lock == 0) {
+ nodes_removed_on_group_call.clear();
}
}
-void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport) {
- HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
- if (!E) {
- return;
+bool SceneTree::ProcessGroupSort::operator()(const ProcessGroup *p_left, const ProcessGroup *p_right) const {
+ int left_order = p_left->owner ? p_left->owner->data.process_thread_group_order : 0;
+ int right_order = p_right->owner ? p_right->owner->data.process_thread_group_order : 0;
+
+ if (left_order == right_order) {
+ int left_threaded = p_left->owner != nullptr && p_left->owner->data.process_thread_group == Node::PROCESS_THREAD_GROUP_SUB_THREAD ? 0 : 1;
+ int right_threaded = p_right->owner != nullptr && p_right->owner->data.process_thread_group == Node::PROCESS_THREAD_GROUP_SUB_THREAD ? 0 : 1;
+ return left_threaded < right_threaded;
+ } else {
+ return left_order < right_order;
}
- Group &g = E->value;
- if (g.nodes.is_empty()) {
- return;
+}
+
+void SceneTree::_remove_process_group(Node *p_node) {
+ _THREAD_SAFE_METHOD_
+ ProcessGroup *pg = (ProcessGroup *)p_node->data.process_group;
+ ERR_FAIL_COND(!pg);
+ ERR_FAIL_COND(pg->removed);
+ pg->removed = true;
+ pg->owner = nullptr;
+ p_node->data.process_group = nullptr;
+ process_groups_dirty = true;
+}
+
+void SceneTree::_add_process_group(Node *p_node) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!p_node);
+
+ ProcessGroup *pg = memnew(ProcessGroup);
+
+ pg->owner = p_node;
+ p_node->data.process_group = pg;
+
+ process_groups.push_back(pg);
+
+ process_groups_dirty = true;
+}
+
+void SceneTree::_remove_node_from_process_group(Node *p_node, Node *p_owner) {
+ _THREAD_SAFE_METHOD_
+ ProcessGroup *pg = p_owner ? (ProcessGroup *)p_owner->data.process_group : &default_process_group;
+
+ if (p_node->is_processing() || p_node->is_processing_internal()) {
+ bool found = pg->nodes.erase(p_node);
+ ERR_FAIL_COND(!found);
+ }
+
+ if (p_node->is_physics_processing() || p_node->is_physics_processing_internal()) {
+ bool found = pg->physics_nodes.erase(p_node);
+ ERR_FAIL_COND(!found);
+ }
+}
+
+void SceneTree::_add_node_to_process_group(Node *p_node, Node *p_owner) {
+ _THREAD_SAFE_METHOD_
+ ProcessGroup *pg = p_owner ? (ProcessGroup *)p_owner->data.process_group : &default_process_group;
+
+ if (p_node->is_processing() || p_node->is_processing_internal()) {
+ pg->nodes.push_back(p_node);
+ pg->node_order_dirty = true;
}
- _update_group_order(g);
+ if (p_node->is_physics_processing() || p_node->is_physics_processing_internal()) {
+ pg->physics_nodes.push_back(p_node);
+ pg->physics_node_order_dirty = true;
+ }
+}
- //copy, so copy on write happens in case something is removed from process while being called
- //performance is not lost because only if something is added/removed the vector is copied.
- Vector<Node *> nodes_copy = g.nodes;
+void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport) {
+ Vector<Node *> nodes_copy;
+ {
+ _THREAD_SAFE_METHOD_
+
+ HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
+ if (!E) {
+ return;
+ }
+ Group &g = E->value;
+ if (g.nodes.is_empty()) {
+ return;
+ }
+
+ _update_group_order(g);
+
+ //copy, so copy on write happens in case something is removed from process while being called
+ //performance is not lost because only if something is added/removed the vector is copied.
+ nodes_copy = g.nodes;
+ }
int gr_node_count = nodes_copy.size();
Node **gr_nodes = nodes_copy.ptrw();
- call_lock++;
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock++;
+ }
Vector<ObjectID> no_context_node_ids; // Nodes may be deleted due to this shortcut input.
@@ -909,7 +1179,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
Node *n = gr_nodes[i];
- if (call_lock && call_skip.has(n)) {
+ if (nodes_removed_on_group_call.has(n)) {
continue;
}
@@ -956,9 +1226,12 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
}
- call_lock--;
- if (call_lock == 0) {
- call_skip.clear();
+ {
+ _THREAD_SAFE_METHOD_
+ nodes_removed_on_group_call_lock--;
+ if (nodes_removed_on_group_call_lock == 0) {
+ nodes_removed_on_group_call.clear();
+ }
}
}
@@ -995,6 +1268,7 @@ int64_t SceneTree::get_frame() const {
}
TypedArray<Node> SceneTree::_get_nodes_in_group(const StringName &p_group) {
+ _THREAD_SAFE_METHOD_
TypedArray<Node> ret;
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
@@ -1018,10 +1292,12 @@ TypedArray<Node> SceneTree::_get_nodes_in_group(const StringName &p_group) {
}
bool SceneTree::has_group(const StringName &p_identifier) const {
+ _THREAD_SAFE_METHOD_
return group_map.has(p_identifier);
}
Node *SceneTree::get_first_node_in_group(const StringName &p_group) {
+ _THREAD_SAFE_METHOD_
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return nullptr; // No group.
@@ -1037,6 +1313,7 @@ Node *SceneTree::get_first_node_in_group(const StringName &p_group) {
}
void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_list) {
+ _THREAD_SAFE_METHOD_
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
if (!E) {
return;
@@ -1073,7 +1350,7 @@ void SceneTree::queue_delete(Object *p_object) {
}
int SceneTree::get_node_count() const {
- return node_count;
+ return nodes_in_tree_count;
}
void SceneTree::set_edited_scene_root(Node *p_node) {
@@ -1091,6 +1368,7 @@ Node *SceneTree::get_edited_scene_root() const {
}
void SceneTree::set_current_scene(Node *p_scene) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread.");
ERR_FAIL_COND(p_scene && p_scene->get_parent() != root);
current_scene = p_scene;
}
@@ -1100,6 +1378,7 @@ Node *SceneTree::get_current_scene() const {
}
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;
@@ -1121,6 +1400,7 @@ void SceneTree::_change_scene(Node *p_to) {
}
Error SceneTree::change_scene_to_file(const String &p_path) {
+ ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Changing scene can only be done from the main thread.");
Ref<PackedScene> new_scene = ResourceLoader::load(p_path);
if (new_scene.is_null()) {
return ERR_CANT_OPEN;
@@ -1140,12 +1420,14 @@ Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) {
}
Error SceneTree::reload_current_scene() {
+ ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Reloading scene can only be done from the main thread.");
ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED);
String fname = current_scene->get_scene_file_path();
return change_scene_to_file(fname);
}
void SceneTree::unload_current_scene() {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Unloading the current scene can only be done from the main thread.");
if (current_scene) {
memdelete(current_scene);
current_scene = nullptr;
@@ -1153,11 +1435,13 @@ void SceneTree::unload_current_scene() {
}
void SceneTree::add_current_scene(Node *p_current) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Adding a current scene can only be done from the main thread.");
current_scene = p_current;
root->add_child(p_current);
}
Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_always, bool p_process_in_physics, bool p_ignore_time_scale) {
+ _THREAD_SAFE_METHOD_
Ref<SceneTreeTimer> stt;
stt.instantiate();
stt->set_process_always(p_process_always);
@@ -1169,12 +1453,14 @@ Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_a
}
Ref<Tween> SceneTree::create_tween() {
+ _THREAD_SAFE_METHOD_
Ref<Tween> tween = memnew(Tween(true));
tweens.push_back(tween);
return tween;
}
TypedArray<Tween> SceneTree::get_processed_tweens() {
+ _THREAD_SAFE_METHOD_
TypedArray<Tween> ret;
ret.resize(tweens.size());
@@ -1188,6 +1474,7 @@ TypedArray<Tween> SceneTree::get_processed_tweens() {
}
Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
+ ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref<MultiplayerAPI>(), "Multiplayer can only be manipulated from the main thread.");
Ref<MultiplayerAPI> out = multiplayer;
for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
const Vector<StringName> snames = E.key.get_names();
@@ -1213,6 +1500,7 @@ Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const
}
void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
if (p_root_path.is_empty()) {
ERR_FAIL_COND(!p_multiplayer.is_valid());
if (multiplayer.is_valid()) {
@@ -1232,6 +1520,7 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
}
void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread.");
multiplayer_poll = p_enabled;
}
@@ -1385,6 +1674,10 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li
}
}
+void SceneTree::set_disable_node_threading(bool p_disable) {
+ node_threading_disabled = p_disable;
+}
+
SceneTree::SceneTree() {
if (singleton == nullptr) {
singleton = this;
@@ -1397,6 +1690,7 @@ SceneTree::SceneTree() {
GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true);
+ process_group_call_queue_allocator = memnew(CallQueue::Allocator(64));
Math::randomize();
// Create with mainloop.
@@ -1534,6 +1828,8 @@ SceneTree::SceneTree() {
#ifdef TOOLS_ENABLED
edited_scene_root = nullptr;
#endif
+
+ process_groups.push_back(&default_process_group);
}
SceneTree::~SceneTree() {
@@ -1543,6 +1839,15 @@ SceneTree::~SceneTree() {
memdelete(root);
}
+ // Process groups are not deleted immediately, they may remain around. Delete them now.
+ for (uint32_t i = 0; i < process_groups.size(); i++) {
+ if (process_groups[i] != &default_process_group) {
+ memdelete(process_groups[i]);
+ }
+ }
+
+ memdelete(process_group_call_queue_allocator);
+
if (singleton == this) {
singleton = nullptr;
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index fc185b4f37..4e7a10c7e9 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -33,6 +33,7 @@
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
+#include "core/templates/paged_allocator.h"
#include "core/templates/self_list.h"
#include "scene/resources/mesh.h"
@@ -86,6 +87,34 @@ public:
typedef void (*IdleCallback)();
private:
+ CallQueue::Allocator *process_group_call_queue_allocator = nullptr;
+
+ struct ProcessGroup {
+ CallQueue call_queue;
+ Vector<Node *> nodes;
+ Vector<Node *> physics_nodes;
+ bool node_order_dirty = true;
+ bool physics_node_order_dirty = true;
+ bool removed = false;
+ Node *owner = nullptr;
+ uint64_t last_pass = 0;
+ };
+
+ struct ProcessGroupSort {
+ _FORCE_INLINE_ bool operator()(const ProcessGroup *p_left, const ProcessGroup *p_right) const;
+ };
+
+ PagedAllocator<ProcessGroup, true> group_allocator; // Allocate groups on pages, to enhance cache usage.
+
+ LocalVector<ProcessGroup *> process_groups;
+ bool process_groups_dirty = true;
+ LocalVector<ProcessGroup *> local_process_group_cache; // Used when processing to group what needs to
+ uint64_t process_last_pass = 1;
+
+ ProcessGroup default_process_group;
+
+ bool node_threading_disabled = false;
+
struct Group {
Vector<Node *> nodes;
bool changed = false;
@@ -103,6 +132,7 @@ private:
bool debug_collisions_hint = false;
bool debug_paths_hint = false;
bool debug_navigation_hint = false;
+ bool debug_avoidance_hint = false;
#endif
bool paused = false;
int root_lock = 0;
@@ -117,7 +147,7 @@ private:
StringName node_renamed_name = "node_renamed";
int64_t current_frame = 0;
- int node_count = 0;
+ int nodes_in_tree_count = 0;
#ifdef TOOLS_ENABLED
Node *edited_scene_root = nullptr;
@@ -134,8 +164,10 @@ private:
};
// Safety for when a node is deleted while a group is being called.
- int call_lock = 0;
- HashSet<Node *> call_skip; // Skip erased nodes.
+
+ bool processing = false;
+ int nodes_removed_on_group_call_lock = 0;
+ HashSet<Node *> nodes_removed_on_group_call; // Skip erased nodes.
List<ObjectID> delete_queue;
@@ -143,7 +175,7 @@ private:
bool ugc_locked = false;
void _flush_ugc();
- _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false);
+ _FORCE_INLINE_ void _update_group_order(Group &g);
TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
@@ -187,7 +219,15 @@ private:
void remove_from_group(const StringName &p_group, Node *p_node);
void make_group_changed(const StringName &p_group);
- void _notify_group_pause(const StringName &p_group, int p_notification);
+ void _process_group(ProcessGroup *p_group, bool p_physics);
+ void _process_groups_thread(uint32_t p_index, bool p_physics);
+ void _process(bool p_physics);
+
+ void _remove_process_group(Node *p_node);
+ void _add_process_group(Node *p_node);
+ void _remove_node_from_process_group(Node *p_node, Node *p_owner);
+ void _add_node_to_process_group(Node *p_node, Node *p_owner);
+
void _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -311,6 +351,9 @@ public:
void set_debug_navigation_hint(bool p_enabled);
bool is_debugging_navigation_hint() const;
+
+ void set_debug_avoidance_hint(bool p_enabled);
+ bool is_debugging_avoidance_hint() const;
#else
void set_debug_collisions_hint(bool p_enabled) {}
bool is_debugging_collisions_hint() const { return false; }
@@ -383,6 +426,7 @@ public:
static void add_idle_callback(IdleCallback p_callback);
+ void set_disable_node_threading(bool p_disable);
//default texture settings
SceneTree();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index f3c2f4b0cc..e6043531ac 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -61,6 +61,10 @@
#include "servers/rendering/rendering_server_globals.h"
void ViewportTexture::setup_local_to_scene() {
+ if (vp_pending) {
+ return;
+ }
+
Node *loc_scene = get_local_scene();
if (!loc_scene) {
return;
@@ -68,26 +72,14 @@ void ViewportTexture::setup_local_to_scene() {
if (vp) {
vp->viewport_textures.erase(this);
+ vp = nullptr;
}
- vp = nullptr;
-
- Node *vpn = loc_scene->get_node(path);
- ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid.");
-
- vp = Object::cast_to<Viewport>(vpn);
-
- ERR_FAIL_COND_MSG(!vp, "ViewportTexture: Path to node does not point to a viewport.");
-
- vp->viewport_textures.insert(this);
-
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- if (proxy_ph.is_valid()) {
- RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
- RS::get_singleton()->free(proxy_ph);
+ if (loc_scene->is_ready()) {
+ _setup_local_to_scene(loc_scene);
} else {
- ERR_FAIL_COND(proxy.is_valid()); // Should be invalid.
- proxy = RS::get_singleton()->texture_proxy_create(vp->texture_rid);
+ loc_scene->connect(SNAME("ready"), callable_mp(this, &ViewportTexture::_setup_local_to_scene).bind(loc_scene), CONNECT_ONE_SHOT);
+ vp_pending = true;
}
}
@@ -98,8 +90,24 @@ void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
path = p_path;
- if (get_local_scene()) {
+ if (vp) {
+ vp->viewport_textures.erase(this);
+ vp = nullptr;
+ }
+
+ if (proxy_ph.is_valid()) {
+ RS::get_singleton()->free(proxy_ph);
+ }
+ if (proxy.is_valid()) {
+ RS::get_singleton()->free(proxy);
+ }
+ proxy_ph = RID();
+ proxy = RID();
+
+ if (get_local_scene() && !path.is_empty()) {
setup_local_to_scene();
+ } else {
+ emit_changed();
}
}
@@ -108,17 +116,32 @@ NodePath ViewportTexture::get_viewport_path_in_scene() const {
}
int ViewportTexture::get_width() const {
- ERR_FAIL_COND_V_MSG(!vp, 0, "Viewport Texture must be set to use it.");
+ if (!vp) {
+ if (!vp_pending) {
+ ERR_PRINT("Viewport Texture must be set to use it.");
+ }
+ return 0;
+ }
return vp->size.width;
}
int ViewportTexture::get_height() const {
- ERR_FAIL_COND_V_MSG(!vp, 0, "Viewport Texture must be set to use it.");
+ if (!vp) {
+ if (!vp_pending) {
+ ERR_PRINT("Viewport Texture must be set to use it.");
+ }
+ return 0;
+ }
return vp->size.height;
}
Size2 ViewportTexture::get_size() const {
- ERR_FAIL_COND_V_MSG(!vp, Size2(), "Viewport Texture must be set to use it.");
+ if (!vp) {
+ if (!vp_pending) {
+ ERR_PRINT("Viewport Texture must be set to use it.");
+ }
+ return Size2();
+ }
return vp->size;
}
@@ -135,10 +158,38 @@ bool ViewportTexture::has_alpha() const {
}
Ref<Image> ViewportTexture::get_image() const {
- ERR_FAIL_COND_V_MSG(!vp, Ref<Image>(), "Viewport Texture must be set to use it.");
+ if (!vp) {
+ if (!vp_pending) {
+ ERR_PRINT("Viewport Texture must be set to use it.");
+ }
+ return Ref<Image>();
+ }
return RS::get_singleton()->texture_2d_get(vp->texture_rid);
}
+void ViewportTexture::_setup_local_to_scene(const Node *p_loc_scene) {
+ Node *vpn = p_loc_scene->get_node(path);
+ ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid.");
+
+ vp = Object::cast_to<Viewport>(vpn);
+
+ ERR_FAIL_COND_MSG(!vp, "ViewportTexture: Path to node does not point to a viewport.");
+
+ vp->viewport_textures.insert(this);
+
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (proxy_ph.is_valid()) {
+ RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
+ RS::get_singleton()->free(proxy_ph);
+ } else {
+ ERR_FAIL_COND(proxy.is_valid()); // Should be invalid.
+ proxy = RS::get_singleton()->texture_proxy_create(vp->texture_rid);
+ }
+ vp_pending = false;
+
+ emit_changed();
+}
+
void ViewportTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
@@ -399,9 +450,28 @@ int Viewport::_sub_window_find(Window *p_window) {
return -1;
}
+void Viewport::_update_viewport_path() {
+ if (viewport_textures.is_empty()) {
+ return;
+ }
+
+ Node *scene_root = get_scene_file_path().is_empty() ? get_owner() : this;
+ if (!scene_root && is_inside_tree()) {
+ scene_root = get_tree()->get_edited_scene_root();
+ }
+ if (scene_root && (scene_root == this || scene_root->is_ancestor_of(this))) {
+ NodePath path_in_scene = scene_root->get_path_to(this);
+ for (ViewportTexture *E : viewport_textures) {
+ E->path = path_in_scene;
+ }
+ }
+}
+
void Viewport::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
+ _update_viewport_path();
+
if (get_parent()) {
parent = get_parent()->get_viewport();
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid());
@@ -494,6 +564,10 @@ void Viewport::_notification(int p_what) {
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
} break;
+ case NOTIFICATION_PATH_RENAMED: {
+ _update_viewport_path();
+ } break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!get_tree()) {
return;
@@ -1091,7 +1165,11 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
}
if (p_world_2d.is_valid()) {
+ bool do_propagate = world_2d.is_valid() && is_inside_tree();
world_2d = p_world_2d;
+ if (do_propagate) {
+ _propagate_world_2d_changed(this);
+ }
} else {
WARN_PRINT("Invalid world_2d");
world_2d = Ref<World2D>(memnew(World2D));
@@ -2601,6 +2679,10 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
}
gui.subwindow_focused->_rect_changed_callback(new_rect);
+
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
+ DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_MOVE);
+ }
}
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
gui.subwindow_drag_close_inside = gui.subwindow_drag_close_rect.has_point(mm->get_position());
@@ -2865,6 +2947,10 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
_gui_cleanup_internal_state(ev);
}
+ if (!is_input_handled()) {
+ push_unhandled_input(ev, true);
+ }
+
event_count++;
}
@@ -3835,6 +3921,25 @@ float Viewport::get_texture_mipmap_bias() const {
#endif // _3D_DISABLED
+void Viewport::_propagate_world_2d_changed(Node *p_node) {
+ if (p_node != this) {
+ if (Object::cast_to<CanvasItem>(p_node)) {
+ p_node->notification(CanvasItem::NOTIFICATION_WORLD_2D_CHANGED);
+ } else {
+ Viewport *v = Object::cast_to<Viewport>(p_node);
+ if (v) {
+ if (v->world_2d.is_valid()) {
+ return;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); ++i) {
+ _propagate_world_2d_changed(p_node->get_child(i));
+ }
+}
+
void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index f12e7921a3..7f93b21eed 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -59,6 +59,9 @@ class ViewportTexture : public Texture2D {
friend class Viewport;
Viewport *vp = nullptr;
+ bool vp_pending = false;
+
+ void _setup_local_to_scene(const Node *p_loc_scene);
mutable RID proxy_ph;
mutable RID proxy;
@@ -314,6 +317,8 @@ private:
Ref<ViewportTexture> default_texture;
HashSet<ViewportTexture *> viewport_textures;
+ void _update_viewport_path();
+
SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT;
SDFScale sdf_scale = SDF_SCALE_50_PERCENT;
@@ -734,6 +739,8 @@ public:
bool is_using_xr();
#endif // _3D_DISABLED
+ void _propagate_world_2d_changed(Node *p_node);
+
void _validate_property(PropertyInfo &p_property) const;
Viewport();
~Viewport();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 3d11e6647e..79d41abe87 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -551,9 +551,11 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id);
}
- for (const Window *E : transient_children) {
- if (E->window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_transient(E->window_id, transient_parent->window_id);
+ if (transient_parent) {
+ for (const Window *E : transient_children) {
+ if (E->window_id != DisplayServer::INVALID_WINDOW_ID) {
+ DisplayServer::get_singleton()->window_set_transient(E->window_id, transient_parent->window_id);
+ }
}
}
@@ -1384,10 +1386,6 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
if (is_inside_tree()) {
push_input(p_ev);
}
-
- if (!is_input_handled() && is_inside_tree()) {
- push_unhandled_input(p_ev);
- }
}
void Window::_window_input_text(const String &p_text) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index feff4a6dc9..e4e06a9a79 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -1142,6 +1142,10 @@ void register_scene_types() {
GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/3d_navigation"), i + 1), "");
}
+ for (int i = 0; i < 32; i++) {
+ GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/avoidance"), i + 1), "");
+ }
+
if (RenderingServer::get_singleton()) {
ColorPicker::init_shaders(); // RenderingServer needs to exist for this to succeed.
}
diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp
index f331e3a22c..669b455f89 100644
--- a/scene/resources/audio_stream_wav.cpp
+++ b/scene/resources/audio_stream_wav.cpp
@@ -87,21 +87,21 @@ void AudioStreamPlaybackWAV::seek(double p_time) {
}
template <class Depth, bool is_stereo, bool is_ima_adpcm>
-void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm) {
+void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &p_offset, int32_t &p_increment, uint32_t p_amount, IMA_ADPCM_State *p_ima_adpcm) {
// this function will be compiled branchless by any decent compiler
int32_t final, final_r, next, next_r;
- while (amount) {
- amount--;
- int64_t pos = offset >> MIX_FRAC_BITS;
+ while (p_amount) {
+ p_amount--;
+ int64_t pos = p_offset >> MIX_FRAC_BITS;
if (is_stereo && !is_ima_adpcm) {
pos <<= 1;
}
if (is_ima_adpcm) {
- int64_t sample_pos = pos + ima_adpcm[0].window_ofs;
+ int64_t sample_pos = pos + p_ima_adpcm[0].window_ofs;
- while (sample_pos > ima_adpcm[0].last_nibble) {
+ while (sample_pos > p_ima_adpcm[0].last_nibble) {
static const int16_t _ima_adpcm_step_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
@@ -122,20 +122,20 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
for (int i = 0; i < (is_stereo ? 2 : 1); i++) {
int16_t nibble, diff, step;
- ima_adpcm[i].last_nibble++;
+ p_ima_adpcm[i].last_nibble++;
const uint8_t *src_ptr = (const uint8_t *)base->data;
src_ptr += AudioStreamWAV::DATA_PAD;
- uint8_t nbb = src_ptr[(ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
- nibble = (ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
- step = _ima_adpcm_step_table[ima_adpcm[i].step_index];
+ uint8_t nbb = src_ptr[(p_ima_adpcm[i].last_nibble >> 1) * (is_stereo ? 2 : 1) + i];
+ nibble = (p_ima_adpcm[i].last_nibble & 1) ? (nbb >> 4) : (nbb & 0xF);
+ step = _ima_adpcm_step_table[p_ima_adpcm[i].step_index];
- ima_adpcm[i].step_index += _ima_adpcm_index_table[nibble];
- if (ima_adpcm[i].step_index < 0) {
- ima_adpcm[i].step_index = 0;
+ p_ima_adpcm[i].step_index += _ima_adpcm_index_table[nibble];
+ if (p_ima_adpcm[i].step_index < 0) {
+ p_ima_adpcm[i].step_index = 0;
}
- if (ima_adpcm[i].step_index > 88) {
- ima_adpcm[i].step_index = 88;
+ if (p_ima_adpcm[i].step_index > 88) {
+ p_ima_adpcm[i].step_index = 88;
}
diff = step >> 3;
@@ -152,26 +152,26 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
diff = -diff;
}
- ima_adpcm[i].predictor += diff;
- if (ima_adpcm[i].predictor < -0x8000) {
- ima_adpcm[i].predictor = -0x8000;
- } else if (ima_adpcm[i].predictor > 0x7FFF) {
- ima_adpcm[i].predictor = 0x7FFF;
+ p_ima_adpcm[i].predictor += diff;
+ if (p_ima_adpcm[i].predictor < -0x8000) {
+ p_ima_adpcm[i].predictor = -0x8000;
+ } else if (p_ima_adpcm[i].predictor > 0x7FFF) {
+ p_ima_adpcm[i].predictor = 0x7FFF;
}
/* store loop if there */
- if (ima_adpcm[i].last_nibble == ima_adpcm[i].loop_pos) {
- ima_adpcm[i].loop_step_index = ima_adpcm[i].step_index;
- ima_adpcm[i].loop_predictor = ima_adpcm[i].predictor;
+ if (p_ima_adpcm[i].last_nibble == p_ima_adpcm[i].loop_pos) {
+ p_ima_adpcm[i].loop_step_index = p_ima_adpcm[i].step_index;
+ p_ima_adpcm[i].loop_predictor = p_ima_adpcm[i].predictor;
}
- //printf("%i - %i - pred %i\n",int(ima_adpcm[i].last_nibble),int(nibble),int(ima_adpcm[i].predictor));
+ //printf("%i - %i - pred %i\n",int(p_ima_adpcm[i].last_nibble),int(nibble),int(p_ima_adpcm[i].predictor));
}
}
- final = ima_adpcm[0].predictor;
+ final = p_ima_adpcm[0].predictor;
if (is_stereo) {
- final_r = ima_adpcm[1].predictor;
+ final_r = p_ima_adpcm[1].predictor;
}
} else {
@@ -201,7 +201,7 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
}
}
- int32_t frac = int64_t(offset & MIX_FRAC_MASK);
+ int32_t frac = int64_t(p_offset & MIX_FRAC_MASK);
final = final + ((next - final) * frac >> MIX_FRAC_BITS);
if (is_stereo) {
@@ -217,7 +217,7 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
p_dst->r = final_r / 32767.0;
p_dst++;
- offset += increment;
+ p_offset += p_increment;
}
}
diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h
index bea273720c..f150a17d21 100644
--- a/scene/resources/audio_stream_wav.h
+++ b/scene/resources/audio_stream_wav.h
@@ -61,7 +61,7 @@ class AudioStreamPlaybackWAV : public AudioStreamPlayback {
Ref<AudioStreamWAV> base;
template <class Depth, bool is_stereo, bool is_ima_adpcm>
- void do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &offset, int32_t &increment, uint32_t amount, IMA_ADPCM_State *ima_adpcm);
+ void do_resample(const Depth *p_src, AudioFrame *p_dst, int64_t &p_offset, int32_t &p_increment, uint32_t p_amount, IMA_ADPCM_State *p_ima_adpcm);
public:
virtual void start(double p_from_pos = 0.0) override;
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 204adbcda0..dd20dc1c66 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -354,7 +354,7 @@ Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2
prevx = stepx;
prevy = stepy;
- ERR_FAIL_COND_V((int)count > width * height, Vector<Vector<Vector2>>());
+ ERR_FAIL_COND_V((int)count > 2 * (width * height + 1), Vector<Vector<Vector2>>());
} while (curx != startx || cury != starty);
// Add remaining points to result.
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index 0d5d735ed0..31c8e68ea5 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -161,7 +161,7 @@ void CanvasItemMaterial::flush_changes() {
void CanvasItemMaterial::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (is_initialized && !element.in_list()) {
+ if (_is_initialized() && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -287,8 +287,8 @@ CanvasItemMaterial::CanvasItemMaterial() :
set_particles_anim_loop(false);
current_key.invalid_key = 1;
- is_initialized = true;
- _queue_shader_change();
+
+ _mark_initialized(callable_mp(this, &CanvasItemMaterial::_queue_shader_change));
}
CanvasItemMaterial::~CanvasItemMaterial() {
diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h
index cf5df76147..7dddd74a31 100644
--- a/scene/resources/canvas_item_material.h
+++ b/scene/resources/canvas_item_material.h
@@ -105,7 +105,6 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
- bool is_initialized = false;
BlendMode blend_mode = BLEND_MODE_MIX;
LightMode light_mode = LIGHT_MODE_NORMAL;
bool particles_animation = false;
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index 5309e54846..8268040ed9 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -97,7 +97,8 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
}
Rect2 CapsuleShape2D::get_rect() const {
- return Rect2(0, 0, radius, height);
+ const Vector2 half_size = Vector2(radius, height * 0.5);
+ return Rect2(-half_size, half_size * 2.0);
}
real_t CapsuleShape2D::get_enclosing_radius() const {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 06daa5a46f..1bfcf8d3ac 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -565,6 +565,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("grabber_disabled", "HSlider", icons["slider_grabber_disabled"]);
theme->set_icon("tick", "HSlider", icons["hslider_tick"]);
+ theme->set_constant("center_grabber", "HSlider", 0);
theme->set_constant("grabber_offset", "HSlider", 0);
// VSlider
@@ -578,6 +579,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("grabber_disabled", "VSlider", icons["slider_grabber_disabled"]);
theme->set_icon("tick", "VSlider", icons["vslider_tick"]);
+ theme->set_constant("center_grabber", "VSlider", 0);
theme->set_constant("grabber_offset", "VSlider", 0);
// SpinBox
@@ -909,6 +911,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
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("center_slider_grabbers", "ColorPicker", 1);
theme->set_icon("folded_arrow", "ColorPicker", icons["arrow_right"]);
theme->set_icon("expanded_arrow", "ColorPicker", icons["arrow_down"]);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 3deb1ccaab..d35c49b266 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -82,6 +82,23 @@ void Material::_validate_property(PropertyInfo &p_property) const {
}
}
+void Material::_mark_initialized(const Callable &p_queue_shader_change_callable) {
+ // If this is happening as part of resource loading, it is not safe to queue the update
+ // as an addition to the dirty list, unless the load is happening on the main thread.
+ if (ResourceLoader::is_within_load() && Thread::get_caller_id() != Thread::get_main_id()) {
+ DEV_ASSERT(init_state != INIT_STATE_READY);
+ if (init_state == INIT_STATE_UNINITIALIZED) { // Prevent queueing twice.
+ // Queue an individual update of this material (the ResourceLoader knows how to handle deferred calls safely).
+ p_queue_shader_change_callable.call_deferred();
+ init_state = INIT_STATE_INITIALIZING;
+ }
+ } else {
+ // Straightforward conditions.
+ init_state = INIT_STATE_READY;
+ p_queue_shader_change_callable.callv(Array());
+ }
+}
+
void Material::inspect_native_shader_code() {
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
RID shader = get_shader_rid();
@@ -211,6 +228,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
shader->get_shader_uniform_list(&list, true);
HashMap<String, HashMap<String, List<PropertyInfo>>> groups;
+ LocalVector<Pair<String, LocalVector<String>>> vgroups;
{
HashMap<String, List<PropertyInfo>> none_subgroup;
none_subgroup.insert("<None>", List<PropertyInfo>());
@@ -248,6 +266,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
subgroup_map.insert("<None>", none_subgroup);
groups.insert(last_group, subgroup_map);
+ vgroups.push_back(Pair<String, LocalVector<String>>(last_group, { "<None>" }));
}
if (!groups[last_group].has(last_subgroup)) {
@@ -260,6 +279,12 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
subgroup.push_back(info);
groups[last_group].insert(last_subgroup, subgroup);
+ for (Pair<String, LocalVector<String>> &group : vgroups) {
+ if (group.first == last_group) {
+ group.second.push_back(last_subgroup);
+ break;
+ }
+ }
}
} else {
last_group = "<None>";
@@ -277,6 +302,8 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
info.name = "Shader Parameters";
info.hint_string = "shader_parameter/";
groups["<None>"]["<None>"].push_back(info);
+
+ vgroups.push_back(Pair<String, LocalVector<String>>("<None>", { "<None>" }));
}
PropertyInfo info = E->get();
@@ -290,21 +317,10 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
groups[last_group][last_subgroup].push_back(info);
}
- List<String> group_names;
- for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) {
- group_names.push_back(group->key);
- }
- group_names.sort();
-
- for (const String &group_name : group_names) {
- List<String> subgroup_names;
- HashMap<String, List<PropertyInfo>> &subgroups = groups[group_name];
- for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = subgroups.begin(); subgroup; ++subgroup) {
- subgroup_names.push_back(subgroup->key);
- }
- subgroup_names.sort();
- for (const String &subgroup_name : subgroup_names) {
- List<PropertyInfo> &prop_infos = subgroups[subgroup_name];
+ for (const Pair<String, LocalVector<String>> &group_pair : vgroups) {
+ String group = group_pair.first;
+ for (const String &subgroup : group_pair.second) {
+ List<PropertyInfo> &prop_infos = groups[group][subgroup];
for (List<PropertyInfo>::Element *item = prop_infos.front(); item; item = item->next()) {
p_list->push_back(item->get());
}
@@ -1486,7 +1502,7 @@ void BaseMaterial3D::flush_changes() {
void BaseMaterial3D::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (is_initialized && !element.in_list()) {
+ if (_is_initialized() && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -3029,8 +3045,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
flags[FLAG_ALBEDO_TEXTURE_MSDF] = false;
flags[FLAG_USE_TEXTURE_REPEAT] = true;
- is_initialized = true;
- _queue_shader_change();
+ _mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change));
}
BaseMaterial3D::~BaseMaterial3D() {
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 1fa9a24bc5..b70522dda1 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -46,6 +46,12 @@ class Material : public Resource {
Ref<Material> next_pass;
int render_priority;
+ enum {
+ INIT_STATE_UNINITIALIZED,
+ INIT_STATE_INITIALIZING,
+ INIT_STATE_READY,
+ } init_state = INIT_STATE_UNINITIALIZED;
+
void inspect_native_shader_code();
protected:
@@ -56,6 +62,9 @@ protected:
void _validate_property(PropertyInfo &p_property) const;
+ void _mark_initialized(const Callable &p_queue_shader_change_callable);
+ bool _is_initialized() { return init_state == INIT_STATE_READY; }
+
GDVIRTUAL0RC(RID, _get_shader_rid)
GDVIRTUAL0RC(Shader::Mode, _get_shader_mode)
GDVIRTUAL0RC(bool, _can_do_next_pass)
@@ -452,7 +461,6 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
- bool is_initialized = false;
bool orm;
Color albedo;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 8a91e9f6e0..81192fa8d5 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -499,6 +499,10 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
}
}
+Vector<Vector3> Mesh::_get_faces() const {
+ return Variant(get_faces());
+}
+
Vector<Face3> Mesh::get_faces() const {
Ref<TriangleMesh> tm = generate_triangle_mesh();
if (tm.is_valid()) {
@@ -793,6 +797,7 @@ void Mesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &Mesh::set_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_aabb"), &Mesh::get_aabb);
+ ClassDB::bind_method(D_METHOD("get_faces"), &Mesh::_get_faces);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
@@ -2245,7 +2250,6 @@ void ArrayMesh::_bind_methods() {
ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normal_maps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("lightmap_unwrap", "transform", "texel_size"), &ArrayMesh::lightmap_unwrap);
ClassDB::set_method_flags(get_class_static(), _scs_create("lightmap_unwrap"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
- ClassDB::bind_method(D_METHOD("get_faces"), &ArrayMesh::get_faces);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &ArrayMesh::generate_triangle_mesh);
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ArrayMesh::set_custom_aabb);
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 4d9b9b4087..435655e80a 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -50,6 +50,8 @@ class Mesh : public Resource {
mutable Vector<Vector3> debug_lines;
Size2i lightmap_size_hint;
+ Vector<Vector3> _get_faces() const;
+
public:
enum PrimitiveType {
PRIMITIVE_POINTS = RenderingServer::PRIMITIVE_POINTS,
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 41edbcb726..cb94d5af36 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -915,7 +915,7 @@ void ParticleProcessMaterial::flush_changes() {
void ParticleProcessMaterial::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (is_initialized && !element.in_list()) {
+ if (_is_initialized() && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -1889,8 +1889,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() :
current_key.invalid_key = 1;
- is_initialized = true;
- _queue_shader_change();
+ _mark_initialized(callable_mp(this, &ParticleProcessMaterial::_queue_shader_change));
}
ParticleProcessMaterial::~ParticleProcessMaterial() {
diff --git a/scene/resources/particle_process_material.h b/scene/resources/particle_process_material.h
index c32a143cdc..533bd9636f 100644
--- a/scene/resources/particle_process_material.h
+++ b/scene/resources/particle_process_material.h
@@ -261,7 +261,6 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
- bool is_initialized = false;
Vector3 direction;
float spread = 0.0f;
float flatness = 0.0f;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index c30e009356..4807af3c27 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -150,32 +150,31 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R
String path = ext_resources[id].path;
String type = ext_resources[id].type;
+ Ref<ResourceLoader::LoadToken> &load_token = ext_resources[id].load_token;
- if (ext_resources[id].cache.is_valid()) {
- r_res = ext_resources[id].cache;
- } else if (use_sub_threads) {
- Ref<Resource> res = ResourceLoader::load_threaded_get(path);
+ if (load_token.is_valid()) { // If not valid, it's OK since then we know this load accepts broken dependencies.
+ Ref<Resource> res = ResourceLoader::_load_complete(*load_token.ptr(), &err);
if (res.is_null()) {
- if (ResourceLoader::get_abort_on_missing_resources()) {
- error = ERR_FILE_MISSING_DEPENDENCIES;
- error_text = "[ext_resource] referenced nonexistent resource at: " + path;
- _printerr();
- err = error;
- } else {
- ResourceLoader::notify_dependency_error(local_path, path, type);
+ if (!ResourceLoader::is_cleaning_tasks()) {
+ if (ResourceLoader::get_abort_on_missing_resources()) {
+ error = ERR_FILE_MISSING_DEPENDENCIES;
+ error_text = "[ext_resource] referenced non-existent resource at: " + path;
+ _printerr();
+ err = error;
+ } else {
+ ResourceLoader::notify_dependency_error(local_path, path, type);
+ }
}
} else {
- ext_resources[id].cache = res;
+#ifdef TOOLS_ENABLED
+ //remember ID for saving
+ res->set_id_for_path(path, id);
+#endif
r_res = res;
}
} else {
- error = ERR_FILE_MISSING_DEPENDENCIES;
- error_text = "[ext_resource] referenced non-loaded resource at: " + path;
- _printerr();
- err = error;
+ r_res = Ref<Resource>();
}
- } else {
- r_res = Ref<Resource>();
}
VariantParser::get_token(p_stream, token, line, r_err_str);
@@ -462,48 +461,20 @@ Error ResourceLoaderText::load() {
path = remaps[path];
}
- ExtResource er;
- er.path = path;
- er.type = type;
-
- if (use_sub_threads) {
- Error err = ResourceLoader::load_threaded_request(path, type, use_sub_threads, ResourceFormatLoader::CACHE_MODE_REUSE, local_path);
-
- if (err != OK) {
- if (ResourceLoader::get_abort_on_missing_resources()) {
- error = ERR_FILE_CORRUPT;
- error_text = "[ext_resource] referenced broken resource at: " + path;
- _printerr();
- return error;
- } else {
- ResourceLoader::notify_dependency_error(local_path, path, type);
- }
- }
-
- } else {
- Ref<Resource> res = ResourceLoader::load(path, type);
-
- if (res.is_null()) {
- if (ResourceLoader::get_abort_on_missing_resources()) {
- error = ERR_FILE_CORRUPT;
- error_text = "[ext_resource] referenced nonexistent resource at: " + path;
- _printerr();
- return error;
- } else {
- ResourceLoader::notify_dependency_error(local_path, path, type);
- }
+ ext_resources[id].path = path;
+ ext_resources[id].type = type;
+ ext_resources[id].load_token = ResourceLoader::_load_start(path, type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, ResourceFormatLoader::CACHE_MODE_REUSE);
+ if (!ext_resources[id].load_token.is_valid()) {
+ if (ResourceLoader::get_abort_on_missing_resources()) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "[ext_resource] referenced non-existent resource at: " + path;
+ _printerr();
+ return error;
} else {
-#ifdef TOOLS_ENABLED
- //remember ID for saving
- res->set_id_for_path(local_path, id);
-#endif
+ ResourceLoader::notify_dependency_error(local_path, path, type);
}
-
- er.cache = res;
}
- ext_resources[id] = er;
-
error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
if (error) {
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 25001d8023..c35a594f72 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -48,7 +48,7 @@ class ResourceLoaderText {
VariantParser::StreamFile stream;
struct ExtResource {
- Ref<Resource> cache;
+ Ref<ResourceLoader::LoadToken> load_token;
String path;
String type;
};
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index ccb3ddee45..c296523475 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -374,6 +374,10 @@ void SurfaceTool::set_smooth_group(uint32_t p_group) {
last_smooth_group = p_group;
}
+void SurfaceTool::_add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<Color> &p_colors, const Vector<Vector2> &p_uv2s, const Vector<Vector3> &p_normals, const TypedArray<Plane> &p_tangents) {
+ add_triangle_fan(p_vertices, p_uv2s, p_colors, p_uv2s, p_normals, Variant(p_tangents));
+}
+
void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<Color> &p_colors, const Vector<Vector2> &p_uv2s, const Vector<Vector3> &p_normals, const Vector<Plane> &p_tangents) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES);
@@ -985,9 +989,21 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur
}
ERR_FAIL_COND(shape_idx == -1);
ERR_FAIL_COND(shape_idx >= arr.size());
- Array mesh = arr[shape_idx];
- ERR_FAIL_COND(mesh.size() != RS::ARRAY_MAX);
- _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format);
+ Array blendshape_mesh_arrays = arr[shape_idx];
+ ERR_FAIL_COND(blendshape_mesh_arrays.size() != RS::ARRAY_MAX);
+
+ Array source_mesh_arrays = p_existing->surface_get_arrays(p_surface);
+ ERR_FAIL_COND(source_mesh_arrays.size() != RS::ARRAY_MAX);
+
+ // Copy BlendShape vertex data over while keeping e.g. bones, weights, index from existing mesh intact.
+ source_mesh_arrays[RS::ARRAY_VERTEX] = blendshape_mesh_arrays[RS::ARRAY_VERTEX];
+ source_mesh_arrays[RS::ARRAY_NORMAL] = blendshape_mesh_arrays[RS::ARRAY_NORMAL];
+ source_mesh_arrays[RS::ARRAY_TANGENT] = blendshape_mesh_arrays[RS::ARRAY_TANGENT];
+
+ _create_list_from_arrays(source_mesh_arrays, &vertex_array, &index_array, format);
+
+ material = p_existing->surface_get_material(p_surface);
+ format = p_existing->surface_get_format(p_surface);
for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
if (format & custom_mask[j]) {
@@ -1335,7 +1351,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom", "channel_index", "custom_color"), &SurfaceTool::set_custom);
ClassDB::bind_method(D_METHOD("set_smooth_group", "index"), &SurfaceTool::set_smooth_group);
- ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>()));
+ ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::_add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(TypedArray<Plane>()));
ClassDB::bind_method(D_METHOD("add_index", "index"), &SurfaceTool::add_index);
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 77318bb061..452aa835f0 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -167,6 +167,8 @@ private:
static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
+ void _add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const TypedArray<Plane> &p_tangents = TypedArray<Plane>());
+
protected:
static void _bind_methods();
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 282c531555..7de10149db 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -168,7 +168,8 @@ void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const {
}
Ref<ImageTexture> ImageTexture::create_from_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND_V_MSG(p_image.is_null() || p_image->is_empty(), Ref<ImageTexture>(), "Invalid 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();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 42e4a0fa3d..a361b7584a 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -611,6 +611,36 @@ void VisualShaderNodeCustom::_set_initialized(bool p_enabled) {
is_initialized = p_enabled;
}
+String VisualShaderNodeCustom::_get_name() const {
+ String ret;
+ GDVIRTUAL_CALL(_get_name, ret);
+ return ret;
+}
+
+String VisualShaderNodeCustom::_get_description() const {
+ String ret;
+ GDVIRTUAL_CALL(_get_description, ret);
+ return ret;
+}
+
+String VisualShaderNodeCustom::_get_category() const {
+ String ret;
+ GDVIRTUAL_CALL(_get_category, ret);
+ return ret;
+}
+
+VisualShaderNodeCustom::PortType VisualShaderNodeCustom::_get_return_icon_type() const {
+ PortType ret = PORT_TYPE_SCALAR;
+ GDVIRTUAL_CALL(_get_return_icon_type, ret);
+ return ret;
+}
+
+bool VisualShaderNodeCustom::_is_highend() const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_is_highend, ret);
+ return ret;
+}
+
void VisualShaderNodeCustom::_bind_methods() {
GDVIRTUAL_BIND(_get_name);
GDVIRTUAL_BIND(_get_description);
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 2838a49209..38d51dba9c 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -411,6 +411,12 @@ public:
bool _is_initialized();
void _set_initialized(bool p_enabled);
+
+ String _get_name() const;
+ String _get_description() const;
+ String _get_category() const;
+ PortType _get_return_icon_type() const;
+ bool _is_highend() const;
};
/////
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index c7304da358..f371703cba 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -59,6 +59,7 @@ RID World2D::get_navigation_map() const {
navigation_map = NavigationServer2D::get_singleton()->map_create();
NavigationServer2D::get_singleton()->map_set_active(navigation_map, true);
NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_GET("navigation/2d/default_cell_size"));
+ NavigationServer2D::get_singleton()->map_set_use_edge_connections(navigation_map, GLOBAL_GET("navigation/2d/use_edge_connections"));
NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_GET("navigation/2d/default_edge_connection_margin"));
NavigationServer2D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_GET("navigation/2d/default_link_connection_radius"));
}
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 82c056d5ee..cc961941cf 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -67,6 +67,7 @@ RID World3D::get_navigation_map() const {
navigation_map = NavigationServer3D::get_singleton()->map_create();
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_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/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
index e6257d9260..e1bfcbaca1 100644
--- a/servers/audio/audio_driver_dummy.cpp
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -41,7 +41,7 @@ Error AudioDriverDummy::init() {
samples_in = nullptr;
if (mix_rate == -1) {
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ mix_rate = _get_configured_mix_rate();
}
channels = get_channels();
@@ -136,7 +136,9 @@ void AudioDriverDummy::mix_audio(int p_frames, int32_t *p_buffer) {
void AudioDriverDummy::finish() {
if (use_threads) {
exit_thread.set();
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
}
if (samples_in) {
diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp
index e82f6e518e..cc6ac31d29 100644
--- a/servers/audio/effects/audio_effect_record.cpp
+++ b/servers/audio/effects/audio_effect_record.cpp
@@ -120,7 +120,9 @@ void AudioEffectRecordInstance::init() {
}
void AudioEffectRecordInstance::finish() {
- io_thread.wait_to_finish();
+ if (io_thread.is_started()) {
+ io_thread.wait_to_finish();
+ }
}
AudioEffectRecordInstance::~AudioEffectRecordInstance() {
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 0344bf322d..49991e41d3 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -115,6 +115,20 @@ void AudioDriver::input_buffer_write(int32_t sample) {
}
}
+int AudioDriver::_get_configured_mix_rate() {
+ StringName audio_driver_setting = "audio/driver/mix_rate";
+ int mix_rate = GLOBAL_GET(audio_driver_setting);
+
+ // In the case of invalid mix rate, let's default to a sensible value..
+ if (mix_rate <= 0) {
+ WARN_PRINT(vformat("Invalid mix rate of %d, consider reassigning setting \'%s\'. \nDefaulting mix rate to value %d.",
+ mix_rate, audio_driver_setting, AudioDriverManager::DEFAULT_MIX_RATE));
+ mix_rate = AudioDriverManager::DEFAULT_MIX_RATE;
+ }
+
+ return mix_rate;
+}
+
AudioDriver::SpeakerMode AudioDriver::get_speaker_mode_by_total_channels(int p_channels) const {
switch (p_channels) {
case 4:
diff --git a/servers/audio_server.h b/servers/audio_server.h
index 155beb2000..6585043f63 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -66,6 +66,8 @@ protected:
void input_buffer_init(int driver_buffer_frames);
void input_buffer_write(int32_t sample);
+ int _get_configured_mix_rate();
+
#ifdef DEBUG_ENABLED
_FORCE_INLINE_ void start_counting_ticks() { prof_ticks = OS::get_singleton()->get_ticks_usec(); }
_FORCE_INLINE_ void stop_counting_ticks() { prof_time += OS::get_singleton()->get_ticks_usec() - prof_ticks; }
@@ -136,7 +138,6 @@ class AudioDriverManager {
MAX_DRIVERS = 10
};
- static const int DEFAULT_MIX_RATE = 44100;
static const int DEFAULT_OUTPUT_LATENCY = 15;
static AudioDriver *drivers[MAX_DRIVERS];
@@ -145,6 +146,8 @@ class AudioDriverManager {
static AudioDriverDummy dummy_driver;
public:
+ static const int DEFAULT_MIX_RATE = 44100;
+
static void add_driver(AudioDriver *p_driver);
static void initialize(int p_driver);
static int get_driver_count();
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
index cd305cfcb2..9df05ba94a 100644
--- a/servers/movie_writer/movie_writer.cpp
+++ b/servers/movie_writer/movie_writer.cpp
@@ -33,6 +33,7 @@
#include "core/io/dir_access.h"
#include "core/os/time.h"
#include "servers/display_server.h"
+#include "servers/rendering_server.h"
MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
uint32_t MovieWriter::writer_count = 0;
@@ -109,6 +110,9 @@ void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String
WARN_PRINT(vformat("Current available space on disk is low (%s). MovieWriter will fail during movie recording if the disk runs out of available space.", String::humanize_size(dir->get_space_left())));
}
+ cpu_time = 0.0f;
+ gpu_time = 0.0f;
+
mix_rate = get_audio_mix_rate();
AudioDriverDummy::get_dummy_singleton()->set_mix_rate(mix_rate);
AudioDriverDummy::get_dummy_singleton()->set_speaker_mode(AudioDriver::SpeakerMode(get_audio_speaker_mode()));
@@ -165,7 +169,7 @@ void MovieWriter::set_extensions_hint() {
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "editor/movie_writer/movie_file", PROPERTY_HINT_GLOBAL_SAVE_FILE, ext_hint));
}
-void MovieWriter::add_frame(const Ref<Image> &p_image) {
+void MovieWriter::add_frame() {
const int movie_time_seconds = Engine::get_singleton()->get_frames_drawn() / fps;
const String movie_time = vformat("%s:%s:%s",
String::num(movie_time_seconds / 3600).pad_zeros(2),
@@ -178,8 +182,17 @@ void MovieWriter::add_frame(const Ref<Image> &p_image) {
DisplayServer::get_singleton()->window_set_title(vformat("MovieWriter: Frame %d (time: %s) - %s", Engine::get_singleton()->get_frames_drawn(), movie_time, project_name));
#endif
+ RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
+ RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
+ Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
+
+ RenderingServer::get_singleton()->viewport_set_measure_render_time(main_vp_rid, true);
+ cpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_cpu(main_vp_rid);
+ cpu_time += RenderingServer::get_singleton()->get_frame_setup_time_cpu();
+ gpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_gpu(main_vp_rid);
+
AudioDriverDummy::get_dummy_singleton()->mix_audio(mix_rate / fps, audio_mix_buffer.ptr());
- write_frame(p_image, audio_mix_buffer.ptr());
+ write_frame(vp_tex, audio_mix_buffer.ptr());
}
void MovieWriter::end() {
@@ -208,5 +221,7 @@ void MovieWriter::end() {
String::num(real_time_seconds % 60).pad_zeros(2));
print_line(vformat("%d frames at %d FPS (movie length: %s), recorded in %s (%d%% of real-time speed).", Engine::get_singleton()->get_frames_drawn(), fps, movie_time, real_time, (float(movie_time_seconds) / real_time_seconds) * 100));
+ print_line(vformat("CPU time: %.2f seconds (average: %.2f ms/frame)", cpu_time / 1000, cpu_time / Engine::get_singleton()->get_frames_drawn()));
+ print_line(vformat("GPU time: %.2f seconds (average: %.2f ms/frame)", gpu_time / 1000, gpu_time / Engine::get_singleton()->get_frames_drawn()));
print_line("----------------");
}
diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h
index c3ebe8d1fa..4757de203c 100644
--- a/servers/movie_writer/movie_writer.h
+++ b/servers/movie_writer/movie_writer.h
@@ -42,6 +42,9 @@ class MovieWriter : public Object {
uint64_t mix_rate = 0;
uint32_t audio_channels = 0;
+ float cpu_time = 0.0f;
+ float gpu_time = 0.0f;
+
String project_name;
LocalVector<int32_t> audio_mix_buffer;
@@ -80,7 +83,7 @@ public:
static MovieWriter *find_writer_for_file(const String &p_file);
void begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
- void add_frame(const Ref<Image> &p_image);
+ void add_frame();
static void set_extensions_hint();
diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp
index 6de8459e3b..c2dddb0627 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -115,6 +115,15 @@ static Vector2 v3_to_v2(const Vector3 &d) {
return Vector2(d.x, d.z);
}
+static Vector<Vector3> vector_v2_to_v3(const Vector<Vector2> &d) {
+ Vector<Vector3> nd;
+ nd.resize(d.size());
+ for (int i(0); i < nd.size(); i++) {
+ nd.write[i] = v2_to_v3(d[i]);
+ }
+ return nd;
+}
+
static Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
Vector<Vector2> nd;
nd.resize(d.size());
@@ -161,6 +170,22 @@ bool NavigationServer2D::get_debug_enabled() const {
}
#ifdef DEBUG_ENABLED
+void NavigationServer2D::set_debug_navigation_enabled(bool p_enabled) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_enabled(p_enabled);
+}
+
+bool NavigationServer2D::get_debug_navigation_enabled() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_enabled();
+}
+
+void NavigationServer2D::set_debug_avoidance_enabled(bool p_enabled) {
+ NavigationServer3D::get_singleton()->set_debug_avoidance_enabled(p_enabled);
+}
+
+bool NavigationServer2D::get_debug_avoidance_enabled() const {
+ return NavigationServer3D::get_singleton()->get_debug_avoidance_enabled();
+}
+
void NavigationServer2D::set_debug_navigation_edge_connection_color(const Color &p_color) {
NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(p_color);
}
@@ -264,6 +289,78 @@ void NavigationServer2D::set_debug_navigation_agent_path_point_size(real_t p_poi
real_t NavigationServer2D::get_debug_navigation_agent_path_point_size() const {
return NavigationServer3D::get_singleton()->get_debug_navigation_agent_path_point_size();
}
+
+void NavigationServer2D::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_agents_radius(p_value);
+}
+
+bool NavigationServer2D::get_debug_navigation_avoidance_enable_agents_radius() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_agents_radius();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_radius(p_value);
+}
+
+bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_radius() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_agents_radius_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_agents_radius_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_agents_radius_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_obstacles_radius_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_obstacles_radius_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_face_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_face_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(p_color);
+}
+
+Color NavigationServer2D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
+}
+
+void NavigationServer2D::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
+ NavigationServer3D::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_static(p_value);
+}
+
+bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static() const {
+ return NavigationServer3D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static();
+}
#endif // DEBUG_ENABLED
void NavigationServer2D::_bind_methods() {
@@ -274,6 +371,8 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer2D::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer2D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size);
+ ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer2D::map_set_use_edge_connections);
+ ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer2D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer2D::map_set_link_connection_radius);
@@ -285,12 +384,15 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer2D::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer2D::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer2D::map_get_agents);
+ ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer2D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update);
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_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);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer2D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer2D::region_set_travel_cost);
@@ -327,18 +429,31 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer2D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer2D::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer2D::agent_set_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer2D::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer2D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer2D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors);
- ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &NavigationServer2D::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity);
- ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer2D::agent_set_target_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed);
- ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "callback"), &NavigationServer2D::agent_set_callback);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority);
+
+ ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create);
+ ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer2D::obstacle_set_map);
+ ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer2D::obstacle_get_map);
+ ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position);
+ ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices);
+ ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free);
@@ -378,6 +493,8 @@ TypedArray<RID> FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid);
TypedArray<RID> FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid);
+TypedArray<RID> FORWARD_1_C(map_get_obstacles, RID, p_map, rid_to_rid);
+
RID FORWARD_1_C(region_get_map, RID, p_region, rid_to_rid);
RID FORWARD_1_C(agent_get_map, RID, p_agent, rid_to_rid);
@@ -395,6 +512,9 @@ void NavigationServer2D::map_force_update(RID p_map) {
void FORWARD_2(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
+void FORWARD_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled, rid_to_rid, bool_to_bool);
+bool FORWARD_1_C(map_get_use_edge_connections, RID, p_map, rid_to_rid);
+
void FORWARD_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
@@ -408,6 +528,9 @@ 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_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);
+
void FORWARD_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_enter_cost, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
@@ -450,24 +573,43 @@ ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
RID NavigationServer2D::agent_create() {
RID agent = NavigationServer3D::get_singleton()->agent_create();
- NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, true);
return agent;
}
+void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_to_rid, bool_to_bool);
+bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
-void FORWARD_2(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
+void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
+void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
+void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
-void FORWARD_2(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
-void FORWARD_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
+
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
-void FORWARD_2(agent_set_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable);
void FORWARD_1(free, RID, p_object, rid_to_rid);
+void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable);
+
+void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real);
+
+RID NavigationServer2D::obstacle_create() {
+ RID obstacle = NavigationServer3D::get_singleton()->obstacle_create();
+ return obstacle;
+}
+void FORWARD_2(obstacle_set_map, RID, p_obstacle, RID, p_map, rid_to_rid, rid_to_rid);
+RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid);
+void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3);
+void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
+
+void NavigationServer2D::obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) {
+ NavigationServer3D::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices));
+}
void NavigationServer2D::query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const {
ERR_FAIL_COND(!p_query_parameters.is_valid());
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index 43ef742bdb..88246019b9 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -70,6 +70,9 @@ public:
/// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const;
+ virtual void map_set_use_edge_connections(RID p_map, bool p_enabled);
+ virtual bool map_get_use_edge_connections(RID p_map) const;
+
/// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin);
@@ -91,12 +94,16 @@ public:
virtual TypedArray<RID> map_get_links(RID p_map) const;
virtual TypedArray<RID> map_get_regions(RID p_map) const;
virtual TypedArray<RID> map_get_agents(RID p_map) const;
+ virtual TypedArray<RID> map_get_obstacles(RID p_map) const;
virtual void map_force_update(RID p_map);
/// Creates a new region.
virtual RID region_create();
+ virtual void region_set_use_edge_connections(RID p_region, bool p_enabled);
+ virtual bool region_get_use_edge_connections(RID p_region) const;
+
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost);
virtual real_t region_get_enter_cost(RID p_region) const;
@@ -171,6 +178,8 @@ public:
/// Put the agent in the map.
virtual void agent_set_map(RID p_agent, RID p_map);
virtual RID agent_get_map(RID p_agent) const;
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled);
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const;
/// The maximum distance (center point to
/// center point) to other agents this agent
@@ -197,7 +206,9 @@ public:
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time);
+
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon);
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon);
/// The radius of this agent.
/// Must be non-negative.
@@ -207,23 +218,33 @@ public:
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed);
- /// Current velocity of the agent
- virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
+ /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
+ virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity);
- /// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity);
+ /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
+ /// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
+ virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position);
- /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore);
-
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const;
/// Callback called at the end of the RVO process
- virtual void agent_set_callback(RID p_agent, Callable p_callback);
+ virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback);
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers);
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask);
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority);
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create();
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map);
+ virtual RID obstacle_get_map(RID p_obstacle) const;
+ virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position);
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices);
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers);
/// Returns a customized navigation path using a query parameters object
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const;
@@ -238,6 +259,12 @@ public:
bool get_debug_enabled() const;
#ifdef DEBUG_ENABLED
+ void set_debug_navigation_enabled(bool p_enabled);
+ bool get_debug_navigation_enabled() const;
+
+ void set_debug_avoidance_enabled(bool p_enabled);
+ bool get_debug_avoidance_enabled() const;
+
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
@@ -276,6 +303,33 @@ public:
void set_debug_navigation_agent_path_point_size(real_t p_point_size);
real_t get_debug_navigation_agent_path_point_size() const;
+
+ void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_agents_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
+
+ void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_agents_radius_color() const;
+
+ void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_obstacles_radius_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_static() const;
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 10f5e71c91..7888a28b7b 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -43,6 +43,8 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer3D::map_get_up);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer3D::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer3D::map_get_cell_size);
+ ClassDB::bind_method(D_METHOD("map_set_use_edge_connections", "map", "enabled"), &NavigationServer3D::map_set_use_edge_connections);
+ ClassDB::bind_method(D_METHOD("map_get_use_edge_connections", "map"), &NavigationServer3D::map_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer3D::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer3D::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer3D::map_set_link_connection_radius);
@@ -56,12 +58,15 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer3D::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer3D::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer3D::map_get_agents);
+ ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer3D::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer3D::map_force_update);
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_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);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer3D::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer3D::region_set_travel_cost);
@@ -99,18 +104,36 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer3D::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer3D::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer3D::agent_set_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer3D::agent_get_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_set_use_3d_avoidance", "agent", "enabled"), &NavigationServer3D::agent_set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("agent_get_use_3d_avoidance", "agent"), &NavigationServer3D::agent_get_use_3d_avoidance);
+
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer3D::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer3D::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer3D::agent_set_neighbor_distance);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer3D::agent_set_max_neighbors);
- ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &NavigationServer3D::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer3D::agent_set_radius);
+ ClassDB::bind_method(D_METHOD("agent_set_height", "agent", "height"), &NavigationServer3D::agent_set_height);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer3D::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer3D::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer3D::agent_set_velocity);
- ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer3D::agent_set_target_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer3D::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer3D::agent_is_map_changed);
- ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "callback"), &NavigationServer3D::agent_set_callback);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer3D::agent_set_avoidance_callback);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer3D::agent_set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer3D::agent_set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer3D::agent_set_avoidance_priority);
+
+ ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer3D::obstacle_create);
+ ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer3D::obstacle_set_map);
+ ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer3D::obstacle_get_map);
+ ClassDB::bind_method(D_METHOD("obstacle_set_height", "obstacle", "height"), &NavigationServer3D::obstacle_set_height);
+ ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer3D::obstacle_set_position);
+ ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer3D::obstacle_set_vertices);
+ ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
@@ -122,6 +145,7 @@ void NavigationServer3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
+ ADD_SIGNAL(MethodInfo("avoidance_debug_changed"));
ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &NavigationServer3D::get_process_info);
@@ -145,13 +169,18 @@ NavigationServer3D::NavigationServer3D() {
singleton = this;
GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1);
+ 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/3d/default_cell_size", 0.25);
+ 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);
+ GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
+ GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
+
#ifdef DEBUG_ENABLED
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
@@ -172,13 +201,24 @@ NavigationServer3D::NavigationServer3D() {
debug_navigation_enable_agent_paths = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths", true);
debug_navigation_enable_agent_paths_xray = GLOBAL_DEF("debug/shapes/navigation/enable_agent_paths_xray", true);
-
debug_navigation_agent_path_point_size = GLOBAL_DEF("debug/shapes/navigation/agent_path_point_size", 4.0);
+ debug_navigation_avoidance_agents_radius_color = GLOBAL_DEF("debug/shapes/avoidance/agents_radius_color", Color(1.0, 1.0, 0.0, 0.25));
+ debug_navigation_avoidance_obstacles_radius_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_radius_color", Color(1.0, 0.5, 0.0, 0.25));
+ debug_navigation_avoidance_static_obstacle_pushin_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushin_color", Color(1.0, 0.0, 0.0, 0.0));
+ debug_navigation_avoidance_static_obstacle_pushin_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushin_color", Color(1.0, 0.0, 0.0, 1.0));
+ debug_navigation_avoidance_static_obstacle_pushout_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushout_color", Color(1.0, 1.0, 0.0, 0.5));
+ debug_navigation_avoidance_static_obstacle_pushout_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushout_color", Color(1.0, 1.0, 0.0, 1.0));
+ debug_navigation_avoidance_enable_agents_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_agents_radius", true);
+ debug_navigation_avoidance_enable_obstacles_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_radius", true);
+ debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_static", true);
+
if (Engine::get_singleton()->is_editor_hint()) {
// enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible
// on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this
set_debug_enabled(true);
+ set_debug_navigation_enabled(true);
+ set_debug_avoidance_enabled(true);
}
#endif // DEBUG_ENABLED
}
@@ -207,11 +247,18 @@ bool NavigationServer3D::get_debug_enabled() const {
#ifdef DEBUG_ENABLED
void NavigationServer3D::_emit_navigation_debug_changed_signal() {
- if (debug_dirty) {
- debug_dirty = false;
+ if (navigation_debug_dirty) {
+ navigation_debug_dirty = false;
emit_signal(SNAME("navigation_debug_changed"));
}
}
+
+void NavigationServer3D::_emit_avoidance_debug_changed_signal() {
+ if (avoidance_debug_dirty) {
+ avoidance_debug_dirty = false;
+ emit_signal(SNAME("avoidance_debug_changed"));
+ }
+}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
@@ -351,6 +398,7 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_line
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+
material->set_albedo(debug_navigation_agent_path_color);
if (debug_navigation_enable_agent_paths_xray) {
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
@@ -367,7 +415,6 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_poin
}
Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
- material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_albedo(debug_navigation_agent_path_color);
material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
material->set_point_size(debug_navigation_agent_path_point_size);
@@ -380,6 +427,103 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_agent_path_poin
return debug_navigation_agent_path_point_material;
}
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_agents_radius_material() {
+ if (debug_navigation_avoidance_agents_radius_material.is_valid()) {
+ return debug_navigation_avoidance_agents_radius_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_agents_radius_color);
+ material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+
+ debug_navigation_avoidance_agents_radius_material = material;
+ return debug_navigation_avoidance_agents_radius_material;
+}
+
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_obstacles_radius_material() {
+ if (debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
+ return debug_navigation_avoidance_obstacles_radius_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_obstacles_radius_color);
+ material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+
+ debug_navigation_avoidance_obstacles_radius_material = material;
+ return debug_navigation_avoidance_obstacles_radius_material;
+}
+
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_face_material() {
+ if (debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
+ return debug_navigation_avoidance_static_obstacle_pushin_face_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_face_color);
+ material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+
+ debug_navigation_avoidance_static_obstacle_pushin_face_material = material;
+ return debug_navigation_avoidance_static_obstacle_pushin_face_material;
+}
+
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_face_material() {
+ if (debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
+ return debug_navigation_avoidance_static_obstacle_pushout_face_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_face_color);
+ material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+
+ debug_navigation_avoidance_static_obstacle_pushout_face_material = material;
+ return debug_navigation_avoidance_static_obstacle_pushout_face_material;
+}
+
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_material() {
+ if (debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
+ return debug_navigation_avoidance_static_obstacle_pushin_edge_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ //material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ //material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_edge_color);
+ //material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
+
+ debug_navigation_avoidance_static_obstacle_pushin_edge_material = material;
+ return debug_navigation_avoidance_static_obstacle_pushin_edge_material;
+}
+
+Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_material() {
+ if (debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
+ return debug_navigation_avoidance_static_obstacle_pushout_edge_material;
+ }
+
+ Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ ///material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ //material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
+ material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_edge_color);
+ //material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 2);
+ material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
+
+ debug_navigation_avoidance_static_obstacle_pushout_edge_material = material;
+ return debug_navigation_avoidance_static_obstacle_pushout_edge_material;
+}
+
void NavigationServer3D::set_debug_navigation_edge_connection_color(const Color &p_color) {
debug_navigation_edge_connection_color = p_color;
if (debug_navigation_edge_connections_material.is_valid()) {
@@ -484,7 +628,7 @@ Color NavigationServer3D::get_debug_navigation_agent_path_color() const {
void NavigationServer3D::set_debug_navigation_enable_edge_connections(const bool p_value) {
debug_navigation_enable_edge_connections = p_value;
- debug_dirty = true;
+ navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -505,7 +649,7 @@ bool NavigationServer3D::get_debug_navigation_enable_edge_connections_xray() con
void NavigationServer3D::set_debug_navigation_enable_edge_lines(const bool p_value) {
debug_navigation_enable_edge_lines = p_value;
- debug_dirty = true;
+ navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -526,7 +670,7 @@ bool NavigationServer3D::get_debug_navigation_enable_edge_lines_xray() const {
void NavigationServer3D::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
debug_navigation_enable_geometry_face_random_color = p_value;
- debug_dirty = true;
+ navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -536,7 +680,7 @@ bool NavigationServer3D::get_debug_navigation_enable_geometry_face_random_color(
void NavigationServer3D::set_debug_navigation_enable_link_connections(const bool p_value) {
debug_navigation_enable_link_connections = p_value;
- debug_dirty = true;
+ navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -555,6 +699,102 @@ bool NavigationServer3D::get_debug_navigation_enable_link_connections_xray() con
return debug_navigation_enable_link_connections_xray;
}
+void NavigationServer3D::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
+ debug_navigation_avoidance_enable_agents_radius = p_value;
+ avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer3D::get_debug_navigation_avoidance_enable_agents_radius() const {
+ return debug_navigation_avoidance_enable_agents_radius;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
+ debug_navigation_avoidance_enable_obstacles_radius = p_value;
+ avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer3D::get_debug_navigation_avoidance_enable_obstacles_radius() const {
+ return debug_navigation_avoidance_enable_obstacles_radius;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
+ debug_navigation_avoidance_enable_obstacles_static = p_value;
+ avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer3D::get_debug_navigation_avoidance_enable_obstacles_static() const {
+ return debug_navigation_avoidance_enable_obstacles_static;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
+ debug_navigation_avoidance_agents_radius_color = p_color;
+ if (debug_navigation_avoidance_agents_radius_material.is_valid()) {
+ debug_navigation_avoidance_agents_radius_material->set_albedo(debug_navigation_avoidance_agents_radius_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_agents_radius_color() const {
+ return debug_navigation_avoidance_agents_radius_color;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
+ debug_navigation_avoidance_obstacles_radius_color = p_color;
+ if (debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
+ debug_navigation_avoidance_obstacles_radius_material->set_albedo(debug_navigation_avoidance_obstacles_radius_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_obstacles_radius_color() const {
+ return debug_navigation_avoidance_obstacles_radius_color;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
+ debug_navigation_avoidance_static_obstacle_pushin_face_color = p_color;
+ if (debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
+ debug_navigation_avoidance_static_obstacle_pushin_face_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_face_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
+ return debug_navigation_avoidance_static_obstacle_pushin_face_color;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
+ debug_navigation_avoidance_static_obstacle_pushout_face_color = p_color;
+ if (debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
+ debug_navigation_avoidance_static_obstacle_pushout_face_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_face_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
+ return debug_navigation_avoidance_static_obstacle_pushout_face_color;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
+ debug_navigation_avoidance_static_obstacle_pushin_edge_color = p_color;
+ if (debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
+ debug_navigation_avoidance_static_obstacle_pushin_edge_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushin_edge_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
+ return debug_navigation_avoidance_static_obstacle_pushin_edge_color;
+}
+
+void NavigationServer3D::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
+ debug_navigation_avoidance_static_obstacle_pushout_edge_color = p_color;
+ if (debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
+ debug_navigation_avoidance_static_obstacle_pushout_edge_material->set_albedo(debug_navigation_avoidance_static_obstacle_pushout_edge_color);
+ }
+}
+
+Color NavigationServer3D::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
+ return debug_navigation_avoidance_static_obstacle_pushout_edge_color;
+}
+
void NavigationServer3D::set_debug_navigation_enable_agent_paths(const bool p_value) {
if (debug_navigation_enable_agent_paths != p_value) {
debug_dirty = true;
@@ -585,6 +825,26 @@ bool NavigationServer3D::get_debug_navigation_enable_agent_paths_xray() const {
return debug_navigation_enable_agent_paths_xray;
}
+void NavigationServer3D::set_debug_navigation_enabled(bool p_enabled) {
+ debug_navigation_enabled = p_enabled;
+ navigation_debug_dirty = true;
+ call_deferred("_emit_navigation_debug_changed_signal");
+}
+
+bool NavigationServer3D::get_debug_navigation_enabled() const {
+ return debug_navigation_enabled;
+}
+
+void NavigationServer3D::set_debug_avoidance_enabled(bool p_enabled) {
+ debug_avoidance_enabled = p_enabled;
+ avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer3D::get_debug_avoidance_enabled() const {
+ return debug_avoidance_enabled;
+}
+
#endif // DEBUG_ENABLED
void NavigationServer3D::query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const {
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 7348dab65a..73698581e9 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -80,6 +80,9 @@ public:
/// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const = 0;
+ virtual void map_set_use_edge_connections(RID p_map, bool p_enabled) = 0;
+ virtual bool map_get_use_edge_connections(RID p_map) const = 0;
+
/// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
@@ -103,12 +106,16 @@ public:
virtual TypedArray<RID> map_get_links(RID p_map) const = 0;
virtual TypedArray<RID> map_get_regions(RID p_map) const = 0;
virtual TypedArray<RID> map_get_agents(RID p_map) const = 0;
+ virtual TypedArray<RID> map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map) = 0;
/// Creates a new region.
virtual RID region_create() = 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;
+
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) = 0;
virtual real_t region_get_enter_cost(RID p_region) const = 0;
@@ -187,6 +194,12 @@ public:
virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
+
+ virtual void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) = 0;
+ virtual bool agent_get_use_3d_avoidance(RID p_agent) const = 0;
+
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
@@ -204,41 +217,53 @@ public:
/// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
- /// The minimal amount of time for which this
- /// agent's velocities that are computed by the
- /// simulation are safe with respect to other
- /// agents. The larger this number, the sooner
- /// this agent will respond to the presence of
- /// other agents, but the less freedom this
- /// agent has in choosing its velocities.
- /// Must be positive.
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time) = 0;
+ // Sets the minimum amount of time in seconds that an agent's
+ // must be able to stay on the calculated velocity while still avoiding collisions with agent's
+ // if this value is set to high an agent will often fall back to using a very low velocity just to be safe
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
+
+ /// Sets the minimum amount of time in seconds that an agent's
+ // must be able to stay on the calculated velocity while still avoiding collisions with obstacle's
+ // if this value is set to high an agent will often fall back to using a very low velocity just to be safe
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
/// The radius of this agent.
/// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0;
+ virtual void agent_set_height(RID p_agent, real_t p_height) = 0;
/// The maximum speed of this agent.
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
- /// Current velocity of the agent
- virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
+ /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
+ virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0;
- /// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) = 0;
+ /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
+ /// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
+ virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0;
- /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) = 0;
-
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process
- virtual void agent_set_callback(RID p_agent, Callable p_callback) = 0;
+ virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0;
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create() = 0;
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
+ virtual RID obstacle_get_map(RID p_obstacle) const = 0;
+ virtual void obstacle_set_height(RID p_obstacle, real_t p_height) = 0;
+ virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) = 0;
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) = 0;
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
/// Destroy the `RID`
virtual void free(RID p_object) = 0;
@@ -282,8 +307,15 @@ private:
#ifdef DEBUG_ENABLED
bool debug_dirty = true;
+
+ bool debug_navigation_enabled = false;
+ bool navigation_debug_dirty = true;
void _emit_navigation_debug_changed_signal();
+ bool debug_avoidance_enabled = false;
+ bool avoidance_debug_dirty = true;
+ void _emit_avoidance_debug_changed_signal();
+
Color debug_navigation_edge_connection_color = Color(1.0, 0.0, 1.0, 1.0);
Color debug_navigation_geometry_edge_color = Color(0.5, 1.0, 1.0, 1.0);
Color debug_navigation_geometry_face_color = Color(0.5, 1.0, 1.0, 0.4);
@@ -295,6 +327,14 @@ private:
real_t debug_navigation_agent_path_point_size = 4.0;
+ Color debug_navigation_avoidance_agents_radius_color = Color(1.0, 1.0, 0.0, 0.25);
+ Color debug_navigation_avoidance_obstacles_radius_color = Color(1.0, 0.5, 0.0, 0.25);
+
+ Color debug_navigation_avoidance_static_obstacle_pushin_face_color = Color(1.0, 0.0, 0.0, 0.0);
+ Color debug_navigation_avoidance_static_obstacle_pushout_face_color = Color(1.0, 1.0, 0.0, 0.5);
+ Color debug_navigation_avoidance_static_obstacle_pushin_edge_color = Color(1.0, 0.0, 0.0, 1.0);
+ Color debug_navigation_avoidance_static_obstacle_pushout_edge_color = Color(1.0, 1.0, 0.0, 1.0);
+
bool debug_navigation_enable_edge_connections = true;
bool debug_navigation_enable_edge_connections_xray = true;
bool debug_navigation_enable_edge_lines = true;
@@ -305,6 +345,10 @@ private:
bool debug_navigation_enable_agent_paths = true;
bool debug_navigation_enable_agent_paths_xray = true;
+ bool debug_navigation_avoidance_enable_agents_radius = true;
+ bool debug_navigation_avoidance_enable_obstacles_radius = true;
+ bool debug_navigation_avoidance_enable_obstacles_static = true;
+
Ref<StandardMaterial3D> debug_navigation_geometry_edge_material;
Ref<StandardMaterial3D> debug_navigation_geometry_face_material;
Ref<StandardMaterial3D> debug_navigation_geometry_edge_disabled_material;
@@ -312,11 +356,24 @@ private:
Ref<StandardMaterial3D> debug_navigation_edge_connections_material;
Ref<StandardMaterial3D> debug_navigation_link_connections_material;
Ref<StandardMaterial3D> debug_navigation_link_connections_disabled_material;
+ Ref<StandardMaterial3D> debug_navigation_avoidance_agents_radius_material;
+ Ref<StandardMaterial3D> debug_navigation_avoidance_obstacles_radius_material;
+
+ Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushin_face_material;
+ Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushout_face_material;
+ Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushin_edge_material;
+ Ref<StandardMaterial3D> debug_navigation_avoidance_static_obstacle_pushout_edge_material;
Ref<StandardMaterial3D> debug_navigation_agent_path_line_material;
Ref<StandardMaterial3D> debug_navigation_agent_path_point_material;
public:
+ void set_debug_navigation_enabled(bool p_enabled);
+ bool get_debug_navigation_enabled() const;
+
+ void set_debug_avoidance_enabled(bool p_enabled);
+ bool get_debug_avoidance_enabled() const;
+
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
@@ -341,6 +398,24 @@ public:
void set_debug_navigation_agent_path_color(const Color &p_color);
Color get_debug_navigation_agent_path_color() const;
+ void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_agents_radius_color() const;
+
+ void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_obstacles_radius_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
+
void set_debug_navigation_enable_edge_connections(const bool p_value);
bool get_debug_navigation_enable_edge_connections() const;
@@ -371,6 +446,15 @@ public:
void set_debug_navigation_agent_path_point_size(real_t p_point_size);
real_t get_debug_navigation_agent_path_point_size() const;
+ void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_agents_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_static() const;
+
Ref<StandardMaterial3D> get_debug_navigation_geometry_face_material();
Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_material();
Ref<StandardMaterial3D> get_debug_navigation_geometry_face_disabled_material();
@@ -381,6 +465,14 @@ public:
Ref<StandardMaterial3D> get_debug_navigation_agent_path_line_material();
Ref<StandardMaterial3D> get_debug_navigation_agent_path_point_material();
+
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_agents_radius_material();
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_obstacles_radius_material();
+
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
+ Ref<StandardMaterial3D> get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
#endif // DEBUG_ENABLED
};
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index fd9226e59e..98dc38990e 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -45,6 +45,8 @@ public:
Vector3 map_get_up(RID p_map) const override { return Vector3(); }
void map_set_cell_size(RID p_map, real_t p_cell_size) override {}
real_t map_get_cell_size(RID p_map) const override { return 0; }
+ void map_set_use_edge_connections(RID p_map, bool p_enabled) override {}
+ bool map_get_use_edge_connections(RID p_map) const override { return false; }
void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) override {}
real_t map_get_edge_connection_margin(RID p_map) const override { return 0; }
void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {}
@@ -57,8 +59,11 @@ public:
TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); }
TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); }
+ 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_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 {}
real_t region_get_enter_cost(RID p_region) const override { return 0; }
void region_set_travel_cost(RID p_region, real_t p_travel_cost) override {}
@@ -96,17 +101,32 @@ public:
RID agent_create() override { return RID(); }
void agent_set_map(RID p_agent, RID p_map) override {}
RID agent_get_map(RID p_agent) const override { return RID(); }
+ void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {}
+ bool agent_get_avoidance_enabled(RID p_agent) const override { return false; }
+ void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) override {}
+ bool agent_get_use_3d_avoidance(RID p_agent) const override { return false; }
void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {}
void agent_set_max_neighbors(RID p_agent, int p_count) override {}
- void agent_set_time_horizon(RID p_agent, real_t p_time) override {}
+ void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {}
+ void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {}
void agent_set_radius(RID p_agent, real_t p_radius) override {}
+ void agent_set_height(RID p_agent, real_t p_height) override {}
void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {}
+ void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) override {}
void agent_set_velocity(RID p_agent, Vector3 p_velocity) override {}
- void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) override {}
void agent_set_position(RID p_agent, Vector3 p_position) override {}
- void agent_set_ignore_y(RID p_agent, bool p_ignore) override {}
bool agent_is_map_changed(RID p_agent) const override { return false; }
- void agent_set_callback(RID p_agent, Callable p_callback) override {}
+ void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {}
+ void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {}
+ void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {}
+ void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {}
+ RID obstacle_create() override { return RID(); }
+ void obstacle_set_map(RID p_obstacle, RID p_map) override {}
+ RID obstacle_get_map(RID p_obstacle) const override { return RID(); }
+ void obstacle_set_height(RID p_obstacle, real_t p_height) override {}
+ void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
+ void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
+ void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
void free(RID p_object) override {}
void set_active(bool p_active) override {}
void process(real_t delta_time) override {}
diff --git a/servers/physics_2d/godot_shape_2d.cpp b/servers/physics_2d/godot_shape_2d.cpp
index 67c24e29b0..dadd8173c6 100644
--- a/servers/physics_2d/godot_shape_2d.cpp
+++ b/servers/physics_2d/godot_shape_2d.cpp
@@ -179,7 +179,7 @@ Variant GodotSeparationRayShape2D::get_data() const {
/*********************************************************/
void GodotSegmentShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
- if (Math::abs(p_normal.dot(n)) > _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(p_normal.dot(n)) > segment_is_valid_support_threshold) {
r_supports[0] = a;
r_supports[1] = b;
r_amount = 2;
@@ -308,7 +308,7 @@ void GodotRectangleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_sup
Vector2 ag;
ag[i] = 1.0;
real_t dp = ag.dot(p_normal);
- if (Math::abs(dp) < _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(dp) <= segment_is_valid_support_threshold) {
continue;
}
@@ -368,10 +368,9 @@ Variant GodotRectangleShape2D::get_data() const {
void GodotCapsuleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
Vector2 n = p_normal;
- real_t d = n.y;
real_t h = height * 0.5 - radius; // half-height of the rectangle part
- if (h > 0 && Math::abs(d) < (1.0 - _SEGMENT_IS_VALID_SUPPORT_THRESHOLD)) {
+ if (h > 0 && Math::abs(n.x) > segment_is_valid_support_threshold) {
// make it flat
n.y = 0.0;
n.normalize();
@@ -384,7 +383,7 @@ void GodotCapsuleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_suppo
r_supports[1].y -= h;
} else {
n *= radius;
- n.y += (d > 0) ? h : -h;
+ n.y += (n.y > 0) ? h : -h;
r_amount = 1;
*r_supports = n;
}
@@ -506,7 +505,7 @@ void GodotConvexPolygonShape2D::get_supports(const Vector2 &p_normal, Vector2 *r
}
//test segment
- if (points[i].normal.dot(p_normal) > _SEGMENT_IS_VALID_SUPPORT_THRESHOLD) {
+ if (points[i].normal.dot(p_normal) > segment_is_valid_support_threshold) {
r_amount = 2;
r_supports[0] = points[i].pos;
r_supports[1] = points[(i + 1) % point_count].pos;
diff --git a/servers/physics_2d/godot_shape_2d.h b/servers/physics_2d/godot_shape_2d.h
index 50b2bfd4e9..28c69574a0 100644
--- a/servers/physics_2d/godot_shape_2d.h
+++ b/servers/physics_2d/godot_shape_2d.h
@@ -32,7 +32,6 @@
#define GODOT_SHAPE_2D_H
#include "servers/physics_server_2d.h"
-#define _SEGMENT_IS_VALID_SUPPORT_THRESHOLD 0.99998
class GodotShape2D;
@@ -53,6 +52,10 @@ class GodotShape2D {
HashMap<GodotShapeOwner2D *, int> owners;
protected:
+ const double segment_is_valid_support_threshold = 0.99998;
+ const double segment_is_valid_support_threshold_lower =
+ Math::sqrt(1.0 - segment_is_valid_support_threshold * segment_is_valid_support_threshold);
+
void configure(const Rect2 &p_aabb);
public:
@@ -95,7 +98,7 @@ public:
}
if (r_amount == 1) {
- if (Math::abs(p_normal.dot(p_cast.normalized())) < (1.0 - _SEGMENT_IS_VALID_SUPPORT_THRESHOLD)) {
+ if (Math::abs(p_normal.dot(p_cast.normalized())) < segment_is_valid_support_threshold_lower) {
//make line because they are parallel
r_amount = 2;
r_supports[1] = r_supports[0] + p_cast;
@@ -105,7 +108,7 @@ public:
}
} else {
- if (Math::abs(p_normal.dot(p_cast.normalized())) < (1.0 - _SEGMENT_IS_VALID_SUPPORT_THRESHOLD)) {
+ if (Math::abs(p_normal.dot(p_cast.normalized())) < segment_is_valid_support_threshold_lower) {
//optimize line and make it larger because they are parallel
if ((r_supports[1] - r_supports[0]).dot(p_cast) > 0) {
//larger towards 1
diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp
index c467a583ba..acd3547c53 100644
--- a/servers/physics_3d/godot_body_pair_3d.cpp
+++ b/servers/physics_3d/godot_body_pair_3d.cpp
@@ -354,6 +354,8 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
bool do_process = false;
+ const Vector3 &offset_A = A->get_transform().get_origin();
+
const Basis &basis_A = A->get_transform().basis;
const Basis &basis_B = B->get_transform().basis;
@@ -382,7 +384,6 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
#ifdef DEBUG_ENABLED
if (space->is_debugging_contacts()) {
- const Vector3 &offset_A = A->get_transform().get_origin();
space->add_debug_contact(global_A + offset_A);
space->add_debug_contact(global_B + offset_A);
}
@@ -410,14 +411,13 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
if (A->can_report_contacts() || B->can_report_contacts()) {
Vector3 crB = B->get_angular_velocity().cross(c.rB) + B->get_linear_velocity();
Vector3 crA = A->get_angular_velocity().cross(c.rA) + A->get_linear_velocity();
- Vector3 wlB = global_B - offset_B;
if (A->can_report_contacts()) {
- A->add_contact(global_A, -c.normal, depth, shape_A, crA, wlB, shape_B, B->get_instance_id(), B->get_self(), crB, c.acc_impulse);
+ A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, crA, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB, c.acc_impulse);
}
if (B->can_report_contacts()) {
- B->add_contact(wlB, c.normal, depth, shape_B, crB, global_A, shape_A, A->get_instance_id(), A->get_self(), crA, -c.acc_impulse);
+ B->add_contact(global_B + offset_A, c.normal, depth, shape_B, crB, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA, -c.acc_impulse);
}
}
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index d566d612ce..50154a85ec 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -52,11 +52,11 @@ subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
-constexpr double edge_support_threshold = 0.0002;
-constexpr double face_support_threshold = 0.9998;
+const double support_threshold = 0.9998;
+const double support_threshold_lower = Math::sqrt(1.0 - support_threshold * support_threshold);
-constexpr double cylinder_edge_support_threshold = 0.002;
-constexpr double cylinder_face_support_threshold = 0.999;
+const double cylinder_support_threshold = 0.999;
+const double cylinder_support_threshold_lower = Math::sqrt(1.0 - cylinder_support_threshold * cylinder_support_threshold);
void GodotShape3D::configure(const AABB &p_aabb) {
aabb = p_aabb;
@@ -184,7 +184,7 @@ Vector3 GodotSeparationRayShape3D::get_support(const Vector3 &p_normal) const {
}
void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
- if (Math::abs(p_normal.z) < edge_support_threshold) {
+ if (Math::abs(p_normal.z) < support_threshold_lower) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = Vector3(0, 0, 0);
@@ -335,7 +335,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
Vector3 axis;
axis[i] = 1.0;
real_t dot = p_normal.dot(axis);
- if (Math::abs(dot) > face_support_threshold) {
+ if (Math::abs(dot) > support_threshold) {
//Vector3 axis_b;
bool neg = dot < 0;
@@ -376,7 +376,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
Vector3 axis;
axis[i] = 1.0;
- if (Math::abs(p_normal.dot(axis)) < edge_support_threshold) {
+ if (Math::abs(p_normal.dot(axis)) < support_threshold_lower) {
r_amount = 2;
r_type = FEATURE_EDGE;
@@ -523,7 +523,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto
real_t d = n.y;
real_t h = height * 0.5 - radius; // half-height of the cylinder part
- if (h > 0 && Math::abs(d) < edge_support_threshold) {
+ if (h > 0 && Math::abs(d) < support_threshold_lower) {
// make it flat
n.y = 0.0;
n.normalize();
@@ -701,7 +701,7 @@ Vector3 GodotCylinderShape3D::get_support(const Vector3 &p_normal) const {
void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
real_t d = p_normal.y;
- if (Math::abs(d) > cylinder_face_support_threshold) {
+ if (Math::abs(d) > cylinder_support_threshold) {
real_t h = (d > 0) ? height : -height;
Vector3 n = p_normal;
@@ -716,7 +716,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect
r_supports[1].x += radius;
r_supports[2] = n;
r_supports[2].z += radius;
- } else if (Math::abs(d) < cylinder_edge_support_threshold) {
+ } else if (Math::abs(d) < cylinder_support_threshold_lower) {
// make it flat
Vector3 n = p_normal;
n.y = 0.0;
@@ -925,7 +925,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
}
for (int i = 0; i < fc; i++) {
- if (faces[i].plane.normal.dot(p_normal) > face_support_threshold) {
+ if (faces[i].plane.normal.dot(p_normal) > support_threshold) {
int ic = faces[i].indices.size();
const int *ind = faces[i].indices.ptr();
@@ -954,7 +954,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
for (int i = 0; i < ec; i++) {
real_t dot = (vertices[edges[i].vertex_a] - vertices[edges[i].vertex_b]).normalized().dot(p_normal);
dot = ABS(dot);
- if (dot < edge_support_threshold && (edges[i].vertex_a == vtx || edges[i].vertex_b == vtx)) {
+ if (dot < support_threshold_lower && (edges[i].vertex_a == vtx || edges[i].vertex_b == vtx)) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = vertices[edges[i].vertex_a];
@@ -1197,7 +1197,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
Vector3 n = p_normal;
/** TEST FACE AS SUPPORT **/
- if (Math::abs(normal.dot(n)) > face_support_threshold) {
+ if (Math::abs(normal.dot(n)) > support_threshold) {
r_amount = 3;
r_type = FEATURE_FACE;
for (int i = 0; i < 3; i++) {
@@ -1231,7 +1231,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
// check if edge is valid as a support
real_t dot = (vertex[i] - vertex[nx]).normalized().dot(n);
dot = ABS(dot);
- if (dot < edge_support_threshold) {
+ if (dot < support_threshold_lower) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = vertex[i];
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index e880cacc02..768b1ba702 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -128,6 +128,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) override { return Size2(); };
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; };
/* DECAL API */
virtual RID decal_allocate() override { return RID(); }
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index b9e3c4f303..706477cedb 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -1177,17 +1177,31 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
}
void RendererCanvasCull::canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) {
- ERR_FAIL_COND(p_points.size() < 2);
+ ERR_FAIL_COND(p_points.is_empty() || p_points.size() % 2 != 0);
+ ERR_FAIL_COND(p_colors.size() != 1 && p_colors.size() * 2 != p_points.size());
// TODO: `canvas_item_add_line`(`multiline`, `polyline`) share logic, should factor out.
if (p_width < 0) {
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!canvas_item);
+ Vector<Color> colors;
+ if (p_colors.size() == 1) {
+ colors = p_colors;
+ } else { //} else if (p_colors.size() << 1 == p_points.size()) {
+ colors.resize(p_points.size());
+ Color *colors_ptr = colors.ptrw();
+ for (int i = 0; i < p_colors.size(); i++) {
+ Color color = p_colors[i];
+ colors_ptr[i * 2 + 0] = color;
+ colors_ptr[i * 2 + 1] = color;
+ }
+ }
+
Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>();
ERR_FAIL_COND(!pline);
pline->primitive = RS::PRIMITIVE_LINES;
- pline->polygon.create(Vector<int>(), p_points, p_colors);
+ pline->polygon.create(Vector<int>(), p_points, colors);
} else {
if (p_colors.size() == 1) {
Color color = p_colors[0];
@@ -1197,16 +1211,14 @@ void RendererCanvasCull::canvas_item_add_multiline(RID p_item, const Vector<Poin
canvas_item_add_line(p_item, from, to, color, p_width);
}
- } else if (p_colors.size() == p_points.size() >> 1) {
- for (int i = 0; i < p_points.size() >> 1; i++) {
+ } else { //} else if (p_colors.size() << 1 == p_points.size()) {
+ for (int i = 0; i < p_colors.size(); i++) {
Color color = p_colors[i];
Vector2 from = p_points[i * 2 + 0];
Vector2 to = p_points[i * 2 + 1];
canvas_item_add_line(p_item, from, to, color, p_width);
}
- } else {
- ERR_FAIL_MSG("Length of p_colors is invalid.");
}
}
}
diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp
index 959a752fba..d2a1a5ab9c 100644
--- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp
+++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp
@@ -47,15 +47,29 @@ ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() {
}
{
+ RD::FramebufferFormatID fb_format;
+ RD::PipelineColorBlendState blend_state;
+ String defines;
+ if (RD::get_singleton()->has_feature(RD::SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS)) {
+ fb_format = RD::get_singleton()->framebuffer_format_create_empty();
+ blend_state = RD::PipelineColorBlendState::create_disabled();
+ } else {
+ Vector<RD::AttachmentFormat> afs;
+ afs.push_back(RD::AttachmentFormat());
+ afs.write[0].usage_flags = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ fb_format = RD::get_singleton()->framebuffer_format_create(afs);
+ defines = "\n#define USE_ATTACHMENT\n";
+ }
+
Vector<String> versions;
versions.push_back("");
- cluster_render.cluster_render_shader.initialize(versions);
+ cluster_render.cluster_render_shader.initialize(versions, defines);
cluster_render.shader_version = cluster_render.cluster_render_shader.version_create();
cluster_render.shader = cluster_render.cluster_render_shader.version_get_shader(cluster_render.shader_version, 0);
- cluster_render.shader_pipelines[ClusterRender::PIPELINE_NORMAL] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, RD::get_singleton()->framebuffer_format_create_empty(), vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState(), 0);
+ cluster_render.shader_pipelines[ClusterRender::PIPELINE_NORMAL] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, fb_format, vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
RD::PipelineMultisampleState ms;
ms.sample_count = RD::TEXTURE_SAMPLES_4;
- cluster_render.shader_pipelines[ClusterRender::PIPELINE_MSAA] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, RD::get_singleton()->framebuffer_format_create_empty(), vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), ms, RD::PipelineDepthStencilState(), RD::PipelineColorBlendState(), 0);
+ cluster_render.shader_pipelines[ClusterRender::PIPELINE_MSAA] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, fb_format, vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), ms, RD::PipelineDepthStencilState(), blend_state, 0);
}
{
Vector<String> versions;
diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp
index 4253ea8610..78b785153f 100644
--- a/servers/rendering/renderer_rd/environment/fog.cpp
+++ b/servers/rendering/renderer_rd/environment/fog.cpp
@@ -388,6 +388,37 @@ Fog::FogShaderData::~FogShaderData() {
////////////////////////////////////////////////////////////////////////////////
// Volumetric Fog
+bool Fog::VolumetricFog::sync_gi_dependent_sets_validity(bool p_ensure_freed) {
+ bool null = gi_dependent_sets.copy_uniform_set.is_null();
+ bool valid = !null && RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.copy_uniform_set);
+
+#ifdef DEV_ENABLED
+ // It's all-or-nothing, or something else has changed that requires dev attention.
+ DEV_ASSERT(null == gi_dependent_sets.process_uniform_set_density.is_null());
+ DEV_ASSERT(null == gi_dependent_sets.process_uniform_set.is_null());
+ DEV_ASSERT(null == gi_dependent_sets.process_uniform_set2.is_null());
+ DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set_density));
+ DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set));
+ DEV_ASSERT(valid == RD::get_singleton()->uniform_set_is_valid(gi_dependent_sets.process_uniform_set2));
+#endif
+
+ if (valid) {
+ if (p_ensure_freed) {
+ RD::get_singleton()->free(gi_dependent_sets.copy_uniform_set);
+ RD::get_singleton()->free(gi_dependent_sets.process_uniform_set_density);
+ RD::get_singleton()->free(gi_dependent_sets.process_uniform_set);
+ RD::get_singleton()->free(gi_dependent_sets.process_uniform_set2);
+ valid = false;
+ }
+ }
+
+ if (!valid && !null) {
+ gi_dependent_sets = {};
+ }
+
+ return valid;
+}
+
void Fog::VolumetricFog::init(const Vector3i &fog_size, RID p_sky_shader) {
width = fog_size.x;
height = fog_size.y;
@@ -463,15 +494,9 @@ Fog::VolumetricFog::~VolumetricFog() {
if (fog_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(fog_uniform_set)) {
RD::get_singleton()->free(fog_uniform_set);
}
- if (process_uniform_set_density.is_valid() && RD::get_singleton()->uniform_set_is_valid(process_uniform_set_density)) {
- RD::get_singleton()->free(process_uniform_set_density);
- }
- if (process_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(process_uniform_set)) {
- RD::get_singleton()->free(process_uniform_set);
- }
- if (process_uniform_set2.is_valid() && RD::get_singleton()->uniform_set_is_valid(process_uniform_set2)) {
- RD::get_singleton()->free(process_uniform_set2);
- }
+
+ sync_gi_dependent_sets_validity(true);
+
if (sdfgi_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sdfgi_uniform_set)) {
RD::get_singleton()->free(sdfgi_uniform_set);
}
@@ -713,7 +738,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
RD::get_singleton()->compute_list_end();
}
- if (fog->process_uniform_set_density.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->process_uniform_set_density)) {
+ if (!fog->sync_gi_dependent_sets_validity()) {
//re create uniform set if needed
Vector<RD::Uniform> uniforms;
Vector<RD::Uniform> copy_uniforms;
@@ -910,9 +935,9 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
uniforms.push_back(u);
}
- fog->copy_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY), 0);
+ fog->gi_dependent_sets.copy_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY), 0);
- fog->process_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0);
+ fog->gi_dependent_sets.process_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0);
RID aux7 = uniforms.write[7].get_id(0);
RID aux8 = uniforms.write[8].get_id(0);
@@ -920,11 +945,11 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
uniforms.write[7].set_id(0, aux8);
uniforms.write[8].set_id(0, aux7);
- fog->process_uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0);
+ fog->gi_dependent_sets.process_uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0);
uniforms.remove_at(8);
uniforms.write[7].set_id(0, aux7);
- fog->process_uniform_set_density = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY), 0);
+ fog->gi_dependent_sets.process_uniform_set_density = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY), 0);
}
bool using_sdfgi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_enabled(p_settings.env) && (p_settings.sdfgi.is_valid());
@@ -1067,7 +1092,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[using_sdfgi ? VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI : VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set_density, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set_density, 0);
if (using_sdfgi) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->sdfgi_uniform_set, 1);
@@ -1078,7 +1103,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
// Copy fog to history buffer
if (RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_temporal_reprojection(p_settings.env)) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->copy_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.copy_uniform_set, 0);
RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth);
RD::get_singleton()->compute_list_add_barrier(compute_list);
}
@@ -1090,7 +1115,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
RENDER_TIMESTAMP("Filter Fog");
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FILTER]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set, 0);
RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth);
RD::get_singleton()->compute_list_end();
@@ -1101,7 +1126,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FILTER]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set2, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set2, 0);
RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth);
RD::get_singleton()->compute_list_add_barrier(compute_list);
@@ -1112,7 +1137,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
RD::get_singleton()->draw_command_begin_label("Integrate Fog");
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set, 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set, 0);
RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, 1);
RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_RASTER);
diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h
index 0b6bcc29fb..277389c596 100644
--- a/servers/rendering/renderer_rd/environment/fog.h
+++ b/servers/rendering/renderer_rd/environment/fog.h
@@ -301,10 +301,14 @@ public:
RID emissive_map;
RID fog_uniform_set;
- RID copy_uniform_set;
- RID process_uniform_set_density;
- RID process_uniform_set;
- RID process_uniform_set2;
+
+ struct {
+ RID copy_uniform_set;
+ RID process_uniform_set_density;
+ RID process_uniform_set;
+ RID process_uniform_set2;
+ } gi_dependent_sets;
+
RID sdfgi_uniform_set;
RID sky_uniform_set;
@@ -313,6 +317,8 @@ public:
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{};
virtual void free_data() override{};
+ bool sync_gi_dependent_sets_validity(bool p_ensure_freed = false);
+
void init(const Vector3i &fog_size, RID p_sky_shader);
~VolumetricFog();
};
diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 08133bf8d6..c2a018c7c6 100644
--- a/servers/rendering/renderer_rd/environment/gi.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -3493,6 +3493,9 @@ void GI::init(SkyRD *p_sky) {
if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) {
defines += "\n#define USE_VRS\n";
}
+ if (!RD::get_singleton()->sampler_is_format_supported_for_filter(RD::DATA_FORMAT_R8G8_UINT, RD::SAMPLER_FILTER_LINEAR)) {
+ defines += "\n#define SAMPLE_VOXEL_GI_NEAREST\n";
+ }
Vector<String> gi_modes;
@@ -3692,18 +3695,6 @@ void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBu
}
rbgi->uniform_set[v] = RID();
}
- if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) {
- Ref<Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG);
-
- if (RD::get_singleton()->uniform_set_is_valid(fog->fog_uniform_set)) {
- RD::get_singleton()->free(fog->fog_uniform_set);
- RD::get_singleton()->free(fog->process_uniform_set);
- RD::get_singleton()->free(fog->process_uniform_set2);
- }
- fog->fog_uniform_set = RID();
- fog->process_uniform_set = RID();
- fog->process_uniform_set2 = RID();
- }
}
if (p_voxel_gi_instances.size() > 0) {
@@ -3929,7 +3920,6 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor
u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
uniforms.push_back(u);
}
-
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
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 c7d85a3bbf..779249c2da 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -334,6 +334,9 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
} else if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW)) {
material_uniform_set = scene_shader.overdraw_material_uniform_set;
shader = scene_shader.overdraw_material_shader_ptr;
+ } else if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_PSSM_SPLITS)) {
+ material_uniform_set = scene_shader.debug_shadow_splits_material_uniform_set;
+ shader = scene_shader.debug_shadow_splits_material_shader_ptr;
} else {
#endif
material_uniform_set = surf->material_uniform_set;
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 6c948d3474..38b9367cbd 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
@@ -465,9 +465,11 @@ SceneShaderForwardClustered::~SceneShaderForwardClustered() {
material_storage->shader_free(overdraw_material_shader);
material_storage->shader_free(default_shader);
+ material_storage->shader_free(debug_shadow_splits_material_shader);
material_storage->material_free(overdraw_material);
material_storage->material_free(default_material);
+ material_storage->material_free(debug_shadow_splits_material);
}
void SceneShaderForwardClustered::init(const String p_defines) {
@@ -632,7 +634,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
- actions.renames["NODE_POSITION_VIEW"] = "(read_model_matrix * scene_data.view_matrix)[3].xyz";
+ actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
actions.renames["VIEW_INDEX"] = "ViewIndex";
actions.renames["VIEW_MONO_LEFT"] = "0";
@@ -641,6 +643,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
//for light
actions.renames["VIEW"] = "view";
+ actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
actions.renames["LIGHT_COLOR"] = "light_color";
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
actions.renames["LIGHT"] = "light";
@@ -719,6 +722,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
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;
@@ -793,6 +797,29 @@ void fragment() {
}
{
+ debug_shadow_splits_material_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(debug_shadow_splits_material_shader);
+ material_storage->shader_set_code(debug_shadow_splits_material_shader, R"(
+// 3D debug shadow splits mode shader(mobile).
+
+shader_type spatial;
+
+render_mode debug_shadow_splits;
+
+void fragment() {
+ ALBEDO = vec3(1.0, 1.0, 1.0);
+}
+)");
+ debug_shadow_splits_material = material_storage->material_allocate();
+ material_storage->material_initialize(debug_shadow_splits_material);
+ material_storage->material_set_shader(debug_shadow_splits_material, debug_shadow_splits_material_shader);
+
+ MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(debug_shadow_splits_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ debug_shadow_splits_material_shader_ptr = md->shader_data;
+ debug_shadow_splits_material_uniform_set = md->uniform_set;
+ }
+
+ {
default_vec4_xform_buffer = RD::get_singleton()->storage_buffer_create(256);
Vector<RD::Uniform> uniforms;
RD::Uniform u;
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 ffaf091b36..bdafc4c27e 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
@@ -222,6 +222,8 @@ public:
RID default_material;
RID overdraw_material_shader;
RID overdraw_material;
+ RID debug_shadow_splits_material_shader;
+ RID debug_shadow_splits_material;
RID default_shader_rd;
RID default_shader_sdfgi_rd;
@@ -236,6 +238,9 @@ public:
RID overdraw_material_uniform_set;
ShaderData *overdraw_material_shader_ptr = nullptr;
+ RID debug_shadow_splits_material_uniform_set;
+ ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
+
Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
HashSet<uint32_t> valid_color_pass_pipelines;
SceneShaderForwardClustered();
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 b9edc46aee..0b4157c5d6 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -2113,6 +2113,9 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
} else if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW)) {
material_uniform_set = scene_shader.overdraw_material_uniform_set;
shader = scene_shader.overdraw_material_shader_ptr;
+ } else if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_PSSM_SPLITS)) {
+ material_uniform_set = scene_shader.debug_shadow_splits_material_uniform_set;
+ shader = scene_shader.debug_shadow_splits_material_shader_ptr;
} else {
#endif
material_uniform_set = surf->material_uniform_set;
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 a93b994467..c161d3ffb0 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
@@ -522,7 +522,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
- actions.renames["NODE_POSITION_VIEW"] = "(read_model_matrix * scene_data.view_matrix)[3].xyz";
+ actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
actions.renames["VIEW_INDEX"] = "ViewIndex";
actions.renames["VIEW_MONO_LEFT"] = "0";
@@ -531,6 +531,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
//for light
actions.renames["VIEW"] = "view";
+ actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
actions.renames["LIGHT_COLOR"] = "light_color";
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
actions.renames["LIGHT"] = "light";
@@ -608,6 +609,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
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;
@@ -682,6 +684,30 @@ void fragment() {
}
{
+ debug_shadow_splits_material_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(debug_shadow_splits_material_shader);
+ // Use relatively low opacity so that more "layers" of overlapping objects can be distinguished.
+ material_storage->shader_set_code(debug_shadow_splits_material_shader, R"(
+// 3D debug shadow splits mode shader(mobile).
+
+shader_type spatial;
+
+render_mode debug_shadow_splits.
+
+void fragment() {
+ ALBEDO = vec3(1.0, 1.0, 1.0);
+}
+)");
+ debug_shadow_splits_material = material_storage->material_allocate();
+ material_storage->material_initialize(debug_shadow_splits_material);
+ material_storage->material_set_shader(debug_shadow_splits_material, debug_shadow_splits_material_shader);
+
+ MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(debug_shadow_splits_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ debug_shadow_splits_material_shader_ptr = md->shader_data;
+ debug_shadow_splits_material_uniform_set = md->uniform_set;
+ }
+
+ {
default_vec4_xform_buffer = RD::get_singleton()->storage_buffer_create(256);
Vector<RD::Uniform> uniforms;
RD::Uniform u;
@@ -723,7 +749,9 @@ SceneShaderForwardMobile::~SceneShaderForwardMobile() {
material_storage->shader_free(overdraw_material_shader);
material_storage->shader_free(default_shader);
+ material_storage->shader_free(debug_shadow_splits_material_shader);
material_storage->material_free(overdraw_material);
material_storage->material_free(default_material);
+ material_storage->material_free(debug_shadow_splits_material);
}
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
index 6f1f00cedc..0b98746d06 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
@@ -181,6 +181,8 @@ public:
RID default_material;
RID overdraw_material_shader;
RID overdraw_material;
+ RID debug_shadow_splits_material_shader;
+ RID debug_shadow_splits_material;
RID default_shader_rd;
RID default_vec4_xform_buffer;
@@ -194,6 +196,9 @@ public:
RID overdraw_material_uniform_set;
ShaderData *overdraw_material_shader_ptr = nullptr;
+ RID debug_shadow_splits_material_uniform_set;
+ ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
+
SceneShaderForwardMobile();
~SceneShaderForwardMobile();
diff --git a/servers/rendering/renderer_rd/shaders/cluster_render.glsl b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
index 8c26a67926..bfc98445c5 100644
--- a/servers/rendering/renderer_rd/shaders/cluster_render.glsl
+++ b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
@@ -100,6 +100,10 @@ layout(set = 0, binding = 3, std430) buffer restrict ClusterRender {
}
cluster_render;
+#ifdef USE_ATTACHMENT
+layout(location = 0) out vec4 frag_color;
+#endif
+
void main() {
//convert from screen to cluster
uvec2 cluster = uvec2(gl_FragCoord.xy) >> state.screen_to_clusters_shift;
@@ -113,6 +117,8 @@ void main() {
uint usage_write_offset = cluster_offset + (element_index >> 5);
uint usage_write_bit = 1 << (element_index & 0x1F);
+ uint aux = 0;
+
#ifdef USE_SUBGROUPS
uint cluster_thread_group_index;
@@ -138,7 +144,7 @@ void main() {
cluster_thread_group_index = subgroupBallotExclusiveBitCount(mask);
if (cluster_thread_group_index == 0) {
- atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
+ aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
}
}
#else
@@ -147,7 +153,7 @@ void main() {
if (!gl_HelperInvocation)
#endif
{
- atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
+ aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
}
#endif
//find the current element in the depth usage list and mark the current depth as used
@@ -162,7 +168,7 @@ void main() {
if (!gl_HelperInvocation) {
z_write_bit = subgroupOr(z_write_bit); //merge all Zs
if (cluster_thread_group_index == 0) {
- atomicOr(cluster_render.data[z_write_offset], z_write_bit);
+ aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
}
}
#else
@@ -171,7 +177,11 @@ void main() {
if (!gl_HelperInvocation)
#endif
{
- atomicOr(cluster_render.data[z_write_offset], z_write_bit);
+ aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
}
#endif
+
+#ifdef USE_ATTACHMENT
+ frag_color = vec4(float(aux));
+#endif
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
index 0828ffd921..b8c64d09f4 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl
@@ -55,109 +55,107 @@ void main() {
// Converted from compute shader which uses absolute coordinates.
// Could possibly simplify this
float face_size = float(params.face_size);
+ float inv_face_size = 1.0 / face_size;
+ vec2 id = floor(uv_interp);
- if (uv_interp.x < face_size && uv_interp.y < face_size) {
- float inv_face_size = 1.0 / face_size;
-
- float u0 = (uv_interp.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
- float u1 = (uv_interp.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
-
- float v0 = (uv_interp.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
- float v1 = (uv_interp.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
-
- float weights[4];
- weights[0] = calcWeight(u0, v0);
- weights[1] = calcWeight(u1, v0);
- weights[2] = calcWeight(u0, v1);
- weights[3] = calcWeight(u1, v1);
-
- const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
- for (int i = 0; i < 4; i++) {
- weights[i] = weights[i] * wsum + .125;
- }
-
- vec3 dir;
- vec4 color;
- switch (params.face_id) {
- case 0:
- get_dir_0(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_0(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_0(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_0(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- case 1:
- get_dir_1(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_1(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_1(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_1(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- case 2:
- get_dir_2(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_2(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_2(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_2(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- case 3:
- get_dir_3(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_3(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_3(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_3(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- case 4:
- get_dir_4(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_4(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_4(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_4(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- default:
- get_dir_5(dir, u0, v0);
- color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
-
- get_dir_5(dir, u1, v0);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
-
- get_dir_5(dir, u0, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
-
- get_dir_5(dir, u1, v1);
- color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
- break;
- }
- frag_color = color;
+ float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
+ float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
+
+ float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
+ float v1 = (id.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
+
+ float weights[4];
+ weights[0] = calcWeight(u0, v0);
+ weights[1] = calcWeight(u1, v0);
+ weights[2] = calcWeight(u0, v1);
+ weights[3] = calcWeight(u1, v1);
+
+ const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
+ for (int i = 0; i < 4; i++) {
+ weights[i] = weights[i] * wsum + .125;
+ }
+
+ vec3 dir;
+ vec4 color;
+ switch (params.face_id) {
+ case 0:
+ get_dir_0(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_0(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_0(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_0(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
+ case 1:
+ get_dir_1(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_1(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_1(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_1(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
+ case 2:
+ get_dir_2(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_2(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_2(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_2(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
+ case 3:
+ get_dir_3(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_3(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_3(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_3(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
+ case 4:
+ get_dir_4(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_4(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_4(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_4(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
+ default:
+ get_dir_5(dir, u0, v0);
+ color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
+
+ get_dir_5(dir, u1, v0);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
+
+ get_dir_5(dir, u0, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
+
+ get_dir_5(dir, u1, v1);
+ color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
+ break;
}
+ frag_color = color;
}
diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
index 459c4dcb1d..59af9501ba 100644
--- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
@@ -4,6 +4,10 @@
#VERSION_DEFINES
+#ifdef SAMPLE_VOXEL_GI_NEAREST
+#extension GL_EXT_samplerless_texture_functions : enable
+#endif
+
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define M_PI 3.141592
@@ -625,7 +629,11 @@ void process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 ref
#ifdef USE_VOXEL_GI_INSTANCES
{
+#ifdef SAMPLE_VOXEL_GI_NEAREST
+ uvec2 voxel_gi_tex = texelFetch(voxel_gi_buffer, pos, 0).rg;
+#else
uvec2 voxel_gi_tex = texelFetch(usampler2D(voxel_gi_buffer, linear_sampler), pos, 0).rg;
+#endif
roughness *= roughness;
//find arbitrary tangent and bitangent, then build a matrix
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
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 9750170724..cec54a2c5f 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
@@ -1817,9 +1817,30 @@ void fragment_shader(in SceneData scene_data) {
blur_shadow(shadow);
+#ifdef DEBUG_DRAW_PSSM_SPLITS
+ vec3 tint = vec3(1.0);
+ if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) {
+ tint = vec3(1.0, 0.0, 0.0);
+ } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) {
+ tint = vec3(0.0, 1.0, 0.0);
+ } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.z) {
+ tint = vec3(0.0, 0.0, 1.0);
+ } else {
+ tint = vec3(1.0, 1.0, 0.0);
+ }
+ tint = mix(tint, vec3(1.0), shadow);
+ shadow = 1.0;
+#endif
+
float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0;
- light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A, directional_lights.data[i].color * directional_lights.data[i].energy, true, shadow, f0, orms, 1.0, albedo, alpha,
+ light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A,
+#ifndef DEBUG_DRAW_PSSM_SPLITS
+ directional_lights.data[i].color * directional_lights.data[i].energy,
+#else
+ directional_lights.data[i].color * directional_lights.data[i].energy * tint,
+#endif
+ true, shadow, f0, orms, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
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 f87fa8d2ab..bb9216c4fa 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
@@ -1534,7 +1534,28 @@ void main() {
#endif
blur_shadow(shadow);
- light_compute(normal, directional_lights.data[i].direction, normalize(view), 0.0, directional_lights.data[i].color * directional_lights.data[i].energy, true, shadow, f0, orms, 1.0, albedo, alpha,
+#ifdef DEBUG_DRAW_PSSM_SPLITS
+ vec3 tint = vec3(1.0);
+ if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) {
+ tint = vec3(1.0, 0.0, 0.0);
+ } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) {
+ tint = vec3(0.0, 1.0, 0.0);
+ } else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.z) {
+ tint = vec3(0.0, 0.0, 1.0);
+ } else {
+ tint = vec3(1.0, 1.0, 0.0);
+ }
+ tint = mix(tint, vec3(1.0), shadow);
+ shadow = 1.0;
+#endif
+
+ light_compute(normal, directional_lights.data[i].direction, normalize(view), 0.0,
+#ifndef DEBUG_DRAW_PSSM_SPLITS
+ directional_lights.data[i].color * directional_lights.data[i].energy,
+#else
+ directional_lights.data[i].color * directional_lights.data[i].energy * tint,
+#endif
+ true, shadow, f0, orms, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index e65d842a67..a5ec62b546 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -634,10 +634,6 @@ void LightStorage::update_light_buffers(RenderDataRD *p_render_data, const Paged
light_data.size = 1.0 - Math::cos(Math::deg_to_rad(size)); //angle to cosine offset
- if (RendererSceneRenderRD::get_singleton()->get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_PSSM_SPLITS) {
- WARN_PRINT_ONCE("The DirectionalLight3D PSSM splits debug draw mode is not reimplemented yet.");
- }
-
light_data.shadow_opacity = (p_using_shadows && light->shadow)
? light->param[RS::LIGHT_PARAM_SHADOW_OPACITY]
: 0.0;
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index c36d1ef503..3360358169 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -93,11 +93,11 @@ private:
struct ShadowTransform {
Projection camera;
Transform3D transform;
- float farplane;
- float split;
- float bias_scale;
- float shadow_texel_size;
- float range_begin;
+ float farplane = 0.0;
+ float split = 0.0;
+ float bias_scale = 0.0;
+ float shadow_texel_size = 0.0;
+ float range_begin = 0.0;
Rect2 atlas_rect;
Vector2 uv_scale;
};
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 8808d63a82..3d6092a9f6 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -1442,6 +1442,17 @@ RID TextureStorage::texture_get_rd_texture(RID p_texture, bool p_srgb) const {
return (p_srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture;
}
+uint64_t TextureStorage::texture_get_native_handle(RID p_texture, bool p_srgb) const {
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!tex, 0);
+
+ if (p_srgb && tex->rd_texture_srgb.is_valid()) {
+ return RD::get_singleton()->texture_native_handle(tex->rd_texture_srgb);
+ } else {
+ return RD::get_singleton()->texture_native_handle(tex->rd_texture);
+ }
+}
+
Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format) {
Ref<Image> image = p_image->duplicate();
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 3e4ac3b833..8f021f3179 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -499,6 +499,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) 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;
//internal usage
_FORCE_INLINE_ TextureType texture_get_type(RID p_texture) {
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index f609fa6023..8813c2e651 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -962,6 +962,13 @@ void RendererViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpda
viewport->update_mode = p_mode;
}
+RID RendererViewport::viewport_get_render_target(RID p_viewport) const {
+ const Viewport *viewport = viewport_owner.get_or_null(p_viewport);
+ ERR_FAIL_COND_V(!viewport, RID());
+
+ return viewport->render_target;
+}
+
RID RendererViewport::viewport_get_texture(RID p_viewport) const {
const Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_COND_V(!viewport, RID());
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index c24275de6e..2f9537a47c 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -231,6 +231,7 @@ public:
void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode);
+ RID viewport_get_render_target(RID p_viewport) const;
RID viewport_get_texture(RID p_viewport) const;
RID viewport_get_occluder_debug_texture(RID p_viewport) const;
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 744fd051f5..0227f01f6f 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -334,6 +334,14 @@ RID RenderingDevice::_compute_pipeline_create(RID p_shader, const TypedArray<RDP
return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants));
}
+RenderingDevice::DrawListID RenderingDevice::_draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) {
+ Vector<RID> stextures;
+ for (int i = 0; i < p_storage_textures.size(); i++) {
+ stextures.push_back(p_storage_textures[i]);
+ }
+ return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures);
+}
+
Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) {
Vector<DrawListID> splits;
splits.resize(p_splits);
@@ -723,6 +731,7 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("framebuffer_is_valid", "framebuffer"), &RenderingDevice::framebuffer_is_valid);
ClassDB::bind_method(D_METHOD("sampler_create", "state"), &RenderingDevice::_sampler_create);
+ ClassDB::bind_method(D_METHOD("sampler_is_format_supported_for_filter", "format", "sampler_filter"), &RenderingDevice::sampler_is_format_supported_for_filter);
ClassDB::bind_method(D_METHOD("vertex_buffer_create", "size_bytes", "data", "use_as_storage"), &RenderingDevice::vertex_buffer_create, DEFVAL(Vector<uint8_t>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("vertex_format_create", "vertex_descriptions"), &RenderingDevice::_vertex_format_create);
@@ -760,7 +769,7 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color()));
- ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>()));
+ ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>()));
ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>()));
ClassDB::bind_method(D_METHOD("draw_list_set_blend_constants", "draw_list", "color"), &RenderingDevice::draw_list_set_blend_constants);
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 447627b08e..82f89daa6d 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -539,6 +539,7 @@ public:
virtual bool texture_is_shared(RID p_texture) = 0;
virtual bool texture_is_valid(RID p_texture) = 0;
virtual Size2i texture_size(RID p_texture) = 0;
+ virtual uint64_t texture_native_handle(RID p_texture) = 0;
virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0;
@@ -653,6 +654,7 @@ public:
};
virtual RID sampler_create(const SamplerState &p_state) = 0;
+ virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const = 0;
/**********************/
/**** VERTEX ARRAY ****/
@@ -703,6 +705,8 @@ public:
SUPPORTS_MULTIVIEW,
SUPPORTS_FSR_HALF_FLOAT,
SUPPORTS_ATTACHMENT_VRS,
+ // If not supported, a fragment shader with only side effets (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver.
+ SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS,
};
virtual bool has_feature(const Features p_feature) const = 0;
@@ -1329,6 +1333,7 @@ protected:
RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants);
RID _compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants);
+ DrawListID _draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>());
Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>());
void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
diff --git a/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h
index 01d0f178c7..e173400299 100644
--- a/servers/rendering/rendering_device_binds.h
+++ b/servers/rendering/rendering_device_binds.h
@@ -349,13 +349,18 @@ public:
return versions[p_version]->get_stages();
}
- Vector<StringName> get_version_list() const {
+ TypedArray<StringName> get_version_list() const {
Vector<StringName> vnames;
for (const KeyValue<StringName, Ref<RDShaderSPIRV>> &E : versions) {
vnames.push_back(E.key);
}
vnames.sort_custom<StringName::AlphCompare>();
- return vnames;
+ TypedArray<StringName> ret;
+ ret.resize(vnames.size());
+ for (int i = 0; i < vnames.size(); i++) {
+ ret[i] = vnames[i];
+ }
+ return ret;
}
void set_base_error(const String &p_error) {
@@ -395,7 +400,7 @@ public:
protected:
Dictionary _get_versions() const {
- Vector<StringName> vnames = get_version_list();
+ TypedArray<StringName> vnames = get_version_list();
Dictionary ret;
for (int i = 0; i < vnames.size(); i++) {
ret[vnames[i]] = versions[vnames[i]];
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 6017eff55e..5f14245307 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -237,7 +237,9 @@ void RenderingServerDefault::init() {
void RenderingServerDefault::finish() {
if (create_thread) {
command_queue.push(this, &RenderingServerDefault::_thread_exit);
- thread.wait_to_finish();
+ if (thread.is_started()) {
+ thread.wait_to_finish();
+ }
} else {
_finish();
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 249e5c2d9d..797fe73ba0 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -214,6 +214,7 @@ public:
FUNC2(texture_set_force_redraw_if_visible, RID, bool)
FUNC2RC(RID, texture_get_rd_texture, RID, bool)
+ FUNC2RC(uint64_t, texture_get_native_handle, RID, bool)
/* SHADER API */
@@ -606,6 +607,7 @@ public:
FUNC2(viewport_set_update_mode, RID, ViewportUpdateMode)
+ FUNC1RC(RID, viewport_get_render_target, RID)
FUNC1RC(RID, viewport_get_texture, RID)
FUNC2(viewport_set_disable_2d, RID, bool)
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 8d16a29659..4ea06afe79 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -38,12 +38,7 @@
#define SL ShaderLanguage
static String _mktab(int p_level) {
- String tb;
- for (int i = 0; i < p_level; i++) {
- tb += "\t";
- }
-
- return tb;
+ return String("\t").repeat(p_level);
}
static String _typestr(SL::DataType p_type) {
@@ -441,7 +436,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
String code;
switch (p_node->type) {
- case SL::Node::TYPE_SHADER: {
+ case SL::Node::NODE_TYPE_SHADER: {
SL::ShaderNode *pnode = (SL::ShaderNode *)p_node;
for (int i = 0; i < pnode->render_modes.size(); i++) {
@@ -786,11 +781,11 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
//code+=dump_node_code(pnode->body,p_level);
} break;
- case SL::Node::TYPE_STRUCT: {
+ case SL::Node::NODE_TYPE_STRUCT: {
} break;
- case SL::Node::TYPE_FUNCTION: {
+ case SL::Node::NODE_TYPE_FUNCTION: {
} break;
- case SL::Node::TYPE_BLOCK: {
+ case SL::Node::NODE_TYPE_BLOCK: {
SL::BlockNode *bnode = (SL::BlockNode *)p_node;
//variables
@@ -801,7 +796,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
for (int i = 0; i < bnode->statements.size(); i++) {
String scode = _dump_node_code(bnode->statements[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->single_statement) {
+ if (bnode->statements[i]->type == SL::Node::NODE_TYPE_CONTROL_FLOW || bnode->single_statement) {
code += scode; //use directly
if (bnode->use_comma_between_statements && i + 1 < bnode->statements.size()) {
code += ",";
@@ -815,7 +810,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_VARIABLE_DECLARATION: {
+ case SL::Node::NODE_TYPE_VARIABLE_DECLARATION: {
SL::VariableDeclarationNode *vdnode = (SL::VariableDeclarationNode *)p_node;
String declaration;
@@ -873,7 +868,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code += declaration;
} break;
- case SL::Node::TYPE_VARIABLE: {
+ case SL::Node::NODE_TYPE_VARIABLE: {
SL::VariableNode *vnode = (SL::VariableNode *)p_node;
bool use_fragment_varying = false;
@@ -966,7 +961,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_ARRAY_CONSTRUCT: {
+ case SL::Node::NODE_TYPE_ARRAY_CONSTRUCT: {
SL::ArrayConstructNode *acnode = (SL::ArrayConstructNode *)p_node;
int sz = acnode->initializer.size();
if (acnode->datatype == SL::TYPE_STRUCT) {
@@ -986,7 +981,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
code += ")";
} break;
- case SL::Node::TYPE_ARRAY: {
+ case SL::Node::NODE_TYPE_ARRAY: {
SL::ArrayNode *anode = (SL::ArrayNode *)p_node;
bool use_fragment_varying = false;
@@ -1077,7 +1072,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_CONSTANT: {
+ case SL::Node::NODE_TYPE_CONSTANT: {
SL::ConstantNode *cnode = (SL::ConstantNode *)p_node;
if (cnode->array_size == 0) {
@@ -1104,7 +1099,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_OPERATOR: {
+ case SL::Node::NODE_TYPE_OPERATOR: {
SL::OperatorNode *onode = (SL::OperatorNode *)p_node;
switch (onode->op) {
@@ -1135,7 +1130,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
case SL::OP_CALL:
case SL::OP_STRUCT:
case SL::OP_CONSTRUCT: {
- ERR_FAIL_COND_V(onode->arguments[0]->type != SL::Node::TYPE_VARIABLE, String());
+ ERR_FAIL_COND_V(onode->arguments[0]->type != SL::Node::NODE_TYPE_VARIABLE, String());
const SL::VariableNode *vnode = static_cast<const SL::VariableNode *>(onode->arguments[0]);
const SL::FunctionNode *func = nullptr;
const bool is_internal_func = internal_functions.has(vnode->name);
@@ -1205,12 +1200,12 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
bool done = false;
do {
switch (node->type) {
- case SL::Node::TYPE_VARIABLE: {
+ case SL::Node::NODE_TYPE_VARIABLE: {
name = static_cast<const SL::VariableNode *>(node)->name;
done = true;
found = true;
} break;
- case SL::Node::TYPE_MEMBER: {
+ case SL::Node::NODE_TYPE_MEMBER: {
node = static_cast<const SL::MemberNode *>(node)->owner;
} break;
default: {
@@ -1232,12 +1227,12 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
bool correct_texture_uniform = false;
switch (onode->arguments[i]->type) {
- case SL::Node::TYPE_VARIABLE: {
+ case SL::Node::NODE_TYPE_VARIABLE: {
const SL::VariableNode *varnode = static_cast<const SL::VariableNode *>(onode->arguments[i]);
texture_uniform = varnode->name;
correct_texture_uniform = true;
} break;
- case SL::Node::TYPE_ARRAY: {
+ case SL::Node::NODE_TYPE_ARRAY: {
const SL::ArrayNode *anode = static_cast<const SL::ArrayNode *>(onode->arguments[i]);
texture_uniform = anode->name;
correct_texture_uniform = true;
@@ -1366,7 +1361,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_CONTROL_FLOW: {
+ case SL::Node::NODE_TYPE_CONTROL_FLOW: {
SL::ControlFlowNode *cfnode = (SL::ControlFlowNode *)p_node;
if (cfnode->flow_op == SL::FLOW_OP_IF) {
code += _mktab(p_level) + "if (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n";
@@ -1418,7 +1413,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
} break;
- case SL::Node::TYPE_MEMBER: {
+ case SL::Node::NODE_TYPE_MEMBER: {
SL::MemberNode *mnode = (SL::MemberNode *)p_node;
code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + mnode->name;
if (mnode->index_expression != nullptr) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 12e99ba606..df23386037 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -3063,7 +3063,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
Vector<StringName> args2;
Vector<int> args3;
- ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false);
+ ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::NODE_TYPE_VARIABLE, false);
StringName name = static_cast<VariableNode *>(p_func->arguments[0])->name.operator String();
@@ -3117,14 +3117,14 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
failed_builtin = true;
bool fail = false;
for (int i = 0; i < argcount; i++) {
- if (p_func->arguments[i + 1]->type == Node::TYPE_ARRAY) {
+ if (p_func->arguments[i + 1]->type == Node::NODE_TYPE_ARRAY) {
const ArrayNode *anode = static_cast<const ArrayNode *>(p_func->arguments[i + 1]);
if (anode->call_expression == nullptr && !anode->is_indexed()) {
fail = true;
break;
}
}
- if (get_scalar_type(args[i]) == args[i] && p_func->arguments[i + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[i + 1]), builtin_func_defs[idx].args[i])) {
+ if (get_scalar_type(args[i]) == args[i] && p_func->arguments[i + 1]->type == Node::NODE_TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[i + 1]), builtin_func_defs[idx].args[i])) {
//all good, but needs implicit conversion later
} else if (args[i] != builtin_func_defs[idx].args[i]) {
fail = true;
@@ -3160,7 +3160,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
int max = builtin_func_const_args[constarg_idx].max;
bool error = false;
- if (p_func->arguments[arg]->type == Node::TYPE_VARIABLE) {
+ if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) {
const VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg]);
bool is_const = false;
@@ -3172,7 +3172,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
error = true;
}
} else {
- if (p_func->arguments[arg]->type == Node::TYPE_CONSTANT) {
+ if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) {
const ConstantNode *cn = static_cast<ConstantNode *>(p_func->arguments[arg]);
if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) {
@@ -3207,17 +3207,17 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
break;
}
if (arg_idx < argcount) {
- if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) {
+ if (p_func->arguments[arg_idx + 1]->type != Node::NODE_TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::NODE_TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::NODE_TYPE_ARRAY) {
_set_error(vformat(RTR("Argument %d of function '%s' is not a variable, array, or member."), arg_idx + 1, String(name)));
return false;
}
- if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
+ if (p_func->arguments[arg_idx + 1]->type == Node::NODE_TYPE_ARRAY) {
ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]);
if (mn->is_const) {
fail = true;
}
- } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
+ } else if (p_func->arguments[arg_idx + 1]->type == Node::NODE_TYPE_MEMBER) {
MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]);
if (mn->basetype_const) {
fail = true;
@@ -3250,18 +3250,18 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
StringName var_name;
- if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) {
+ if (p_func->arguments[arg_idx + 1]->type == Node::NODE_TYPE_ARRAY) {
var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name;
- } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) {
+ } else if (p_func->arguments[arg_idx + 1]->type == Node::NODE_TYPE_MEMBER) {
Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner;
- while (n->type == Node::TYPE_MEMBER) {
+ while (n->type == Node::NODE_TYPE_MEMBER) {
n = static_cast<const MemberNode *>(n)->owner;
}
- if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
+ if (n->type != Node::NODE_TYPE_VARIABLE && n->type != Node::NODE_TYPE_ARRAY) {
_set_error(vformat(RTR("Argument %d of function '%s' is not a variable, array, or member."), arg_idx + 1, String(name)));
return false;
}
- if (n->type == Node::TYPE_VARIABLE) {
+ if (n->type == Node::NODE_TYPE_VARIABLE) {
var_name = static_cast<const VariableNode *>(n)->name;
} else { // TYPE_ARRAY
var_name = static_cast<const ArrayNode *>(n)->name;
@@ -3298,7 +3298,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
//implicitly convert values if possible
for (int i = 0; i < argcount; i++) {
- if (get_scalar_type(args[i]) != args[i] || args[i] == builtin_func_defs[idx].args[i] || p_func->arguments[i + 1]->type != Node::TYPE_CONSTANT) {
+ if (get_scalar_type(args[i]) != args[i] || args[i] == builtin_func_defs[idx].args[i] || p_func->arguments[i + 1]->type != Node::NODE_TYPE_CONSTANT) {
//can't do implicit conversion here
continue;
}
@@ -3422,7 +3422,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
bool fail = false;
for (int j = 0; j < args.size(); j++) {
- if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
+ if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::NODE_TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
//all good, but it needs implicit conversion later
} else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) {
String func_arg_name;
@@ -3457,7 +3457,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
if (!fail) {
//implicitly convert values if possible
for (int k = 0; k < args.size(); k++) {
- if (get_scalar_type(args[k]) != args[k] || args[k] == pfunc->arguments[k].type || p_func->arguments[k + 1]->type != Node::TYPE_CONSTANT) {
+ if (get_scalar_type(args[k]) != args[k] || args[k] == pfunc->arguments[k].type || p_func->arguments[k + 1]->type != Node::NODE_TYPE_CONSTANT) {
//can't do implicit conversion here
continue;
}
@@ -3565,7 +3565,7 @@ bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Functio
return false;
}
- if (is_const_decl && arg->type == Node::TYPE_VARIABLE) {
+ if (is_const_decl && arg->type == Node::NODE_TYPE_VARIABLE) {
const VariableNode *var = static_cast<const VariableNode *>(arg);
if (!var->is_const) {
_set_error(RTR("Expected constant expression."));
@@ -4531,7 +4531,7 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St
bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
switch (p_node->type) {
- case Node::TYPE_OPERATOR: {
+ case Node::NODE_TYPE_OPERATOR: {
const OperatorNode *op_node = static_cast<const OperatorNode *>(p_node);
for (int i = int(op_node->op == OP_CALL); i < op_node->arguments.size(); i++) {
if (!_check_node_constness(op_node->arguments[i])) {
@@ -4539,15 +4539,15 @@ bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
}
}
} break;
- case Node::TYPE_CONSTANT:
+ case Node::NODE_TYPE_CONSTANT:
break;
- case Node::TYPE_VARIABLE: {
+ case Node::NODE_TYPE_VARIABLE: {
const VariableNode *var_node = static_cast<const VariableNode *>(p_node);
if (!var_node->is_const) {
return false;
}
} break;
- case Node::TYPE_ARRAY: {
+ case Node::NODE_TYPE_ARRAY: {
const ArrayNode *arr_node = static_cast<const ArrayNode *>(p_node);
if (!arr_node->is_const) {
return false;
@@ -4560,7 +4560,7 @@ bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
}
bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {
- if (p_node->type == Node::TYPE_OPERATOR) {
+ if (p_node->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
if (op->op == OP_INDEX) {
@@ -4577,7 +4577,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
return false;
}
- } else if (p_node->type == Node::TYPE_MEMBER) {
+ } else if (p_node->type == Node::NODE_TYPE_MEMBER) {
MemberNode *member = static_cast<MemberNode *>(p_node);
if (member->has_swizzling_duplicates) {
@@ -4589,7 +4589,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
return _validate_assign(member->owner, p_function_info, r_message);
- } else if (p_node->type == Node::TYPE_VARIABLE) {
+ } else if (p_node->type == Node::NODE_TYPE_VARIABLE) {
VariableNode *var = static_cast<VariableNode *>(p_node);
if (shader->uniforms.has(var->name)) {
@@ -4609,7 +4609,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
if (!(p_function_info.built_ins.has(var->name) && p_function_info.built_ins[var->name].constant)) {
return true;
}
- } else if (p_node->type == Node::TYPE_ARRAY) {
+ } else if (p_node->type == Node::NODE_TYPE_ARRAY) {
ArrayNode *arr = static_cast<ArrayNode *>(p_node);
if (shader->constants.has(arr->name) || arr->is_const) {
@@ -4727,7 +4727,7 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &
_set_tkpos(pos);
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (n) {
- if (n->type == Node::TYPE_VARIABLE) {
+ if (n->type == Node::NODE_TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
if (vn) {
ConstantNode::Value v;
@@ -4750,7 +4750,7 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &
}
}
}
- } else if (n->type == Node::TYPE_OPERATOR) {
+ } else if (n->type == Node::NODE_TYPE_OPERATOR) {
_set_error(vformat(RTR("Array size expressions are not supported.")));
return ERR_PARSE_ERROR;
}
@@ -5296,10 +5296,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ArgumentQualifier arg_qual = call_function->arguments[i].qualifier;
bool is_out_arg = arg_qual != ArgumentQualifier::ARGUMENT_QUALIFIER_IN;
- if (n->type == Node::TYPE_VARIABLE || n->type == Node::TYPE_ARRAY) {
+ if (n->type == Node::NODE_TYPE_VARIABLE || n->type == Node::NODE_TYPE_ARRAY) {
StringName varname;
- if (n->type == Node::TYPE_VARIABLE) {
+ if (n->type == Node::NODE_TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
varname = vn->name;
} else { // TYPE_ARRAY
@@ -5347,23 +5347,23 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (is_const_arg || is_out_arg) {
StringName varname;
- if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR || n->type == Node::TYPE_ARRAY_CONSTRUCT) {
+ if (n->type == Node::NODE_TYPE_CONSTANT || n->type == Node::NODE_TYPE_OPERATOR || n->type == Node::NODE_TYPE_ARRAY_CONSTRUCT) {
if (!is_const_arg) {
error = true;
}
- } else if (n->type == Node::TYPE_ARRAY) {
+ } else if (n->type == Node::NODE_TYPE_ARRAY) {
ArrayNode *an = static_cast<ArrayNode *>(n);
if (!is_const_arg && (an->call_expression != nullptr || an->is_const)) {
error = true;
}
varname = an->name;
- } else if (n->type == Node::TYPE_VARIABLE) {
+ } else if (n->type == Node::NODE_TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
if (vn->is_const && !is_const_arg) {
error = true;
}
varname = vn->name;
- } else if (n->type == Node::TYPE_MEMBER) {
+ } else if (n->type == Node::NODE_TYPE_MEMBER) {
MemberNode *mn = static_cast<MemberNode *>(n);
if (mn->basetype_const && is_out_arg) {
error = true;
@@ -5389,7 +5389,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (is_sampler_type(call_function->arguments[i].type)) {
//let's see where our argument comes from
- ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
+ ERR_CONTINUE(n->type != Node::NODE_TYPE_VARIABLE); //bug? this should always be a variable
VariableNode *vn = static_cast<VariableNode *>(n);
StringName varname = vn->name;
if (shader->uniforms.has(varname)) {
@@ -5599,7 +5599,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
- if (index_expression->type == Node::TYPE_CONSTANT) {
+ if (index_expression->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *cnode = static_cast<ConstantNode *>(index_expression);
if (cnode) {
if (!cnode->values.is_empty()) {
@@ -6064,7 +6064,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
- if (index_expression->type == Node::TYPE_CONSTANT) {
+ if (index_expression->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *cnode = static_cast<ConstantNode *>(index_expression);
if (cnode) {
if (!cnode->values.is_empty()) {
@@ -6118,7 +6118,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
String member_struct_name;
if (expr->get_array_size() > 0) {
- if (index->type == Node::TYPE_CONSTANT) {
+ if (index->type == Node::NODE_TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= (uint32_t)expr->get_array_size()) {
_set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, expr->get_array_size() - 1));
@@ -6136,7 +6136,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC2:
case TYPE_UVEC2:
case TYPE_MAT2:
- if (index->type == Node::TYPE_CONSTANT) {
+ if (index->type == Node::NODE_TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 2) {
_set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 1));
@@ -6170,7 +6170,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC3:
case TYPE_UVEC3:
case TYPE_MAT3:
- if (index->type == Node::TYPE_CONSTANT) {
+ if (index->type == Node::NODE_TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 3) {
_set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 2));
@@ -6203,7 +6203,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
case TYPE_IVEC4:
case TYPE_UVEC4:
case TYPE_MAT4:
- if (index->type == Node::TYPE_CONSTANT) {
+ if (index->type == Node::NODE_TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 4) {
_set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 3));
@@ -6702,7 +6702,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node) {
- if (p_node->type != Node::TYPE_OPERATOR) {
+ if (p_node->type != Node::NODE_TYPE_OPERATOR) {
return p_node;
}
@@ -6710,7 +6710,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
OperatorNode *op = static_cast<OperatorNode *>(p_node);
if (op->op == OP_CONSTRUCT) {
- ERR_FAIL_COND_V(op->arguments[0]->type != Node::TYPE_VARIABLE, p_node);
+ ERR_FAIL_COND_V(op->arguments[0]->type != Node::NODE_TYPE_VARIABLE, p_node);
DataType type = op->get_datatype();
DataType base = get_scalar_type(type);
@@ -6720,7 +6720,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
for (int i = 1; i < op->arguments.size(); i++) {
op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]);
- if (op->arguments[i]->type == Node::TYPE_CONSTANT) {
+ if (op->arguments[i]->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i]);
if (get_scalar_type(cn->datatype) == base) {
@@ -6772,7 +6772,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
return cn;
} else if (op->op == OP_NEGATE) {
op->arguments.write[0] = _reduce_expression(p_block, op->arguments[0]);
- if (op->arguments[0]->type == Node::TYPE_CONSTANT) {
+ if (op->arguments[0]->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[0]);
DataType base = get_scalar_type(cn->datatype);
@@ -7179,7 +7179,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
- if (is_const && n->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
+ if (is_const && n->type == Node::NODE_TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
_set_error(RTR("Expected a constant expression."));
return ERR_PARSE_ERROR;
}
@@ -7235,7 +7235,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!n) {
return ERR_PARSE_ERROR;
}
- if (is_const && n->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
+ if (is_const && n->type == Node::NODE_TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
OperatorNode *op = static_cast<OperatorNode *>(n);
for (int i = 1; i < op->arguments.size(); i++) {
if (!_check_node_constness(op->arguments[i])) {
@@ -7245,7 +7245,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
- if (n->type == Node::TYPE_CONSTANT) {
+ if (n->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *const_node = static_cast<ConstantNode *>(n);
if (const_node && const_node->values.size() == 1) {
var.value = const_node->values[0];
@@ -7412,7 +7412,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
ControlFlowNode *flow = static_cast<ControlFlowNode *>(switch_block->statements[i]);
if (flow) {
if (flow->flow_op == FLOW_OP_CASE) {
- if (flow->expressions[0]->type == Node::TYPE_CONSTANT) {
+ if (flow->expressions[0]->type == Node::NODE_TYPE_CONSTANT) {
ConstantNode *cn = static_cast<ConstantNode *>(flow->expressions[0]);
if (!cn || cn->values.is_empty()) {
return ERR_PARSE_ERROR;
@@ -7422,7 +7422,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
constants.insert(cn->values[0].sint);
- } else if (flow->expressions[0]->type == Node::TYPE_VARIABLE) {
+ } else if (flow->expressions[0]->type == Node::NODE_TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(flow->expressions[0]);
if (!vn) {
return ERR_PARSE_ERROR;
@@ -7852,9 +7852,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
- is_condition = expr->type == Node::TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
+ is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
- if (expr->type == Node::TYPE_OPERATOR) {
+ if (expr->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
if (op->op == OP_EMPTY) {
is_var_init = true;
@@ -8027,6 +8027,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
while (tk.type != TK_EOF) {
switch (tk.type) {
case TK_RENDER_MODE: {
+#ifdef DEBUG_ENABLED
+ keyword_completion_context = CF_UNSPECIFIED;
+#endif // DEBUG_ENABLED
while (true) {
StringName mode;
_get_completable_identifier(nullptr, COMPLETION_RENDER_MODE, mode);
@@ -8114,6 +8117,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
}
}
+#ifdef DEBUG_ENABLED
+ keyword_completion_context = CF_GLOBAL_SPACE;
+#endif // DEBUG_ENABLED
} break;
case TK_STRUCT: {
ShaderNode::Struct st;
@@ -8916,7 +8922,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
if (!expr) {
return ERR_PARSE_ERROR;
}
- if (expr->type != Node::TYPE_CONSTANT) {
+ if (expr->type != Node::NODE_TYPE_CONSTANT) {
_set_error(RTR("Expected constant expression after '='."));
return ERR_PARSE_ERROR;
}
@@ -9276,7 +9282,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
}
- if (n->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
+ if (n->type == Node::NODE_TYPE_OPERATOR && static_cast<OperatorNode *>(n)->op == OP_CALL) {
_set_error(RTR("Expected constant expression."));
return ERR_PARSE_ERROR;
}
@@ -9343,7 +9349,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
keyword_completion_context = CF_GLOBAL_SPACE;
}
#endif // DEBUG_ENABLED
- if (expr->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(expr)->op == OP_CALL) {
+ if (expr->type == Node::NODE_TYPE_OPERATOR && static_cast<OperatorNode *>(expr)->op == OP_CALL) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
for (int i = 1; i < op->arguments.size(); i++) {
if (!_check_node_constness(op->arguments[i])) {
@@ -9756,7 +9762,7 @@ Error ShaderLanguage::_find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOper
bool found = false;
for (int i = p_flow->blocks.size() - 1; i >= 0; i--) {
- if (p_flow->blocks[i]->type == Node::TYPE_BLOCK) {
+ if (p_flow->blocks[i]->type == Node::NODE_TYPE_BLOCK) {
BlockNode *last_block = static_cast<BlockNode *>(p_flow->blocks[i]);
if (_find_last_flow_op_in_block(last_block, p_op) == OK) {
found = true;
@@ -9774,7 +9780,7 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat
bool found = false;
for (int i = p_block->statements.size() - 1; i >= 0; i--) {
- if (p_block->statements[i]->type == Node::TYPE_CONTROL_FLOW) {
+ if (p_block->statements[i]->type == Node::NODE_TYPE_CONTROL_FLOW) {
ControlFlowNode *flow = static_cast<ControlFlowNode *>(p_block->statements[i]);
if (flow->flow_op == p_op) {
found = true;
@@ -9785,7 +9791,7 @@ Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperat
break;
}
}
- } else if (p_block->statements[i]->type == Node::TYPE_BLOCK) {
+ } else if (p_block->statements[i]->type == Node::NODE_TYPE_BLOCK) {
BlockNode *block = static_cast<BlockNode *>(p_block->statements[i]);
if (_find_last_flow_op_in_block(block, p_op) == OK) {
found = true;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 220160e5fd..0ddd27f028 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -357,18 +357,18 @@ public:
Node *next = nullptr;
enum Type {
- TYPE_SHADER,
- TYPE_FUNCTION,
- TYPE_BLOCK,
- TYPE_VARIABLE,
- TYPE_VARIABLE_DECLARATION,
- TYPE_CONSTANT,
- TYPE_OPERATOR,
- TYPE_CONTROL_FLOW,
- TYPE_MEMBER,
- TYPE_ARRAY,
- TYPE_ARRAY_CONSTRUCT,
- TYPE_STRUCT,
+ NODE_TYPE_SHADER,
+ NODE_TYPE_FUNCTION,
+ NODE_TYPE_BLOCK,
+ NODE_TYPE_VARIABLE,
+ NODE_TYPE_VARIABLE_DECLARATION,
+ NODE_TYPE_CONSTANT,
+ NODE_TYPE_OPERATOR,
+ NODE_TYPE_CONTROL_FLOW,
+ NODE_TYPE_MEMBER,
+ NODE_TYPE_ARRAY,
+ NODE_TYPE_ARRAY_CONSTRUCT,
+ NODE_TYPE_STRUCT,
};
Type type;
@@ -407,7 +407,7 @@ public:
virtual bool is_indexed() const override { return op == OP_INDEX; }
OperatorNode() :
- Node(TYPE_OPERATOR) {}
+ Node(NODE_TYPE_OPERATOR) {}
};
struct VariableNode : public Node {
@@ -421,7 +421,7 @@ public:
virtual String get_datatype_name() const override { return String(struct_name); }
VariableNode() :
- Node(TYPE_VARIABLE) {}
+ Node(NODE_TYPE_VARIABLE) {}
};
struct VariableDeclarationNode : public Node {
@@ -442,7 +442,7 @@ public:
virtual DataType get_datatype() const override { return datatype; }
VariableDeclarationNode() :
- Node(TYPE_VARIABLE_DECLARATION) {}
+ Node(NODE_TYPE_VARIABLE_DECLARATION) {}
};
struct ArrayNode : public Node {
@@ -462,7 +462,7 @@ public:
virtual bool is_indexed() const override { return index_expression != nullptr; }
ArrayNode() :
- Node(TYPE_ARRAY) {}
+ Node(NODE_TYPE_ARRAY) {}
};
struct ArrayConstructNode : public Node {
@@ -475,7 +475,7 @@ public:
virtual int get_array_size() const override { return initializer.size(); }
ArrayConstructNode() :
- Node(TYPE_ARRAY_CONSTRUCT) {}
+ Node(NODE_TYPE_ARRAY_CONSTRUCT) {}
};
struct ConstantNode : public Node {
@@ -498,7 +498,7 @@ public:
virtual int get_array_size() const override { return array_size; }
ConstantNode() :
- Node(TYPE_CONSTANT) {}
+ Node(NODE_TYPE_CONSTANT) {}
};
struct FunctionNode;
@@ -536,7 +536,7 @@ public:
bool use_comma_between_statements = false;
BlockNode() :
- Node(TYPE_BLOCK) {}
+ Node(NODE_TYPE_BLOCK) {}
};
struct ControlFlowNode : public Node {
@@ -545,7 +545,7 @@ public:
Vector<BlockNode *> blocks;
ControlFlowNode() :
- Node(TYPE_CONTROL_FLOW) {}
+ Node(NODE_TYPE_CONTROL_FLOW) {}
};
struct MemberNode : public Node {
@@ -569,13 +569,13 @@ public:
virtual bool is_indexed() const override { return index_expression != nullptr || call_expression != nullptr; }
MemberNode() :
- Node(TYPE_MEMBER) {}
+ Node(NODE_TYPE_MEMBER) {}
};
struct StructNode : public Node {
List<MemberNode *> members;
StructNode() :
- Node(TYPE_STRUCT) {}
+ Node(NODE_TYPE_STRUCT) {}
};
struct FunctionNode : public Node {
@@ -611,7 +611,7 @@ public:
virtual int get_array_size() const override { return return_array_size; }
FunctionNode() :
- Node(TYPE_FUNCTION) {}
+ Node(NODE_TYPE_FUNCTION) {}
};
struct ShaderNode : public Node {
@@ -718,7 +718,7 @@ public:
Vector<Struct> vstructs;
ShaderNode() :
- Node(TYPE_SHADER) {}
+ Node(NODE_TYPE_SHADER) {}
};
struct UniformOrderComparator {
diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp
index ff1d55f905..0644f5918c 100644
--- a/servers/rendering/shader_preprocessor.cpp
+++ b/servers/rendering/shader_preprocessor.cpp
@@ -54,9 +54,9 @@ int ShaderPreprocessor::Tokenizer::get_index() const {
return index;
}
-void ShaderPreprocessor::Tokenizer::get_and_clear_generated(Vector<ShaderPreprocessor::Token> *r_out) {
- for (int i = 0; i < generated.size(); i++) {
- r_out->push_back(generated[i]);
+void ShaderPreprocessor::Tokenizer::get_and_clear_generated(LocalVector<char32_t> *r_out) {
+ for (uint32_t i = 0; i < generated.size(); i++) {
+ r_out->push_back(generated[i].text);
}
generated.clear();
}
@@ -1206,18 +1206,14 @@ Error ShaderPreprocessor::preprocess(State *p_state, const String &p_code, Strin
break;
}
+ // Add autogenerated tokens if there are any.
+ p_tokenizer.get_and_clear_generated(&output);
+
if (state->disabled) {
// Preprocessor was disabled.
// Read the rest of the file into the output.
output.push_back(t.text);
continue;
- } else {
- // Add autogenerated tokens.
- Vector<Token> generated;
- p_tokenizer.get_and_clear_generated(&generated);
- for (int i = 0; i < generated.size(); i++) {
- output.push_back(generated[i].text);
- }
}
if (t.text == '#') {
diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h
index f198af66f0..406b663228 100644
--- a/servers/rendering/shader_preprocessor.h
+++ b/servers/rendering/shader_preprocessor.h
@@ -83,7 +83,7 @@ private:
int line;
int index;
int size;
- Vector<Token> generated;
+ LocalVector<Token> generated;
private:
void add_generated(const Token &p_t);
@@ -95,7 +95,7 @@ private:
char32_t peek();
int consume_line_continuations(int p_offset);
- void get_and_clear_generated(Vector<Token> *r_out);
+ void get_and_clear_generated(LocalVector<char32_t> *r_out);
void backtrack(char32_t p_what);
LocalVector<Token> advance(char32_t p_what);
void skip_whitespace();
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index d95caeddc5..c256d37344 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -187,6 +187,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["UV2"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3);
+ shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_AMOUNT"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT_COLOR"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT_IS_DIRECTIONAL"] = constt(ShaderLanguage::TYPE_BOOL);
@@ -225,6 +226,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("particle_trails") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("alpha_to_coverage") });
shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("alpha_to_coverage_and_one") });
+ shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("debug_shadow_splits") });
}
/************ CANVAS ITEM **************************/
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index 0a59c70526..93b32bd372 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -101,6 +101,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) = 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;
/* Decal API */
virtual RID decal_allocate() = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index cc09825298..4d41343ff8 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -35,6 +35,7 @@
#include "core/variant/typed_array.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering/shader_language.h"
+#include "servers/rendering/shader_warnings.h"
RenderingServer *RenderingServer::singleton = nullptr;
RenderingServer *(*RenderingServer::create_func)() = nullptr;
@@ -1698,6 +1699,7 @@ void RenderingServer::_bind_methods() {
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_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));
BIND_ENUM_CONSTANT(TEXTURE_LAYERED_2D_ARRAY);
BIND_ENUM_CONSTANT(TEXTURE_LAYERED_CUBEMAP);
@@ -2198,6 +2200,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_texture_mipmap_bias", "viewport", "mipmap_bias"), &RenderingServer::viewport_set_texture_mipmap_bias);
ClassDB::bind_method(D_METHOD("viewport_set_update_mode", "viewport", "update_mode"), &RenderingServer::viewport_set_update_mode);
ClassDB::bind_method(D_METHOD("viewport_set_clear_mode", "viewport", "clear_mode"), &RenderingServer::viewport_set_clear_mode);
+ ClassDB::bind_method(D_METHOD("viewport_get_render_target", "viewport"), &RenderingServer::viewport_get_render_target);
ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport"), &RenderingServer::viewport_get_texture);
ClassDB::bind_method(D_METHOD("viewport_set_disable_3d", "viewport", "disable"), &RenderingServer::viewport_set_disable_3d);
ClassDB::bind_method(D_METHOD("viewport_set_disable_2d", "viewport", "disable"), &RenderingServer::viewport_set_disable_2d);
@@ -2602,6 +2605,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_add_line", "item", "from", "to", "color", "width", "antialiased"), &RenderingServer::canvas_item_add_line, DEFVAL(-1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("canvas_item_add_polyline", "item", "points", "colors", "width", "antialiased"), &RenderingServer::canvas_item_add_polyline, DEFVAL(-1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("canvas_item_add_multiline", "item", "points", "colors", "width"), &RenderingServer::canvas_item_add_multiline, DEFVAL(-1.0));
ClassDB::bind_method(D_METHOD("canvas_item_add_rect", "item", "rect", "color"), &RenderingServer::canvas_item_add_rect);
ClassDB::bind_method(D_METHOD("canvas_item_add_circle", "item", "pos", "radius", "color"), &RenderingServer::canvas_item_add_circle);
ClassDB::bind_method(D_METHOD("canvas_item_add_texture_rect", "item", "rect", "texture", "tile", "modulate", "transpose"), &RenderingServer::canvas_item_add_texture_rect, DEFVAL(false), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
@@ -2716,7 +2720,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("global_shader_parameter_add", "name", "type", "default_value"), &RenderingServer::global_shader_parameter_add);
ClassDB::bind_method(D_METHOD("global_shader_parameter_remove", "name"), &RenderingServer::global_shader_parameter_remove);
- ClassDB::bind_method(D_METHOD("global_shader_parameter_get_list"), &RenderingServer::global_shader_parameter_get_list);
+ ClassDB::bind_method(D_METHOD("global_shader_parameter_get_list"), &RenderingServer::_global_shader_parameter_get_list);
ClassDB::bind_method(D_METHOD("global_shader_parameter_set", "name", "value"), &RenderingServer::global_shader_parameter_set);
ClassDB::bind_method(D_METHOD("global_shader_parameter_set_override", "name", "value"), &RenderingServer::global_shader_parameter_set_override);
ClassDB::bind_method(D_METHOD("global_shader_parameter_get", "name"), &RenderingServer::global_shader_parameter_get);
@@ -2855,6 +2859,16 @@ RenderingServer::RenderingServer() {
singleton = this;
}
+TypedArray<StringName> RenderingServer::_global_shader_parameter_get_list() const {
+ TypedArray<StringName> gsp;
+ Vector<StringName> gsp_sn = global_shader_parameter_get_list();
+ gsp.resize(gsp_sn.size());
+ for (int i = 0; i < gsp_sn.size(); i++) {
+ gsp[i] = gsp_sn[i];
+ }
+ return gsp;
+}
+
void RenderingServer::init() {
GLOBAL_DEF_RST_NOVAL_BASIC("rendering/textures/vram_compression/import_s3tc_bptc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC);
GLOBAL_DEF_RST_NOVAL_BASIC("rendering/textures/vram_compression/import_etc2_astc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC);
@@ -2978,6 +2992,15 @@ void RenderingServer::init() {
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/limits/opengl/max_lights_per_object", PROPERTY_HINT_RANGE, "2,1024,1"), 8);
GLOBAL_DEF_RST_BASIC("xr/shaders/enabled", false);
+
+ GLOBAL_DEF("debug/shader_language/warnings/enable", true);
+ GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
+
+#ifdef DEBUG_ENABLED
+ for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
+ GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
+ }
+#endif
}
RenderingServer::~RenderingServer() {
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index a283744443..deac2a59f9 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -57,6 +57,8 @@ class RenderingServer : public Object {
const Vector2 SMALL_VEC2 = Vector2(CMP_EPSILON, CMP_EPSILON);
const Vector3 SMALL_VEC3 = Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON);
+ virtual TypedArray<StringName> _global_shader_parameter_get_list() const;
+
protected:
RID _make_test_cube();
void _free_internal_rids();
@@ -157,6 +159,7 @@ public:
virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 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;
/* SHADER API */
@@ -837,6 +840,7 @@ public:
virtual void viewport_set_clear_mode(RID p_viewport, ViewportClearMode p_clear_mode) = 0;
+ virtual RID viewport_get_render_target(RID p_viewport) const = 0;
virtual RID viewport_get_texture(RID p_viewport) const = 0;
enum ViewportEnvironmentMode {
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
index 3e587390f8..b6cb9620f1 100644
--- a/tests/core/math/test_math_funcs.h
+++ b/tests/core/math/test_math_funcs.h
@@ -157,15 +157,15 @@ TEST_CASE_TEMPLATE("[Math] asin/acos/atan", T, float, double) {
CHECK(Math::asin((T)0.1) == doctest::Approx((T)0.1001674212));
CHECK(Math::asin((T)0.5) == doctest::Approx((T)0.5235987756));
CHECK(Math::asin((T)1.0) == doctest::Approx((T)1.5707963268));
- CHECK(Math::is_nan(Math::asin((T)1.5)));
- CHECK(Math::is_nan(Math::asin((T)450.0)));
+ CHECK(Math::asin((T)2.0) == doctest::Approx((T)1.5707963268));
+ CHECK(Math::asin((T)-2.0) == doctest::Approx((T)-1.5707963268));
CHECK(Math::acos((T)-0.1) == doctest::Approx((T)1.670963748));
CHECK(Math::acos((T)0.1) == doctest::Approx((T)1.4706289056));
CHECK(Math::acos((T)0.5) == doctest::Approx((T)1.0471975512));
CHECK(Math::acos((T)1.0) == doctest::Approx((T)0.0));
- CHECK(Math::is_nan(Math::acos((T)1.5)));
- CHECK(Math::is_nan(Math::acos((T)450.0)));
+ CHECK(Math::acos((T)2.0) == doctest::Approx((T)0.0));
+ CHECK(Math::acos((T)-2.0) == doctest::Approx((T)Math_PI));
CHECK(Math::atan((T)-0.1) == doctest::Approx((T)-0.0996686525));
CHECK(Math::atan((T)0.1) == doctest::Approx((T)0.0996686525));
@@ -472,7 +472,7 @@ TEST_CASE_TEMPLATE("[Math] wrapf", T, float, double) {
CHECK(Math::wrapf(300'000'000'000.0, -20.0, 160.0) == doctest::Approx((T)120.0));
// float's precision is too low for 300'000'000'000.0, so we reduce it by a factor of 1000.
- CHECK(Math::wrapf((float)300'000'000.0, (float)-20.0, (float)160.0) == doctest::Approx((T)128.0));
+ CHECK(Math::wrapf((float)15'000'000.0, (float)-20.0, (float)160.0) == doctest::Approx((T)60.0));
}
TEST_CASE_TEMPLATE("[Math] fract", T, float, double) {
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index e68995e539..3f091fd2fe 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -487,13 +487,13 @@ void add_exposed_classes(Context &r_context) {
}
if (!ClassDB::is_class_exposed(class_name)) {
- MESSAGE(vformat("Ignoring class '%s' because it's not exposed.", class_name));
+ INFO(vformat("Ignoring class '%s' because it's not exposed.", class_name));
class_list.pop_front();
continue;
}
if (!ClassDB::is_class_enabled(class_name)) {
- MESSAGE(vformat("Ignoring class '%s' because it's not enabled.", class_name));
+ INFO(vformat("Ignoring class '%s' because it's not enabled.", class_name));
class_list.pop_front();
continue;
}
@@ -717,15 +717,10 @@ void add_exposed_classes(Context &r_context) {
bool method_conflict = exposed_class.find_property_by_name(signal.name);
- // TODO:
- // ClassDB allows signal names that conflict with method or property names.
- // However registering a signal with a conflicting name is still considered wrong.
- // Unfortunately there are some existing cases that are yet to be fixed.
- // Until those are fixed we will print a warning instead of failing the test.
String warn_msg = vformat(
"Signal name conflicts with %s: '%s.%s.",
method_conflict ? "method" : "property", class_name, signal.name);
- TEST_FAIL_COND_WARN((method_conflict || exposed_class.find_method_by_name(signal.name)),
+ TEST_FAIL_COND((method_conflict || exposed_class.find_method_by_name(signal.name)),
warn_msg.utf8().get_data());
exposed_class.signals_.push_back(signal);
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index 7e8f23a14f..98f9b3da65 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -339,6 +339,48 @@ TEST_CASE("[Object] Signals") {
CHECK_EQ(signals_after.size(), signals_after_empty_added.size());
}
+ SUBCASE("Deleting an object with connected signals should disconnect them") {
+ List<Object::Connection> signal_connections;
+
+ {
+ Object target;
+ target.add_user_signal(MethodInfo("my_custom_signal"));
+
+ ERR_PRINT_OFF;
+ target.connect("nonexistent_signal1", callable_mp(&object, &Object::notify_property_list_changed));
+ target.connect("my_custom_signal", callable_mp(&object, &Object::notify_property_list_changed));
+ target.connect("nonexistent_signal2", callable_mp(&object, &Object::notify_property_list_changed));
+ ERR_PRINT_ON;
+
+ signal_connections.clear();
+ object.get_all_signal_connections(&signal_connections);
+ CHECK(signal_connections.size() == 0);
+ signal_connections.clear();
+ object.get_signals_connected_to_this(&signal_connections);
+ CHECK(signal_connections.size() == 1);
+
+ ERR_PRINT_OFF;
+ object.connect("nonexistent_signal1", callable_mp(&target, &Object::notify_property_list_changed));
+ object.connect("my_custom_signal", callable_mp(&target, &Object::notify_property_list_changed));
+ object.connect("nonexistent_signal2", 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() == 1);
+ signal_connections.clear();
+ object.get_signals_connected_to_this(&signal_connections);
+ CHECK(signal_connections.size() == 1);
+ }
+
+ signal_connections.clear();
+ object.get_all_signal_connections(&signal_connections);
+ CHECK(signal_connections.size() == 0);
+ signal_connections.clear();
+ object.get_signals_connected_to_this(&signal_connections);
+ CHECK(signal_connections.size() == 0);
+ }
+
SUBCASE("Emitting a non existing signal will return an error") {
Error err = object.emit_signal("some_signal");
CHECK(err == ERR_UNAVAILABLE);
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index abe9f78ccc..7c76e7aa7b 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -395,6 +395,12 @@ TEST_CASE("[String] Insertion") {
CHECK(s == "Who is Frederic Chopin?");
}
+TEST_CASE("[String] Erasing") {
+ String s = "Josephine is such a cute girl!";
+ s = s.erase(s.find("cute "), String("cute ").length());
+ CHECK(s == "Josephine is such a girl!");
+}
+
TEST_CASE("[String] Number to string") {
CHECK(String::num(0) == "0");
CHECK(String::num(0.0) == "0"); // No trailing zeros.
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index c5407d4bcd..a201f0f3b0 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -2314,6 +2314,143 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
}
}
+ SUBCASE("[CodeEdit] convert indent to tabs") {
+ code_edit->set_indent_size(4);
+ code_edit->set_indent_using_spaces(false);
+
+ // Only line.
+ code_edit->insert_text_at_caret(" test");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(8);
+ code_edit->select(0, 8, 0, 9);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(0) == "\t\ttest");
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_from_column() == 2);
+ CHECK(code_edit->get_selection_to_column() == 3);
+
+ // First line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret(" test\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(8);
+ code_edit->select(0, 8, 0, 9);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(0) == "\t\ttest");
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_from_column() == 2);
+ CHECK(code_edit->get_selection_to_column() == 3);
+
+ // Middle line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\n test\n");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(8);
+ code_edit->select(1, 8, 1, 9);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(1) == "\t\ttest");
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_from_column() == 2);
+ CHECK(code_edit->get_selection_to_column() == 3);
+
+ // End line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\n test");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(8);
+ code_edit->select(1, 8, 1, 9);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(1) == "\t\ttest");
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_from_column() == 2);
+ CHECK(code_edit->get_selection_to_column() == 3);
+
+ // Within provided range.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret(" test\n test\n");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(8);
+ code_edit->select(1, 8, 1, 9);
+ code_edit->convert_indent(1, 1);
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_line(1) == "\t\ttest");
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_from_column() == 2);
+ CHECK(code_edit->get_selection_to_column() == 3);
+ }
+
+ SUBCASE("[CodeEdit] convert indent to spaces") {
+ code_edit->set_indent_size(4);
+ code_edit->set_indent_using_spaces(true);
+
+ // Only line.
+ code_edit->insert_text_at_caret("\t\ttest");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(2);
+ code_edit->select(0, 2, 0, 3);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
+ CHECK(code_edit->get_selection_from_column() == 8);
+ CHECK(code_edit->get_selection_to_column() == 9);
+
+ // First line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\t\ttest\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(2);
+ code_edit->select(0, 2, 0, 3);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
+ CHECK(code_edit->get_selection_from_column() == 8);
+ CHECK(code_edit->get_selection_to_column() == 9);
+
+ // Middle line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\n\t\ttest\n");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->select(1, 2, 1, 3);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(1) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
+ CHECK(code_edit->get_selection_from_column() == 8);
+ CHECK(code_edit->get_selection_to_column() == 9);
+
+ // End line.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\n\t\ttest");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->select(1, 2, 1, 3);
+ code_edit->convert_indent();
+ CHECK(code_edit->get_line(1) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
+ CHECK(code_edit->get_selection_from_column() == 8);
+ CHECK(code_edit->get_selection_to_column() == 9);
+
+ // Within provided range.
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("\ttest\n\t\ttest\n");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->select(1, 2, 1, 3);
+ code_edit->convert_indent(1, 1);
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_line(1) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
+ CHECK(code_edit->get_selection_from_column() == 8);
+ CHECK(code_edit->get_selection_to_column() == 9);
+
+ // Outside of range.
+ ERR_PRINT_OFF;
+ code_edit->convert_indent(0, 4);
+ code_edit->convert_indent(4, 5);
+ code_edit->convert_indent(4, 1);
+ ERR_PRINT_ON;
+ }
+
memdelete(code_edit);
}
@@ -3345,9 +3482,9 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") {
SIGNAL_CHECK("symbol_validate", signal_args);
SIGNAL_UNWATCH(code_edit, "symbol_validate");
-
- memdelete(code_edit);
}
+
+ memdelete(code_edit);
}
TEST_CASE("[SceneTree][CodeEdit] line length guidelines") {
diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h
new file mode 100644
index 0000000000..0f0d413354
--- /dev/null
+++ b/tests/scene/test_curve_3d.h
@@ -0,0 +1,261 @@
+/**************************************************************************/
+/* test_curve_3d.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_CURVE_3D_H
+#define TEST_CURVE_3D_H
+
+#include "core/math/math_funcs.h"
+#include "scene/resources/curve.h"
+
+#include "tests/test_macros.h"
+
+namespace TestCurve3D {
+
+void add_sample_curve_points(Ref<Curve3D> &curve) {
+ Vector3 p0 = Vector3(0, 0, 0);
+ Vector3 p1 = Vector3(50, 0, 0);
+ Vector3 p2 = Vector3(50, 50, 50);
+ Vector3 p3 = Vector3(0, 50, 0);
+
+ Vector3 control0 = p1 - p0;
+ Vector3 control1 = p3 - p2;
+
+ curve->add_point(p0, Vector3(), control0);
+ curve->add_point(p3, control1, Vector3());
+}
+
+TEST_CASE("[Curve3D] Default curve is empty") {
+ const Ref<Curve3D> curve = memnew(Curve3D);
+ CHECK(curve->get_point_count() == 0);
+}
+
+TEST_CASE("[Curve3D] Point management") {
+ Ref<Curve3D> curve = memnew(Curve3D);
+
+ SUBCASE("Functions for adding/removing points should behave as expected") {
+ curve->set_point_count(2);
+ CHECK(curve->get_point_count() == 2);
+
+ curve->remove_point(0);
+ CHECK(curve->get_point_count() == 1);
+
+ curve->add_point(Vector3());
+ CHECK(curve->get_point_count() == 2);
+
+ curve->clear_points();
+ CHECK(curve->get_point_count() == 0);
+ }
+
+ SUBCASE("Functions for changing single point properties should behave as expected") {
+ Vector3 new_in = Vector3(1, 1, 1);
+ Vector3 new_out = Vector3(1, 1, 1);
+ Vector3 new_pos = Vector3(1, 1, 1);
+ real_t new_tilt = 1;
+
+ curve->add_point(Vector3());
+
+ CHECK(curve->get_point_in(0) != new_in);
+ curve->set_point_in(0, new_in);
+ CHECK(curve->get_point_in(0) == new_in);
+
+ CHECK(curve->get_point_out(0) != new_out);
+ curve->set_point_out(0, new_out);
+ CHECK(curve->get_point_out(0) == new_out);
+
+ CHECK(curve->get_point_position(0) != new_pos);
+ curve->set_point_position(0, new_pos);
+ CHECK(curve->get_point_position(0) == new_pos);
+
+ CHECK(curve->get_point_tilt(0) != new_tilt);
+ curve->set_point_tilt(0, new_tilt);
+ CHECK(curve->get_point_tilt(0) == new_tilt);
+ }
+}
+
+TEST_CASE("[Curve3D] Baked") {
+ Ref<Curve3D> curve = memnew(Curve3D);
+
+ SUBCASE("Single Point") {
+ curve->add_point(Vector3());
+
+ CHECK(curve->get_baked_length() == 0);
+ CHECK(curve->get_baked_points().size() == 1);
+ CHECK(curve->get_baked_tilts().size() == 1);
+ CHECK(curve->get_baked_up_vectors().size() == 1);
+ }
+
+ SUBCASE("Straight line") {
+ curve->add_point(Vector3());
+ curve->add_point(Vector3(0, 50, 0));
+
+ CHECK(Math::is_equal_approx(curve->get_baked_length(), 50));
+ CHECK(curve->get_baked_points().size() == 369);
+ CHECK(curve->get_baked_tilts().size() == 369);
+ CHECK(curve->get_baked_up_vectors().size() == 369);
+ }
+
+ SUBCASE("Beziér Curve") {
+ add_sample_curve_points(curve);
+
+ real_t len = curve->get_baked_length();
+ real_t n_points = curve->get_baked_points().size();
+ // Curve length should be bigger than a straight line between points
+ CHECK(len > 50);
+
+ SUBCASE("Increase bake interval") {
+ curve->set_bake_interval(10.0);
+ CHECK(curve->get_bake_interval() == 10.0);
+ // Lower resolution should imply less points and smaller length
+ CHECK(curve->get_baked_length() < len);
+ CHECK(curve->get_baked_points().size() < n_points);
+ CHECK(curve->get_baked_tilts().size() < n_points);
+ CHECK(curve->get_baked_up_vectors().size() < n_points);
+ }
+
+ SUBCASE("Disable up vectors") {
+ curve->set_up_vector_enabled(false);
+ CHECK(curve->is_up_vector_enabled() == false);
+ CHECK(curve->get_baked_up_vectors().size() == 0);
+ }
+ }
+}
+
+TEST_CASE("[Curve3D] Sampling") {
+ // Sampling over a simple straight line to make assertions simpler
+ Ref<Curve3D> curve = memnew(Curve3D);
+ curve->add_point(Vector3());
+ curve->add_point(Vector3(0, 50, 0));
+
+ SUBCASE("sample") {
+ CHECK(curve->sample(0, 0) == Vector3(0, 0, 0));
+ CHECK(curve->sample(0, 0.5) == Vector3(0, 25, 0));
+ CHECK(curve->sample(0, 1) == Vector3(0, 50, 0));
+ }
+
+ SUBCASE("samplef") {
+ CHECK(curve->samplef(0) == Vector3(0, 0, 0));
+ CHECK(curve->samplef(0.5) == Vector3(0, 25, 0));
+ CHECK(curve->samplef(1) == Vector3(0, 50, 0));
+ }
+
+ SUBCASE("sample_baked, cubic = false") {
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 0, 0))) == Vector3(0, 0, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 25, 0))) == Vector3(0, 25, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 50, 0))) == Vector3(0, 50, 0));
+ }
+
+ SUBCASE("sample_baked, cubic = true") {
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 0, 0)), true) == Vector3(0, 0, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 25, 0)), true) == Vector3(0, 25, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Vector3(0, 50, 0));
+ }
+
+ SUBCASE("sample_baked_with_rotation") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(0, 1, 0)), Vector3(0, 50, 0)));
+ }
+
+ SUBCASE("sample_baked_tilt") {
+ CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 0, 0))) == 0);
+ CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 25, 0))) == 0);
+ CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 50, 0))) == 0);
+ }
+
+ SUBCASE("sample_baked_up_vector, p_apply_tilt = false") {
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 0, 0))) == Vector3(1, 0, 0));
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 25, 0))) == Vector3(1, 0, 0));
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 50, 0))) == Vector3(1, 0, 0));
+ }
+
+ SUBCASE("sample_baked_up_vector, p_apply_tilt = true") {
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 0, 0)), true) == Vector3(1, 0, 0));
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 25, 0)), true) == Vector3(1, 0, 0));
+ CHECK(curve->sample_baked_up_vector(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Vector3(1, 0, 0));
+ }
+
+ SUBCASE("get_closest_point") {
+ CHECK(curve->get_closest_point(Vector3(0, 0, 0)) == Vector3(0, 0, 0));
+ CHECK(curve->get_closest_point(Vector3(0, 25, 0)) == Vector3(0, 25, 0));
+ CHECK(curve->get_closest_point(Vector3(50, 25, 0)) == Vector3(0, 25, 0));
+ CHECK(curve->get_closest_point(Vector3(0, 50, 0)) == Vector3(0, 50, 0));
+ CHECK(curve->get_closest_point(Vector3(50, 50, 0)) == Vector3(0, 50, 0));
+ CHECK(curve->get_closest_point(Vector3(0, 100, 0)) == Vector3(0, 50, 0));
+ }
+}
+
+TEST_CASE("[Curve3D] Tessellation") {
+ Ref<Curve3D> curve = memnew(Curve3D);
+ add_sample_curve_points(curve);
+
+ const int default_size = curve->tessellate().size();
+
+ SUBCASE("Increase to max stages should increase num of points") {
+ CHECK(curve->tessellate(6).size() > default_size);
+ }
+
+ SUBCASE("Decrease to max stages should decrease num of points") {
+ CHECK(curve->tessellate(4).size() < default_size);
+ }
+
+ SUBCASE("Increase to tolerance should decrease num of points") {
+ CHECK(curve->tessellate(5, 5).size() < default_size);
+ }
+
+ SUBCASE("Decrease to tolerance should increase num of points") {
+ CHECK(curve->tessellate(5, 3).size() > default_size);
+ }
+
+ SUBCASE("Adding a straight segment should only add the last point to tessellate return array") {
+ curve->add_point(Vector3(0, 100, 0));
+ PackedVector3Array tes = curve->tessellate();
+ CHECK(tes.size() == default_size + 1);
+ CHECK(tes[tes.size() - 1] == Vector3(0, 100, 0));
+ CHECK(tes[tes.size() - 2] == Vector3(0, 50, 0));
+ }
+}
+
+TEST_CASE("[Curve3D] Even length tessellation") {
+ Ref<Curve3D> curve = memnew(Curve3D);
+ add_sample_curve_points(curve);
+
+ const int default_size = curve->tessellate_even_length().size();
+
+ // Default tessellate_even_length tolerance_length is 20.0, by adding a 100 units
+ // straight, we expect the total size to be increased by more than 5,
+ // that is, the algo will pick a length < 20.0 and will divide the straight as
+ // well as the curve as opposed to tessellate() which only adds the final point.
+ curve->add_point(Vector3(0, 150, 0));
+ CHECK(curve->tessellate_even_length().size() > default_size + 5);
+}
+
+} // namespace TestCurve3D
+
+#endif // TEST_CURVE_3D_H
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index 62f4635927..d76fc40125 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -221,7 +221,9 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
SUBCASE("[Viewport][GuiInputEvent] nullptr as argument doesn't lead to a crash.") {
+ ERR_PRINT_OFF;
CHECK_NOTHROW(root->push_input(nullptr));
+ ERR_PRINT_ON;
}
// Unit tests for Viewport::_gui_input_event (Mouse Buttons)
diff --git a/tests/test_macros.h b/tests/test_macros.h
index 5d1bcdecf4..d39da7f8e8 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -147,7 +147,7 @@ int register_test_command(String p_command, TestFunc p_function);
{ \
const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \
const List<Ref<InputEvent>>::Element *first_event = events->front(); \
- Ref<InputEventKey> event = first_event->get(); \
+ Ref<InputEventKey> event = first_event->get()->duplicate(); \
event->set_pressed(true); \
_SEND_DISPLAYSERVER_EVENT(event); \
MessageQueue::get_singleton()->flush(); \
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 291321a45e..6b6257e25d 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -93,6 +93,7 @@
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_curve.h"
#include "tests/scene/test_curve_2d.h"
+#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_agent_3d.h"
@@ -134,6 +135,8 @@ int test_main(int argc, char *argv[]) {
OS::get_singleton()->set_cmdline("", args, List<String>());
DisplayServerMock::register_mock_driver();
+ WorkerThreadPool::get_singleton()->init();
+
// Run custom test tools.
if (test_commands) {
for (const KeyValue<String, TestFunc> &E : (*test_commands)) {
@@ -255,8 +258,8 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
- navigation_server_2d = memnew(NavigationServer2D);
navigation_server_3d = NavigationServer3DManager::new_default_server();
+ navigation_server_2d = memnew(NavigationServer2D);
return;
}
}
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 41d18bdad0..9862729ea3 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -20,7 +20,7 @@ Files extracted from upstream source:
## astcenc
- Upstream: https://github.com/ARM-software/astc-encoder
-- Version: 4.3.0 (ec83dda79fcefe07f69cdae7ed980d169bf2c4d4, 2023)
+- Version: 4.4.0 (5a5b5a1ef60dd47c27c28c66c118d22c40e3197e, 2023)
- License: Apache 2.0
Files extracted from upstream source:
@@ -32,7 +32,7 @@ Files extracted from upstream source:
## basis_universal
- Upstream: https://github.com/BinomialLLC/basis_universal
-- Version: git (a91e94c8495d7f470d3df326a364d49324cfd4a3, 2022)
+- Version: 1.16.4 (900e40fb5d2502927360fe2f31762bdbb624455f, 2023)
- License: Apache 2.0
Files extracted from upstream source:
@@ -40,6 +40,9 @@ Files extracted from upstream source:
- `encoder/` and `transcoder/` folders
- `LICENSE`
+Applied upstream PR https://github.com/BinomialLLC/basis_universal/pull/344 to
+fix build with our own copy of zstd (patch in `patches`).
+
## brotli
@@ -56,7 +59,7 @@ Files extracted from upstream source:
## certs
- Upstream: Mozilla, via https://github.com/bagder/ca-bundle
-- Version: git (b2f7415648411b6fd7c298c6c92d6552f0165f60, 2022)
+- Version: git (8bcd1092d29849d9fe0a3261ab3bb875eb410694, 2023)
- License: MPL 2.0
@@ -79,7 +82,7 @@ in the `patches/` folder when syncing on newer upstream commits.
## doctest
- Upstream: https://github.com/onqtam/doctest
-- Version: 2.4.9 (b7c21ec5ceeadb4951b00396fc1e4642dd347e5f, 2022)
+- Version: 2.4.11 (ae7a13539fb71f270b87eb2e874fbac80bc8dda2, 2023)
- License: MIT
Files extracted from upstream source:
@@ -378,6 +381,7 @@ File extracted from upstream release tarball:
Applied the patch in `patches/windows-arm64-hardclock.diff`
- Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h`
providing configuration for light bundling with core.
+- Added the file `godot_module_mbedtls_config.h` to customize the build configuration when bundling the full library.
## meshoptimizer
@@ -622,18 +626,27 @@ Files extracted from upstream source:
## rvo2
+For 2D in `rvo2_2d` folder
+
+- Upstream: https://github.com/snape/RVO2
+- Version: git (f7c5380235f6c9ac8d19cbf71fc94e2d4758b0a3, 2021)
+- License: Apache 2.0
+
+For 3D in `rvo2_3d` folder
+
- Upstream: https://github.com/snape/RVO2-3D
- Version: git (bfc048670a4e85066e86a1f923d8ea92e3add3b2, 2021)
- License: Apache 2.0
Files extracted from upstream source:
-- All .cpp and .h files in the `src/` folder except for Export.h, RVO.h, RVOSimulator.cpp and RVOSimulator.h
+- All .cpp and .h files in the `src/` folder except for Export.h and RVO.h
- LICENSE
-Important: Some files have Godot-made changes; so to enrich the features
-originally proposed by this library and better integrate this library with
-Godot. See the patch in the `patches` folder for details.
+Important: Nearly all files have Godot-made changes and renames
+to make the 2D and 3D rvo libraries compatible with each other
+and solve conflicts and also enrich the feature set originally
+proposed by these libraries and better integrate them with Godot.
## spirv-reflect
@@ -687,8 +700,8 @@ instead of `miniz.h` as an external dependency.
## thorvg
-- Upstream: https://github.com/Samsung/thorvg
-- Version: 0.8.4 (b0b7f207c6235691d694fc3f76e0b96e4858e606, 2023)
+- Upstream: https://github.com/thorvg/thorvg
+- Version: 0.9.0 (a744006aa1edb918bacf0a415d0a57ca058e25f4, 2023)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/astcenc/astcenc.h b/thirdparty/astcenc/astcenc.h
index 70ae783373..dbf4599841 100644
--- a/thirdparty/astcenc/astcenc.h
+++ b/thirdparty/astcenc/astcenc.h
@@ -43,6 +43,14 @@
* for faster processing. The caller is responsible for creating the worker threads, and
* synchronizing between images.
*
+ * Extended instruction set support
+ * ================================
+ *
+ * This library supports use of extended instruction sets, such as SSE4.1 and AVX2. These are
+ * enabled at compile time when building the library. There is no runtime checking in the core
+ * library that the instruction sets used are actually available. Checking compatibility is the
+ * responsibility of the calling code.
+ *
* Threading
* =========
*
@@ -191,8 +199,6 @@ enum astcenc_error {
ASTCENC_ERR_OUT_OF_MEM,
/** @brief The call failed due to the build using fast math. */
ASTCENC_ERR_BAD_CPU_FLOAT,
- /** @brief The call failed due to the build using an unsupported ISA. */
- ASTCENC_ERR_BAD_CPU_ISA,
/** @brief The call failed due to an out-of-spec parameter. */
ASTCENC_ERR_BAD_PARAM,
/** @brief The call failed due to an out-of-spec block size. */
@@ -472,7 +478,7 @@ struct astcenc_config
/**
* @brief The number of trial candidates per mode search (-candidatelimit).
*
- * Valid values are between 1 and TUNE_MAX_TRIAL_CANDIDATES (default 4).
+ * Valid values are between 1 and TUNE_MAX_TRIAL_CANDIDATES.
*/
unsigned int tune_candidate_limit;
@@ -520,21 +526,21 @@ struct astcenc_config
*
* This option is further scaled for normal maps, so it skips less often.
*/
- float tune_2_partition_early_out_limit_factor;
+ float tune_2partition_early_out_limit_factor;
/**
* @brief The threshold for skipping 4.1 trials (-3partitionlimitfactor).
*
* This option is further scaled for normal maps, so it skips less often.
*/
- float tune_3_partition_early_out_limit_factor;
+ float tune_3partition_early_out_limit_factor;
/**
* @brief The threshold for skipping two weight planes (-2planelimitcorrelation).
*
* This option is ineffective for normal maps.
*/
- float tune_2_plane_early_out_limit_correlation;
+ float tune_2plane_early_out_limit_correlation;
#if defined(ASTCENC_DIAGNOSTICS)
/**
diff --git a/thirdparty/astcenc/astcenc_averages_and_directions.cpp b/thirdparty/astcenc/astcenc_averages_and_directions.cpp
index d1f003844a..dcff0d224b 100644
--- a/thirdparty/astcenc/astcenc_averages_and_directions.cpp
+++ b/thirdparty/astcenc/astcenc_averages_and_directions.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2011-2022 Arm Limited
+// Copyright 2011-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -725,8 +725,7 @@ void compute_error_squared_rgba(
const image_block& blk,
const processed_line4 uncor_plines[BLOCK_MAX_PARTITIONS],
const processed_line4 samec_plines[BLOCK_MAX_PARTITIONS],
- float uncor_lengths[BLOCK_MAX_PARTITIONS],
- float samec_lengths[BLOCK_MAX_PARTITIONS],
+ float line_lengths[BLOCK_MAX_PARTITIONS],
float& uncor_error,
float& samec_error
) {
@@ -740,12 +739,6 @@ void compute_error_squared_rgba(
{
const uint8_t *texel_indexes = pi.texels_of_partition[partition];
- float uncor_loparam = 1e10f;
- float uncor_hiparam = -1e10f;
-
- float samec_loparam = 1e10f;
- float samec_hiparam = -1e10f;
-
processed_line4 l_uncor = uncor_plines[partition];
processed_line4 l_samec = samec_plines[partition];
@@ -773,9 +766,6 @@ void compute_error_squared_rgba(
vfloat uncor_loparamv(1e10f);
vfloat uncor_hiparamv(-1e10f);
- vfloat samec_loparamv(1e10f);
- vfloat samec_hiparamv(-1e10f);
-
vfloat ew_r(blk.channel_weight.lane<0>());
vfloat ew_g(blk.channel_weight.lane<1>());
vfloat ew_b(blk.channel_weight.lane<2>());
@@ -825,9 +815,6 @@ void compute_error_squared_rgba(
+ (data_b * l_samec_bs2)
+ (data_a * l_samec_bs3);
- samec_loparamv = min(samec_param, samec_loparamv);
- samec_hiparamv = max(samec_param, samec_hiparamv);
-
vfloat samec_dist0 = samec_param * l_samec_bs0 - data_r;
vfloat samec_dist1 = samec_param * l_samec_bs1 - data_g;
vfloat samec_dist2 = samec_param * l_samec_bs2 - data_b;
@@ -843,18 +830,9 @@ void compute_error_squared_rgba(
lane_ids += vint(ASTCENC_SIMD_WIDTH);
}
- uncor_loparam = hmin_s(uncor_loparamv);
- uncor_hiparam = hmax_s(uncor_hiparamv);
-
- samec_loparam = hmin_s(samec_loparamv);
- samec_hiparam = hmax_s(samec_hiparamv);
-
- float uncor_linelen = uncor_hiparam - uncor_loparam;
- float samec_linelen = samec_hiparam - samec_loparam;
-
// Turn very small numbers and NaNs into a small number
- uncor_lengths[partition] = astc::max(uncor_linelen, 1e-7f);
- samec_lengths[partition] = astc::max(samec_linelen, 1e-7f);
+ float uncor_linelen = hmax_s(uncor_hiparamv) - hmin_s(uncor_loparamv);
+ line_lengths[partition] = astc::max(uncor_linelen, 1e-7f);
}
uncor_error = hadd_s(uncor_errorsumv);
@@ -882,19 +860,9 @@ void compute_error_squared_rgb(
unsigned int texel_count = pi.partition_texel_count[partition];
promise(texel_count > 0);
- float uncor_loparam = 1e10f;
- float uncor_hiparam = -1e10f;
-
- float samec_loparam = 1e10f;
- float samec_hiparam = -1e10f;
-
processed_line3 l_uncor = pl.uncor_pline;
processed_line3 l_samec = pl.samec_pline;
- // This implementation is an example vectorization of this function.
- // It works for - the codec is a 2-4% faster than not vectorizing - but
- // the benefit is limited by the use of gathers and register pressure
-
// Vectorize some useful scalar inputs
vfloat l_uncor_bs0(l_uncor.bs.lane<0>());
vfloat l_uncor_bs1(l_uncor.bs.lane<1>());
@@ -913,9 +881,6 @@ void compute_error_squared_rgb(
vfloat uncor_loparamv(1e10f);
vfloat uncor_hiparamv(-1e10f);
- vfloat samec_loparamv(1e10f);
- vfloat samec_hiparamv(-1e10f);
-
vfloat ew_r(blk.channel_weight.lane<0>());
vfloat ew_g(blk.channel_weight.lane<1>());
vfloat ew_b(blk.channel_weight.lane<2>());
@@ -958,9 +923,6 @@ void compute_error_squared_rgb(
+ (data_g * l_samec_bs1)
+ (data_b * l_samec_bs2);
- samec_loparamv = min(samec_param, samec_loparamv);
- samec_hiparamv = max(samec_param, samec_hiparamv);
-
vfloat samec_dist0 = samec_param * l_samec_bs0 - data_r;
vfloat samec_dist1 = samec_param * l_samec_bs1 - data_g;
vfloat samec_dist2 = samec_param * l_samec_bs2 - data_b;
@@ -974,18 +936,9 @@ void compute_error_squared_rgb(
lane_ids += vint(ASTCENC_SIMD_WIDTH);
}
- uncor_loparam = hmin_s(uncor_loparamv);
- uncor_hiparam = hmax_s(uncor_hiparamv);
-
- samec_loparam = hmin_s(samec_loparamv);
- samec_hiparam = hmax_s(samec_hiparamv);
-
- float uncor_linelen = uncor_hiparam - uncor_loparam;
- float samec_linelen = samec_hiparam - samec_loparam;
-
// Turn very small numbers and NaNs into a small number
- pl.uncor_line_len = astc::max(uncor_linelen, 1e-7f);
- pl.samec_line_len = astc::max(samec_linelen, 1e-7f);
+ float uncor_linelen = hmax_s(uncor_hiparamv) - hmin_s(uncor_loparamv);
+ pl.line_length = astc::max(uncor_linelen, 1e-7f);
}
uncor_error = hadd_s(uncor_errorsumv);
diff --git a/thirdparty/astcenc/astcenc_block_sizes.cpp b/thirdparty/astcenc/astcenc_block_sizes.cpp
index 1c22d06a5c..17e51dfc3f 100644
--- a/thirdparty/astcenc/astcenc_block_sizes.cpp
+++ b/thirdparty/astcenc/astcenc_block_sizes.cpp
@@ -776,8 +776,8 @@ static void construct_dt_entry_2d(
assert(maxprec_1plane >= 0 || maxprec_2planes >= 0);
bsd.decimation_modes[index].maxprec_1plane = static_cast<int8_t>(maxprec_1plane);
bsd.decimation_modes[index].maxprec_2planes = static_cast<int8_t>(maxprec_2planes);
- bsd.decimation_modes[index].refprec_1_plane = 0;
- bsd.decimation_modes[index].refprec_2_planes = 0;
+ bsd.decimation_modes[index].refprec_1plane = 0;
+ bsd.decimation_modes[index].refprec_2planes = 0;
}
/**
@@ -934,11 +934,11 @@ static void construct_block_size_descriptor_2d(
if (is_dual_plane)
{
- dm.set_ref_2_plane(bm.get_weight_quant_mode());
+ dm.set_ref_2plane(bm.get_weight_quant_mode());
}
else
{
- dm.set_ref_1_plane(bm.get_weight_quant_mode());
+ dm.set_ref_1plane(bm.get_weight_quant_mode());
}
bsd.block_mode_packed_index[i] = static_cast<uint16_t>(packed_bm_idx);
@@ -969,8 +969,8 @@ static void construct_block_size_descriptor_2d(
{
bsd.decimation_modes[i].maxprec_1plane = -1;
bsd.decimation_modes[i].maxprec_2planes = -1;
- bsd.decimation_modes[i].refprec_1_plane = 0;
- bsd.decimation_modes[i].refprec_2_planes = 0;
+ bsd.decimation_modes[i].refprec_1plane = 0;
+ bsd.decimation_modes[i].refprec_2planes = 0;
}
// Determine the texels to use for kmeans clustering.
@@ -1055,8 +1055,8 @@ static void construct_block_size_descriptor_3d(
bsd.decimation_modes[decimation_mode_count].maxprec_1plane = static_cast<int8_t>(maxprec_1plane);
bsd.decimation_modes[decimation_mode_count].maxprec_2planes = static_cast<int8_t>(maxprec_2planes);
- bsd.decimation_modes[decimation_mode_count].refprec_1_plane = maxprec_1plane == -1 ? 0 : 0xFFFF;
- bsd.decimation_modes[decimation_mode_count].refprec_2_planes = maxprec_2planes == -1 ? 0 : 0xFFFF;
+ bsd.decimation_modes[decimation_mode_count].refprec_1plane = maxprec_1plane == -1 ? 0 : 0xFFFF;
+ bsd.decimation_modes[decimation_mode_count].refprec_2planes = maxprec_2planes == -1 ? 0 : 0xFFFF;
decimation_mode_count++;
}
}
@@ -1067,8 +1067,8 @@ static void construct_block_size_descriptor_3d(
{
bsd.decimation_modes[i].maxprec_1plane = -1;
bsd.decimation_modes[i].maxprec_2planes = -1;
- bsd.decimation_modes[i].refprec_1_plane = 0;
- bsd.decimation_modes[i].refprec_2_planes = 0;
+ bsd.decimation_modes[i].refprec_1plane = 0;
+ bsd.decimation_modes[i].refprec_2planes = 0;
}
bsd.decimation_mode_count_always = 0; // Skipped for 3D modes
diff --git a/thirdparty/astcenc/astcenc_color_quantize.cpp b/thirdparty/astcenc/astcenc_color_quantize.cpp
index edcfe4f853..b0fec7a74c 100644
--- a/thirdparty/astcenc/astcenc_color_quantize.cpp
+++ b/thirdparty/astcenc/astcenc_color_quantize.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2011-2021 Arm Limited
+// Copyright 2011-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -44,17 +44,43 @@
* @brief Determine the quantized value given a quantization level.
*
* @param quant_level The quantization level to use.
- * @param value The value to convert. This may be outside of the 0-255 range and will be
- * clamped before the value is looked up.
+ * @param value The value to convert. This must be in the 0-255 range.
*
- * @return The encoded quantized value. These are not necessarily in order; the compressor
- * scrambles the values slightly to make hardware implementation easier.
+ * @return The unpacked quantized value, returned in 0-255 range.
*/
static inline uint8_t quant_color(
quant_method quant_level,
int value
) {
- return color_unquant_to_uquant_tables[quant_level - QUANT_6][value];
+ int index = value * 2 + 1;
+ return color_unquant_to_uquant_tables[quant_level - QUANT_6][index];
+}
+
+/**
+ * @brief Determine the quantized value given a quantization level and residual.
+ *
+ * @param quant_level The quantization level to use.
+ * @param value The value to convert. This must be in the 0-255 range.
+ * @param valuef The original value before rounding, used to compute a residual.
+ *
+ * @return The unpacked quantized value, returned in 0-255 range.
+ */
+static inline uint8_t quant_color(
+ quant_method quant_level,
+ int value,
+ float valuef
+) {
+ int index = value * 2;
+
+ // Compute the residual to determine if we should round down or up ties.
+ // Test should be residual >= 0, but empirical testing shows small bias helps.
+ float residual = valuef - static_cast<float>(value);
+ if (residual >= -0.1f)
+ {
+ index++;
+ }
+
+ return color_unquant_to_uquant_tables[quant_level - QUANT_6][index];
}
/**
@@ -86,16 +112,16 @@ static void quantize_rgb(
float b1 = astc::clamp255f(color1.lane<2>() * scale);
int ri0, gi0, bi0, ri1, gi1, bi1;
- float rgb0_addon = 0.5f;
- float rgb1_addon = 0.5f;
+ float rgb0_addon = 0.0f;
+ float rgb1_addon = 0.0f;
do
{
- ri0 = quant_color(quant_level, astc::max(astc::flt2int_rd(r0 + rgb0_addon), 0));
- gi0 = quant_color(quant_level, astc::max(astc::flt2int_rd(g0 + rgb0_addon), 0));
- bi0 = quant_color(quant_level, astc::max(astc::flt2int_rd(b0 + rgb0_addon), 0));
- ri1 = quant_color(quant_level, astc::min(astc::flt2int_rd(r1 + rgb1_addon), 255));
- gi1 = quant_color(quant_level, astc::min(astc::flt2int_rd(g1 + rgb1_addon), 255));
- bi1 = quant_color(quant_level, astc::min(astc::flt2int_rd(b1 + rgb1_addon), 255));
+ ri0 = quant_color(quant_level, astc::max(astc::flt2int_rtn(r0 + rgb0_addon), 0), r0 + rgb0_addon);
+ gi0 = quant_color(quant_level, astc::max(astc::flt2int_rtn(g0 + rgb0_addon), 0), g0 + rgb0_addon);
+ bi0 = quant_color(quant_level, astc::max(astc::flt2int_rtn(b0 + rgb0_addon), 0), b0 + rgb0_addon);
+ ri1 = quant_color(quant_level, astc::min(astc::flt2int_rtn(r1 + rgb1_addon), 255), r1 + rgb1_addon);
+ gi1 = quant_color(quant_level, astc::min(astc::flt2int_rtn(g1 + rgb1_addon), 255), g1 + rgb1_addon);
+ bi1 = quant_color(quant_level, astc::min(astc::flt2int_rtn(b1 + rgb1_addon), 255), b1 + rgb1_addon);
rgb0_addon -= 0.2f;
rgb1_addon += 0.2f;
@@ -133,8 +159,8 @@ static void quantize_rgba(
float a0 = astc::clamp255f(color0.lane<3>() * scale);
float a1 = astc::clamp255f(color1.lane<3>() * scale);
- output[6] = quant_color(quant_level, astc::flt2int_rtn(a0));
- output[7] = quant_color(quant_level, astc::flt2int_rtn(a1));
+ output[6] = quant_color(quant_level, astc::flt2int_rtn(a0), a0);
+ output[7] = quant_color(quant_level, astc::flt2int_rtn(a1), a1);
quantize_rgb(color0, color1, output, quant_level);
}
@@ -180,13 +206,13 @@ static bool try_quantize_rgb_blue_contract(
}
// Quantize the inverse-blue-contracted color
- int ri0 = quant_color(quant_level, astc::flt2int_rtn(r0));
- int gi0 = quant_color(quant_level, astc::flt2int_rtn(g0));
- int bi0 = quant_color(quant_level, astc::flt2int_rtn(b0));
+ int ri0 = quant_color(quant_level, astc::flt2int_rtn(r0), r0);
+ int gi0 = quant_color(quant_level, astc::flt2int_rtn(g0), g0);
+ int bi0 = quant_color(quant_level, astc::flt2int_rtn(b0), b0);
- int ri1 = quant_color(quant_level, astc::flt2int_rtn(r1));
- int gi1 = quant_color(quant_level, astc::flt2int_rtn(g1));
- int bi1 = quant_color(quant_level, astc::flt2int_rtn(b1));
+ int ri1 = quant_color(quant_level, astc::flt2int_rtn(r1), r1);
+ int gi1 = quant_color(quant_level, astc::flt2int_rtn(g1), g1);
+ int bi1 = quant_color(quant_level, astc::flt2int_rtn(b1), b1);
// If color #1 is not larger than color #0 then blue-contraction cannot be used. Note that
// blue-contraction and quantization change this order, which is why we must test afterwards.
@@ -217,7 +243,7 @@ static bool try_quantize_rgb_blue_contract(
*
* @return Returns @c false on failure, @c true on success.
*/
-static int try_quantize_rgba_blue_contract(
+static bool try_quantize_rgba_blue_contract(
vfloat4 color0,
vfloat4 color1,
uint8_t output[8],
@@ -228,8 +254,8 @@ static int try_quantize_rgba_blue_contract(
float a0 = astc::clamp255f(color0.lane<3>() * scale);
float a1 = astc::clamp255f(color1.lane<3>() * scale);
- output[6] = quant_color(quant_level, astc::flt2int_rtn(a1));
- output[7] = quant_color(quant_level, astc::flt2int_rtn(a0));
+ output[6] = quant_color(quant_level, astc::flt2int_rtn(a1), a1);
+ output[7] = quant_color(quant_level, astc::flt2int_rtn(a0), a0);
return try_quantize_rgb_blue_contract(color0, color1, output, quant_level);
}
@@ -433,7 +459,7 @@ static bool try_quantize_rgb_delta_blue_contract(
g1d |= (g0b & 0x100) >> 1;
b1d |= (b0b & 0x100) >> 1;
- // Then quantize and unquantize; if this causes any of the top two bits to flip,
+ // Then quantize and unquantize; if this causes any of the top two bits to flip,
// then encoding fails, since we have then corrupted either the top bit of the base
// or the sign bit of the offset.
int r1de = quant_color(quant_level, r1d);
@@ -728,9 +754,9 @@ static void quantize_rgbs(
float g = astc::clamp255f(color.lane<1>() * scale);
float b = astc::clamp255f(color.lane<2>() * scale);
- int ri = quant_color(quant_level, astc::flt2int_rtn(r));
- int gi = quant_color(quant_level, astc::flt2int_rtn(g));
- int bi = quant_color(quant_level, astc::flt2int_rtn(b));
+ int ri = quant_color(quant_level, astc::flt2int_rtn(r), r);
+ int gi = quant_color(quant_level, astc::flt2int_rtn(g), g);
+ int bi = quant_color(quant_level, astc::flt2int_rtn(b), b);
float oldcolorsum = hadd_rgb_s(color) * scale;
float newcolorsum = static_cast<float>(ri + gi + bi);
@@ -764,8 +790,8 @@ static void quantize_rgbs_alpha(
float a0 = astc::clamp255f(color0.lane<3>() * scale);
float a1 = astc::clamp255f(color1.lane<3>() * scale);
- output[4] = quant_color(quant_level, astc::flt2int_rtn(a0));
- output[5] = quant_color(quant_level, astc::flt2int_rtn(a1));
+ output[4] = quant_color(quant_level, astc::flt2int_rtn(a0), a0);
+ output[5] = quant_color(quant_level, astc::flt2int_rtn(a1), a1);
quantize_rgbs(color, output, quant_level);
}
@@ -799,8 +825,8 @@ static void quantize_luminance(
lum1 = avg;
}
- output[0] = quant_color(quant_level, astc::flt2int_rtn(lum0));
- output[1] = quant_color(quant_level, astc::flt2int_rtn(lum1));
+ output[0] = quant_color(quant_level, astc::flt2int_rtn(lum0), lum0);
+ output[1] = quant_color(quant_level, astc::flt2int_rtn(lum1), lum1);
}
/**
@@ -828,48 +854,10 @@ static void quantize_luminance_alpha(
float a0 = astc::clamp255f(color0.lane<3>());
float a1 = astc::clamp255f(color1.lane<3>());
- // If endpoints are close then pull apart slightly; this gives > 8 bit normal map precision.
- if (quant_level > 18)
- {
- if (fabsf(lum0 - lum1) < 3.0f)
- {
- if (lum0 < lum1)
- {
- lum0 -= 0.5f;
- lum1 += 0.5f;
- }
- else
- {
- lum0 += 0.5f;
- lum1 -= 0.5f;
- }
-
- lum0 = astc::clamp255f(lum0);
- lum1 = astc::clamp255f(lum1);
- }
-
- if (fabsf(a0 - a1) < 3.0f)
- {
- if (a0 < a1)
- {
- a0 -= 0.5f;
- a1 += 0.5f;
- }
- else
- {
- a0 += 0.5f;
- a1 -= 0.5f;
- }
-
- a0 = astc::clamp255f(a0);
- a1 = astc::clamp255f(a1);
- }
- }
-
- output[0] = quant_color(quant_level, astc::flt2int_rtn(lum0));
- output[1] = quant_color(quant_level, astc::flt2int_rtn(lum1));
- output[2] = quant_color(quant_level, astc::flt2int_rtn(a0));
- output[3] = quant_color(quant_level, astc::flt2int_rtn(a1));
+ output[0] = quant_color(quant_level, astc::flt2int_rtn(lum0), lum0);
+ output[1] = quant_color(quant_level, astc::flt2int_rtn(lum1), lum1);
+ output[2] = quant_color(quant_level, astc::flt2int_rtn(a0), a0);
+ output[3] = quant_color(quant_level, astc::flt2int_rtn(a1), a1);
}
/**
@@ -1661,8 +1649,8 @@ static void quantize_hdr_rgb_ldr_alpha(
float a0 = astc::clamp255f(color0.lane<3>() * scale);
float a1 = astc::clamp255f(color1.lane<3>() * scale);
- output[6] = quant_color(quant_level, astc::flt2int_rtn(a0));
- output[7] = quant_color(quant_level, astc::flt2int_rtn(a1));
+ output[6] = quant_color(quant_level, astc::flt2int_rtn(a0), a0);
+ output[7] = quant_color(quant_level, astc::flt2int_rtn(a1), a1);
quantize_hdr_rgb(color0, color1, output, quant_level);
}
diff --git a/thirdparty/astcenc/astcenc_compress_symbolic.cpp b/thirdparty/astcenc/astcenc_compress_symbolic.cpp
index afb76246e7..41a8558267 100644
--- a/thirdparty/astcenc/astcenc_compress_symbolic.cpp
+++ b/thirdparty/astcenc/astcenc_compress_symbolic.cpp
@@ -391,7 +391,7 @@ static float compress_symbolic_block_for_partition_1plane(
for (unsigned int i = 0; i < max_decimation_modes; i++)
{
const auto& dm = bsd.get_decimation_mode(i);
- if (!dm.is_ref_1_plane(static_cast<quant_method>(max_weight_quant)))
+ if (!dm.is_ref_1plane(static_cast<quant_method>(max_weight_quant)))
{
continue;
}
@@ -561,7 +561,7 @@ static float compress_symbolic_block_for_partition_1plane(
workscb.color_formats_matched = 0;
if (partition_count >= 2 && all_same)
{
- uint8_t colorvals[BLOCK_MAX_PARTITIONS][12];
+ uint8_t colorvals[BLOCK_MAX_PARTITIONS][8];
uint8_t color_formats_mod[BLOCK_MAX_PARTITIONS] { 0 };
bool all_same_mod = true;
for (unsigned int j = 0; j < partition_count; j++)
@@ -743,7 +743,7 @@ static float compress_symbolic_block_for_partition_2planes(
for (unsigned int i = 0; i < bsd.decimation_mode_count_selected; i++)
{
const auto& dm = bsd.get_decimation_mode(i);
- if (!dm.is_ref_2_plane(static_cast<quant_method>(max_weight_quant)))
+ if (!dm.is_ref_2plane(static_cast<quant_method>(max_weight_quant)))
{
continue;
}
@@ -1263,8 +1263,8 @@ void compress_block(
float exit_thresholds_for_pcount[BLOCK_MAX_PARTITIONS] {
0.0f,
- ctx.config.tune_2_partition_early_out_limit_factor,
- ctx.config.tune_3_partition_early_out_limit_factor,
+ ctx.config.tune_2partition_early_out_limit_factor,
+ ctx.config.tune_3partition_early_out_limit_factor,
0.0f
};
@@ -1318,7 +1318,7 @@ void compress_block(
lowest_correl = prepare_block_statistics(bsd.texel_count, blk);
#endif
- block_skip_two_plane = lowest_correl > ctx.config.tune_2_plane_early_out_limit_correlation;
+ block_skip_two_plane = lowest_correl > ctx.config.tune_2plane_early_out_limit_correlation;
// Test the four possible 1-partition, 2-planes modes. Do this in reverse, as
// alpha is the most likely to be non-correlated if it is present in the data.
@@ -1331,7 +1331,7 @@ void compress_block(
if (block_skip_two_plane)
{
- trace_add_data("skip", "tune_2_plane_early_out_limit_correlation");
+ trace_add_data("skip", "tune_2plane_early_out_limit_correlation");
continue;
}
diff --git a/thirdparty/astcenc/astcenc_diagnostic_trace.cpp b/thirdparty/astcenc/astcenc_diagnostic_trace.cpp
index 7fa7ab1a8b..bcd6fa72a5 100644
--- a/thirdparty/astcenc/astcenc_diagnostic_trace.cpp
+++ b/thirdparty/astcenc/astcenc_diagnostic_trace.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2021-2022 Arm Limited
+// Copyright 2021-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -24,6 +24,8 @@
#include <cassert>
#include <cstdarg>
#include <cstdio>
+#include <cmath>
+#include <limits>
#include <string>
#include "astcenc_diagnostic_trace.h"
@@ -203,7 +205,20 @@ void trace_add_data(
const char* key,
float value
) {
- char buffer[256];
+ // Turn infinities into parseable values
+ if (std::isinf(value))
+ {
+ if (value > 0.0f)
+ {
+ value = std::numeric_limits<float>::max();
+ }
+ else
+ {
+ value = -std::numeric_limits<float>::max();
+ }
+ }
+
+ char buffer[256];
sprintf(buffer, "%.20g", (double)value);
TraceNode* node = g_TraceLog->get_current_leaf();
node->add_attrib("float", key, buffer);
diff --git a/thirdparty/astcenc/astcenc_entry.cpp b/thirdparty/astcenc/astcenc_entry.cpp
index e59f1fe61a..e53762c26a 100644
--- a/thirdparty/astcenc/astcenc_entry.cpp
+++ b/thirdparty/astcenc/astcenc_entry.cpp
@@ -52,9 +52,9 @@ struct astcenc_preset_config
float tune_db_limit_a_base;
float tune_db_limit_b_base;
float tune_mse_overshoot;
- float tune_2_partition_early_out_limit_factor;
- float tune_3_partition_early_out_limit_factor;
- float tune_2_plane_early_out_limit_correlation;
+ float tune_2partition_early_out_limit_factor;
+ float tune_3partition_early_out_limit_factor;
+ float tune_2plane_early_out_limit_correlation;
};
/**
@@ -158,48 +158,6 @@ static astcenc_error validate_cpu_float()
}
/**
- * @brief Validate CPU ISA support meets the requirements of this build of the library.
- *
- * Each library build is statically compiled for a particular set of CPU ISA features, such as the
- * SIMD support or other ISA extensions such as POPCNT. This function checks that the host CPU
- * actually supports everything this build needs.
- *
- * @return Return @c ASTCENC_SUCCESS if validated, otherwise an error on failure.
- */
-static astcenc_error validate_cpu_isa()
-{
- #if ASTCENC_SSE >= 41
- if (!cpu_supports_sse41())
- {
- return ASTCENC_ERR_BAD_CPU_ISA;
- }
- #endif
-
- #if ASTCENC_POPCNT >= 1
- if (!cpu_supports_popcnt())
- {
- return ASTCENC_ERR_BAD_CPU_ISA;
- }
- #endif
-
- #if ASTCENC_F16C >= 1
- if (!cpu_supports_f16c())
- {
- return ASTCENC_ERR_BAD_CPU_ISA;
- }
- #endif
-
- #if ASTCENC_AVX >= 2
- if (!cpu_supports_avx2())
- {
- return ASTCENC_ERR_BAD_CPU_ISA;
- }
- #endif
-
- return ASTCENC_SUCCESS;
-}
-
-/**
* @brief Validate config profile.
*
* @param profile The profile to check.
@@ -439,9 +397,9 @@ static astcenc_error validate_config(
config.tune_4partitioning_candidate_limit = astc::clamp(config.tune_4partitioning_candidate_limit, 1u, TUNE_MAX_PARTITIONING_CANDIDATES);
config.tune_db_limit = astc::max(config.tune_db_limit, 0.0f);
config.tune_mse_overshoot = astc::max(config.tune_mse_overshoot, 1.0f);
- config.tune_2_partition_early_out_limit_factor = astc::max(config.tune_2_partition_early_out_limit_factor, 0.0f);
- config.tune_3_partition_early_out_limit_factor = astc::max(config.tune_3_partition_early_out_limit_factor, 0.0f);
- config.tune_2_plane_early_out_limit_correlation = astc::max(config.tune_2_plane_early_out_limit_correlation, 0.0f);
+ config.tune_2partition_early_out_limit_factor = astc::max(config.tune_2partition_early_out_limit_factor, 0.0f);
+ config.tune_3partition_early_out_limit_factor = astc::max(config.tune_3partition_early_out_limit_factor, 0.0f);
+ config.tune_2plane_early_out_limit_correlation = astc::max(config.tune_2plane_early_out_limit_correlation, 0.0f);
// Specifying a zero weight color component is not allowed; force to small value
float max_weight = astc::max(astc::max(config.cw_r_weight, config.cw_g_weight),
@@ -475,14 +433,6 @@ astcenc_error astcenc_config_init(
) {
astcenc_error status;
- // Check basic library compatibility options here so they are checked early. Note, these checks
- // are repeated in context_alloc for cases where callers use a manually defined config struct
- status = validate_cpu_isa();
- if (status != ASTCENC_SUCCESS)
- {
- return status;
- }
-
status = validate_cpu_float();
if (status != ASTCENC_SUCCESS)
{
@@ -563,9 +513,9 @@ astcenc_error astcenc_config_init(
config.tune_mse_overshoot = (*preset_configs)[start].tune_mse_overshoot;
- config.tune_2_partition_early_out_limit_factor = (*preset_configs)[start].tune_2_partition_early_out_limit_factor;
- config.tune_3_partition_early_out_limit_factor =(*preset_configs)[start].tune_3_partition_early_out_limit_factor;
- config.tune_2_plane_early_out_limit_correlation = (*preset_configs)[start].tune_2_plane_early_out_limit_correlation;
+ config.tune_2partition_early_out_limit_factor = (*preset_configs)[start].tune_2partition_early_out_limit_factor;
+ config.tune_3partition_early_out_limit_factor = (*preset_configs)[start].tune_3partition_early_out_limit_factor;
+ config.tune_2plane_early_out_limit_correlation = (*preset_configs)[start].tune_2plane_early_out_limit_correlation;
}
// Start and end node are not the same - so interpolate between them
else
@@ -605,9 +555,9 @@ astcenc_error astcenc_config_init(
config.tune_mse_overshoot = LERP(tune_mse_overshoot);
- config.tune_2_partition_early_out_limit_factor = LERP(tune_2_partition_early_out_limit_factor);
- config.tune_3_partition_early_out_limit_factor = LERP(tune_3_partition_early_out_limit_factor);
- config.tune_2_plane_early_out_limit_correlation = LERP(tune_2_plane_early_out_limit_correlation);
+ config.tune_2partition_early_out_limit_factor = LERP(tune_2partition_early_out_limit_factor);
+ config.tune_3partition_early_out_limit_factor = LERP(tune_3partition_early_out_limit_factor);
+ config.tune_2plane_early_out_limit_correlation = LERP(tune_2plane_early_out_limit_correlation);
#undef LERP
#undef LERPI
#undef LERPUI
@@ -656,9 +606,9 @@ astcenc_error astcenc_config_init(
config.cw_g_weight = 0.0f;
config.cw_b_weight = 0.0f;
- config.tune_2_partition_early_out_limit_factor *= 1.5f;
- config.tune_3_partition_early_out_limit_factor *= 1.5f;
- config.tune_2_plane_early_out_limit_correlation = 0.99f;
+ config.tune_2partition_early_out_limit_factor *= 1.5f;
+ config.tune_3partition_early_out_limit_factor *= 1.5f;
+ config.tune_2plane_early_out_limit_correlation = 0.99f;
// Normals are prone to blocking artifacts on smooth curves
// so force compressor to try harder here ...
@@ -702,12 +652,6 @@ astcenc_error astcenc_context_alloc(
astcenc_error status;
const astcenc_config& config = *configp;
- status = validate_cpu_isa();
- if (status != ASTCENC_SUCCESS)
- {
- return status;
- }
-
status = validate_cpu_float();
if (status != ASTCENC_SUCCESS)
{
@@ -1399,8 +1343,6 @@ const char* astcenc_get_error_string(
return "ASTCENC_ERR_OUT_OF_MEM";
case ASTCENC_ERR_BAD_CPU_FLOAT:
return "ASTCENC_ERR_BAD_CPU_FLOAT";
- case ASTCENC_ERR_BAD_CPU_ISA:
- return "ASTCENC_ERR_BAD_CPU_ISA";
case ASTCENC_ERR_BAD_PARAM:
return "ASTCENC_ERR_BAD_PARAM";
case ASTCENC_ERR_BAD_BLOCK_SIZE:
diff --git a/thirdparty/astcenc/astcenc_find_best_partitioning.cpp b/thirdparty/astcenc/astcenc_find_best_partitioning.cpp
index ffde3c7060..789c3964ef 100644
--- a/thirdparty/astcenc/astcenc_find_best_partitioning.cpp
+++ b/thirdparty/astcenc/astcenc_find_best_partitioning.cpp
@@ -604,8 +604,7 @@ unsigned int find_best_partition_candidates(
processed_line4 uncor_plines[BLOCK_MAX_PARTITIONS];
processed_line4 samec_plines[BLOCK_MAX_PARTITIONS];
- float uncor_line_lens[BLOCK_MAX_PARTITIONS];
- float samec_line_lens[BLOCK_MAX_PARTITIONS];
+ float line_lengths[BLOCK_MAX_PARTITIONS];
for (unsigned int j = 0; j < partition_count; j++)
{
@@ -631,8 +630,7 @@ unsigned int find_best_partition_candidates(
blk,
uncor_plines,
samec_plines,
- uncor_line_lens,
- samec_line_lens,
+ line_lengths,
uncor_error,
samec_error);
@@ -651,8 +649,8 @@ unsigned int find_best_partition_candidates(
float tpp = static_cast<float>(pi.partition_texel_count[j]);
vfloat4 error_weights(tpp * weight_imprecision_estim);
- vfloat4 uncor_vector = uncor_lines[j].b * uncor_line_lens[j];
- vfloat4 samec_vector = samec_lines[j].b * samec_line_lens[j];
+ vfloat4 uncor_vector = uncor_lines[j].b * line_lengths[j];
+ vfloat4 samec_vector = samec_lines[j].b * line_lengths[j];
uncor_error += dot_s(uncor_vector * uncor_vector, error_weights);
samec_error += dot_s(samec_vector * samec_vector, error_weights);
@@ -719,8 +717,8 @@ unsigned int find_best_partition_candidates(
float tpp = static_cast<float>(pi.partition_texel_count[j]);
vfloat4 error_weights(tpp * weight_imprecision_estim);
- vfloat4 uncor_vector = pl.uncor_line.b * pl.uncor_line_len;
- vfloat4 samec_vector = pl.samec_line.b * pl.samec_line_len;
+ vfloat4 uncor_vector = pl.uncor_line.b * pl.line_length;
+ vfloat4 samec_vector = pl.samec_line.b * pl.line_length;
uncor_error += dot3_s(uncor_vector * uncor_vector, error_weights);
samec_error += dot3_s(samec_vector * samec_vector, error_weights);
@@ -731,21 +729,11 @@ unsigned int find_best_partition_candidates(
}
}
- bool best_is_uncor = uncor_best_partitions[0] > samec_best_partitions[0];
-
unsigned int interleave[2 * TUNE_MAX_PARTITIONING_CANDIDATES];
for (unsigned int i = 0; i < requested_candidates; i++)
{
- if (best_is_uncor)
- {
- interleave[2 * i] = bsd.get_raw_partition_info(partition_count, uncor_best_partitions[i]).partition_index;
- interleave[2 * i + 1] = bsd.get_raw_partition_info(partition_count, samec_best_partitions[i]).partition_index;
- }
- else
- {
- interleave[2 * i] = bsd.get_raw_partition_info(partition_count, samec_best_partitions[i]).partition_index;
- interleave[2 * i + 1] = bsd.get_raw_partition_info(partition_count, uncor_best_partitions[i]).partition_index;
- }
+ interleave[2 * i] = bsd.get_raw_partition_info(partition_count, uncor_best_partitions[i]).partition_index;
+ interleave[2 * i + 1] = bsd.get_raw_partition_info(partition_count, samec_best_partitions[i]).partition_index;
}
uint64_t bitmasks[1024/64] { 0 };
diff --git a/thirdparty/astcenc/astcenc_internal.h b/thirdparty/astcenc/astcenc_internal.h
index 0aa8fa0f81..63bbf8af53 100644
--- a/thirdparty/astcenc/astcenc_internal.h
+++ b/thirdparty/astcenc/astcenc_internal.h
@@ -293,11 +293,13 @@ struct partition_lines3
/** @brief Post-processed line for correlated chroma, passing though the origin. */
processed_line3 samec_pline;
- /** @brief The length of the line for uncorrelated chroma. */
- float uncor_line_len;
-
- /** @brief The length of the line for correlated chroma. */
- float samec_line_len;
+ /**
+ * @brief The length of the line for uncorrelated chroma.
+ *
+ * This is used for both the uncorrelated and same chroma lines - they are normally very similar
+ * and only used for the relative ranking of partitionings against one another.
+ */
+ float line_length;
};
/**
@@ -319,8 +321,8 @@ struct partition_info
/**
* @brief The number of texels in each partition.
*
- * Note that some seeds result in zero texels assigned to a partition are valid, but are skipped
- * by this compressor as there is no point spending bits encoding an unused color endpoint.
+ * Note that some seeds result in zero texels assigned to a partition. These are valid, but are
+ * skipped by this compressor as there is no point spending bits encoding an unused endpoints.
*/
uint8_t partition_texel_count[BLOCK_MAX_PARTITIONS];
@@ -455,23 +457,23 @@ struct decimation_mode
*
* Bit 0 = QUANT_2, Bit 1 = QUANT_3, etc.
*/
- uint16_t refprec_1_plane;
+ uint16_t refprec_1plane;
/**
* @brief Bitvector indicating weight quant methods used by active 2 plane block modes.
*
* Bit 0 = QUANT_2, Bit 1 = QUANT_3, etc.
*/
- uint16_t refprec_2_planes;
+ uint16_t refprec_2planes;
/**
* @brief Set a 1 plane weight quant as active.
*
* @param weight_quant The quant method to set.
*/
- void set_ref_1_plane(quant_method weight_quant)
+ void set_ref_1plane(quant_method weight_quant)
{
- refprec_1_plane |= (1 << weight_quant);
+ refprec_1plane |= (1 << weight_quant);
}
/**
@@ -479,10 +481,10 @@ struct decimation_mode
*
* @param max_weight_quant The max quant method to test.
*/
- bool is_ref_1_plane(quant_method max_weight_quant) const
+ bool is_ref_1plane(quant_method max_weight_quant) const
{
uint16_t mask = static_cast<uint16_t>((1 << (max_weight_quant + 1)) - 1);
- return (refprec_1_plane & mask) != 0;
+ return (refprec_1plane & mask) != 0;
}
/**
@@ -490,9 +492,9 @@ struct decimation_mode
*
* @param weight_quant The quant method to set.
*/
- void set_ref_2_plane(quant_method weight_quant)
+ void set_ref_2plane(quant_method weight_quant)
{
- refprec_2_planes |= static_cast<uint16_t>(1 << weight_quant);
+ refprec_2planes |= static_cast<uint16_t>(1 << weight_quant);
}
/**
@@ -500,10 +502,10 @@ struct decimation_mode
*
* @param max_weight_quant The max quant method to test.
*/
- bool is_ref_2_plane(quant_method max_weight_quant) const
+ bool is_ref_2plane(quant_method max_weight_quant) const
{
uint16_t mask = static_cast<uint16_t>((1 << (max_weight_quant + 1)) - 1);
- return (refprec_2_planes & mask) != 0;
+ return (refprec_2planes & mask) != 0;
}
};
@@ -1336,9 +1338,14 @@ bool is_legal_3d_block_size(
* Converts unquant value in 0-255 range into quant value in 0-255 range.
* No BISE scrambling is applied at this stage.
*
- * Indexed by [quant_mode - 4][data_value].
+ * The BISE encoding results in ties where available quant<256> values are
+ * equidistant the available quant<BISE> values. This table stores two values
+ * for each input - one for use with a negative residual, and one for use with
+ * a positive residual.
+ *
+ * Indexed by [quant_mode - 4][data_value * 2 + residual].
*/
-extern const uint8_t color_unquant_to_uquant_tables[17][256];
+extern const uint8_t color_unquant_to_uquant_tables[17][512];
/**
* @brief The precomputed table for packing quantized color values.
@@ -1528,8 +1535,7 @@ void compute_error_squared_rgb(
* @param blk The image block color data to be compressed.
* @param uncor_plines Processed uncorrelated partition lines for each partition.
* @param samec_plines Processed same chroma partition lines for each partition.
- * @param[out] uncor_lengths The length of each components deviation from the line.
- * @param[out] samec_lengths The length of each components deviation from the line.
+ * @param[out] line_lengths The length of each components deviation from the line.
* @param[out] uncor_error The cumulative error for using the uncorrelated line.
* @param[out] samec_error The cumulative error for using the same chroma line.
*/
@@ -1538,8 +1544,7 @@ void compute_error_squared_rgba(
const image_block& blk,
const processed_line4 uncor_plines[BLOCK_MAX_PARTITIONS],
const processed_line4 samec_plines[BLOCK_MAX_PARTITIONS],
- float uncor_lengths[BLOCK_MAX_PARTITIONS],
- float samec_lengths[BLOCK_MAX_PARTITIONS],
+ float line_lengths[BLOCK_MAX_PARTITIONS],
float& uncor_error,
float& samec_error);
@@ -2121,34 +2126,6 @@ void physical_to_symbolic(
Platform-specific functions.
============================================================================ */
/**
- * @brief Run-time detection if the host CPU supports the POPCNT extension.
- *
- * @return @c true if supported, @c false if not.
- */
-bool cpu_supports_popcnt();
-
-/**
- * @brief Run-time detection if the host CPU supports F16C extension.
- *
- * @return @c true if supported, @c false if not.
- */
-bool cpu_supports_f16c();
-
-/**
- * @brief Run-time detection if the host CPU supports SSE 4.1 extension.
- *
- * @return @c true if supported, @c false if not.
- */
-bool cpu_supports_sse41();
-
-/**
- * @brief Run-time detection if the host CPU supports AVX 2 extension.
- *
- * @return @c true if supported, @c false if not.
- */
-bool cpu_supports_avx2();
-
-/**
* @brief Allocate an aligned memory buffer.
*
* Allocated memory must be freed by aligned_free;
diff --git a/thirdparty/astcenc/astcenc_platform_isa_detection.cpp b/thirdparty/astcenc/astcenc_platform_isa_detection.cpp
deleted file mode 100644
index 8ed98437ea..0000000000
--- a/thirdparty/astcenc/astcenc_platform_isa_detection.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// ----------------------------------------------------------------------------
-// Copyright 2020-2022 Arm Limited
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at:
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-// ----------------------------------------------------------------------------
-
-/**
- * @brief Platform-specific function implementations.
- *
- * This module contains functions for querying the host extended ISA support.
- */
-
-// Include before the defines below to pick up any auto-setup based on compiler
-// built-in config, if not being set explicitly by the build system
-#include "astcenc_internal.h"
-
-#if (ASTCENC_SSE > 0) || (ASTCENC_AVX > 0) || \
- (ASTCENC_POPCNT > 0) || (ASTCENC_F16C > 0)
-
-static bool g_init { false };
-
-/** Does this CPU support SSE 4.1? Set to -1 if not yet initialized. */
-static bool g_cpu_has_sse41 { false };
-
-/** Does this CPU support AVX2? Set to -1 if not yet initialized. */
-static bool g_cpu_has_avx2 { false };
-
-/** Does this CPU support POPCNT? Set to -1 if not yet initialized. */
-static bool g_cpu_has_popcnt { false };
-
-/** Does this CPU support F16C? Set to -1 if not yet initialized. */
-static bool g_cpu_has_f16c { false };
-
-/* ============================================================================
- Platform code for Visual Studio
-============================================================================ */
-#if !defined(__clang__) && defined(_MSC_VER)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <intrin.h>
-
-/**
- * @brief Detect platform CPU ISA support and update global trackers.
- */
-static void detect_cpu_isa()
-{
- int data[4];
-
- __cpuid(data, 0);
- int num_id = data[0];
-
- if (num_id >= 1)
- {
- __cpuidex(data, 1, 0);
- // SSE41 = Bank 1, ECX, bit 19
- g_cpu_has_sse41 = data[2] & (1 << 19) ? true : false;
- // POPCNT = Bank 1, ECX, bit 23
- g_cpu_has_popcnt = data[2] & (1 << 23) ? true : false;
- // F16C = Bank 1, ECX, bit 29
- g_cpu_has_f16c = data[2] & (1 << 29) ? true : false;
- }
-
- if (num_id >= 7)
- {
- __cpuidex(data, 7, 0);
- // AVX2 = Bank 7, EBX, bit 5
- g_cpu_has_avx2 = data[1] & (1 << 5) ? true : false;
- }
-
- // Ensure state bits are updated before init flag is updated
- MemoryBarrier();
- g_init = true;
-}
-
-/* ============================================================================
- Platform code for GCC and Clang
-============================================================================ */
-#else
-#include <cpuid.h>
-
-/**
- * @brief Detect platform CPU ISA support and update global trackers.
- */
-static void detect_cpu_isa()
-{
- unsigned int data[4];
-
- if (__get_cpuid_count(1, 0, &data[0], &data[1], &data[2], &data[3]))
- {
- // SSE41 = Bank 1, ECX, bit 19
- g_cpu_has_sse41 = data[2] & (1 << 19) ? true : false;
- // POPCNT = Bank 1, ECX, bit 23
- g_cpu_has_popcnt = data[2] & (1 << 23) ? true : false;
- // F16C = Bank 1, ECX, bit 29
- g_cpu_has_f16c = data[2] & (1 << 29) ? true : false;
- }
-
- g_cpu_has_avx2 = 0;
- if (__get_cpuid_count(7, 0, &data[0], &data[1], &data[2], &data[3]))
- {
- // AVX2 = Bank 7, EBX, bit 5
- g_cpu_has_avx2 = data[1] & (1 << 5) ? true : false;
- }
-
- // Ensure state bits are updated before init flag is updated
- __sync_synchronize();
- g_init = true;
-}
-#endif
-
-/* See header for documentation. */
-bool cpu_supports_popcnt()
-{
- if (!g_init)
- {
- detect_cpu_isa();
- }
-
- return g_cpu_has_popcnt;
-}
-
-/* See header for documentation. */
-bool cpu_supports_f16c()
-{
- if (!g_init)
- {
- detect_cpu_isa();
- }
-
- return g_cpu_has_f16c;
-}
-
-/* See header for documentation. */
-bool cpu_supports_sse41()
-{
- if (!g_init)
- {
- detect_cpu_isa();
- }
-
- return g_cpu_has_sse41;
-}
-
-/* See header for documentation. */
-bool cpu_supports_avx2()
-{
- if (!g_init)
- {
- detect_cpu_isa();
- }
-
- return g_cpu_has_avx2;
-}
-
-#endif
diff --git a/thirdparty/astcenc/astcenc_quantization.cpp b/thirdparty/astcenc/astcenc_quantization.cpp
index 478a21ead7..84d758dbfa 100644
--- a/thirdparty/astcenc/astcenc_quantization.cpp
+++ b/thirdparty/astcenc/astcenc_quantization.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2011-2021 Arm Limited
+// Copyright 2011-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -23,321 +23,320 @@
#if !defined(ASTCENC_DECOMPRESS_ONLY)
-// Starts from QUANT_6
-// Not scrambled
-const uint8_t color_unquant_to_uquant_tables[17][256] {
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 102, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
- 153, 153, 153, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
- 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
- 36, 36, 36, 36, 36, 36, 36, 73, 73, 73, 73, 73, 73, 73, 73, 73,
- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 109, 109, 109, 109,
- 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
- 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
- 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
- 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
- 146, 146, 146, 146, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182,
- 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182,
- 182, 182, 182, 182, 182, 182, 182, 182, 182, 219, 219, 219, 219, 219, 219, 219,
- 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219,
- 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
- 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 56, 56, 56, 56, 56,
- 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
- 56, 56, 56, 56, 56, 56, 56, 84, 84, 84, 84, 84, 84, 84, 84, 84,
- 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
- 84, 84, 84, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
- 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
- 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142,
- 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 171, 171, 171,
- 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171,
- 171, 171, 171, 171, 171, 171, 171, 171, 171, 199, 199, 199, 199, 199, 199, 199,
- 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
- 199, 199, 199, 199, 199, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
- 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
- 227, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
- 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 69, 69, 69, 69, 69, 69,
- 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
- 69, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
- 92, 92, 92, 92, 92, 92, 92, 92, 92, 116, 116, 116, 116, 116, 116, 116,
- 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
- 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
- 139, 139, 139, 139, 139, 139, 139, 163, 163, 163, 163, 163, 163, 163, 163, 163,
- 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 186,
- 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- 186, 186, 186, 186, 186, 186, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
- 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 232, 232, 232,
- 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
- 232, 232, 232, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 51, 51, 51, 51, 51,
- 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 85, 85, 85,
- 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 102, 102,
- 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 119,
- 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119,
- 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
- 136, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
- 153, 153, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
- 170, 170, 170, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
- 187, 187, 187, 187, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
- 204, 204, 204, 204, 204, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
- 221, 221, 221, 221, 221, 221, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238,
- 238, 238, 238, 238, 238, 238, 238, 255, 255, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
- 27, 27, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 67, 67, 67,
- 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 80, 80, 80, 80, 80, 80,
- 80, 80, 80, 80, 80, 80, 80, 80, 94, 94, 94, 94, 94, 94, 94, 94,
- 94, 94, 94, 94, 94, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
- 107, 107, 107, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
- 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 148, 148, 148,
- 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 161, 161, 161, 161, 161,
- 161, 161, 161, 161, 161, 161, 161, 161, 175, 175, 175, 175, 175, 175, 175, 175,
- 175, 175, 175, 175, 175, 175, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188,
- 188, 188, 188, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
- 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 228, 228,
- 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 242, 242, 242, 242, 242,
- 242, 242, 242, 242, 242, 242, 242, 242, 242, 255, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44,
- 44, 44, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 66, 66, 66,
- 66, 66, 66, 66, 66, 66, 66, 66, 77, 77, 77, 77, 77, 77, 77, 77,
- 77, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 110, 110, 110, 110, 110, 110, 110,
- 110, 110, 110, 110, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
- 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 145, 145, 145, 145,
- 145, 145, 145, 145, 145, 145, 145, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 178, 178, 178,
- 178, 178, 178, 178, 178, 178, 178, 178, 189, 189, 189, 189, 189, 189, 189, 189,
- 189, 189, 189, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 211, 211,
- 211, 211, 211, 211, 211, 211, 211, 211, 211, 222, 222, 222, 222, 222, 222, 222,
- 222, 222, 222, 222, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 244,
- 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 255, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16,
- 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 41, 41, 41, 41, 41, 41, 41, 41, 49, 49,
- 49, 49, 49, 49, 49, 49, 57, 57, 57, 57, 57, 57, 57, 57, 66, 66,
- 66, 66, 66, 66, 66, 66, 66, 74, 74, 74, 74, 74, 74, 74, 74, 82,
- 82, 82, 82, 82, 82, 82, 82, 90, 90, 90, 90, 90, 90, 90, 90, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 107, 107, 107, 107, 107, 107, 107, 107,
- 115, 115, 115, 115, 115, 115, 115, 115, 123, 123, 123, 123, 123, 123, 123, 123,
- 132, 132, 132, 132, 132, 132, 132, 132, 140, 140, 140, 140, 140, 140, 140, 140,
- 148, 148, 148, 148, 148, 148, 148, 148, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 165, 165, 165, 165, 165, 165, 165, 165, 173, 173, 173, 173, 173, 173, 173,
- 173, 181, 181, 181, 181, 181, 181, 181, 181, 189, 189, 189, 189, 189, 189, 189,
- 189, 189, 198, 198, 198, 198, 198, 198, 198, 198, 206, 206, 206, 206, 206, 206,
- 206, 206, 214, 214, 214, 214, 214, 214, 214, 214, 222, 222, 222, 222, 222, 222,
- 222, 222, 222, 231, 231, 231, 231, 231, 231, 231, 231, 239, 239, 239, 239, 239,
- 239, 239, 239, 247, 247, 247, 247, 247, 247, 247, 247, 255, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13,
- 13, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 32, 32,
- 32, 32, 32, 32, 39, 39, 39, 39, 39, 39, 39, 45, 45, 45, 45, 45,
- 45, 52, 52, 52, 52, 52, 52, 52, 58, 58, 58, 58, 58, 58, 65, 65,
- 65, 65, 65, 65, 65, 71, 71, 71, 71, 71, 71, 78, 78, 78, 78, 78,
- 78, 78, 84, 84, 84, 84, 84, 84, 91, 91, 91, 91, 91, 91, 91, 97,
- 97, 97, 97, 97, 97, 104, 104, 104, 104, 104, 104, 104, 110, 110, 110, 110,
- 110, 110, 117, 117, 117, 117, 117, 117, 117, 123, 123, 123, 123, 123, 123, 123,
- 132, 132, 132, 132, 132, 132, 132, 138, 138, 138, 138, 138, 138, 138, 145, 145,
- 145, 145, 145, 145, 151, 151, 151, 151, 151, 151, 151, 158, 158, 158, 158, 158,
- 158, 164, 164, 164, 164, 164, 164, 164, 171, 171, 171, 171, 171, 171, 177, 177,
- 177, 177, 177, 177, 177, 184, 184, 184, 184, 184, 184, 190, 190, 190, 190, 190,
- 190, 190, 197, 197, 197, 197, 197, 197, 203, 203, 203, 203, 203, 203, 203, 210,
- 210, 210, 210, 210, 210, 216, 216, 216, 216, 216, 216, 216, 223, 223, 223, 223,
- 223, 223, 229, 229, 229, 229, 229, 229, 229, 236, 236, 236, 236, 236, 236, 242,
- 242, 242, 242, 242, 242, 242, 249, 249, 249, 249, 249, 249, 255, 255, 255, 255
- },
- {
- 0, 0, 0, 5, 5, 5, 5, 5, 5, 11, 11, 11, 11, 11, 16, 16,
- 16, 16, 16, 21, 21, 21, 21, 21, 21, 27, 27, 27, 27, 27, 32, 32,
- 32, 32, 32, 32, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 48, 48,
- 48, 48, 48, 48, 54, 54, 54, 54, 54, 59, 59, 59, 59, 59, 59, 65,
- 65, 65, 65, 65, 70, 70, 70, 70, 70, 70, 76, 76, 76, 76, 76, 81,
- 81, 81, 81, 81, 86, 86, 86, 86, 86, 86, 92, 92, 92, 92, 92, 97,
- 97, 97, 97, 97, 97, 103, 103, 103, 103, 103, 108, 108, 108, 108, 108, 113,
- 113, 113, 113, 113, 113, 119, 119, 119, 119, 119, 124, 124, 124, 124, 124, 124,
- 131, 131, 131, 131, 131, 131, 136, 136, 136, 136, 136, 142, 142, 142, 142, 142,
- 142, 147, 147, 147, 147, 147, 152, 152, 152, 152, 152, 158, 158, 158, 158, 158,
- 158, 163, 163, 163, 163, 163, 169, 169, 169, 169, 169, 169, 174, 174, 174, 174,
- 174, 179, 179, 179, 179, 179, 185, 185, 185, 185, 185, 185, 190, 190, 190, 190,
- 190, 196, 196, 196, 196, 196, 196, 201, 201, 201, 201, 201, 207, 207, 207, 207,
- 207, 207, 212, 212, 212, 212, 212, 217, 217, 217, 217, 217, 223, 223, 223, 223,
- 223, 223, 228, 228, 228, 228, 228, 234, 234, 234, 234, 234, 234, 239, 239, 239,
- 239, 239, 244, 244, 244, 244, 244, 250, 250, 250, 250, 250, 250, 255, 255, 255
- },
- {
- 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, 16,
- 16, 16, 16, 20, 20, 20, 20, 24, 24, 24, 24, 28, 28, 28, 28, 32,
- 32, 32, 32, 36, 36, 36, 36, 40, 40, 40, 40, 44, 44, 44, 44, 48,
- 48, 48, 48, 52, 52, 52, 52, 56, 56, 56, 56, 60, 60, 60, 60, 65,
- 65, 65, 65, 65, 69, 69, 69, 69, 73, 73, 73, 73, 77, 77, 77, 77,
- 81, 81, 81, 81, 85, 85, 85, 85, 89, 89, 89, 89, 93, 93, 93, 93,
- 97, 97, 97, 97, 101, 101, 101, 101, 105, 105, 105, 105, 109, 109, 109, 109,
- 113, 113, 113, 113, 117, 117, 117, 117, 121, 121, 121, 121, 125, 125, 125, 125,
- 130, 130, 130, 130, 134, 134, 134, 134, 138, 138, 138, 138, 142, 142, 142, 142,
- 146, 146, 146, 146, 150, 150, 150, 150, 154, 154, 154, 154, 158, 158, 158, 158,
- 162, 162, 162, 162, 166, 166, 166, 166, 170, 170, 170, 170, 174, 174, 174, 174,
- 178, 178, 178, 178, 182, 182, 182, 182, 186, 186, 186, 186, 190, 190, 190, 190,
- 190, 195, 195, 195, 195, 199, 199, 199, 199, 203, 203, 203, 203, 207, 207, 207,
- 207, 211, 211, 211, 211, 215, 215, 215, 215, 219, 219, 219, 219, 223, 223, 223,
- 223, 227, 227, 227, 227, 231, 231, 231, 231, 235, 235, 235, 235, 239, 239, 239,
- 239, 243, 243, 243, 243, 247, 247, 247, 247, 251, 251, 251, 251, 255, 255, 255
- },
- {
- 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9, 9, 13, 13, 13, 16,
- 16, 16, 19, 19, 19, 22, 22, 22, 25, 25, 25, 25, 29, 29, 29, 32,
- 32, 32, 35, 35, 35, 38, 38, 38, 38, 42, 42, 42, 45, 45, 45, 48,
- 48, 48, 51, 51, 51, 54, 54, 54, 54, 58, 58, 58, 61, 61, 61, 64,
- 64, 64, 67, 67, 67, 67, 71, 71, 71, 74, 74, 74, 77, 77, 77, 80,
- 80, 80, 83, 83, 83, 83, 87, 87, 87, 90, 90, 90, 93, 93, 93, 96,
- 96, 96, 96, 100, 100, 100, 103, 103, 103, 106, 106, 106, 109, 109, 109, 112,
- 112, 112, 112, 116, 116, 116, 119, 119, 119, 122, 122, 122, 125, 125, 125, 125,
- 130, 130, 130, 130, 133, 133, 133, 136, 136, 136, 139, 139, 139, 143, 143, 143,
- 143, 146, 146, 146, 149, 149, 149, 152, 152, 152, 155, 155, 155, 159, 159, 159,
- 159, 162, 162, 162, 165, 165, 165, 168, 168, 168, 172, 172, 172, 172, 175, 175,
- 175, 178, 178, 178, 181, 181, 181, 184, 184, 184, 188, 188, 188, 188, 191, 191,
- 191, 194, 194, 194, 197, 197, 197, 201, 201, 201, 201, 204, 204, 204, 207, 207,
- 207, 210, 210, 210, 213, 213, 213, 217, 217, 217, 217, 220, 220, 220, 223, 223,
- 223, 226, 226, 226, 230, 230, 230, 230, 233, 233, 233, 236, 236, 236, 239, 239,
- 239, 242, 242, 242, 246, 246, 246, 246, 249, 249, 249, 252, 252, 252, 255, 255
- },
- {
- 0, 0, 2, 2, 5, 5, 5, 8, 8, 8, 10, 10, 13, 13, 13, 16,
- 16, 16, 18, 18, 21, 21, 21, 24, 24, 24, 26, 26, 29, 29, 29, 32,
- 32, 32, 35, 35, 35, 37, 37, 40, 40, 40, 43, 43, 43, 45, 45, 48,
- 48, 48, 51, 51, 51, 53, 53, 56, 56, 56, 59, 59, 59, 61, 61, 64,
- 64, 64, 67, 67, 67, 70, 70, 70, 72, 72, 75, 75, 75, 78, 78, 78,
- 80, 80, 83, 83, 83, 86, 86, 86, 88, 88, 91, 91, 91, 94, 94, 94,
- 96, 96, 99, 99, 99, 102, 102, 102, 104, 104, 107, 107, 107, 110, 110, 110,
- 112, 112, 115, 115, 115, 118, 118, 118, 120, 120, 123, 123, 123, 126, 126, 126,
- 129, 129, 129, 132, 132, 132, 135, 135, 137, 137, 137, 140, 140, 140, 143, 143,
- 145, 145, 145, 148, 148, 148, 151, 151, 153, 153, 153, 156, 156, 156, 159, 159,
- 161, 161, 161, 164, 164, 164, 167, 167, 169, 169, 169, 172, 172, 172, 175, 175,
- 177, 177, 177, 180, 180, 180, 183, 183, 185, 185, 185, 188, 188, 188, 191, 191,
- 191, 194, 194, 196, 196, 196, 199, 199, 199, 202, 202, 204, 204, 204, 207, 207,
- 207, 210, 210, 212, 212, 212, 215, 215, 215, 218, 218, 220, 220, 220, 223, 223,
- 223, 226, 226, 226, 229, 229, 231, 231, 231, 234, 234, 234, 237, 237, 239, 239,
- 239, 242, 242, 242, 245, 245, 247, 247, 247, 250, 250, 250, 253, 253, 255, 255
- },
- {
- 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14,
- 16, 16, 18, 18, 20, 20, 22, 22, 24, 24, 26, 26, 28, 28, 30, 30,
- 32, 32, 34, 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, 44, 46, 46,
- 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 58, 58, 60, 60, 62, 62,
- 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 74, 74, 76, 76, 78, 78,
- 80, 80, 82, 82, 84, 84, 86, 86, 88, 88, 90, 90, 92, 92, 94, 94,
- 96, 96, 98, 98, 100, 100, 102, 102, 104, 104, 106, 106, 108, 108, 110, 110,
- 112, 112, 114, 114, 116, 116, 118, 118, 120, 120, 122, 122, 124, 124, 126, 126,
- 129, 129, 131, 131, 133, 133, 135, 135, 137, 137, 139, 139, 141, 141, 143, 143,
- 145, 145, 147, 147, 149, 149, 151, 151, 153, 153, 155, 155, 157, 157, 159, 159,
- 161, 161, 163, 163, 165, 165, 167, 167, 169, 169, 171, 171, 173, 173, 175, 175,
- 177, 177, 179, 179, 181, 181, 183, 183, 185, 185, 187, 187, 189, 189, 191, 191,
- 193, 193, 195, 195, 197, 197, 199, 199, 201, 201, 203, 203, 205, 205, 207, 207,
- 209, 209, 211, 211, 213, 213, 215, 215, 217, 217, 219, 219, 221, 221, 223, 223,
- 225, 225, 227, 227, 229, 229, 231, 231, 233, 233, 235, 235, 237, 237, 239, 239,
- 241, 241, 243, 243, 245, 245, 247, 247, 249, 249, 251, 251, 253, 253, 255, 255
- },
- {
- 0, 1, 1, 3, 4, 4, 6, 6, 8, 9, 9, 11, 12, 12, 14, 14,
- 16, 17, 17, 19, 20, 20, 22, 22, 24, 25, 25, 27, 28, 28, 30, 30,
- 32, 33, 33, 35, 36, 36, 38, 38, 40, 41, 41, 43, 44, 44, 46, 46,
- 48, 49, 49, 51, 52, 52, 54, 54, 56, 57, 57, 59, 60, 60, 62, 62,
- 64, 65, 65, 67, 68, 68, 70, 70, 72, 73, 73, 75, 76, 76, 78, 78,
- 80, 81, 81, 83, 84, 84, 86, 86, 88, 89, 89, 91, 92, 92, 94, 94,
- 96, 97, 97, 99, 100, 100, 102, 102, 104, 105, 105, 107, 108, 108, 110, 110,
- 112, 113, 113, 115, 116, 116, 118, 118, 120, 121, 121, 123, 124, 124, 126, 126,
- 129, 129, 131, 131, 132, 134, 134, 135, 137, 137, 139, 139, 140, 142, 142, 143,
- 145, 145, 147, 147, 148, 150, 150, 151, 153, 153, 155, 155, 156, 158, 158, 159,
- 161, 161, 163, 163, 164, 166, 166, 167, 169, 169, 171, 171, 172, 174, 174, 175,
- 177, 177, 179, 179, 180, 182, 182, 183, 185, 185, 187, 187, 188, 190, 190, 191,
- 193, 193, 195, 195, 196, 198, 198, 199, 201, 201, 203, 203, 204, 206, 206, 207,
- 209, 209, 211, 211, 212, 214, 214, 215, 217, 217, 219, 219, 220, 222, 222, 223,
- 225, 225, 227, 227, 228, 230, 230, 231, 233, 233, 235, 235, 236, 238, 238, 239,
- 241, 241, 243, 243, 244, 246, 246, 247, 249, 249, 251, 251, 252, 254, 254, 255
- },
- {
- 0, 1, 2, 2, 4, 5, 6, 6, 8, 9, 10, 10, 12, 13, 14, 14,
- 16, 17, 18, 18, 20, 21, 22, 22, 24, 25, 26, 26, 28, 29, 30, 30,
- 32, 33, 34, 34, 36, 37, 38, 38, 40, 41, 42, 42, 44, 45, 46, 46,
- 48, 49, 50, 50, 52, 53, 54, 54, 56, 57, 58, 58, 60, 61, 62, 62,
- 64, 65, 66, 66, 68, 69, 70, 70, 72, 73, 74, 74, 76, 77, 78, 78,
- 80, 81, 82, 82, 84, 85, 86, 86, 88, 89, 90, 90, 92, 93, 94, 94,
- 96, 97, 98, 98, 100, 101, 102, 102, 104, 105, 106, 106, 108, 109, 110, 110,
- 112, 113, 114, 114, 116, 117, 118, 118, 120, 121, 122, 122, 124, 125, 126, 126,
- 129, 129, 130, 131, 133, 133, 134, 135, 137, 137, 138, 139, 141, 141, 142, 143,
- 145, 145, 146, 147, 149, 149, 150, 151, 153, 153, 154, 155, 157, 157, 158, 159,
- 161, 161, 162, 163, 165, 165, 166, 167, 169, 169, 170, 171, 173, 173, 174, 175,
- 177, 177, 178, 179, 181, 181, 182, 183, 185, 185, 186, 187, 189, 189, 190, 191,
- 193, 193, 194, 195, 197, 197, 198, 199, 201, 201, 202, 203, 205, 205, 206, 207,
- 209, 209, 210, 211, 213, 213, 214, 215, 217, 217, 218, 219, 221, 221, 222, 223,
- 225, 225, 226, 227, 229, 229, 230, 231, 233, 233, 234, 235, 237, 237, 238, 239,
- 241, 241, 242, 243, 245, 245, 246, 247, 249, 249, 250, 251, 253, 253, 254, 255
- },
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
- 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
- 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
- 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
- 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
- 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
- 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
- }
+// Not scrambled, starts from QUANT_6
+const uint8_t color_unquant_to_uquant_tables[17][512] {
+ { // QUANT_6
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_8
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
+ 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
+ 146, 146, 146, 146, 146, 146, 146, 146, 146, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182,
+ 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182,
+ 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
+ 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
+ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142,
+ 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_12
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
+ 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
+ 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
+ 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163,
+ 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_16
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 119, 119,
+ 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238,
+ 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_20
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 107, 107, 107, 107, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+ 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 148, 148, 148, 148, 148,
+ 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161,
+ 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
+ 201, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 228, 228, 228, 228,
+ 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 242, 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_24
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+ 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
+ 156, 156, 156, 156, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 211, 211, 211, 211,
+ 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 222, 222, 222, 222, 222, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 244, 244,
+ 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_32
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 115,
+ 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
+ 156, 156, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 222, 222, 222, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_40
+ 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158,
+ 158, 158, 158, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 190, 190, 190, 190, 190, 190, 190, 190, 190,
+ 190, 190, 190, 190, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 210, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 223, 223, 223, 223, 223, 223, 223, 223,
+ 223, 223, 223, 223, 223, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 255, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_48
+ 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 113, 113,
+ 113, 113, 113, 113, 113, 113, 113, 113, 113, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+ 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 142, 142, 142, 142, 142, 142, 142, 142, 142,
+ 142, 142, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 158, 158, 158, 158, 158, 158, 158, 158, 158,
+ 158, 158, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 190, 190, 190, 190, 190, 190, 190, 190,
+ 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 223, 223, 223, 223, 223, 223, 223,
+ 223, 223, 223, 223, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 255, 255, 255, 255, 255, 255
+ },
+ { // QUANT_64
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 16, 16, 16,
+ 16, 16, 16, 16, 16, 20, 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 28, 28, 28, 28, 28, 28, 28, 28, 32, 32, 32,
+ 32, 32, 32, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 48, 48, 48,
+ 48, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 56, 56, 56, 56, 56, 56, 56, 56, 60, 60, 60, 60, 60, 60, 60, 60, 60, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 69, 69, 69, 69, 69, 69, 69, 69, 73, 73, 73, 73, 73, 73, 73, 73, 77, 77, 77, 77, 77, 77, 77, 77, 81,
+ 81, 81, 81, 81, 81, 81, 81, 85, 85, 85, 85, 85, 85, 85, 85, 89, 89, 89, 89, 89, 89, 89, 89, 93, 93, 93, 93, 93, 93, 93, 93, 97,
+ 97, 97, 97, 97, 97, 97, 97, 101, 101, 101, 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 109, 109, 109, 109, 109, 109, 109, 109, 113,
+ 113, 113, 113, 113, 113, 113, 113, 117, 117, 117, 117, 117, 117, 117, 117, 121, 121, 121, 121, 121, 121, 121, 121, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 130, 130, 130, 130, 130, 130, 130, 130, 130, 134, 134, 134, 134, 134, 134, 134, 134, 138, 138, 138, 138, 138, 138, 138, 138, 142, 142, 142, 142, 142, 142, 142,
+ 142, 146, 146, 146, 146, 146, 146, 146, 146, 150, 150, 150, 150, 150, 150, 150, 150, 154, 154, 154, 154, 154, 154, 154, 154, 158, 158, 158, 158, 158, 158, 158,
+ 158, 162, 162, 162, 162, 162, 162, 162, 162, 166, 166, 166, 166, 166, 166, 166, 166, 170, 170, 170, 170, 170, 170, 170, 170, 174, 174, 174, 174, 174, 174, 174,
+ 174, 178, 178, 178, 178, 178, 178, 178, 178, 182, 182, 182, 182, 182, 182, 182, 182, 186, 186, 186, 186, 186, 186, 186, 186, 190, 190, 190, 190, 190, 190, 190,
+ 190, 190, 195, 195, 195, 195, 195, 195, 195, 195, 195, 199, 199, 199, 199, 199, 199, 199, 199, 203, 203, 203, 203, 203, 203, 203, 203, 207, 207, 207, 207, 207,
+ 207, 207, 207, 211, 211, 211, 211, 211, 211, 211, 211, 215, 215, 215, 215, 215, 215, 215, 215, 219, 219, 219, 219, 219, 219, 219, 219, 223, 223, 223, 223, 223,
+ 223, 223, 223, 227, 227, 227, 227, 227, 227, 227, 227, 231, 231, 231, 231, 231, 231, 231, 231, 235, 235, 235, 235, 235, 235, 235, 235, 239, 239, 239, 239, 239,
+ 239, 239, 239, 243, 243, 243, 243, 243, 243, 243, 243, 247, 247, 247, 247, 247, 247, 247, 247, 251, 251, 251, 251, 251, 251, 251, 251, 255, 255, 255, 255, 255
+ },
+ { // QUANT_80
+ 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 9, 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 16, 16,
+ 16, 16, 16, 16, 19, 19, 19, 19, 19, 19, 22, 22, 22, 22, 22, 22, 25, 25, 25, 25, 25, 25, 25, 29, 29, 29, 29, 29, 29, 29, 32, 32,
+ 32, 32, 32, 32, 35, 35, 35, 35, 35, 35, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, 42, 42, 45, 45, 45, 45, 45, 45, 48, 48,
+ 48, 48, 48, 48, 51, 51, 51, 51, 51, 51, 54, 54, 54, 54, 54, 54, 54, 58, 58, 58, 58, 58, 58, 58, 61, 61, 61, 61, 61, 61, 64, 64,
+ 64, 64, 64, 64, 67, 67, 67, 67, 67, 67, 67, 71, 71, 71, 71, 71, 71, 71, 74, 74, 74, 74, 74, 74, 77, 77, 77, 77, 77, 77, 80, 80,
+ 80, 80, 80, 80, 83, 83, 83, 83, 83, 83, 83, 87, 87, 87, 87, 87, 87, 87, 90, 90, 90, 90, 90, 90, 93, 93, 93, 93, 93, 93, 96, 96,
+ 96, 96, 96, 96, 96, 100, 100, 100, 100, 100, 100, 100, 103, 103, 103, 103, 103, 103, 106, 106, 106, 106, 106, 106, 109, 109, 109, 109, 109, 109, 112, 112,
+ 112, 112, 112, 112, 112, 116, 116, 116, 116, 116, 116, 116, 119, 119, 119, 119, 119, 119, 122, 122, 122, 122, 122, 122, 125, 125, 125, 125, 125, 125, 125, 125,
+ 130, 130, 130, 130, 130, 130, 130, 130, 133, 133, 133, 133, 133, 133, 136, 136, 136, 136, 136, 136, 139, 139, 139, 139, 139, 139, 139, 143, 143, 143, 143, 143,
+ 143, 143, 146, 146, 146, 146, 146, 146, 149, 149, 149, 149, 149, 149, 152, 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 159, 159, 159, 159, 159,
+ 159, 159, 162, 162, 162, 162, 162, 162, 165, 165, 165, 165, 165, 165, 168, 168, 168, 168, 168, 168, 168, 172, 172, 172, 172, 172, 172, 172, 175, 175, 175, 175,
+ 175, 175, 178, 178, 178, 178, 178, 178, 181, 181, 181, 181, 181, 181, 184, 184, 184, 184, 184, 184, 184, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191,
+ 191, 191, 194, 194, 194, 194, 194, 194, 197, 197, 197, 197, 197, 197, 197, 201, 201, 201, 201, 201, 201, 201, 204, 204, 204, 204, 204, 204, 207, 207, 207, 207,
+ 207, 207, 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 217, 217, 217, 217, 217, 217, 217, 220, 220, 220, 220, 220, 220, 223, 223, 223, 223,
+ 223, 223, 226, 226, 226, 226, 226, 226, 226, 230, 230, 230, 230, 230, 230, 230, 233, 233, 233, 233, 233, 233, 236, 236, 236, 236, 236, 236, 239, 239, 239, 239,
+ 239, 239, 242, 242, 242, 242, 242, 242, 242, 246, 246, 246, 246, 246, 246, 246, 249, 249, 249, 249, 249, 249, 252, 252, 252, 252, 252, 252, 255, 255, 255, 255
+ },
+ { // QUANT_96
+ 0, 0, 0, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 16, 16,
+ 16, 16, 16, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 24, 24, 24, 24, 24, 26, 26, 26, 26, 26, 29, 29, 29, 29, 29, 29, 32, 32,
+ 32, 32, 32, 32, 35, 35, 35, 35, 35, 37, 37, 37, 37, 37, 40, 40, 40, 40, 40, 40, 43, 43, 43, 43, 43, 45, 45, 45, 45, 45, 48, 48,
+ 48, 48, 48, 48, 51, 51, 51, 51, 51, 53, 53, 53, 53, 53, 56, 56, 56, 56, 56, 56, 59, 59, 59, 59, 59, 61, 61, 61, 61, 61, 64, 64,
+ 64, 64, 64, 64, 67, 67, 67, 67, 67, 67, 70, 70, 70, 70, 70, 72, 72, 72, 72, 72, 75, 75, 75, 75, 75, 75, 78, 78, 78, 78, 78, 80,
+ 80, 80, 80, 80, 83, 83, 83, 83, 83, 83, 86, 86, 86, 86, 86, 88, 88, 88, 88, 88, 91, 91, 91, 91, 91, 91, 94, 94, 94, 94, 94, 96,
+ 96, 96, 96, 96, 99, 99, 99, 99, 99, 99, 102, 102, 102, 102, 102, 104, 104, 104, 104, 104, 107, 107, 107, 107, 107, 107, 110, 110, 110, 110, 110, 112,
+ 112, 112, 112, 112, 115, 115, 115, 115, 115, 115, 118, 118, 118, 118, 118, 120, 120, 120, 120, 120, 123, 123, 123, 123, 123, 123, 126, 126, 126, 126, 126, 126,
+ 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, 132, 132, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 140, 140, 140, 140, 140, 140, 143, 143, 143, 143,
+ 143, 145, 145, 145, 145, 145, 148, 148, 148, 148, 148, 148, 151, 151, 151, 151, 151, 153, 153, 153, 153, 153, 156, 156, 156, 156, 156, 156, 159, 159, 159, 159,
+ 159, 161, 161, 161, 161, 161, 164, 164, 164, 164, 164, 164, 167, 167, 167, 167, 167, 169, 169, 169, 169, 169, 172, 172, 172, 172, 172, 172, 175, 175, 175, 175,
+ 175, 177, 177, 177, 177, 177, 180, 180, 180, 180, 180, 180, 183, 183, 183, 183, 183, 185, 185, 185, 185, 185, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191,
+ 191, 191, 194, 194, 194, 194, 194, 196, 196, 196, 196, 196, 199, 199, 199, 199, 199, 199, 202, 202, 202, 202, 202, 204, 204, 204, 204, 204, 207, 207, 207, 207,
+ 207, 207, 210, 210, 210, 210, 210, 212, 212, 212, 212, 212, 215, 215, 215, 215, 215, 215, 218, 218, 218, 218, 218, 220, 220, 220, 220, 220, 223, 223, 223, 223,
+ 223, 223, 226, 226, 226, 226, 226, 226, 229, 229, 229, 229, 229, 231, 231, 231, 231, 231, 234, 234, 234, 234, 234, 234, 237, 237, 237, 237, 237, 239, 239, 239,
+ 239, 239, 242, 242, 242, 242, 242, 242, 245, 245, 245, 245, 245, 247, 247, 247, 247, 247, 250, 250, 250, 250, 250, 250, 253, 253, 253, 253, 253, 255, 255, 255
+ },
+ { // QUANT_128
+ 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, 10, 10, 10, 10, 12, 12, 12, 12, 14, 14, 14, 14, 16,
+ 16, 16, 16, 18, 18, 18, 18, 20, 20, 20, 20, 22, 22, 22, 22, 24, 24, 24, 24, 26, 26, 26, 26, 28, 28, 28, 28, 30, 30, 30, 30, 32,
+ 32, 32, 32, 34, 34, 34, 34, 36, 36, 36, 36, 38, 38, 38, 38, 40, 40, 40, 40, 42, 42, 42, 42, 44, 44, 44, 44, 46, 46, 46, 46, 48,
+ 48, 48, 48, 50, 50, 50, 50, 52, 52, 52, 52, 54, 54, 54, 54, 56, 56, 56, 56, 58, 58, 58, 58, 60, 60, 60, 60, 62, 62, 62, 62, 64,
+ 64, 64, 64, 66, 66, 66, 66, 68, 68, 68, 68, 70, 70, 70, 70, 72, 72, 72, 72, 74, 74, 74, 74, 76, 76, 76, 76, 78, 78, 78, 78, 80,
+ 80, 80, 80, 82, 82, 82, 82, 84, 84, 84, 84, 86, 86, 86, 86, 88, 88, 88, 88, 90, 90, 90, 90, 92, 92, 92, 92, 94, 94, 94, 94, 96,
+ 96, 96, 96, 98, 98, 98, 98, 100, 100, 100, 100, 102, 102, 102, 102, 104, 104, 104, 104, 106, 106, 106, 106, 108, 108, 108, 108, 110, 110, 110, 110, 112,
+ 112, 112, 112, 114, 114, 114, 114, 116, 116, 116, 116, 118, 118, 118, 118, 120, 120, 120, 120, 122, 122, 122, 122, 124, 124, 124, 124, 126, 126, 126, 126, 126,
+ 129, 129, 129, 129, 129, 131, 131, 131, 131, 133, 133, 133, 133, 135, 135, 135, 135, 137, 137, 137, 137, 139, 139, 139, 139, 141, 141, 141, 141, 143, 143, 143,
+ 143, 145, 145, 145, 145, 147, 147, 147, 147, 149, 149, 149, 149, 151, 151, 151, 151, 153, 153, 153, 153, 155, 155, 155, 155, 157, 157, 157, 157, 159, 159, 159,
+ 159, 161, 161, 161, 161, 163, 163, 163, 163, 165, 165, 165, 165, 167, 167, 167, 167, 169, 169, 169, 169, 171, 171, 171, 171, 173, 173, 173, 173, 175, 175, 175,
+ 175, 177, 177, 177, 177, 179, 179, 179, 179, 181, 181, 181, 181, 183, 183, 183, 183, 185, 185, 185, 185, 187, 187, 187, 187, 189, 189, 189, 189, 191, 191, 191,
+ 191, 193, 193, 193, 193, 195, 195, 195, 195, 197, 197, 197, 197, 199, 199, 199, 199, 201, 201, 201, 201, 203, 203, 203, 203, 205, 205, 205, 205, 207, 207, 207,
+ 207, 209, 209, 209, 209, 211, 211, 211, 211, 213, 213, 213, 213, 215, 215, 215, 215, 217, 217, 217, 217, 219, 219, 219, 219, 221, 221, 221, 221, 223, 223, 223,
+ 223, 225, 225, 225, 225, 227, 227, 227, 227, 229, 229, 229, 229, 231, 231, 231, 231, 233, 233, 233, 233, 235, 235, 235, 235, 237, 237, 237, 237, 239, 239, 239,
+ 239, 241, 241, 241, 241, 243, 243, 243, 243, 245, 245, 245, 245, 247, 247, 247, 247, 249, 249, 249, 249, 251, 251, 251, 251, 253, 253, 253, 253, 255, 255, 255
+ },
+ { // QUANT_160
+ 0, 0, 1, 1, 1, 3, 3, 3, 4, 4, 4, 6, 6, 6, 6, 8, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 12, 14, 14, 14, 14, 16,
+ 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 20, 22, 22, 22, 22, 24, 24, 24, 25, 25, 25, 27, 27, 27, 28, 28, 28, 30, 30, 30, 30, 32,
+ 32, 32, 33, 33, 33, 35, 35, 35, 36, 36, 36, 38, 38, 38, 38, 40, 40, 40, 41, 41, 41, 43, 43, 43, 44, 44, 44, 46, 46, 46, 46, 48,
+ 48, 48, 49, 49, 49, 51, 51, 51, 52, 52, 52, 54, 54, 54, 54, 56, 56, 56, 57, 57, 57, 59, 59, 59, 60, 60, 60, 62, 62, 62, 62, 64,
+ 64, 64, 65, 65, 65, 67, 67, 67, 68, 68, 68, 70, 70, 70, 70, 72, 72, 72, 73, 73, 73, 75, 75, 75, 76, 76, 76, 78, 78, 78, 78, 80,
+ 80, 80, 81, 81, 81, 83, 83, 83, 84, 84, 84, 86, 86, 86, 86, 88, 88, 88, 89, 89, 89, 91, 91, 91, 92, 92, 92, 94, 94, 94, 94, 96,
+ 96, 96, 97, 97, 97, 99, 99, 99, 100, 100, 100, 102, 102, 102, 102, 104, 104, 104, 105, 105, 105, 107, 107, 107, 108, 108, 108, 110, 110, 110, 110, 112,
+ 112, 112, 113, 113, 113, 115, 115, 115, 116, 116, 116, 118, 118, 118, 118, 120, 120, 120, 121, 121, 121, 123, 123, 123, 124, 124, 124, 126, 126, 126, 126, 126,
+ 129, 129, 129, 129, 129, 131, 131, 131, 132, 132, 132, 134, 134, 134, 135, 135, 135, 137, 137, 137, 137, 139, 139, 139, 140, 140, 140, 142, 142, 142, 143, 143,
+ 143, 145, 145, 145, 145, 147, 147, 147, 148, 148, 148, 150, 150, 150, 151, 151, 151, 153, 153, 153, 153, 155, 155, 155, 156, 156, 156, 158, 158, 158, 159, 159,
+ 159, 161, 161, 161, 161, 163, 163, 163, 164, 164, 164, 166, 166, 166, 167, 167, 167, 169, 169, 169, 169, 171, 171, 171, 172, 172, 172, 174, 174, 174, 175, 175,
+ 175, 177, 177, 177, 177, 179, 179, 179, 180, 180, 180, 182, 182, 182, 183, 183, 183, 185, 185, 185, 185, 187, 187, 187, 188, 188, 188, 190, 190, 190, 191, 191,
+ 191, 193, 193, 193, 193, 195, 195, 195, 196, 196, 196, 198, 198, 198, 199, 199, 199, 201, 201, 201, 201, 203, 203, 203, 204, 204, 204, 206, 206, 206, 207, 207,
+ 207, 209, 209, 209, 209, 211, 211, 211, 212, 212, 212, 214, 214, 214, 215, 215, 215, 217, 217, 217, 217, 219, 219, 219, 220, 220, 220, 222, 222, 222, 223, 223,
+ 223, 225, 225, 225, 225, 227, 227, 227, 228, 228, 228, 230, 230, 230, 231, 231, 231, 233, 233, 233, 233, 235, 235, 235, 236, 236, 236, 238, 238, 238, 239, 239,
+ 239, 241, 241, 241, 241, 243, 243, 243, 244, 244, 244, 246, 246, 246, 247, 247, 247, 249, 249, 249, 249, 251, 251, 251, 252, 252, 252, 254, 254, 254, 255, 255
+ },
+ { // QUANT_192
+ 0, 0, 1, 1, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6, 6, 8, 8, 8, 9, 9, 10, 10, 10, 12, 12, 12, 13, 13, 14, 14, 14, 16,
+ 16, 16, 17, 17, 18, 18, 18, 20, 20, 20, 21, 21, 22, 22, 22, 24, 24, 24, 25, 25, 26, 26, 26, 28, 28, 28, 29, 29, 30, 30, 30, 32,
+ 32, 32, 33, 33, 34, 34, 34, 36, 36, 36, 37, 37, 38, 38, 38, 40, 40, 40, 41, 41, 42, 42, 42, 44, 44, 44, 45, 45, 46, 46, 46, 48,
+ 48, 48, 49, 49, 50, 50, 50, 52, 52, 52, 53, 53, 54, 54, 54, 56, 56, 56, 57, 57, 58, 58, 58, 60, 60, 60, 61, 61, 62, 62, 62, 64,
+ 64, 64, 65, 65, 66, 66, 66, 68, 68, 68, 69, 69, 70, 70, 70, 72, 72, 72, 73, 73, 74, 74, 74, 76, 76, 76, 77, 77, 78, 78, 78, 80,
+ 80, 80, 81, 81, 82, 82, 82, 84, 84, 84, 85, 85, 86, 86, 86, 88, 88, 88, 89, 89, 90, 90, 90, 92, 92, 92, 93, 93, 94, 94, 94, 96,
+ 96, 96, 97, 97, 98, 98, 98, 100, 100, 100, 101, 101, 102, 102, 102, 104, 104, 104, 105, 105, 106, 106, 106, 108, 108, 108, 109, 109, 110, 110, 110, 112,
+ 112, 112, 113, 113, 114, 114, 114, 116, 116, 116, 117, 117, 118, 118, 118, 120, 120, 120, 121, 121, 122, 122, 122, 124, 124, 124, 125, 125, 126, 126, 126, 126,
+ 129, 129, 129, 129, 130, 130, 131, 131, 131, 133, 133, 133, 134, 134, 135, 135, 135, 137, 137, 137, 138, 138, 139, 139, 139, 141, 141, 141, 142, 142, 143, 143,
+ 143, 145, 145, 145, 146, 146, 147, 147, 147, 149, 149, 149, 150, 150, 151, 151, 151, 153, 153, 153, 154, 154, 155, 155, 155, 157, 157, 157, 158, 158, 159, 159,
+ 159, 161, 161, 161, 162, 162, 163, 163, 163, 165, 165, 165, 166, 166, 167, 167, 167, 169, 169, 169, 170, 170, 171, 171, 171, 173, 173, 173, 174, 174, 175, 175,
+ 175, 177, 177, 177, 178, 178, 179, 179, 179, 181, 181, 181, 182, 182, 183, 183, 183, 185, 185, 185, 186, 186, 187, 187, 187, 189, 189, 189, 190, 190, 191, 191,
+ 191, 193, 193, 193, 194, 194, 195, 195, 195, 197, 197, 197, 198, 198, 199, 199, 199, 201, 201, 201, 202, 202, 203, 203, 203, 205, 205, 205, 206, 206, 207, 207,
+ 207, 209, 209, 209, 210, 210, 211, 211, 211, 213, 213, 213, 214, 214, 215, 215, 215, 217, 217, 217, 218, 218, 219, 219, 219, 221, 221, 221, 222, 222, 223, 223,
+ 223, 225, 225, 225, 226, 226, 227, 227, 227, 229, 229, 229, 230, 230, 231, 231, 231, 233, 233, 233, 234, 234, 235, 235, 235, 237, 237, 237, 238, 238, 239, 239,
+ 239, 241, 241, 241, 242, 242, 243, 243, 243, 245, 245, 245, 246, 246, 247, 247, 247, 249, 249, 249, 250, 250, 251, 251, 251, 253, 253, 253, 254, 254, 255, 255
+ },
+ { // QUANT_256
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
+ 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47,
+ 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63,
+ 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79,
+ 80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95,
+ 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111,
+ 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127,
+ 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 142, 143, 143,
+ 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150, 150, 151, 151, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159,
+ 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174, 174, 175, 175,
+ 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, 191,
+ 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207,
+ 208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223,
+ 224, 224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239,
+ 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255
+ },
};
// Starts from QUANT_6
// Scrambled
const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
- {
+ { // QUANT_6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -355,13 +354,13 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
},
- {
+ { // QUANT_8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -373,35 +372,35 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
},
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ { // QUANT_10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
},
- {
+ { // QUANT_12
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10,
+ 6, 6, 6, 6, 6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
@@ -409,7 +408,7 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
},
- {
+ { // QUANT_16
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
@@ -427,25 +426,25 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15
},
- {
+ { // QUANT_20
0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10,
+ 6, 6, 6, 6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 15, 15, 15,
+ 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 7, 7, 7,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5,
+ 17, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1
},
- {
+ { // QUANT_24
0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10,
@@ -463,15 +462,15 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
3, 3, 3, 3, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1, 1
},
- {
- 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
- 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10,
- 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12,
- 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13,
- 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+ { // QUANT_32
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6,
+ 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
+ 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10,
+ 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
+ 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15,
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
19, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21,
@@ -481,51 +480,51 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29,
29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31
},
- {
- 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16,
- 16, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 2, 2,
- 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18,
- 18, 26, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 4, 4,
- 4, 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 20, 20, 20, 20, 20,
- 20, 20, 28, 28, 28, 28, 28, 28, 36, 36, 36, 36, 36, 36, 36, 6,
- 6, 6, 6, 6, 6, 14, 14, 14, 14, 14, 14, 14, 22, 22, 22, 22,
- 22, 22, 30, 30, 30, 30, 30, 30, 30, 38, 38, 38, 38, 38, 38, 38,
- 39, 39, 39, 39, 39, 39, 39, 31, 31, 31, 31, 31, 31, 31, 23, 23,
- 23, 23, 23, 23, 15, 15, 15, 15, 15, 15, 15, 7, 7, 7, 7, 7,
- 7, 37, 37, 37, 37, 37, 37, 37, 29, 29, 29, 29, 29, 29, 21, 21,
- 21, 21, 21, 21, 21, 13, 13, 13, 13, 13, 13, 5, 5, 5, 5, 5,
- 5, 5, 35, 35, 35, 35, 35, 35, 27, 27, 27, 27, 27, 27, 27, 19,
- 19, 19, 19, 19, 19, 11, 11, 11, 11, 11, 11, 11, 3, 3, 3, 3,
- 3, 3, 33, 33, 33, 33, 33, 33, 33, 25, 25, 25, 25, 25, 25, 17,
- 17, 17, 17, 17, 17, 17, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1
- },
- {
- 0, 0, 0, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 2, 2,
- 2, 2, 2, 18, 18, 18, 18, 18, 18, 34, 34, 34, 34, 34, 4, 4,
- 4, 4, 4, 4, 20, 20, 20, 20, 20, 36, 36, 36, 36, 36, 6, 6,
- 6, 6, 6, 6, 22, 22, 22, 22, 22, 38, 38, 38, 38, 38, 38, 8,
- 8, 8, 8, 8, 24, 24, 24, 24, 24, 24, 40, 40, 40, 40, 40, 10,
- 10, 10, 10, 10, 26, 26, 26, 26, 26, 26, 42, 42, 42, 42, 42, 12,
- 12, 12, 12, 12, 12, 28, 28, 28, 28, 28, 44, 44, 44, 44, 44, 14,
- 14, 14, 14, 14, 14, 30, 30, 30, 30, 30, 46, 46, 46, 46, 46, 46,
- 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 15, 15, 15, 15, 15,
- 15, 45, 45, 45, 45, 45, 29, 29, 29, 29, 29, 13, 13, 13, 13, 13,
- 13, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 11, 11, 11, 11,
- 11, 41, 41, 41, 41, 41, 25, 25, 25, 25, 25, 25, 9, 9, 9, 9,
- 9, 39, 39, 39, 39, 39, 39, 23, 23, 23, 23, 23, 7, 7, 7, 7,
- 7, 7, 37, 37, 37, 37, 37, 21, 21, 21, 21, 21, 5, 5, 5, 5,
- 5, 5, 35, 35, 35, 35, 35, 19, 19, 19, 19, 19, 19, 3, 3, 3,
- 3, 3, 33, 33, 33, 33, 33, 17, 17, 17, 17, 17, 17, 1, 1, 1
- },
- {
- 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4,
- 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8,
- 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12,
- 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16,
- 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
- 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
- 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
- 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
+ { // QUANT_40
+ 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16,
+ 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 2, 2,
+ 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 18, 18, 18, 18, 18, 18,
+ 18, 26, 26, 26, 26, 26, 26, 34, 34, 34, 34, 34, 34, 34, 4, 4,
+ 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 20, 20,
+ 20, 28, 28, 28, 28, 28, 28, 28, 36, 36, 36, 36, 36, 36, 36, 6,
+ 6, 6, 6, 6, 6, 14, 14, 14, 14, 14, 14, 22, 22, 22, 22, 22,
+ 22, 22, 30, 30, 30, 30, 30, 30, 38, 38, 38, 38, 38, 38, 38, 38,
+ 39, 39, 39, 39, 39, 39, 39, 39, 31, 31, 31, 31, 31, 31, 23, 23,
+ 23, 23, 23, 23, 23, 15, 15, 15, 15, 15, 15, 7, 7, 7, 7, 7,
+ 7, 37, 37, 37, 37, 37, 37, 37, 29, 29, 29, 29, 29, 29, 29, 21,
+ 21, 21, 21, 21, 21, 13, 13, 13, 13, 13, 13, 13, 5, 5, 5, 5,
+ 5, 5, 35, 35, 35, 35, 35, 35, 35, 27, 27, 27, 27, 27, 27, 19,
+ 19, 19, 19, 19, 19, 19, 11, 11, 11, 11, 11, 11, 3, 3, 3, 3,
+ 3, 3, 33, 33, 33, 33, 33, 33, 33, 25, 25, 25, 25, 25, 25, 25,
+ 17, 17, 17, 17, 17, 17, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1
+ },
+ { // QUANT_48
+ 0, 0, 0, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 2, 2,
+ 2, 2, 2, 18, 18, 18, 18, 18, 34, 34, 34, 34, 34, 34, 4, 4,
+ 4, 4, 4, 20, 20, 20, 20, 20, 20, 36, 36, 36, 36, 36, 6, 6,
+ 6, 6, 6, 22, 22, 22, 22, 22, 22, 38, 38, 38, 38, 38, 38, 8,
+ 8, 8, 8, 8, 24, 24, 24, 24, 24, 40, 40, 40, 40, 40, 40, 10,
+ 10, 10, 10, 10, 26, 26, 26, 26, 26, 42, 42, 42, 42, 42, 42, 12,
+ 12, 12, 12, 12, 28, 28, 28, 28, 28, 28, 44, 44, 44, 44, 44, 14,
+ 14, 14, 14, 14, 30, 30, 30, 30, 30, 30, 46, 46, 46, 46, 46, 46,
+ 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 31, 15, 15, 15, 15,
+ 15, 45, 45, 45, 45, 45, 29, 29, 29, 29, 29, 29, 13, 13, 13, 13,
+ 13, 43, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 11, 11, 11, 11,
+ 11, 41, 41, 41, 41, 41, 41, 25, 25, 25, 25, 25, 9, 9, 9, 9,
+ 9, 39, 39, 39, 39, 39, 39, 23, 23, 23, 23, 23, 23, 7, 7, 7,
+ 7, 7, 37, 37, 37, 37, 37, 21, 21, 21, 21, 21, 21, 5, 5, 5,
+ 5, 5, 35, 35, 35, 35, 35, 35, 19, 19, 19, 19, 19, 3, 3, 3,
+ 3, 3, 33, 33, 33, 33, 33, 33, 17, 17, 17, 17, 17, 1, 1, 1
+ },
+ { // QUANT_64
+ 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+ 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8,
+ 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12,
+ 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16,
+ 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20,
+ 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24,
+ 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28,
+ 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 31,
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
@@ -535,29 +534,29 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59,
59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63
},
- {
- 0, 0, 16, 16, 16, 32, 32, 32, 48, 48, 48, 48, 64, 64, 64, 2,
- 2, 2, 18, 18, 18, 34, 34, 34, 50, 50, 50, 50, 66, 66, 66, 4,
- 4, 4, 20, 20, 20, 36, 36, 36, 36, 52, 52, 52, 68, 68, 68, 6,
- 6, 6, 22, 22, 22, 38, 38, 38, 38, 54, 54, 54, 70, 70, 70, 8,
- 8, 8, 24, 24, 24, 24, 40, 40, 40, 56, 56, 56, 72, 72, 72, 10,
- 10, 10, 26, 26, 26, 26, 42, 42, 42, 58, 58, 58, 74, 74, 74, 12,
- 12, 12, 12, 28, 28, 28, 44, 44, 44, 60, 60, 60, 76, 76, 76, 14,
- 14, 14, 14, 30, 30, 30, 46, 46, 46, 62, 62, 62, 78, 78, 78, 78,
- 79, 79, 79, 79, 63, 63, 63, 47, 47, 47, 31, 31, 31, 15, 15, 15,
- 15, 77, 77, 77, 61, 61, 61, 45, 45, 45, 29, 29, 29, 13, 13, 13,
- 13, 75, 75, 75, 59, 59, 59, 43, 43, 43, 27, 27, 27, 27, 11, 11,
- 11, 73, 73, 73, 57, 57, 57, 41, 41, 41, 25, 25, 25, 25, 9, 9,
- 9, 71, 71, 71, 55, 55, 55, 39, 39, 39, 39, 23, 23, 23, 7, 7,
- 7, 69, 69, 69, 53, 53, 53, 37, 37, 37, 37, 21, 21, 21, 5, 5,
- 5, 67, 67, 67, 51, 51, 51, 51, 35, 35, 35, 19, 19, 19, 3, 3,
- 3, 65, 65, 65, 49, 49, 49, 49, 33, 33, 33, 17, 17, 17, 1, 1
- },
- {
- 0, 0, 32, 32, 64, 64, 64, 2, 2, 2, 34, 34, 66, 66, 66, 4,
- 4, 4, 36, 36, 68, 68, 68, 6, 6, 6, 38, 38, 70, 70, 70, 8,
- 8, 8, 40, 40, 40, 72, 72, 10, 10, 10, 42, 42, 42, 74, 74, 12,
- 12, 12, 44, 44, 44, 76, 76, 14, 14, 14, 46, 46, 46, 78, 78, 16,
+ { // QUANT_80
+ 0, 0, 16, 16, 16, 32, 32, 32, 48, 48, 48, 64, 64, 64, 64, 2,
+ 2, 2, 18, 18, 18, 34, 34, 34, 50, 50, 50, 66, 66, 66, 66, 4,
+ 4, 4, 20, 20, 20, 36, 36, 36, 52, 52, 52, 52, 68, 68, 68, 6,
+ 6, 6, 22, 22, 22, 38, 38, 38, 54, 54, 54, 54, 70, 70, 70, 8,
+ 8, 8, 24, 24, 24, 40, 40, 40, 40, 56, 56, 56, 72, 72, 72, 10,
+ 10, 10, 26, 26, 26, 42, 42, 42, 42, 58, 58, 58, 74, 74, 74, 12,
+ 12, 12, 28, 28, 28, 28, 44, 44, 44, 60, 60, 60, 76, 76, 76, 14,
+ 14, 14, 30, 30, 30, 30, 46, 46, 46, 62, 62, 62, 78, 78, 78, 78,
+ 79, 79, 79, 79, 63, 63, 63, 47, 47, 47, 31, 31, 31, 31, 15, 15,
+ 15, 77, 77, 77, 61, 61, 61, 45, 45, 45, 29, 29, 29, 29, 13, 13,
+ 13, 75, 75, 75, 59, 59, 59, 43, 43, 43, 43, 27, 27, 27, 11, 11,
+ 11, 73, 73, 73, 57, 57, 57, 41, 41, 41, 41, 25, 25, 25, 9, 9,
+ 9, 71, 71, 71, 55, 55, 55, 55, 39, 39, 39, 23, 23, 23, 7, 7,
+ 7, 69, 69, 69, 53, 53, 53, 53, 37, 37, 37, 21, 21, 21, 5, 5,
+ 5, 67, 67, 67, 67, 51, 51, 51, 35, 35, 35, 19, 19, 19, 3, 3,
+ 3, 65, 65, 65, 65, 49, 49, 49, 33, 33, 33, 17, 17, 17, 1, 1
+ },
+ { // QUANT_96
+ 0, 32, 32, 32, 64, 64, 64, 2, 2, 34, 34, 34, 66, 66, 66, 4,
+ 4, 36, 36, 36, 68, 68, 68, 6, 6, 38, 38, 38, 70, 70, 70, 8,
+ 8, 8, 40, 40, 72, 72, 72, 10, 10, 10, 42, 42, 74, 74, 74, 12,
+ 12, 12, 44, 44, 76, 76, 76, 14, 14, 14, 46, 46, 78, 78, 78, 16,
16, 16, 48, 48, 48, 80, 80, 80, 18, 18, 50, 50, 50, 82, 82, 82,
20, 20, 52, 52, 52, 84, 84, 84, 22, 22, 54, 54, 54, 86, 86, 86,
24, 24, 56, 56, 56, 88, 88, 88, 26, 26, 58, 58, 58, 90, 90, 90,
@@ -566,20 +565,20 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
91, 91, 91, 59, 59, 59, 27, 27, 89, 89, 89, 57, 57, 57, 25, 25,
87, 87, 87, 55, 55, 55, 23, 23, 85, 85, 85, 53, 53, 53, 21, 21,
83, 83, 83, 51, 51, 51, 19, 19, 81, 81, 81, 49, 49, 49, 17, 17,
- 17, 79, 79, 47, 47, 47, 15, 15, 15, 77, 77, 45, 45, 45, 13, 13,
- 13, 75, 75, 43, 43, 43, 11, 11, 11, 73, 73, 41, 41, 41, 9, 9,
- 9, 71, 71, 71, 39, 39, 7, 7, 7, 69, 69, 69, 37, 37, 5, 5,
- 5, 67, 67, 67, 35, 35, 3, 3, 3, 65, 65, 65, 33, 33, 1, 1
- },
- {
- 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
- 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
- 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23,
- 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31,
- 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39,
- 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47,
- 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
- 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63,
+ 17, 79, 79, 79, 47, 47, 15, 15, 15, 77, 77, 77, 45, 45, 13, 13,
+ 13, 75, 75, 75, 43, 43, 11, 11, 11, 73, 73, 73, 41, 41, 9, 9,
+ 9, 71, 71, 71, 39, 39, 39, 7, 7, 69, 69, 69, 37, 37, 37, 5,
+ 5, 67, 67, 67, 35, 35, 35, 3, 3, 65, 65, 65, 33, 33, 33, 1
+ },
+ { // QUANT_128
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
+ 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24,
+ 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32,
+ 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40,
+ 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48,
+ 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56,
+ 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 63,
64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71,
72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79,
80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, 87,
@@ -589,25 +588,25 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119,
120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127
},
- {
- 0, 32, 32, 64, 96, 96, 128, 128, 2, 34, 34, 66, 98, 98, 130, 130,
- 4, 36, 36, 68, 100, 100, 132, 132, 6, 38, 38, 70, 102, 102, 134, 134,
- 8, 40, 40, 72, 104, 104, 136, 136, 10, 42, 42, 74, 106, 106, 138, 138,
- 12, 44, 44, 76, 108, 108, 140, 140, 14, 46, 46, 78, 110, 110, 142, 142,
- 16, 48, 48, 80, 112, 112, 144, 144, 18, 50, 50, 82, 114, 114, 146, 146,
- 20, 52, 52, 84, 116, 116, 148, 148, 22, 54, 54, 86, 118, 118, 150, 150,
- 24, 56, 56, 88, 120, 120, 152, 152, 26, 58, 58, 90, 122, 122, 154, 154,
- 28, 60, 60, 92, 124, 124, 156, 156, 30, 62, 62, 94, 126, 126, 158, 158,
- 159, 159, 127, 127, 95, 63, 63, 31, 157, 157, 125, 125, 93, 61, 61, 29,
- 155, 155, 123, 123, 91, 59, 59, 27, 153, 153, 121, 121, 89, 57, 57, 25,
- 151, 151, 119, 119, 87, 55, 55, 23, 149, 149, 117, 117, 85, 53, 53, 21,
- 147, 147, 115, 115, 83, 51, 51, 19, 145, 145, 113, 113, 81, 49, 49, 17,
- 143, 143, 111, 111, 79, 47, 47, 15, 141, 141, 109, 109, 77, 45, 45, 13,
- 139, 139, 107, 107, 75, 43, 43, 11, 137, 137, 105, 105, 73, 41, 41, 9,
- 135, 135, 103, 103, 71, 39, 39, 7, 133, 133, 101, 101, 69, 37, 37, 5,
- 131, 131, 99, 99, 67, 35, 35, 3, 129, 129, 97, 97, 65, 33, 33, 1
- },
- {
+ { // QUANT_160
+ 0, 32, 64, 64, 96, 128, 128, 128, 2, 34, 66, 66, 98, 130, 130, 130,
+ 4, 36, 68, 68, 100, 132, 132, 132, 6, 38, 70, 70, 102, 134, 134, 134,
+ 8, 40, 72, 72, 104, 136, 136, 136, 10, 42, 74, 74, 106, 138, 138, 138,
+ 12, 44, 76, 76, 108, 140, 140, 140, 14, 46, 78, 78, 110, 142, 142, 142,
+ 16, 48, 80, 80, 112, 144, 144, 144, 18, 50, 82, 82, 114, 146, 146, 146,
+ 20, 52, 84, 84, 116, 148, 148, 148, 22, 54, 86, 86, 118, 150, 150, 150,
+ 24, 56, 88, 88, 120, 152, 152, 152, 26, 58, 90, 90, 122, 154, 154, 154,
+ 28, 60, 92, 92, 124, 156, 156, 156, 30, 62, 94, 94, 126, 158, 158, 158,
+ 159, 159, 159, 127, 95, 95, 63, 31, 157, 157, 157, 125, 93, 93, 61, 29,
+ 155, 155, 155, 123, 91, 91, 59, 27, 153, 153, 153, 121, 89, 89, 57, 25,
+ 151, 151, 151, 119, 87, 87, 55, 23, 149, 149, 149, 117, 85, 85, 53, 21,
+ 147, 147, 147, 115, 83, 83, 51, 19, 145, 145, 145, 113, 81, 81, 49, 17,
+ 143, 143, 143, 111, 79, 79, 47, 15, 141, 141, 141, 109, 77, 77, 45, 13,
+ 139, 139, 139, 107, 75, 75, 43, 11, 137, 137, 137, 105, 73, 73, 41, 9,
+ 135, 135, 135, 103, 71, 71, 39, 7, 133, 133, 133, 101, 69, 69, 37, 5,
+ 131, 131, 131, 99, 67, 67, 35, 3, 129, 129, 129, 97, 65, 65, 33, 1
+ },
+ { // QUANT_192
0, 64, 128, 128, 2, 66, 130, 130, 4, 68, 132, 132, 6, 70, 134, 134,
8, 72, 136, 136, 10, 74, 138, 138, 12, 76, 140, 140, 14, 78, 142, 142,
16, 80, 144, 144, 18, 82, 146, 146, 20, 84, 148, 148, 22, 86, 150, 150,
@@ -625,7 +624,7 @@ const uint8_t color_uquant_to_scrambled_pquant_tables[17][256] {
143, 143, 79, 15, 141, 141, 77, 13, 139, 139, 75, 11, 137, 137, 73, 9,
135, 135, 71, 7, 133, 133, 69, 5, 131, 131, 67, 3, 129, 129, 65, 1
},
- {
+ { // QUANT_256
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
diff --git a/thirdparty/astcenc/astcenc_symbolic_physical.cpp b/thirdparty/astcenc/astcenc_symbolic_physical.cpp
index 80221a6013..49a8a1504b 100644
--- a/thirdparty/astcenc/astcenc_symbolic_physical.cpp
+++ b/thirdparty/astcenc/astcenc_symbolic_physical.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2011-2021 Arm Limited
+// Copyright 2011-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -24,36 +24,21 @@
#include <cassert>
/**
- * @brief Write up to 8 bits at an arbitrary bit offset.
- *
- * The stored value is at most 8 bits, but can be stored at an offset of between 0 and 7 bits so
- * may span two separate bytes in memory.
+ * @brief Reverse bits in a byte.
*
- * @param value The value to write.
- * @param bitcount The number of bits to write, starting from LSB.
- * @param bitoffset The bit offset to store at, between 0 and 7.
- * @param[in,out] ptr The data pointer to write to.
+ * @param p The value to reverse.
+ *
+ * @return The reversed result.
*/
-static inline void write_bits(
- int value,
- int bitcount,
- int bitoffset,
- uint8_t* ptr
-) {
- int mask = (1 << bitcount) - 1;
- value &= mask;
- ptr += bitoffset >> 3;
- bitoffset &= 7;
- value <<= bitoffset;
- mask <<= bitoffset;
- mask = ~mask;
-
- ptr[0] &= mask;
- ptr[0] |= value;
- ptr[1] &= mask >> 8;
- ptr[1] |= value >> 8;
+static inline int bitrev8(int p)
+{
+ p = ((p & 0x0F) << 4) | ((p >> 4) & 0x0F);
+ p = ((p & 0x33) << 2) | ((p >> 2) & 0x33);
+ p = ((p & 0x55) << 1) | ((p >> 1) & 0x55);
+ return p;
}
+
/**
* @brief Read up to 8 bits at an arbitrary bit offset.
*
@@ -80,19 +65,37 @@ static inline int read_bits(
return value;
}
+#if !defined(ASTCENC_DECOMPRESS_ONLY)
+
/**
- * @brief Reverse bits in a byte.
+ * @brief Write up to 8 bits at an arbitrary bit offset.
*
- * @param p The value to reverse.
- *
- * @return The reversed result.
+ * The stored value is at most 8 bits, but can be stored at an offset of between 0 and 7 bits so
+ * may span two separate bytes in memory.
+ *
+ * @param value The value to write.
+ * @param bitcount The number of bits to write, starting from LSB.
+ * @param bitoffset The bit offset to store at, between 0 and 7.
+ * @param[in,out] ptr The data pointer to write to.
*/
-static inline int bitrev8(int p)
-{
- p = ((p & 0x0F) << 4) | ((p >> 4) & 0x0F);
- p = ((p & 0x33) << 2) | ((p >> 2) & 0x33);
- p = ((p & 0x55) << 1) | ((p >> 1) & 0x55);
- return p;
+static inline void write_bits(
+ int value,
+ int bitcount,
+ int bitoffset,
+ uint8_t* ptr
+) {
+ int mask = (1 << bitcount) - 1;
+ value &= mask;
+ ptr += bitoffset >> 3;
+ bitoffset &= 7;
+ value <<= bitoffset;
+ mask <<= bitoffset;
+ mask = ~mask;
+
+ ptr[0] &= mask;
+ ptr[0] |= value;
+ ptr[1] &= mask >> 8;
+ ptr[1] |= value >> 8;
}
/* See header for documentation. */
@@ -282,6 +285,8 @@ void symbolic_to_physical(
scb.partition_count == 1 ? 17 : 19 + PARTITION_INDEX_BITS);
}
+#endif
+
/* See header for documentation. */
void physical_to_symbolic(
const block_size_descriptor& bsd,
diff --git a/thirdparty/astcenc/astcenc_weight_align.cpp b/thirdparty/astcenc/astcenc_weight_align.cpp
index e40a318cf5..aa6ab61fa1 100644
--- a/thirdparty/astcenc/astcenc_weight_align.cpp
+++ b/thirdparty/astcenc/astcenc_weight_align.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
-// Copyright 2011-2022 Arm Limited
+// Copyright 2011-2023 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
@@ -353,7 +353,7 @@ void compute_angular_endpoints_1plane(
for (unsigned int i = 0; i < max_decimation_modes; i++)
{
const decimation_mode& dm = bsd.decimation_modes[i];
- if (!dm.is_ref_1_plane(static_cast<quant_method>(max_weight_quant)))
+ if (!dm.is_ref_1plane(static_cast<quant_method>(max_weight_quant)))
{
continue;
}
@@ -422,7 +422,7 @@ void compute_angular_endpoints_2planes(
for (unsigned int i = 0; i < bsd.decimation_mode_count_selected; i++)
{
const decimation_mode& dm = bsd.decimation_modes[i];
- if (!dm.is_ref_2_plane(static_cast<quant_method>(max_weight_quant)))
+ if (!dm.is_ref_2plane(static_cast<quant_method>(max_weight_quant)))
{
continue;
}
diff --git a/thirdparty/basis_universal/patches/basisu-pr344.patch b/thirdparty/basis_universal/patches/basisu-pr344.patch
new file mode 100644
index 0000000000..37390d3534
--- /dev/null
+++ b/thirdparty/basis_universal/patches/basisu-pr344.patch
@@ -0,0 +1,43 @@
+From b4a0fa23c13da413d94b99f307e401c3b83e0108 Mon Sep 17 00:00:00 2001
+From: Ondrej Stava <ondrej.stava@gmail.com>
+Date: Fri, 23 Apr 2021 18:59:45 -0700
+Subject: [PATCH] Made it easier to use the library with external zstdlib
+ implementations (mostly in non CMake builds).
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In our internal repository, we have our own version of zstdlib and introducing extra copy is both undesirable and potentially dangerous (due to ODR violations).
+
+Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
+---
+ encoder/basisu_comp.cpp | 2 +-
+ transcoder/basisu_transcoder.cpp | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp
+index 41eae2b7..4e69e9e2 100644
+--- a/encoder/basisu_comp.cpp
++++ b/encoder/basisu_comp.cpp
+@@ -28,7 +28,7 @@
+ #endif
+
+ #if BASISD_SUPPORT_KTX2_ZSTD
+-#include "../zstd/zstd.h"
++#include <zstd.h>
+ #endif
+
+ // Set to 1 to disable the mipPadding alignment workaround (which only seems to be needed when no key-values are written at all)
+diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp
+index 3aeba0ee..c698861f 100644
+--- a/transcoder/basisu_transcoder.cpp
++++ b/transcoder/basisu_transcoder.cpp
+@@ -155,7 +155,7 @@
+ // If BASISD_SUPPORT_KTX2_ZSTD is 0, UASTC files compressed with Zstd cannot be loaded.
+ #if BASISD_SUPPORT_KTX2_ZSTD
+ // We only use two Zstd API's: ZSTD_decompress() and ZSTD_isError()
+- #include "../zstd/zstd.h"
++ #include <zstd.h>
+ #endif
+ #endif
+
diff --git a/thirdparty/basis_universal/transcoder/basisu_transcoder_internal.h b/thirdparty/basis_universal/transcoder/basisu_transcoder_internal.h
index 776a99861a..0505df6ea6 100644
--- a/thirdparty/basis_universal/transcoder/basisu_transcoder_internal.h
+++ b/thirdparty/basis_universal/transcoder/basisu_transcoder_internal.h
@@ -162,7 +162,7 @@ namespace basist
next_code[i + 1] = (total = ((total + syms_using_codesize[i]) << 1));
}
- if (((1U << basisu::cHuffmanMaxSupportedInternalCodeSize) != total) && (used_syms > 1U))
+ if (((1U << basisu::cHuffmanMaxSupportedInternalCodeSize) != total) && (used_syms != 1U))
return false;
for (int tree_next = -1, sym_index = 0; sym_index < (int)total_syms; ++sym_index)
diff --git a/thirdparty/certs/ca-certificates.crt b/thirdparty/certs/ca-certificates.crt
index 5ed6adf574..37262a7e23 100644
--- a/thirdparty/certs/ca-certificates.crt
+++ b/thirdparty/certs/ca-certificates.crt
@@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Fri Oct 21 16:14:43 2022 GMT
+## Certificate data from Mozilla as of: Thu Mar 23 23:04:36 2023 GMT
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
-## SHA256: 3ff8bd209b5f2e739b9f2b96eacb694a774114685b02978257824f37ff528f71
+## SHA256: 90c470e705b4b5f36f09684dc50e2b79c8b86989a848b62cd1a7bd6460ee65f6
##
@@ -489,29 +489,6 @@ IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
-----END CERTIFICATE-----
-Network Solutions Certificate Authority
-=======================================
------BEGIN CERTIFICATE-----
-MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
-EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
-IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
-MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
-MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
-jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
-aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
-crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
-/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
-AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
-BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
-bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
-A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
-4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
-GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
-wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
-ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
------END CERTIFICATE-----
-
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -1284,40 +1261,6 @@ Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
-E-Tugra Certification Authority
-===============================
------BEGIN CERTIFICATE-----
-MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
-DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
-ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
-ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
-NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
-QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
-cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
-DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
-MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
-hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
-CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
-ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
-BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
-E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
-rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
-jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
-rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
-dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
-/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
-MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
-kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
-XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
-VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
-a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
-dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
-KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
-Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
-8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
-C7TbO6Orb1wdtn7os4I07QZcJA==
------END CERTIFICATE-----
-
T-TeleSec GlobalRoot Class 2
============================
-----BEGIN CERTIFICATE-----
@@ -1654,36 +1597,6 @@ uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----
-Staat der Nederlanden EV Root CA
-================================
------BEGIN CERTIFICATE-----
-MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
-CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
-RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
-MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
-cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
-SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
-O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
-0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
-Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
-XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
-08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
-0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
-74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
-fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
-MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
-ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
-eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
-c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
-5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
-b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
-f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
-5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
-WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
-DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
-eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
------END CERTIFICATE-----
-
IdenTrust Commercial Root CA 1
==============================
-----BEGIN CERTIFICATE-----
@@ -2135,87 +2048,6 @@ F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ
aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
-----END CERTIFICATE-----
-TrustCor RootCert CA-1
-======================
------BEGIN CERTIFICATE-----
-MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP
-MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
-U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
-dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx
-MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu
-YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe
-VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy
-dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq
-jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4
-pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0
-JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h
-gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw
-/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j
-BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5
-mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
-ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C
-qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P
-3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk=
------END CERTIFICATE-----
-
-TrustCor RootCert CA-2
-======================
------BEGIN CERTIFICATE-----
-MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w
-DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT
-eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0
-eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy
-MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h
-bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
-cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0
-IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb
-ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk
-RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1
-oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb
-XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1
-/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q
-jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP
-eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg
-rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
-8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU
-2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD
-VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h
-Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp
-kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv
-2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3
-S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw
-PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv
-DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU
-RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE
-xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX
-RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ
------END CERTIFICATE-----
-
-TrustCor ECA-1
-==============
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP
-MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
-U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
-dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw
-N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5
-MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y
-IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR
-MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23
-xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc
-p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+
-fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj
-YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL
-f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
-AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u
-/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
-hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs
-J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC
-jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g==
------END CERTIFICATE-----
-
SSL.com Root Certification Authority RSA
========================================
-----BEGIN CERTIFICATE-----
diff --git a/thirdparty/doctest/LICENSE.txt b/thirdparty/doctest/LICENSE.txt
index d67bb64f9d..5ae0eb1052 100644
--- a/thirdparty/doctest/LICENSE.txt
+++ b/thirdparty/doctest/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2016-2021 Viktor Kirilov
+Copyright (c) 2016-2023 Viktor Kirilov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/doctest/doctest.h b/thirdparty/doctest/doctest.h
index aa2724c738..5c754cde08 100644
--- a/thirdparty/doctest/doctest.h
+++ b/thirdparty/doctest/doctest.h
@@ -4,7 +4,7 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2021 Viktor Kirilov
+// Copyright (c) 2016-2023 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
@@ -48,7 +48,7 @@
#define DOCTEST_VERSION_MAJOR 2
#define DOCTEST_VERSION_MINOR 4
-#define DOCTEST_VERSION_PATCH 9
+#define DOCTEST_VERSION_PATCH 11
// util we need here
#define DOCTEST_TOSTR_IMPL(x) #x
@@ -85,12 +85,15 @@
DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
#endif // MSVC
#endif // MSVC
-#if defined(__clang__) && defined(__clang_minor__)
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
!defined(__INTEL_COMPILER)
#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
#ifndef DOCTEST_MSVC
#define DOCTEST_MSVC 0
@@ -101,12 +104,15 @@
#ifndef DOCTEST_GCC
#define DOCTEST_GCC 0
#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
// =================================================================================================
// == COMPILER WARNINGS HELPERS ====================================================================
// =================================================================================================
-#if DOCTEST_CLANG
+#if DOCTEST_CLANG && !DOCTEST_ICC
#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
@@ -152,7 +158,7 @@
// =================================================================================================
// both the header and the implementation suppress all of these,
-// so it only makes sense to aggregrate them like so
+// so it only makes sense to aggregate them like so
#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
@@ -178,7 +184,7 @@
DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
- /* */ \
+ /* common ones */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
@@ -192,6 +198,7 @@
DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
/* static analysis */ \
DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
@@ -236,7 +243,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
- DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -352,6 +360,12 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
#endif
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
#ifndef DOCTEST_NORETURN
#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
#define DOCTEST_NORETURN
@@ -378,6 +392,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#endif // DOCTEST_MSVC
#endif // DOCTEST_CONSTEXPR
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
// =================================================================================================
@@ -475,12 +497,13 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
// https://github.com/doctest/doctest/issues/356
#if DOCTEST_CLANG
#include <ciso646>
+#endif // clang
+
#ifdef _LIBCPP_VERSION
#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
#define DOCTEST_CONFIG_USE_STD_HEADERS
#endif
#endif // _LIBCPP_VERSION
-#endif // clang
#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
@@ -970,7 +993,7 @@ namespace detail {
struct deferred_false : types::false_type { };
// MSVS 2015 :(
-#if defined(_MSC_VER) && _MSC_VER <= 1900
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
template <typename T, typename = void>
struct has_global_insertion_operator : types::false_type { };
@@ -1000,8 +1023,13 @@ namespace detail {
struct has_insertion_operator : types::false_type { };
#endif
-template <typename T>
-struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
+ template <typename T>
+ struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
+
+ template <typename T>
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum<T>::value && !doctest::detail::has_insertion_operator<T>::value;
+ };
DOCTEST_INTERFACE std::ostream* tlssPush();
DOCTEST_INTERFACE String tlssPop();
@@ -1063,7 +1091,7 @@ struct StringMaker : public detail::StringMakerBase<
template <typename T>
String toString() {
-#if DOCTEST_MSVC >= 0 && DOCTEST_CLANG == 0 && DOCTEST_GCC == 0
+#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0
String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void)
String::size_type beginPos = ret.find('<');
return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(sizeof(">(void)")));
@@ -1074,7 +1102,7 @@ String toString() {
#endif
}
-template <typename T, typename detail::types::enable_if<!detail::types::is_enum<T>::value, bool>::type = true>
+template <typename T, typename detail::types::enable_if<!detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
return StringMaker<T>::convert(value);
}
@@ -1110,7 +1138,7 @@ DOCTEST_INTERFACE String toString(long unsigned in);
DOCTEST_INTERFACE String toString(long long in);
DOCTEST_INTERFACE String toString(long long unsigned in);
-template <typename T, typename detail::types::enable_if<detail::types::is_enum<T>::value, bool>::type = true>
+template <typename T, typename detail::types::enable_if<detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
using UT = typename detail::types::underlying_type<T>::type;
return (DOCTEST_STRINGIFY(static_cast<UT>(value)));
@@ -1162,8 +1190,18 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
template <typename T>
struct filldata<T*> {
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180)
static void fill(std::ostream* stream, const T* in) {
- filldata<const void*>::fill(stream, in);
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast")
+ filldata<const void*>::fill(stream,
+#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)
+ reinterpret_cast<const void*>(in)
+#else
+ *reinterpret_cast<const void* const*>(&in)
+#endif
+ );
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
}
};
}
@@ -1275,9 +1313,9 @@ namespace detail {
template<class T, unsigned N> struct decay_array<T[N]> { using type = T*; };
template<class T> struct decay_array<T[]> { using type = T*; };
- template<class T> struct not_char_pointer { static DOCTEST_CONSTEXPR value = 1; };
- template<> struct not_char_pointer<char*> { static DOCTEST_CONSTEXPR value = 0; };
- template<> struct not_char_pointer<const char*> { static DOCTEST_CONSTEXPR value = 0; };
+ template<class T> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; };
+ template<> struct not_char_pointer<char*> { static DOCTEST_CONSTEXPR int value = 0; };
+ template<> struct not_char_pointer<const char*> { static DOCTEST_CONSTEXPR int value = 0; };
template<class T> struct can_use_op : public not_char_pointer<typename decay_array<T>::type> {};
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
@@ -1326,7 +1364,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
// If not it doesn't find the operator or if the operator at global scope is defined after
// this template, the template won't be instantiated due to SFINAE. Once the template is not
// instantiated it can look for global operator using normal conversions.
+#ifdef __NVCC__
+#define SFINAE_OP(ret,op) ret
+#else
#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{})
+#endif
#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
template <typename R> \
@@ -2129,13 +2171,13 @@ int registerReporter(const char* name, int priority, bool isReporter) {
{ \
void f(); \
}; \
- static inline DOCTEST_NOINLINE void func() { \
+ static DOCTEST_INLINE_NOINLINE void func() { \
der v; \
v.f(); \
} \
DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \
} \
- inline DOCTEST_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
+ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \
static void f(); \
@@ -3119,7 +3161,9 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <utility>
#include <fstream>
#include <sstream>
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <iostream>
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <algorithm>
#include <iomanip>
#include <vector>
@@ -3156,9 +3200,11 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
// defines for a leaner windows.h
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
+#define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
+#define DOCTEST_UNDEF_NOMINMAX
#endif // NOMINMAX
// not sure what AfxWin.h is for - here I do what Catch does
@@ -3239,8 +3285,14 @@ namespace {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
throw e;
#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION
+ DOCTEST_CONFIG_HANDLE_EXCEPTION(e);
+#else // DOCTEST_CONFIG_HANDLE_EXCEPTION
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
std::cerr << "doctest will terminate because it needed to throw an exception.\n"
<< "The message was: " << e.what() << '\n';
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION
std::terminate();
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
}
@@ -3315,7 +3367,7 @@ namespace detail {
namespace timer_large_integer
{
-
+
#if defined(DOCTEST_PLATFORM_WINDOWS)
using type = ULONGLONG;
#else // DOCTEST_PLATFORM_WINDOWS
@@ -3777,7 +3829,7 @@ namespace Color {
// clang-format off
const char* assertString(assertType::Enum at) {
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitely handled
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled
#define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
#define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
@@ -4105,11 +4157,13 @@ namespace {
return false;
}
+ DOCTEST_NO_SANITIZE_INTEGER
unsigned long long hash(unsigned long long a, unsigned long long b) {
return (a << 5) + b;
}
// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
+ DOCTEST_NO_SANITIZE_INTEGER
unsigned long long hash(const char* str) {
unsigned long long hash = 5381;
char c;
@@ -4949,7 +5003,7 @@ namespace detail {
m_string = tlssPop();
logged = true;
}
-
+
DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
const bool isWarn = m_severity & assertType::is_warn;
@@ -5018,7 +5072,11 @@ namespace {
mutable XmlWriter* m_writer = nullptr;
};
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
XmlWriter( std::ostream& os = std::cout );
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ XmlWriter( std::ostream& os );
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
~XmlWriter();
XmlWriter( XmlWriter const& ) = delete;
@@ -5500,7 +5558,7 @@ namespace {
test_case_start_impl(in);
xml.ensureTagClosed();
}
-
+
void test_case_reenter(const TestCaseData&) override {}
void test_case_end(const CurrentTestCaseStats& st) override {
@@ -5848,7 +5906,22 @@ namespace {
testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
}
- void log_message(const MessageData&) override {}
+ void log_message(const MessageData& mb) override {
+ if(mb.m_severity & assertType::is_warn) // report only failures
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ std::ostringstream os;
+ os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(")
+ << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
+
+ os << mb.m_string.c_str() << "\n";
+ log_contexts(os);
+
+ testCaseData.addFailure(mb.m_string.c_str(),
+ mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str());
+ }
void test_case_skipped(const TestCaseData&) override {}
@@ -6188,9 +6261,9 @@ namespace {
separator_to_stream();
s << std::dec;
- auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
- auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
- auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
+ auto totwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
+ auto passwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
+ auto failwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
<< p.numTestCasesPassingFilters << " | "
@@ -6222,7 +6295,7 @@ namespace {
subcasesStack.clear();
currentSubcaseLevel = 0;
}
-
+
void test_case_reenter(const TestCaseData&) override {
subcasesStack.clear();
}
@@ -6739,8 +6812,12 @@ int Context::run() {
fstr.open(p->out.c_str(), std::fstream::out);
p->cout = &fstr;
} else {
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
// stdout by default
p->cout = &std::cout;
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ return EXIT_FAILURE;
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
}
}
@@ -6905,7 +6982,7 @@ int Context::run() {
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
p->timer.start();
-
+
bool run_test = true;
do {
@@ -6946,7 +7023,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
run_test = false;
p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
}
-
+
if(!p->nextSubcaseStack.empty() && run_test)
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
if(p->nextSubcaseStack.empty())
@@ -7017,3 +7094,13 @@ DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
#endif // DOCTEST_LIBRARY_IMPLEMENTATION
#endif // DOCTEST_CONFIG_IMPLEMENT
+
+#ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN
+#undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+
+#ifdef DOCTEST_UNDEF_NOMINMAX
+#undef NOMINMAX
+#undef DOCTEST_UNDEF_NOMINMAX
+#endif // DOCTEST_UNDEF_NOMINMAX
diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp
index ea7f4957a2..2cbfe59fc6 100644
--- a/thirdparty/enet/godot.cpp
+++ b/thirdparty/enet/godot.cpp
@@ -436,6 +436,7 @@ ENetSocket enet_socket_create(ENetSocketType type) {
}
int enet_host_dtls_server_setup(ENetHost *host, void *p_options) {
+ ERR_FAIL_COND_V_MSG(!DTLSServer::is_available(), -1, "DTLS server is not available in this build.");
ENetGodotSocket *sock = (ENetGodotSocket *)host->socket;
if (!sock->can_upgrade()) {
return -1;
@@ -446,6 +447,7 @@ int enet_host_dtls_server_setup(ENetHost *host, void *p_options) {
}
int enet_host_dtls_client_setup(ENetHost *host, const char *p_for_hostname, void *p_options) {
+ ERR_FAIL_COND_V_MSG(!PacketPeerDTLS::is_available(), -1, "DTLS is not available in this build.");
ENetGodotSocket *sock = (ENetGodotSocket *)host->socket;
if (!sock->can_upgrade()) {
return -1;
diff --git a/thirdparty/glad/gl.c b/thirdparty/glad/gl.c
index 6be716284c..8d12541ed4 100644
--- a/thirdparty/glad/gl.c
+++ b/thirdparty/glad/gl.c
@@ -37,6 +37,7 @@ int GLAD_GL_VERSION_3_2 = 0;
int GLAD_GL_VERSION_3_3 = 0;
int GLAD_GL_ARB_debug_output = 0;
int GLAD_GL_ARB_framebuffer_object = 0;
+int GLAD_GL_ARB_get_program_binary = 0;
int GLAD_GL_EXT_framebuffer_blit = 0;
int GLAD_GL_EXT_framebuffer_multisample = 0;
int GLAD_GL_EXT_framebuffer_object = 0;
@@ -292,6 +293,7 @@ PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL;
PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL;
PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;
PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL;
+PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL;
PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;
PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
@@ -485,6 +487,8 @@ PFNGLPOPMATRIXPROC glad_glPopMatrix = NULL;
PFNGLPOPNAMEPROC glad_glPopName = NULL;
PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL;
+PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL;
+PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL;
PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL;
PFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL;
PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL;
@@ -1588,6 +1592,12 @@ static void glad_gl_load_GL_ARB_framebuffer_object( GLADuserptrloadfunc load, vo
glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage");
glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample");
}
+static void glad_gl_load_GL_ARB_get_program_binary( GLADuserptrloadfunc load, void* userptr) {
+ if(!GLAD_GL_ARB_get_program_binary) return;
+ glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) load(userptr, "glGetProgramBinary");
+ glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC) load(userptr, "glProgramBinary");
+ glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri");
+}
static void glad_gl_load_GL_EXT_framebuffer_blit( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GL_EXT_framebuffer_blit) return;
glad_glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC) load(userptr, "glBlitFramebufferEXT");
@@ -1729,6 +1739,7 @@ static int glad_gl_find_extensions_gl( int version) {
GLAD_GL_ARB_debug_output = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_debug_output");
GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_object");
+ GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_program_binary");
GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit");
GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample");
GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object");
@@ -1804,6 +1815,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
if (!glad_gl_find_extensions_gl(version)) return 0;
glad_gl_load_GL_ARB_debug_output(load, userptr);
glad_gl_load_GL_ARB_framebuffer_object(load, userptr);
+ glad_gl_load_GL_ARB_get_program_binary(load, userptr);
glad_gl_load_GL_EXT_framebuffer_blit(load, userptr);
glad_gl_load_GL_EXT_framebuffer_multisample(load, userptr);
glad_gl_load_GL_EXT_framebuffer_object(load, userptr);
diff --git a/thirdparty/glad/glad/gl.h b/thirdparty/glad/glad/gl.h
index 9836296226..b180a2e391 100644
--- a/thirdparty/glad/glad/gl.h
+++ b/thirdparty/glad/glad/gl.h
@@ -1,11 +1,11 @@
/**
- * Loader generated by glad 2.0.2 on Mon Nov 7 12:17:15 2022
+ * Loader generated by glad 2.0.4 on Sat Apr 29 13:24:27 2023
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
* Generator: C/C++
* Specification: gl
- * Extensions: 7
+ * Extensions: 8
*
* APIs:
* - gl:compatibility=3.3
@@ -19,10 +19,10 @@
* - ON_DEMAND = False
*
* Commandline:
- * --api='gl:compatibility=3.3' --extensions='GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2' c --loader
+ * --api='gl:compatibility=3.3' --extensions='GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_ARB_get_program_binary,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2' c --loader
*
* Online:
- * http://glad.sh/#api=gl%3Acompatibility%3D3.3&extensions=GL_ARB_debug_output%2CGL_ARB_framebuffer_object%2CGL_EXT_framebuffer_blit%2CGL_EXT_framebuffer_multisample%2CGL_EXT_framebuffer_object%2CGL_OVR_multiview%2CGL_OVR_multiview2&generator=c&options=LOADER
+ * http://glad.sh/#api=gl%3Acompatibility%3D3.3&extensions=GL_ARB_debug_output%2CGL_ARB_framebuffer_object%2CGL_ARB_get_program_binary%2CGL_EXT_framebuffer_blit%2CGL_EXT_framebuffer_multisample%2CGL_EXT_framebuffer_object%2CGL_OVR_multiview%2CGL_OVR_multiview2&generator=c&options=LOADER
*
*/
@@ -164,7 +164,7 @@ extern "C" {
#define GLAD_VERSION_MAJOR(version) (version / 10000)
#define GLAD_VERSION_MINOR(version) (version % 10000)
-#define GLAD_GENERATOR_VERSION "2.0.2"
+#define GLAD_GENERATOR_VERSION "2.0.4"
typedef void (*GLADapiproc)(void);
@@ -889,6 +889,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
#define GL_NO_ERROR 0
#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
#define GL_NUM_EXTENSIONS 0x821D
+#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
#define GL_OBJECT_LINEAR 0x2401
#define GL_OBJECT_PLANE 0x2501
#define GL_OBJECT_TYPE 0x9112
@@ -981,6 +982,9 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
#define GL_PRIMITIVES_GENERATED 0x8C87
#define GL_PRIMITIVE_RESTART 0x8F9D
#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E
+#define GL_PROGRAM_BINARY_FORMATS 0x87FF
+#define GL_PROGRAM_BINARY_LENGTH 0x8741
+#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
#define GL_PROGRAM_POINT_SIZE 0x8642
#define GL_PROJECTION 0x1701
#define GL_PROJECTION_MATRIX 0x0BA7
@@ -1605,6 +1609,8 @@ GLAD_API_CALL int GLAD_GL_VERSION_3_3;
GLAD_API_CALL int GLAD_GL_ARB_debug_output;
#define GL_ARB_framebuffer_object 1
GLAD_API_CALL int GLAD_GL_ARB_framebuffer_object;
+#define GL_ARB_get_program_binary 1
+GLAD_API_CALL int GLAD_GL_ARB_get_program_binary;
#define GL_EXT_framebuffer_blit 1
GLAD_API_CALL int GLAD_GL_EXT_framebuffer_blit;
#define GL_EXT_framebuffer_multisample 1
@@ -1864,6 +1870,7 @@ typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values
typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values);
typedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params);
typedef void (GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask);
+typedef void (GLAD_API_PTR *PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary);
typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog);
typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params);
typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params);
@@ -2057,6 +2064,8 @@ typedef void (GLAD_API_PTR *PFNGLPOPMATRIXPROC)(void);
typedef void (GLAD_API_PTR *PFNGLPOPNAMEPROC)(void);
typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index);
typedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities);
+typedef void (GLAD_API_PTR *PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLsizei length);
+typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value);
typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode);
typedef void (GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask);
typedef void (GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask);
@@ -2860,6 +2869,8 @@ GLAD_API_CALL PFNGLGETPOINTERVPROC glad_glGetPointerv;
#define glGetPointerv glad_glGetPointerv
GLAD_API_CALL PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple;
#define glGetPolygonStipple glad_glGetPolygonStipple
+GLAD_API_CALL PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;
+#define glGetProgramBinary glad_glGetProgramBinary
GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
#define glGetProgramInfoLog glad_glGetProgramInfoLog
GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
@@ -3246,6 +3257,10 @@ GLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;
#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex
GLAD_API_CALL PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures;
#define glPrioritizeTextures glad_glPrioritizeTextures
+GLAD_API_CALL PFNGLPROGRAMBINARYPROC glad_glProgramBinary;
+#define glProgramBinary glad_glProgramBinary
+GLAD_API_CALL PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;
+#define glProgramParameteri glad_glProgramParameteri
GLAD_API_CALL PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;
#define glProvokingVertex glad_glProvokingVertex
GLAD_API_CALL PFNGLPUSHATTRIBPROC glad_glPushAttrib;
diff --git a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
index 9e7b2742a7..d27bf608fb 100644
--- a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
+++ b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
@@ -1,3 +1,38 @@
+/**************************************************************************/
+/* godot_core_mbedtls_config.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 GODOT_CORE_MBEDTLS_CONFIG_H
+#define GODOT_CORE_MBEDTLS_CONFIG_H
+
+#include <limits.h>
+
// For AES
#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_CIPHER_MODE_CFB
@@ -15,4 +50,4 @@
#define MBEDTLS_PLATFORM_ZEROIZE_ALT
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
-#include <limits.h>
+#endif // GODOT_CORE_MBEDTLS_CONFIG_H
diff --git a/thirdparty/mbedtls/include/godot_module_mbedtls_config.h b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
new file mode 100644
index 0000000000..c35f158041
--- /dev/null
+++ b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* godot_module_mbedtls_config.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 GODOT_MODULE_MBEDTLS_CONFIG_H
+#define GODOT_MODULE_MBEDTLS_CONFIG_H
+
+#include "platform_config.h"
+
+#ifdef GODOT_MBEDTLS_INCLUDE_H
+
+// Allow platforms to customize the mbedTLS configuration.
+#include GODOT_MBEDTLS_INCLUDE_H
+
+#else
+
+// Include default mbedTLS config.
+#include <mbedtls/config.h>
+
+// Disable weak cryptography.
+#undef MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
+#undef MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
+#undef MBEDTLS_SSL_CBC_RECORD_SPLITTING
+#undef MBEDTLS_SSL_PROTO_TLS1
+#undef MBEDTLS_SSL_PROTO_TLS1_1
+#undef MBEDTLS_ARC4_C
+#undef MBEDTLS_DES_C
+#undef MBEDTLS_DHM_C
+
+#endif // GODOT_MBEDTLS_INCLUDE_H
+
+#endif // GODOT_MODULE_MBEDTLS_CONFIG_H
diff --git a/thirdparty/rvo2/patches/rvo2-godot-changes.patch b/thirdparty/rvo2/patches/rvo2-godot-changes.patch
deleted file mode 100644
index 16dbc203ed..0000000000
--- a/thirdparty/rvo2/patches/rvo2-godot-changes.patch
+++ /dev/null
@@ -1,282 +0,0 @@
-diff --git a/thirdparty/rvo2/Agent.cpp b/thirdparty/rvo2/Agent.cpp
-index 5e49a3554c..b35eee9c12 100644
---- a/thirdparty/rvo2/Agent.cpp
-+++ b/thirdparty/rvo2/Agent.cpp
-@@ -105,18 +105,17 @@ namespace RVO {
- */
- void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result);
-
-- Agent::Agent(RVOSimulator *sim) : sim_(sim), id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f) { }
-+ Agent::Agent() : id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f), ignore_y_(false) { }
-
-- void Agent::computeNeighbors()
-+ void Agent::computeNeighbors(KdTree *kdTree_)
- {
- agentNeighbors_.clear();
--
- if (maxNeighbors_ > 0) {
-- sim_->kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);
-+ kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);
- }
- }
-
-- void Agent::computeNewVelocity()
-+ void Agent::computeNewVelocity(float timeStep)
- {
- orcaPlanes_.clear();
- const float invTimeHorizon = 1.0f / timeHorizon_;
-@@ -124,10 +123,24 @@ namespace RVO {
- /* Create agent ORCA planes. */
- for (size_t i = 0; i < agentNeighbors_.size(); ++i) {
- const Agent *const other = agentNeighbors_[i].second;
-- const Vector3 relativePosition = other->position_ - position_;
-- const Vector3 relativeVelocity = velocity_ - other->velocity_;
-- const float distSq = absSq(relativePosition);
-+
-+ Vector3 relativePosition = other->position_ - position_;
-+ Vector3 relativeVelocity = velocity_ - other->velocity_;
- const float combinedRadius = radius_ + other->radius_;
-+
-+ // This is a Godot feature that allow the agents to avoid the collision
-+ // by moving only on the horizontal plane relative to the player velocity.
-+ if (ignore_y_) {
-+ // Skip if these are in two different heights
-+#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
-+ if (ABS(relativePosition[1]) > combinedRadius * 2) {
-+ continue;
-+ }
-+ relativePosition[1] = 0;
-+ relativeVelocity[1] = 0;
-+ }
-+
-+ const float distSq = absSq(relativePosition);
- const float combinedRadiusSq = sqr(combinedRadius);
-
- Plane plane;
-@@ -165,7 +178,7 @@ namespace RVO {
- }
- else {
- /* Collision. */
-- const float invTimeStep = 1.0f / sim_->timeStep_;
-+ const float invTimeStep = 1.0f / timeStep;
- const Vector3 w = relativeVelocity - invTimeStep * relativePosition;
- const float wLength = abs(w);
- const Vector3 unitW = w / wLength;
-@@ -183,6 +196,11 @@ namespace RVO {
- if (planeFail < orcaPlanes_.size()) {
- linearProgram4(orcaPlanes_, planeFail, maxSpeed_, newVelocity_);
- }
-+
-+ if (ignore_y_) {
-+ // Not 100% necessary, but better to have.
-+ newVelocity_[1] = prefVelocity_[1];
-+ }
- }
-
- void Agent::insertAgentNeighbor(const Agent *agent, float &rangeSq)
-@@ -211,12 +229,6 @@ namespace RVO {
- }
- }
-
-- void Agent::update()
-- {
-- velocity_ = newVelocity_;
-- position_ += velocity_ * sim_->timeStep_;
-- }
--
- bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)
- {
- const float dotProduct = line.point * line.direction;
-diff --git a/thirdparty/rvo2/Agent.h b/thirdparty/rvo2/Agent.h
-index d3922ec645..45fbead2f5 100644
---- a/thirdparty/rvo2/Agent.h
-+++ b/thirdparty/rvo2/Agent.h
-@@ -41,30 +41,52 @@
- #include <utility>
- #include <vector>
-
--#include "RVOSimulator.h"
- #include "Vector3.h"
-
-+// Note: Slightly modified to work better in Godot.
-+// - The agent can be created by anyone.
-+// - The simulator pointer is removed.
-+// - The update function is removed.
-+// - The compute velocity function now need the timeStep.
-+// - Moved the `Plane` class here.
-+// - Added a new parameter `ignore_y_` in the `Agent`. This parameter is used to control a godot feature that allows to avoid collisions by moving on the horizontal plane.
- namespace RVO {
-+ /**
-+ * \brief Defines a plane.
-+ */
-+ class Plane {
-+ public:
-+ /**
-+ * \brief A point on the plane.
-+ */
-+ Vector3 point;
-+
-+ /**
-+ * \brief The normal to the plane.
-+ */
-+ Vector3 normal;
-+ };
-+
- /**
- * \brief Defines an agent in the simulation.
- */
- class Agent {
-- private:
-+ public:
- /**
- * \brief Constructs an agent instance.
- * \param sim The simulator instance.
- */
-- explicit Agent(RVOSimulator *sim);
-+ explicit Agent();
-
- /**
- * \brief Computes the neighbors of this agent.
- */
-- void computeNeighbors();
-+ void computeNeighbors(class KdTree *kdTree_);
-
- /**
- * \brief Computes the new velocity of this agent.
- */
-- void computeNewVelocity();
-+ void computeNewVelocity(float timeStep);
-
- /**
- * \brief Inserts an agent neighbor into the set of neighbors of this agent.
-@@ -73,16 +95,10 @@ namespace RVO {
- */
- void insertAgentNeighbor(const Agent *agent, float &rangeSq);
-
-- /**
-- * \brief Updates the three-dimensional position and three-dimensional velocity of this agent.
-- */
-- void update();
--
- Vector3 newVelocity_;
- Vector3 position_;
- Vector3 prefVelocity_;
- Vector3 velocity_;
-- RVOSimulator *sim_;
- size_t id_;
- size_t maxNeighbors_;
- float maxSpeed_;
-@@ -91,9 +107,11 @@ namespace RVO {
- float timeHorizon_;
- std::vector<std::pair<float, const Agent *> > agentNeighbors_;
- std::vector<Plane> orcaPlanes_;
-+ /// This is a godot feature that allows the Agent to avoid collision by mooving
-+ /// on the horizontal plane.
-+ bool ignore_y_;
-
- friend class KdTree;
-- friend class RVOSimulator;
- };
- }
-
-diff --git a/thirdparty/rvo2/KdTree.cpp b/thirdparty/rvo2/KdTree.cpp
-index 5e9e9777a6..c857f299df 100644
---- a/thirdparty/rvo2/KdTree.cpp
-+++ b/thirdparty/rvo2/KdTree.cpp
-@@ -36,16 +36,15 @@
-
- #include "Agent.h"
- #include "Definitions.h"
--#include "RVOSimulator.h"
-
- namespace RVO {
- const size_t RVO3D_MAX_LEAF_SIZE = 10;
-
-- KdTree::KdTree(RVOSimulator *sim) : sim_(sim) { }
-+ KdTree::KdTree() { }
-
-- void KdTree::buildAgentTree()
-+ void KdTree::buildAgentTree(std::vector<Agent *> agents)
- {
-- agents_ = sim_->agents_;
-+ agents_.swap(agents);
-
- if (!agents_.empty()) {
- agentTree_.resize(2 * agents_.size() - 1);
-diff --git a/thirdparty/rvo2/KdTree.h b/thirdparty/rvo2/KdTree.h
-index a09384c20f..69d8920ce0 100644
---- a/thirdparty/rvo2/KdTree.h
-+++ b/thirdparty/rvo2/KdTree.h
-@@ -41,6 +41,9 @@
-
- #include "Vector3.h"
-
-+// Note: Slightly modified to work better with Godot.
-+// - Removed `sim_`.
-+// - KdTree things are public
- namespace RVO {
- class Agent;
- class RVOSimulator;
-@@ -49,7 +52,7 @@ namespace RVO {
- * \brief Defines <i>k</i>d-trees for agents in the simulation.
- */
- class KdTree {
-- private:
-+ public:
- /**
- * \brief Defines an agent <i>k</i>d-tree node.
- */
-@@ -90,12 +93,12 @@ namespace RVO {
- * \brief Constructs a <i>k</i>d-tree instance.
- * \param sim The simulator instance.
- */
-- explicit KdTree(RVOSimulator *sim);
-+ explicit KdTree();
-
- /**
- * \brief Builds an agent <i>k</i>d-tree.
- */
-- void buildAgentTree();
-+ void buildAgentTree(std::vector<Agent *> agents);
-
- void buildAgentTreeRecursive(size_t begin, size_t end, size_t node);
-
-@@ -110,7 +113,6 @@ namespace RVO {
-
- std::vector<Agent *> agents_;
- std::vector<AgentTreeNode> agentTree_;
-- RVOSimulator *sim_;
-
- friend class Agent;
- friend class RVOSimulator;
-diff --git a/thirdparty/rvo2/Vector3.h b/thirdparty/rvo2/Vector3.h
-index 6c3223bb87..f44e311f29 100644
---- a/thirdparty/rvo2/Vector3.h
-+++ b/thirdparty/rvo2/Vector3.h
-@@ -41,7 +41,7 @@
- #include <cstddef>
- #include <ostream>
-
--#include "Export.h"
-+#define RVO3D_EXPORT
-
- namespace RVO {
- /**
-@@ -59,17 +59,6 @@ namespace RVO {
- val_[2] = 0.0f;
- }
-
-- /**
-- * \brief Constructs and initializes a three-dimensional vector from the specified three-dimensional vector.
-- * \param vector The three-dimensional vector containing the xyz-coordinates.
-- */
-- inline Vector3(const Vector3 &vector)
-- {
-- val_[0] = vector[0];
-- val_[1] = vector[1];
-- val_[2] = vector[2];
-- }
--
- /**
- * \brief Constructs and initializes a three-dimensional vector from the specified three-element array.
- * \param val The three-element array containing the xyz-coordinates.
diff --git a/thirdparty/rvo2/rvo2_2d/Agent2d.cpp b/thirdparty/rvo2/rvo2_2d/Agent2d.cpp
new file mode 100644
index 0000000000..3ff95a4922
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Agent2d.cpp
@@ -0,0 +1,594 @@
+/*
+ * Agent2d.cpp
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "Agent2d.h"
+
+#include "KdTree2d.h"
+#include "Obstacle2d.h"
+
+namespace RVO2D {
+ Agent2D::Agent2D() : maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f), timeHorizonObst_(0.0f), id_(0) { }
+
+ void Agent2D::computeNeighbors(RVOSimulator2D *sim_)
+ {
+ obstacleNeighbors_.clear();
+ float rangeSq = sqr(timeHorizonObst_ * maxSpeed_ + radius_);
+ sim_->kdTree_->computeObstacleNeighbors(this, rangeSq);
+
+ agentNeighbors_.clear();
+
+ if (maxNeighbors_ > 0) {
+ rangeSq = sqr(neighborDist_);
+ sim_->kdTree_->computeAgentNeighbors(this, rangeSq);
+ }
+ }
+
+ /* Search for the best new velocity. */
+ void Agent2D::computeNewVelocity(RVOSimulator2D *sim_)
+ {
+ orcaLines_.clear();
+
+ const float invTimeHorizonObst = 1.0f / timeHorizonObst_;
+
+ /* Create obstacle ORCA lines. */
+ for (size_t i = 0; i < obstacleNeighbors_.size(); ++i) {
+
+ const Obstacle2D *obstacle1 = obstacleNeighbors_[i].second;
+ const Obstacle2D *obstacle2 = obstacle1->nextObstacle_;
+
+ const Vector2 relativePosition1 = obstacle1->point_ - position_;
+ const Vector2 relativePosition2 = obstacle2->point_ - position_;
+
+ /*
+ * Check if velocity obstacle of obstacle is already taken care of by
+ * previously constructed obstacle ORCA lines.
+ */
+ bool alreadyCovered = false;
+
+ for (size_t j = 0; j < orcaLines_.size(); ++j) {
+ if (det(invTimeHorizonObst * relativePosition1 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVO_EPSILON && det(invTimeHorizonObst * relativePosition2 - orcaLines_[j].point, orcaLines_[j].direction) - invTimeHorizonObst * radius_ >= -RVO_EPSILON) {
+ alreadyCovered = true;
+ break;
+ }
+ }
+
+ if (alreadyCovered) {
+ continue;
+ }
+
+ /* Not yet covered. Check for collisions. */
+
+ const float distSq1 = absSq(relativePosition1);
+ const float distSq2 = absSq(relativePosition2);
+
+ const float radiusSq = sqr(radius_);
+
+ const Vector2 obstacleVector = obstacle2->point_ - obstacle1->point_;
+ const float s = (-relativePosition1 * obstacleVector) / absSq(obstacleVector);
+ const float distSqLine = absSq(-relativePosition1 - s * obstacleVector);
+
+ Line line;
+
+ if (s < 0.0f && distSq1 <= radiusSq) {
+ /* Collision with left vertex. Ignore if non-convex. */
+ if (obstacle1->isConvex_) {
+ line.point = Vector2(0.0f, 0.0f);
+ line.direction = normalize(Vector2(-relativePosition1.y(), relativePosition1.x()));
+ orcaLines_.push_back(line);
+ }
+
+ continue;
+ }
+ else if (s > 1.0f && distSq2 <= radiusSq) {
+ /* Collision with right vertex. Ignore if non-convex
+ * or if it will be taken care of by neighoring obstace */
+ if (obstacle2->isConvex_ && det(relativePosition2, obstacle2->unitDir_) >= 0.0f) {
+ line.point = Vector2(0.0f, 0.0f);
+ line.direction = normalize(Vector2(-relativePosition2.y(), relativePosition2.x()));
+ orcaLines_.push_back(line);
+ }
+
+ continue;
+ }
+ else if (s >= 0.0f && s < 1.0f && distSqLine <= radiusSq) {
+ /* Collision with obstacle segment. */
+ line.point = Vector2(0.0f, 0.0f);
+ line.direction = -obstacle1->unitDir_;
+ orcaLines_.push_back(line);
+ continue;
+ }
+
+ /*
+ * No collision.
+ * Compute legs. When obliquely viewed, both legs can come from a single
+ * vertex. Legs extend cut-off line when nonconvex vertex.
+ */
+
+ Vector2 leftLegDirection, rightLegDirection;
+
+ if (s < 0.0f && distSqLine <= radiusSq) {
+ /*
+ * Obstacle viewed obliquely so that left vertex
+ * defines velocity obstacle.
+ */
+ if (!obstacle1->isConvex_) {
+ /* Ignore obstacle. */
+ continue;
+ }
+
+ obstacle2 = obstacle1;
+
+ const float leg1 = std::sqrt(distSq1 - radiusSq);
+ leftLegDirection = Vector2(relativePosition1.x() * leg1 - relativePosition1.y() * radius_, relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
+ rightLegDirection = Vector2(relativePosition1.x() * leg1 + relativePosition1.y() * radius_, -relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
+ }
+ else if (s > 1.0f && distSqLine <= radiusSq) {
+ /*
+ * Obstacle viewed obliquely so that
+ * right vertex defines velocity obstacle.
+ */
+ if (!obstacle2->isConvex_) {
+ /* Ignore obstacle. */
+ continue;
+ }
+
+ obstacle1 = obstacle2;
+
+ const float leg2 = std::sqrt(distSq2 - radiusSq);
+ leftLegDirection = Vector2(relativePosition2.x() * leg2 - relativePosition2.y() * radius_, relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
+ rightLegDirection = Vector2(relativePosition2.x() * leg2 + relativePosition2.y() * radius_, -relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
+ }
+ else {
+ /* Usual situation. */
+ if (obstacle1->isConvex_) {
+ const float leg1 = std::sqrt(distSq1 - radiusSq);
+ leftLegDirection = Vector2(relativePosition1.x() * leg1 - relativePosition1.y() * radius_, relativePosition1.x() * radius_ + relativePosition1.y() * leg1) / distSq1;
+ }
+ else {
+ /* Left vertex non-convex; left leg extends cut-off line. */
+ leftLegDirection = -obstacle1->unitDir_;
+ }
+
+ if (obstacle2->isConvex_) {
+ const float leg2 = std::sqrt(distSq2 - radiusSq);
+ rightLegDirection = Vector2(relativePosition2.x() * leg2 + relativePosition2.y() * radius_, -relativePosition2.x() * radius_ + relativePosition2.y() * leg2) / distSq2;
+ }
+ else {
+ /* Right vertex non-convex; right leg extends cut-off line. */
+ rightLegDirection = obstacle1->unitDir_;
+ }
+ }
+
+ /*
+ * Legs can never point into neighboring edge when convex vertex,
+ * take cutoff-line of neighboring edge instead. If velocity projected on
+ * "foreign" leg, no constraint is added.
+ */
+
+ const Obstacle2D *const leftNeighbor = obstacle1->prevObstacle_;
+
+ bool isLeftLegForeign = false;
+ bool isRightLegForeign = false;
+
+ if (obstacle1->isConvex_ && det(leftLegDirection, -leftNeighbor->unitDir_) >= 0.0f) {
+ /* Left leg points into obstacle. */
+ leftLegDirection = -leftNeighbor->unitDir_;
+ isLeftLegForeign = true;
+ }
+
+ if (obstacle2->isConvex_ && det(rightLegDirection, obstacle2->unitDir_) <= 0.0f) {
+ /* Right leg points into obstacle. */
+ rightLegDirection = obstacle2->unitDir_;
+ isRightLegForeign = true;
+ }
+
+ /* Compute cut-off centers. */
+ const Vector2 leftCutoff = invTimeHorizonObst * (obstacle1->point_ - position_);
+ const Vector2 rightCutoff = invTimeHorizonObst * (obstacle2->point_ - position_);
+ const Vector2 cutoffVec = rightCutoff - leftCutoff;
+
+ /* Project current velocity on velocity obstacle. */
+
+ /* Check if current velocity is projected on cutoff circles. */
+ const float t = (obstacle1 == obstacle2 ? 0.5f : ((velocity_ - leftCutoff) * cutoffVec) / absSq(cutoffVec));
+ const float tLeft = ((velocity_ - leftCutoff) * leftLegDirection);
+ const float tRight = ((velocity_ - rightCutoff) * rightLegDirection);
+
+ if ((t < 0.0f && tLeft < 0.0f) || (obstacle1 == obstacle2 && tLeft < 0.0f && tRight < 0.0f)) {
+ /* Project on left cut-off circle. */
+ const Vector2 unitW = normalize(velocity_ - leftCutoff);
+
+ line.direction = Vector2(unitW.y(), -unitW.x());
+ line.point = leftCutoff + radius_ * invTimeHorizonObst * unitW;
+ orcaLines_.push_back(line);
+ continue;
+ }
+ else if (t > 1.0f && tRight < 0.0f) {
+ /* Project on right cut-off circle. */
+ const Vector2 unitW = normalize(velocity_ - rightCutoff);
+
+ line.direction = Vector2(unitW.y(), -unitW.x());
+ line.point = rightCutoff + radius_ * invTimeHorizonObst * unitW;
+ orcaLines_.push_back(line);
+ continue;
+ }
+
+ /*
+ * Project on left leg, right leg, or cut-off line, whichever is closest
+ * to velocity.
+ */
+ const float distSqCutoff = ((t < 0.0f || t > 1.0f || obstacle1 == obstacle2) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (leftCutoff + t * cutoffVec)));
+ const float distSqLeft = ((tLeft < 0.0f) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (leftCutoff + tLeft * leftLegDirection)));
+ const float distSqRight = ((tRight < 0.0f) ? std::numeric_limits<float>::infinity() : absSq(velocity_ - (rightCutoff + tRight * rightLegDirection)));
+
+ if (distSqCutoff <= distSqLeft && distSqCutoff <= distSqRight) {
+ /* Project on cut-off line. */
+ line.direction = -obstacle1->unitDir_;
+ line.point = leftCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
+ orcaLines_.push_back(line);
+ continue;
+ }
+ else if (distSqLeft <= distSqRight) {
+ /* Project on left leg. */
+ if (isLeftLegForeign) {
+ continue;
+ }
+
+ line.direction = leftLegDirection;
+ line.point = leftCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
+ orcaLines_.push_back(line);
+ continue;
+ }
+ else {
+ /* Project on right leg. */
+ if (isRightLegForeign) {
+ continue;
+ }
+
+ line.direction = -rightLegDirection;
+ line.point = rightCutoff + radius_ * invTimeHorizonObst * Vector2(-line.direction.y(), line.direction.x());
+ orcaLines_.push_back(line);
+ continue;
+ }
+ }
+
+ const size_t numObstLines = orcaLines_.size();
+
+ const float invTimeHorizon = 1.0f / timeHorizon_;
+
+ /* Create agent ORCA lines. */
+ for (size_t i = 0; i < agentNeighbors_.size(); ++i) {
+ const Agent2D *const other = agentNeighbors_[i].second;
+
+ //const float timeHorizon_mod = (avoidance_priority_ - other->avoidance_priority_ + 1.0f) * 0.5f;
+ //const float invTimeHorizon = (1.0f / timeHorizon_) * timeHorizon_mod;
+
+ const Vector2 relativePosition = other->position_ - position_;
+ const Vector2 relativeVelocity = velocity_ - other->velocity_;
+ const float distSq = absSq(relativePosition);
+ const float combinedRadius = radius_ + other->radius_;
+ const float combinedRadiusSq = sqr(combinedRadius);
+
+ Line line;
+ Vector2 u;
+
+ if (distSq > combinedRadiusSq) {
+ /* No collision. */
+ const Vector2 w = relativeVelocity - invTimeHorizon * relativePosition;
+ /* Vector from cutoff center to relative velocity. */
+ const float wLengthSq = absSq(w);
+
+ const float dotProduct1 = w * relativePosition;
+
+ if (dotProduct1 < 0.0f && sqr(dotProduct1) > combinedRadiusSq * wLengthSq) {
+ /* Project on cut-off circle. */
+ const float wLength = std::sqrt(wLengthSq);
+ const Vector2 unitW = w / wLength;
+
+ line.direction = Vector2(unitW.y(), -unitW.x());
+ u = (combinedRadius * invTimeHorizon - wLength) * unitW;
+ }
+ else {
+ /* Project on legs. */
+ const float leg = std::sqrt(distSq - combinedRadiusSq);
+
+ if (det(relativePosition, w) > 0.0f) {
+ /* Project on left leg. */
+ line.direction = Vector2(relativePosition.x() * leg - relativePosition.y() * combinedRadius, relativePosition.x() * combinedRadius + relativePosition.y() * leg) / distSq;
+ }
+ else {
+ /* Project on right leg. */
+ line.direction = -Vector2(relativePosition.x() * leg + relativePosition.y() * combinedRadius, -relativePosition.x() * combinedRadius + relativePosition.y() * leg) / distSq;
+ }
+
+ const float dotProduct2 = relativeVelocity * line.direction;
+
+ u = dotProduct2 * line.direction - relativeVelocity;
+ }
+ }
+ else {
+ /* Collision. Project on cut-off circle of time timeStep. */
+ const float invTimeStep = 1.0f / sim_->timeStep_;
+
+ /* Vector from cutoff center to relative velocity. */
+ const Vector2 w = relativeVelocity - invTimeStep * relativePosition;
+
+ const float wLength = abs(w);
+ const Vector2 unitW = w / wLength;
+
+ line.direction = Vector2(unitW.y(), -unitW.x());
+ u = (combinedRadius * invTimeStep - wLength) * unitW;
+ }
+
+ line.point = velocity_ + 0.5f * u;
+ orcaLines_.push_back(line);
+ }
+
+ size_t lineFail = linearProgram2(orcaLines_, maxSpeed_, prefVelocity_, false, newVelocity_);
+
+ if (lineFail < orcaLines_.size()) {
+ linearProgram3(orcaLines_, numObstLines, lineFail, maxSpeed_, newVelocity_);
+ }
+ }
+
+ void Agent2D::insertAgentNeighbor(const Agent2D *agent, float &rangeSq)
+ {
+ // no point processing same agent
+ if (this == agent) {
+ return;
+ }
+ // ignore other agent if layers/mask bitmasks have no matching bit
+ if ((avoidance_mask_ & agent->avoidance_layers_) == 0) {
+ return;
+ }
+ // ignore other agent if this agent is below or above
+ if ((elevation_ > agent->elevation_ + agent->height_) || (elevation_ + height_ < agent->elevation_)) {
+ return;
+ }
+
+ if (avoidance_priority_ > agent->avoidance_priority_) {
+ return;
+ }
+
+ const float distSq = absSq(position_ - agent->position_);
+
+ if (distSq < rangeSq) {
+ if (agentNeighbors_.size() < maxNeighbors_) {
+ agentNeighbors_.push_back(std::make_pair(distSq, agent));
+ }
+
+ size_t i = agentNeighbors_.size() - 1;
+
+ while (i != 0 && distSq < agentNeighbors_[i - 1].first) {
+ agentNeighbors_[i] = agentNeighbors_[i - 1];
+ --i;
+ }
+
+ agentNeighbors_[i] = std::make_pair(distSq, agent);
+
+ if (agentNeighbors_.size() == maxNeighbors_) {
+ rangeSq = agentNeighbors_.back().first;
+ }
+ }
+ }
+
+ void Agent2D::insertObstacleNeighbor(const Obstacle2D *obstacle, float rangeSq)
+ {
+ const Obstacle2D *const nextObstacle = obstacle->nextObstacle_;
+
+ // ignore obstacle if no matching layer/mask
+ if ((avoidance_mask_ & nextObstacle->avoidance_layers_) == 0) {
+ return;
+ }
+ // ignore obstacle if below or above
+ if ((elevation_ > obstacle->elevation_ + obstacle->height_) || (elevation_ + height_ < obstacle->elevation_)) {
+ return;
+ }
+
+ const float distSq = distSqPointLineSegment(obstacle->point_, nextObstacle->point_, position_);
+
+ if (distSq < rangeSq) {
+ obstacleNeighbors_.push_back(std::make_pair(distSq, obstacle));
+
+ size_t i = obstacleNeighbors_.size() - 1;
+
+ while (i != 0 && distSq < obstacleNeighbors_[i - 1].first) {
+ obstacleNeighbors_[i] = obstacleNeighbors_[i - 1];
+ --i;
+ }
+
+ obstacleNeighbors_[i] = std::make_pair(distSq, obstacle);
+ }
+ //}
+ }
+
+ void Agent2D::update(RVOSimulator2D *sim_)
+ {
+ velocity_ = newVelocity_;
+ position_ += velocity_ * sim_->timeStep_;
+ }
+
+ bool linearProgram1(const std::vector<Line> &lines, size_t lineNo, float radius, const Vector2 &optVelocity, bool directionOpt, Vector2 &result)
+ {
+ const float dotProduct = lines[lineNo].point * lines[lineNo].direction;
+ const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(lines[lineNo].point);
+
+ if (discriminant < 0.0f) {
+ /* Max speed circle fully invalidates line lineNo. */
+ return false;
+ }
+
+ const float sqrtDiscriminant = std::sqrt(discriminant);
+ float tLeft = -dotProduct - sqrtDiscriminant;
+ float tRight = -dotProduct + sqrtDiscriminant;
+
+ for (size_t i = 0; i < lineNo; ++i) {
+ const float denominator = det(lines[lineNo].direction, lines[i].direction);
+ const float numerator = det(lines[i].direction, lines[lineNo].point - lines[i].point);
+
+ if (std::fabs(denominator) <= RVO_EPSILON) {
+ /* Lines lineNo and i are (almost) parallel. */
+ if (numerator < 0.0f) {
+ return false;
+ }
+ else {
+ continue;
+ }
+ }
+
+ const float t = numerator / denominator;
+
+ if (denominator >= 0.0f) {
+ /* Line i bounds line lineNo on the right. */
+ tRight = std::min(tRight, t);
+ }
+ else {
+ /* Line i bounds line lineNo on the left. */
+ tLeft = std::max(tLeft, t);
+ }
+
+ if (tLeft > tRight) {
+ return false;
+ }
+ }
+
+ if (directionOpt) {
+ /* Optimize direction. */
+ if (optVelocity * lines[lineNo].direction > 0.0f) {
+ /* Take right extreme. */
+ result = lines[lineNo].point + tRight * lines[lineNo].direction;
+ }
+ else {
+ /* Take left extreme. */
+ result = lines[lineNo].point + tLeft * lines[lineNo].direction;
+ }
+ }
+ else {
+ /* Optimize closest point. */
+ const float t = lines[lineNo].direction * (optVelocity - lines[lineNo].point);
+
+ if (t < tLeft) {
+ result = lines[lineNo].point + tLeft * lines[lineNo].direction;
+ }
+ else if (t > tRight) {
+ result = lines[lineNo].point + tRight * lines[lineNo].direction;
+ }
+ else {
+ result = lines[lineNo].point + t * lines[lineNo].direction;
+ }
+ }
+
+ return true;
+ }
+
+ size_t linearProgram2(const std::vector<Line> &lines, float radius, const Vector2 &optVelocity, bool directionOpt, Vector2 &result)
+ {
+ if (directionOpt) {
+ /*
+ * Optimize direction. Note that the optimization velocity is of unit
+ * length in this case.
+ */
+ result = optVelocity * radius;
+ }
+ else if (absSq(optVelocity) > sqr(radius)) {
+ /* Optimize closest point and outside circle. */
+ result = normalize(optVelocity) * radius;
+ }
+ else {
+ /* Optimize closest point and inside circle. */
+ result = optVelocity;
+ }
+
+ for (size_t i = 0; i < lines.size(); ++i) {
+ if (det(lines[i].direction, lines[i].point - result) > 0.0f) {
+ /* Result does not satisfy constraint i. Compute new optimal result. */
+ const Vector2 tempResult = result;
+
+ if (!linearProgram1(lines, i, radius, optVelocity, directionOpt, result)) {
+ result = tempResult;
+ return i;
+ }
+ }
+ }
+
+ return lines.size();
+ }
+
+ void linearProgram3(const std::vector<Line> &lines, size_t numObstLines, size_t beginLine, float radius, Vector2 &result)
+ {
+ float distance = 0.0f;
+
+ for (size_t i = beginLine; i < lines.size(); ++i) {
+ if (det(lines[i].direction, lines[i].point - result) > distance) {
+ /* Result does not satisfy constraint of line i. */
+ std::vector<Line> projLines(lines.begin(), lines.begin() + static_cast<ptrdiff_t>(numObstLines));
+
+ for (size_t j = numObstLines; j < i; ++j) {
+ Line line;
+
+ float determinant = det(lines[i].direction, lines[j].direction);
+
+ if (std::fabs(determinant) <= RVO_EPSILON) {
+ /* Line i and line j are parallel. */
+ if (lines[i].direction * lines[j].direction > 0.0f) {
+ /* Line i and line j point in the same direction. */
+ continue;
+ }
+ else {
+ /* Line i and line j point in opposite direction. */
+ line.point = 0.5f * (lines[i].point + lines[j].point);
+ }
+ }
+ else {
+ line.point = lines[i].point + (det(lines[j].direction, lines[i].point - lines[j].point) / determinant) * lines[i].direction;
+ }
+
+ line.direction = normalize(lines[j].direction - lines[i].direction);
+ projLines.push_back(line);
+ }
+
+ const Vector2 tempResult = result;
+
+ if (linearProgram2(projLines, radius, Vector2(-lines[i].direction.y(), lines[i].direction.x()), true, result) < projLines.size()) {
+ /* This should in principle not happen. The result is by definition
+ * already in the feasible region of this linear program. If it fails,
+ * it is due to small floating point error, and the current result is
+ * kept.
+ */
+ result = tempResult;
+ }
+
+ distance = det(lines[i].direction, lines[i].point - result);
+ }
+ }
+ }
+}
diff --git a/thirdparty/rvo2/rvo2_2d/Agent2d.h b/thirdparty/rvo2/rvo2_2d/Agent2d.h
new file mode 100644
index 0000000000..c666c2de7b
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Agent2d.h
@@ -0,0 +1,160 @@
+/*
+ * Agent2d.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO2D_AGENT_H_
+#define RVO2D_AGENT_H_
+
+/**
+ * \file Agent2d.h
+ * \brief Contains the Agent class.
+ */
+
+#include "Definitions.h"
+#include "RVOSimulator2d.h"
+
+namespace RVO2D {
+ /**
+ * \brief Defines an agent in the simulation.
+ */
+ class Agent2D {
+ public:
+ /**
+ * \brief Constructs an agent instance.
+ * \param sim The simulator instance.
+ */
+ explicit Agent2D();
+
+ /**
+ * \brief Computes the neighbors of this agent.
+ */
+ void computeNeighbors(RVOSimulator2D *sim_);
+
+ /**
+ * \brief Computes the new velocity of this agent.
+ */
+ void computeNewVelocity(RVOSimulator2D *sim_);
+
+ /**
+ * \brief Inserts an agent neighbor into the set of neighbors of
+ * this agent.
+ * \param agent A pointer to the agent to be inserted.
+ * \param rangeSq The squared range around this agent.
+ */
+ void insertAgentNeighbor(const Agent2D *agent, float &rangeSq);
+
+ /**
+ * \brief Inserts a static obstacle neighbor into the set of neighbors
+ * of this agent.
+ * \param obstacle The number of the static obstacle to be
+ * inserted.
+ * \param rangeSq The squared range around this agent.
+ */
+ void insertObstacleNeighbor(const Obstacle2D *obstacle, float rangeSq);
+
+ /**
+ * \brief Updates the two-dimensional position and two-dimensional
+ * velocity of this agent.
+ */
+ void update(RVOSimulator2D *sim_);
+
+ std::vector<std::pair<float, const Agent2D *> > agentNeighbors_;
+ size_t maxNeighbors_;
+ float maxSpeed_;
+ float neighborDist_;
+ Vector2 newVelocity_;
+ std::vector<std::pair<float, const Obstacle2D *> > obstacleNeighbors_;
+ std::vector<Line> orcaLines_;
+ Vector2 position_;
+ Vector2 prefVelocity_;
+ float radius_;
+ float timeHorizon_;
+ float timeHorizonObst_;
+ Vector2 velocity_;
+ float height_ = 0.0;
+ float elevation_ = 0.0;
+ uint32_t avoidance_layers_ = 1;
+ uint32_t avoidance_mask_ = 1;
+ float avoidance_priority_ = 1.0;
+
+ size_t id_;
+
+ friend class KdTree2D;
+ friend class RVOSimulator2D;
+ };
+
+ /**
+ * \relates Agent
+ * \brief Solves a one-dimensional linear program on a specified line
+ * subject to linear constraints defined by lines and a circular
+ * constraint.
+ * \param lines Lines defining the linear constraints.
+ * \param lineNo The specified line constraint.
+ * \param radius The radius of the circular constraint.
+ * \param optVelocity The optimization velocity.
+ * \param directionOpt True if the direction should be optimized.
+ * \param result A reference to the result of the linear program.
+ * \return True if successful.
+ */
+ bool linearProgram1(const std::vector<Line> &lines, size_t lineNo,
+ float radius, const Vector2 &optVelocity,
+ bool directionOpt, Vector2 &result);
+
+ /**
+ * \relates Agent
+ * \brief Solves a two-dimensional linear program subject to linear
+ * constraints defined by lines and a circular constraint.
+ * \param lines Lines defining the linear constraints.
+ * \param radius The radius of the circular constraint.
+ * \param optVelocity The optimization velocity.
+ * \param directionOpt True if the direction should be optimized.
+ * \param result A reference to the result of the linear program.
+ * \return The number of the line it fails on, and the number of lines if successful.
+ */
+ size_t linearProgram2(const std::vector<Line> &lines, float radius,
+ const Vector2 &optVelocity, bool directionOpt,
+ Vector2 &result);
+
+ /**
+ * \relates Agent
+ * \brief Solves a two-dimensional linear program subject to linear
+ * constraints defined by lines and a circular constraint.
+ * \param lines Lines defining the linear constraints.
+ * \param numObstLines Count of obstacle lines.
+ * \param beginLine The line on which the 2-d linear program failed.
+ * \param radius The radius of the circular constraint.
+ * \param result A reference to the result of the linear program.
+ */
+ void linearProgram3(const std::vector<Line> &lines, size_t numObstLines, size_t beginLine,
+ float radius, Vector2 &result);
+}
+
+#endif /* RVO2D_AGENT_H_ */
diff --git a/thirdparty/rvo2/rvo2_2d/Definitions.h b/thirdparty/rvo2/rvo2_2d/Definitions.h
new file mode 100644
index 0000000000..a5553d8378
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Definitions.h
@@ -0,0 +1,110 @@
+/*
+ * Definitions.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO2D_DEFINITIONS_H_
+#define RVO2D_DEFINITIONS_H_
+
+/**
+ * \file Definitions.h
+ * \brief Contains functions and constants used in multiple classes.
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+#include "Vector2.h"
+
+/**
+ * \brief A sufficiently small positive number.
+ */
+const float RVO_EPSILON = 0.00001f;
+
+namespace RVO2D {
+ class Agent2D;
+ class Obstacle2D;
+ class RVOSimulator2D;
+
+ /**
+ * \brief Computes the squared distance from a line segment with the
+ * specified endpoints to a specified point.
+ * \param a The first endpoint of the line segment.
+ * \param b The second endpoint of the line segment.
+ * \param c The point to which the squared distance is to
+ * be calculated.
+ * \return The squared distance from the line segment to the point.
+ */
+ inline float distSqPointLineSegment(const Vector2 &a, const Vector2 &b,
+ const Vector2 &c)
+ {
+ const float r = ((c - a) * (b - a)) / absSq(b - a);
+
+ if (r < 0.0f) {
+ return absSq(c - a);
+ }
+ else if (r > 1.0f) {
+ return absSq(c - b);
+ }
+ else {
+ return absSq(c - (a + r * (b - a)));
+ }
+ }
+
+ /**
+ * \brief Computes the signed distance from a line connecting the
+ * specified points to a specified point.
+ * \param a The first point on the line.
+ * \param b The second point on the line.
+ * \param c The point to which the signed distance is to
+ * be calculated.
+ * \return Positive when the point c lies to the left of the line ab.
+ */
+ inline float leftOf(const Vector2 &a, const Vector2 &b, const Vector2 &c)
+ {
+ return det(a - c, b - a);
+ }
+
+ /**
+ * \brief Computes the square of a float.
+ * \param a The float to be squared.
+ * \return The square of the float.
+ */
+ inline float sqr(float a)
+ {
+ return a * a;
+ }
+}
+
+#endif /* RVO2D_DEFINITIONS_H_ */
diff --git a/thirdparty/rvo2/rvo2_2d/KdTree2d.cpp b/thirdparty/rvo2/rvo2_2d/KdTree2d.cpp
new file mode 100644
index 0000000000..184bc74fe2
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/KdTree2d.cpp
@@ -0,0 +1,357 @@
+/*
+ * KdTree2d.cpp
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "KdTree2d.h"
+
+#include "Agent2d.h"
+#include "RVOSimulator2d.h"
+#include "Obstacle2d.h"
+
+namespace RVO2D {
+ KdTree2D::KdTree2D(RVOSimulator2D *sim) : obstacleTree_(NULL), sim_(sim) { }
+
+ KdTree2D::~KdTree2D()
+ {
+ deleteObstacleTree(obstacleTree_);
+ }
+
+ void KdTree2D::buildAgentTree(std::vector<Agent2D *> agents)
+ {
+ agents_.swap(agents);
+
+ if (!agents_.empty()) {
+ agentTree_.resize(2 * agents_.size() - 1);
+ buildAgentTreeRecursive(0, agents_.size(), 0);
+ }
+ }
+
+ void KdTree2D::buildAgentTreeRecursive(size_t begin, size_t end, size_t node)
+ {
+ agentTree_[node].begin = begin;
+ agentTree_[node].end = end;
+ agentTree_[node].minX = agentTree_[node].maxX = agents_[begin]->position_.x();
+ agentTree_[node].minY = agentTree_[node].maxY = agents_[begin]->position_.y();
+
+ for (size_t i = begin + 1; i < end; ++i) {
+ agentTree_[node].maxX = std::max(agentTree_[node].maxX, agents_[i]->position_.x());
+ agentTree_[node].minX = std::min(agentTree_[node].minX, agents_[i]->position_.x());
+ agentTree_[node].maxY = std::max(agentTree_[node].maxY, agents_[i]->position_.y());
+ agentTree_[node].minY = std::min(agentTree_[node].minY, agents_[i]->position_.y());
+ }
+
+ if (end - begin > MAX_LEAF_SIZE) {
+ /* No leaf node. */
+ const bool isVertical = (agentTree_[node].maxX - agentTree_[node].minX > agentTree_[node].maxY - agentTree_[node].minY);
+ const float splitValue = (isVertical ? 0.5f * (agentTree_[node].maxX + agentTree_[node].minX) : 0.5f * (agentTree_[node].maxY + agentTree_[node].minY));
+
+ size_t left = begin;
+ size_t right = end;
+
+ while (left < right) {
+ while (left < right && (isVertical ? agents_[left]->position_.x() : agents_[left]->position_.y()) < splitValue) {
+ ++left;
+ }
+
+ while (right > left && (isVertical ? agents_[right - 1]->position_.x() : agents_[right - 1]->position_.y()) >= splitValue) {
+ --right;
+ }
+
+ if (left < right) {
+ std::swap(agents_[left], agents_[right - 1]);
+ ++left;
+ --right;
+ }
+ }
+
+ if (left == begin) {
+ ++left;
+ ++right;
+ }
+
+ agentTree_[node].left = node + 1;
+ agentTree_[node].right = node + 2 * (left - begin);
+
+ buildAgentTreeRecursive(begin, left, agentTree_[node].left);
+ buildAgentTreeRecursive(left, end, agentTree_[node].right);
+ }
+ }
+
+ void KdTree2D::buildObstacleTree(std::vector<Obstacle2D *> obstacles)
+ {
+ deleteObstacleTree(obstacleTree_);
+
+ obstacleTree_ = buildObstacleTreeRecursive(obstacles);
+ }
+
+
+ KdTree2D::ObstacleTreeNode *KdTree2D::buildObstacleTreeRecursive(const std::vector<Obstacle2D *> &obstacles)
+ {
+ if (obstacles.empty()) {
+ return NULL;
+ }
+ else {
+ ObstacleTreeNode *const node = new ObstacleTreeNode;
+
+ size_t optimalSplit = 0;
+ size_t minLeft = obstacles.size();
+ size_t minRight = obstacles.size();
+
+ for (size_t i = 0; i < obstacles.size(); ++i) {
+ size_t leftSize = 0;
+ size_t rightSize = 0;
+
+ const Obstacle2D *const obstacleI1 = obstacles[i];
+ const Obstacle2D *const obstacleI2 = obstacleI1->nextObstacle_;
+
+ /* Compute optimal split node. */
+ for (size_t j = 0; j < obstacles.size(); ++j) {
+ if (i == j) {
+ continue;
+ }
+
+ const Obstacle2D *const obstacleJ1 = obstacles[j];
+ const Obstacle2D *const obstacleJ2 = obstacleJ1->nextObstacle_;
+
+ const float j1LeftOfI = leftOf(obstacleI1->point_, obstacleI2->point_, obstacleJ1->point_);
+ const float j2LeftOfI = leftOf(obstacleI1->point_, obstacleI2->point_, obstacleJ2->point_);
+
+ if (j1LeftOfI >= -RVO_EPSILON && j2LeftOfI >= -RVO_EPSILON) {
+ ++leftSize;
+ }
+ else if (j1LeftOfI <= RVO_EPSILON && j2LeftOfI <= RVO_EPSILON) {
+ ++rightSize;
+ }
+ else {
+ ++leftSize;
+ ++rightSize;
+ }
+
+ if (std::make_pair(std::max(leftSize, rightSize), std::min(leftSize, rightSize)) >= std::make_pair(std::max(minLeft, minRight), std::min(minLeft, minRight))) {
+ break;
+ }
+ }
+
+ if (std::make_pair(std::max(leftSize, rightSize), std::min(leftSize, rightSize)) < std::make_pair(std::max(minLeft, minRight), std::min(minLeft, minRight))) {
+ minLeft = leftSize;
+ minRight = rightSize;
+ optimalSplit = i;
+ }
+ }
+
+ /* Build split node. */
+ std::vector<Obstacle2D *> leftObstacles(minLeft);
+ std::vector<Obstacle2D *> rightObstacles(minRight);
+
+ size_t leftCounter = 0;
+ size_t rightCounter = 0;
+ const size_t i = optimalSplit;
+
+ const Obstacle2D *const obstacleI1 = obstacles[i];
+ const Obstacle2D *const obstacleI2 = obstacleI1->nextObstacle_;
+
+ for (size_t j = 0; j < obstacles.size(); ++j) {
+ if (i == j) {
+ continue;
+ }
+
+ Obstacle2D *const obstacleJ1 = obstacles[j];
+ Obstacle2D *const obstacleJ2 = obstacleJ1->nextObstacle_;
+
+ const float j1LeftOfI = leftOf(obstacleI1->point_, obstacleI2->point_, obstacleJ1->point_);
+ const float j2LeftOfI = leftOf(obstacleI1->point_, obstacleI2->point_, obstacleJ2->point_);
+
+ if (j1LeftOfI >= -RVO_EPSILON && j2LeftOfI >= -RVO_EPSILON) {
+ leftObstacles[leftCounter++] = obstacles[j];
+ }
+ else if (j1LeftOfI <= RVO_EPSILON && j2LeftOfI <= RVO_EPSILON) {
+ rightObstacles[rightCounter++] = obstacles[j];
+ }
+ else {
+ /* Split obstacle j. */
+ const float t = det(obstacleI2->point_ - obstacleI1->point_, obstacleJ1->point_ - obstacleI1->point_) / det(obstacleI2->point_ - obstacleI1->point_, obstacleJ1->point_ - obstacleJ2->point_);
+
+ const Vector2 splitpoint = obstacleJ1->point_ + t * (obstacleJ2->point_ - obstacleJ1->point_);
+
+ Obstacle2D *const newObstacle = new Obstacle2D();
+ newObstacle->point_ = splitpoint;
+ newObstacle->prevObstacle_ = obstacleJ1;
+ newObstacle->nextObstacle_ = obstacleJ2;
+ newObstacle->isConvex_ = true;
+ newObstacle->unitDir_ = obstacleJ1->unitDir_;
+
+ newObstacle->id_ = sim_->obstacles_.size();
+
+ sim_->obstacles_.push_back(newObstacle);
+
+ obstacleJ1->nextObstacle_ = newObstacle;
+ obstacleJ2->prevObstacle_ = newObstacle;
+
+ if (j1LeftOfI > 0.0f) {
+ leftObstacles[leftCounter++] = obstacleJ1;
+ rightObstacles[rightCounter++] = newObstacle;
+ }
+ else {
+ rightObstacles[rightCounter++] = obstacleJ1;
+ leftObstacles[leftCounter++] = newObstacle;
+ }
+ }
+ }
+
+ node->obstacle = obstacleI1;
+ node->left = buildObstacleTreeRecursive(leftObstacles);
+ node->right = buildObstacleTreeRecursive(rightObstacles);
+ return node;
+ }
+ }
+
+ void KdTree2D::computeAgentNeighbors(Agent2D *agent, float &rangeSq) const
+ {
+ queryAgentTreeRecursive(agent, rangeSq, 0);
+ }
+
+ void KdTree2D::computeObstacleNeighbors(Agent2D *agent, float rangeSq) const
+ {
+ queryObstacleTreeRecursive(agent, rangeSq, obstacleTree_);
+ }
+
+ void KdTree2D::deleteObstacleTree(ObstacleTreeNode *node)
+ {
+ if (node != NULL) {
+ deleteObstacleTree(node->left);
+ deleteObstacleTree(node->right);
+ delete node;
+ }
+ }
+
+ void KdTree2D::queryAgentTreeRecursive(Agent2D *agent, float &rangeSq, size_t node) const
+ {
+ if (agentTree_[node].end - agentTree_[node].begin <= MAX_LEAF_SIZE) {
+ for (size_t i = agentTree_[node].begin; i < agentTree_[node].end; ++i) {
+ agent->insertAgentNeighbor(agents_[i], rangeSq);
+ }
+ }
+ else {
+ const float distSqLeft = sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minX - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].left].maxX)) + sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minY - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].left].maxY));
+
+ const float distSqRight = sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minX - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].right].maxX)) + sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minY - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].right].maxY));
+
+ if (distSqLeft < distSqRight) {
+ if (distSqLeft < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
+
+ if (distSqRight < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
+ }
+ }
+ }
+ else {
+ if (distSqRight < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
+
+ if (distSqLeft < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
+ }
+ }
+ }
+
+ }
+ }
+
+ void KdTree2D::queryObstacleTreeRecursive(Agent2D *agent, float rangeSq, const ObstacleTreeNode *node) const
+ {
+ if (node == NULL) {
+ return;
+ }
+ else {
+ const Obstacle2D *const obstacle1 = node->obstacle;
+ const Obstacle2D *const obstacle2 = obstacle1->nextObstacle_;
+
+ const float agentLeftOfLine = leftOf(obstacle1->point_, obstacle2->point_, agent->position_);
+
+ queryObstacleTreeRecursive(agent, rangeSq, (agentLeftOfLine >= 0.0f ? node->left : node->right));
+
+ const float distSqLine = sqr(agentLeftOfLine) / absSq(obstacle2->point_ - obstacle1->point_);
+
+ if (distSqLine < rangeSq) {
+ if (agentLeftOfLine < 0.0f) {
+ /*
+ * Try obstacle at this node only if agent is on right side of
+ * obstacle (and can see obstacle).
+ */
+ agent->insertObstacleNeighbor(node->obstacle, rangeSq);
+ }
+
+ /* Try other side of line. */
+ queryObstacleTreeRecursive(agent, rangeSq, (agentLeftOfLine >= 0.0f ? node->right : node->left));
+
+ }
+ }
+ }
+
+ bool KdTree2D::queryVisibility(const Vector2 &q1, const Vector2 &q2, float radius) const
+ {
+ return queryVisibilityRecursive(q1, q2, radius, obstacleTree_);
+ }
+
+ bool KdTree2D::queryVisibilityRecursive(const Vector2 &q1, const Vector2 &q2, float radius, const ObstacleTreeNode *node) const
+ {
+ if (node == NULL) {
+ return true;
+ }
+ else {
+ const Obstacle2D *const obstacle1 = node->obstacle;
+ const Obstacle2D *const obstacle2 = obstacle1->nextObstacle_;
+
+ const float q1LeftOfI = leftOf(obstacle1->point_, obstacle2->point_, q1);
+ const float q2LeftOfI = leftOf(obstacle1->point_, obstacle2->point_, q2);
+ const float invLengthI = 1.0f / absSq(obstacle2->point_ - obstacle1->point_);
+
+ if (q1LeftOfI >= 0.0f && q2LeftOfI >= 0.0f) {
+ return queryVisibilityRecursive(q1, q2, radius, node->left) && ((sqr(q1LeftOfI) * invLengthI >= sqr(radius) && sqr(q2LeftOfI) * invLengthI >= sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node->right));
+ }
+ else if (q1LeftOfI <= 0.0f && q2LeftOfI <= 0.0f) {
+ return queryVisibilityRecursive(q1, q2, radius, node->right) && ((sqr(q1LeftOfI) * invLengthI >= sqr(radius) && sqr(q2LeftOfI) * invLengthI >= sqr(radius)) || queryVisibilityRecursive(q1, q2, radius, node->left));
+ }
+ else if (q1LeftOfI >= 0.0f && q2LeftOfI <= 0.0f) {
+ /* One can see through obstacle from left to right. */
+ return queryVisibilityRecursive(q1, q2, radius, node->left) && queryVisibilityRecursive(q1, q2, radius, node->right);
+ }
+ else {
+ const float point1LeftOfQ = leftOf(q1, q2, obstacle1->point_);
+ const float point2LeftOfQ = leftOf(q1, q2, obstacle2->point_);
+ const float invLengthQ = 1.0f / absSq(q2 - q1);
+
+ return (point1LeftOfQ * point2LeftOfQ >= 0.0f && sqr(point1LeftOfQ) * invLengthQ > sqr(radius) && sqr(point2LeftOfQ) * invLengthQ > sqr(radius) && queryVisibilityRecursive(q1, q2, radius, node->left) && queryVisibilityRecursive(q1, q2, radius, node->right));
+ }
+ }
+ }
+}
diff --git a/thirdparty/rvo2/rvo2_2d/KdTree2d.h b/thirdparty/rvo2/rvo2_2d/KdTree2d.h
new file mode 100644
index 0000000000..c7159eab97
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/KdTree2d.h
@@ -0,0 +1,203 @@
+/*
+ * KdTree2d.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO2D_KD_TREE_H_
+#define RVO2D_KD_TREE_H_
+
+/**
+ * \file KdTree2d.h
+ * \brief Contains the KdTree class.
+ */
+
+#include "Definitions.h"
+
+namespace RVO2D {
+ /**
+ * \brief Defines <i>k</i>d-trees for agents and static obstacles in the
+ * simulation.
+ */
+ class KdTree2D {
+ public:
+ /**
+ * \brief Defines an agent <i>k</i>d-tree node.
+ */
+ class AgentTreeNode {
+ public:
+ /**
+ * \brief The beginning node number.
+ */
+ size_t begin;
+
+ /**
+ * \brief The ending node number.
+ */
+ size_t end;
+
+ /**
+ * \brief The left node number.
+ */
+ size_t left;
+
+ /**
+ * \brief The maximum x-coordinate.
+ */
+ float maxX;
+
+ /**
+ * \brief The maximum y-coordinate.
+ */
+ float maxY;
+
+ /**
+ * \brief The minimum x-coordinate.
+ */
+ float minX;
+
+ /**
+ * \brief The minimum y-coordinate.
+ */
+ float minY;
+
+ /**
+ * \brief The right node number.
+ */
+ size_t right;
+ };
+
+ /**
+ * \brief Defines an obstacle <i>k</i>d-tree node.
+ */
+ class ObstacleTreeNode {
+ public:
+ /**
+ * \brief The left obstacle tree node.
+ */
+ ObstacleTreeNode *left;
+
+ /**
+ * \brief The obstacle number.
+ */
+ const Obstacle2D *obstacle;
+
+ /**
+ * \brief The right obstacle tree node.
+ */
+ ObstacleTreeNode *right;
+ };
+
+ /**
+ * \brief Constructs a <i>k</i>d-tree instance.
+ * \param sim The simulator instance.
+ */
+ explicit KdTree2D(RVOSimulator2D *sim);
+
+ /**
+ * \brief Destroys this kd-tree instance.
+ */
+ ~KdTree2D();
+
+ /**
+ * \brief Builds an agent <i>k</i>d-tree.
+ */
+ void buildAgentTree(std::vector<Agent2D *> agents);
+
+ void buildAgentTreeRecursive(size_t begin, size_t end, size_t node);
+
+ /**
+ * \brief Builds an obstacle <i>k</i>d-tree.
+ */
+ void buildObstacleTree(std::vector<Obstacle2D *> obstacles);
+
+ ObstacleTreeNode *buildObstacleTreeRecursive(const std::vector<Obstacle2D *> &
+ obstacles);
+
+ /**
+ * \brief Computes the agent neighbors of the specified agent.
+ * \param agent A pointer to the agent for which agent
+ * neighbors are to be computed.
+ * \param rangeSq The squared range around the agent.
+ */
+ void computeAgentNeighbors(Agent2D *agent, float &rangeSq) const;
+
+ /**
+ * \brief Computes the obstacle neighbors of the specified agent.
+ * \param agent A pointer to the agent for which obstacle
+ * neighbors are to be computed.
+ * \param rangeSq The squared range around the agent.
+ */
+ void computeObstacleNeighbors(Agent2D *agent, float rangeSq) const;
+
+ /**
+ * \brief Deletes the specified obstacle tree node.
+ * \param node A pointer to the obstacle tree node to be
+ * deleted.
+ */
+ void deleteObstacleTree(ObstacleTreeNode *node);
+
+ void queryAgentTreeRecursive(Agent2D *agent, float &rangeSq,
+ size_t node) const;
+
+ void queryObstacleTreeRecursive(Agent2D *agent, float rangeSq,
+ const ObstacleTreeNode *node) const;
+
+ /**
+ * \brief Queries the visibility between two points within a
+ * specified radius.
+ * \param q1 The first point between which visibility is
+ * to be tested.
+ * \param q2 The second point between which visibility is
+ * to be tested.
+ * \param radius The radius within which visibility is to be
+ * tested.
+ * \return True if q1 and q2 are mutually visible within the radius;
+ * false otherwise.
+ */
+ bool queryVisibility(const Vector2 &q1, const Vector2 &q2,
+ float radius) const;
+
+ bool queryVisibilityRecursive(const Vector2 &q1, const Vector2 &q2,
+ float radius,
+ const ObstacleTreeNode *node) const;
+
+ std::vector<Agent2D *> agents_;
+ std::vector<AgentTreeNode> agentTree_;
+ ObstacleTreeNode *obstacleTree_;
+ RVOSimulator2D *sim_;
+
+ static const size_t MAX_LEAF_SIZE = 10;
+
+ friend class Agent2D;
+ friend class RVOSimulator2D;
+ };
+}
+
+#endif /* RVO2D_KD_TREE_H_ */
diff --git a/thirdparty/rvo2/rvo2_2d/Obstacle2d.cpp b/thirdparty/rvo2/rvo2_2d/Obstacle2d.cpp
new file mode 100644
index 0000000000..a80c8af136
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Obstacle2d.cpp
@@ -0,0 +1,38 @@
+/*
+ * Obstacle2d.cpp
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "Obstacle2d.h"
+#include "RVOSimulator2d.h"
+
+namespace RVO2D {
+ Obstacle2D::Obstacle2D() : isConvex_(false), nextObstacle_(NULL), prevObstacle_(NULL), id_(0) { }
+}
diff --git a/thirdparty/rvo2/rvo2_2d/Obstacle2d.h b/thirdparty/rvo2/rvo2_2d/Obstacle2d.h
new file mode 100644
index 0000000000..9ba5937053
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Obstacle2d.h
@@ -0,0 +1,72 @@
+/*
+ * Obstacle2d.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO2D_OBSTACLE_H_
+#define RVO2D_OBSTACLE_H_
+
+/**
+ * \file Obstacle2d.h
+ * \brief Contains the Obstacle class.
+ */
+
+#include "Definitions.h"
+
+namespace RVO2D {
+ /**
+ * \brief Defines static obstacles in the simulation.
+ */
+ class Obstacle2D {
+ public:
+ /**
+ * \brief Constructs a static obstacle instance.
+ */
+ Obstacle2D();
+
+ bool isConvex_;
+ Obstacle2D *nextObstacle_;
+ Vector2 point_;
+ Obstacle2D *prevObstacle_;
+ Vector2 unitDir_;
+
+ float height_ = 1.0;
+ float elevation_ = 0.0;
+ uint32_t avoidance_layers_ = 1;
+
+ size_t id_;
+
+ friend class Agent2D;
+ friend class KdTree2D;
+ friend class RVOSimulator2D;
+ };
+}
+
+#endif /* RVO2D_OBSTACLE_H_ */
diff --git a/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.cpp b/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.cpp
new file mode 100644
index 0000000000..9fb1555ebc
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.cpp
@@ -0,0 +1,363 @@
+/*
+ * RVOSimulator2d.cpp
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "RVOSimulator2d.h"
+
+#include "Agent2d.h"
+#include "KdTree2d.h"
+#include "Obstacle2d.h"
+
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+namespace RVO2D {
+ RVOSimulator2D::RVOSimulator2D() : defaultAgent_(NULL), globalTime_(0.0f), kdTree_(NULL), timeStep_(0.0f)
+ {
+ kdTree_ = new KdTree2D(this);
+ }
+
+ RVOSimulator2D::RVOSimulator2D(float timeStep, float neighborDist, size_t maxNeighbors, float timeHorizon, float timeHorizonObst, float radius, float maxSpeed, const Vector2 &velocity) : defaultAgent_(NULL), globalTime_(0.0f), kdTree_(NULL), timeStep_(timeStep)
+ {
+ kdTree_ = new KdTree2D(this);
+ defaultAgent_ = new Agent2D();
+
+ defaultAgent_->maxNeighbors_ = maxNeighbors;
+ defaultAgent_->maxSpeed_ = maxSpeed;
+ defaultAgent_->neighborDist_ = neighborDist;
+ defaultAgent_->radius_ = radius;
+ defaultAgent_->timeHorizon_ = timeHorizon;
+ defaultAgent_->timeHorizonObst_ = timeHorizonObst;
+ defaultAgent_->velocity_ = velocity;
+ }
+
+ RVOSimulator2D::~RVOSimulator2D()
+ {
+ if (defaultAgent_ != NULL) {
+ delete defaultAgent_;
+ }
+
+ for (size_t i = 0; i < agents_.size(); ++i) {
+ delete agents_[i];
+ }
+
+ for (size_t i = 0; i < obstacles_.size(); ++i) {
+ delete obstacles_[i];
+ }
+
+ delete kdTree_;
+ }
+
+ size_t RVOSimulator2D::addAgent(const Vector2 &position)
+ {
+ if (defaultAgent_ == NULL) {
+ return RVO2D_ERROR;
+ }
+
+ Agent2D *agent = new Agent2D();
+
+ agent->position_ = position;
+ agent->maxNeighbors_ = defaultAgent_->maxNeighbors_;
+ agent->maxSpeed_ = defaultAgent_->maxSpeed_;
+ agent->neighborDist_ = defaultAgent_->neighborDist_;
+ agent->radius_ = defaultAgent_->radius_;
+ agent->timeHorizon_ = defaultAgent_->timeHorizon_;
+ agent->timeHorizonObst_ = defaultAgent_->timeHorizonObst_;
+ agent->velocity_ = defaultAgent_->velocity_;
+
+ agent->id_ = agents_.size();
+
+ agents_.push_back(agent);
+
+ return agents_.size() - 1;
+ }
+
+ size_t RVOSimulator2D::addAgent(const Vector2 &position, float neighborDist, size_t maxNeighbors, float timeHorizon, float timeHorizonObst, float radius, float maxSpeed, const Vector2 &velocity)
+ {
+ Agent2D *agent = new Agent2D();
+
+ agent->position_ = position;
+ agent->maxNeighbors_ = maxNeighbors;
+ agent->maxSpeed_ = maxSpeed;
+ agent->neighborDist_ = neighborDist;
+ agent->radius_ = radius;
+ agent->timeHorizon_ = timeHorizon;
+ agent->timeHorizonObst_ = timeHorizonObst;
+ agent->velocity_ = velocity;
+
+ agent->id_ = agents_.size();
+
+ agents_.push_back(agent);
+
+ return agents_.size() - 1;
+ }
+
+ size_t RVOSimulator2D::addObstacle(const std::vector<Vector2> &vertices)
+ {
+ if (vertices.size() < 2) {
+ return RVO2D_ERROR;
+ }
+
+ const size_t obstacleNo = obstacles_.size();
+
+ for (size_t i = 0; i < vertices.size(); ++i) {
+ Obstacle2D *obstacle = new Obstacle2D();
+ obstacle->point_ = vertices[i];
+
+ if (i != 0) {
+ obstacle->prevObstacle_ = obstacles_.back();
+ obstacle->prevObstacle_->nextObstacle_ = obstacle;
+ }
+
+ if (i == vertices.size() - 1) {
+ obstacle->nextObstacle_ = obstacles_[obstacleNo];
+ obstacle->nextObstacle_->prevObstacle_ = obstacle;
+ }
+
+ obstacle->unitDir_ = normalize(vertices[(i == vertices.size() - 1 ? 0 : i + 1)] - vertices[i]);
+
+ if (vertices.size() == 2) {
+ obstacle->isConvex_ = true;
+ }
+ else {
+ obstacle->isConvex_ = (leftOf(vertices[(i == 0 ? vertices.size() - 1 : i - 1)], vertices[i], vertices[(i == vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);
+ }
+
+ obstacle->id_ = obstacles_.size();
+
+ obstacles_.push_back(obstacle);
+ }
+
+ return obstacleNo;
+ }
+
+ void RVOSimulator2D::doStep()
+ {
+ kdTree_->buildAgentTree(agents_);
+
+ for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
+ agents_[i]->computeNeighbors(this);
+ agents_[i]->computeNewVelocity(this);
+ }
+
+ for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
+ agents_[i]->update(this);
+ }
+
+ globalTime_ += timeStep_;
+ }
+
+ size_t RVOSimulator2D::getAgentAgentNeighbor(size_t agentNo, size_t neighborNo) const
+ {
+ return agents_[agentNo]->agentNeighbors_[neighborNo].second->id_;
+ }
+
+ size_t RVOSimulator2D::getAgentMaxNeighbors(size_t agentNo) const
+ {
+ return agents_[agentNo]->maxNeighbors_;
+ }
+
+ float RVOSimulator2D::getAgentMaxSpeed(size_t agentNo) const
+ {
+ return agents_[agentNo]->maxSpeed_;
+ }
+
+ float RVOSimulator2D::getAgentNeighborDist(size_t agentNo) const
+ {
+ return agents_[agentNo]->neighborDist_;
+ }
+
+ size_t RVOSimulator2D::getAgentNumAgentNeighbors(size_t agentNo) const
+ {
+ return agents_[agentNo]->agentNeighbors_.size();
+ }
+
+ size_t RVOSimulator2D::getAgentNumObstacleNeighbors(size_t agentNo) const
+ {
+ return agents_[agentNo]->obstacleNeighbors_.size();
+ }
+
+ size_t RVOSimulator2D::getAgentNumORCALines(size_t agentNo) const
+ {
+ return agents_[agentNo]->orcaLines_.size();
+ }
+
+ size_t RVOSimulator2D::getAgentObstacleNeighbor(size_t agentNo, size_t neighborNo) const
+ {
+ return agents_[agentNo]->obstacleNeighbors_[neighborNo].second->id_;
+ }
+
+ const Line &RVOSimulator2D::getAgentORCALine(size_t agentNo, size_t lineNo) const
+ {
+ return agents_[agentNo]->orcaLines_[lineNo];
+ }
+
+ const Vector2 &RVOSimulator2D::getAgentPosition(size_t agentNo) const
+ {
+ return agents_[agentNo]->position_;
+ }
+
+ const Vector2 &RVOSimulator2D::getAgentPrefVelocity(size_t agentNo) const
+ {
+ return agents_[agentNo]->prefVelocity_;
+ }
+
+ float RVOSimulator2D::getAgentRadius(size_t agentNo) const
+ {
+ return agents_[agentNo]->radius_;
+ }
+
+ float RVOSimulator2D::getAgentTimeHorizon(size_t agentNo) const
+ {
+ return agents_[agentNo]->timeHorizon_;
+ }
+
+ float RVOSimulator2D::getAgentTimeHorizonObst(size_t agentNo) const
+ {
+ return agents_[agentNo]->timeHorizonObst_;
+ }
+
+ const Vector2 &RVOSimulator2D::getAgentVelocity(size_t agentNo) const
+ {
+ return agents_[agentNo]->velocity_;
+ }
+
+ float RVOSimulator2D::getGlobalTime() const
+ {
+ return globalTime_;
+ }
+
+ size_t RVOSimulator2D::getNumAgents() const
+ {
+ return agents_.size();
+ }
+
+ size_t RVOSimulator2D::getNumObstacleVertices() const
+ {
+ return obstacles_.size();
+ }
+
+ const Vector2 &RVOSimulator2D::getObstacleVertex(size_t vertexNo) const
+ {
+ return obstacles_[vertexNo]->point_;
+ }
+
+ size_t RVOSimulator2D::getNextObstacleVertexNo(size_t vertexNo) const
+ {
+ return obstacles_[vertexNo]->nextObstacle_->id_;
+ }
+
+ size_t RVOSimulator2D::getPrevObstacleVertexNo(size_t vertexNo) const
+ {
+ return obstacles_[vertexNo]->prevObstacle_->id_;
+ }
+
+ float RVOSimulator2D::getTimeStep() const
+ {
+ return timeStep_;
+ }
+
+ void RVOSimulator2D::processObstacles()
+ {
+ kdTree_->buildObstacleTree(obstacles_);
+ }
+
+ bool RVOSimulator2D::queryVisibility(const Vector2 &point1, const Vector2 &point2, float radius) const
+ {
+ return kdTree_->queryVisibility(point1, point2, radius);
+ }
+
+ void RVOSimulator2D::setAgentDefaults(float neighborDist, size_t maxNeighbors, float timeHorizon, float timeHorizonObst, float radius, float maxSpeed, const Vector2 &velocity)
+ {
+ if (defaultAgent_ == NULL) {
+ defaultAgent_ = new Agent2D();
+ }
+
+ defaultAgent_->maxNeighbors_ = maxNeighbors;
+ defaultAgent_->maxSpeed_ = maxSpeed;
+ defaultAgent_->neighborDist_ = neighborDist;
+ defaultAgent_->radius_ = radius;
+ defaultAgent_->timeHorizon_ = timeHorizon;
+ defaultAgent_->timeHorizonObst_ = timeHorizonObst;
+ defaultAgent_->velocity_ = velocity;
+ }
+
+ void RVOSimulator2D::setAgentMaxNeighbors(size_t agentNo, size_t maxNeighbors)
+ {
+ agents_[agentNo]->maxNeighbors_ = maxNeighbors;
+ }
+
+ void RVOSimulator2D::setAgentMaxSpeed(size_t agentNo, float maxSpeed)
+ {
+ agents_[agentNo]->maxSpeed_ = maxSpeed;
+ }
+
+ void RVOSimulator2D::setAgentNeighborDist(size_t agentNo, float neighborDist)
+ {
+ agents_[agentNo]->neighborDist_ = neighborDist;
+ }
+
+ void RVOSimulator2D::setAgentPosition(size_t agentNo, const Vector2 &position)
+ {
+ agents_[agentNo]->position_ = position;
+ }
+
+ void RVOSimulator2D::setAgentPrefVelocity(size_t agentNo, const Vector2 &prefVelocity)
+ {
+ agents_[agentNo]->prefVelocity_ = prefVelocity;
+ }
+
+ void RVOSimulator2D::setAgentRadius(size_t agentNo, float radius)
+ {
+ agents_[agentNo]->radius_ = radius;
+ }
+
+ void RVOSimulator2D::setAgentTimeHorizon(size_t agentNo, float timeHorizon)
+ {
+ agents_[agentNo]->timeHorizon_ = timeHorizon;
+ }
+
+ void RVOSimulator2D::setAgentTimeHorizonObst(size_t agentNo, float timeHorizonObst)
+ {
+ agents_[agentNo]->timeHorizonObst_ = timeHorizonObst;
+ }
+
+ void RVOSimulator2D::setAgentVelocity(size_t agentNo, const Vector2 &velocity)
+ {
+ agents_[agentNo]->velocity_ = velocity;
+ }
+
+ void RVOSimulator2D::setTimeStep(float timeStep)
+ {
+ timeStep_ = timeStep;
+ }
+}
diff --git a/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.h b/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.h
new file mode 100644
index 0000000000..e074e0fe0e
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/RVOSimulator2d.h
@@ -0,0 +1,592 @@
+/*
+ * RVOSimulator2d.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO2D_RVO_SIMULATOR_H_
+#define RVO2D_RVO_SIMULATOR_H_
+
+/**
+ * \file RVOSimulator2d.h
+ * \brief Contains the RVOSimulator2D class.
+ */
+
+#include <cstddef>
+#include <limits>
+#include <vector>
+
+#include "Vector2.h"
+
+namespace RVO2D {
+ /**
+ * \brief Error value.
+ *
+ * A value equal to the largest unsigned integer that is returned in case
+ * of an error by functions in RVO2D::RVOSimulator2D.
+ */
+ const size_t RVO2D_ERROR = std::numeric_limits<size_t>::max();
+
+ /**
+ * \brief Defines a directed line.
+ */
+ class Line {
+ public:
+ /**
+ * \brief A point on the directed line.
+ */
+ Vector2 point;
+
+ /**
+ * \brief The direction of the directed line.
+ */
+ Vector2 direction;
+ };
+
+ class Agent2D;
+ class KdTree2D;
+ class Obstacle2D;
+
+ /**
+ * \brief Defines the simulation.
+ *
+ * The main class of the library that contains all simulation functionality.
+ */
+ class RVOSimulator2D {
+ public:
+ /**
+ * \brief Constructs a simulator instance.
+ */
+ RVOSimulator2D();
+
+ /**
+ * \brief Constructs a simulator instance and sets the default
+ * properties for any new agent that is added.
+ * \param timeStep The time step of the simulation.
+ * Must be positive.
+ * \param neighborDist The default maximum distance (center point
+ * to center point) to other agents a new agent
+ * takes into account in the navigation. The
+ * larger this number, the longer he running
+ * time of the simulation. If the number is too
+ * low, the simulation will not be safe. Must be
+ * non-negative.
+ * \param maxNeighbors The default maximum number of other agents a
+ * new agent takes into account in the
+ * navigation. The larger this number, the
+ * longer the running time of the simulation.
+ * If the number is too low, the simulation
+ * will not be safe.
+ * \param timeHorizon The default minimal amount of time for which
+ * a new agent's velocities that are computed
+ * by the simulation are safe with respect to
+ * other agents. The larger this number, the
+ * sooner an agent will respond to the presence
+ * of other agents, but the less freedom the
+ * agent has in choosing its velocities.
+ * Must be positive.
+ * \param timeHorizonObst The default minimal amount of time for which
+ * a new agent's velocities that are computed
+ * by the simulation are safe with respect to
+ * obstacles. The larger this number, the
+ * sooner an agent will respond to the presence
+ * of obstacles, but the less freedom the agent
+ * has in choosing its velocities.
+ * Must be positive.
+ * \param radius The default radius of a new agent.
+ * Must be non-negative.
+ * \param maxSpeed The default maximum speed of a new agent.
+ * Must be non-negative.
+ * \param velocity The default initial two-dimensional linear
+ * velocity of a new agent (optional).
+ */
+ RVOSimulator2D(float timeStep, float neighborDist, size_t maxNeighbors,
+ float timeHorizon, float timeHorizonObst, float radius,
+ float maxSpeed, const Vector2 &velocity = Vector2());
+
+ /**
+ * \brief Destroys this simulator instance.
+ */
+ ~RVOSimulator2D();
+
+ /**
+ * \brief Adds a new agent with default properties to the
+ * simulation.
+ * \param position The two-dimensional starting position of
+ * this agent.
+ * \return The number of the agent, or RVO2D::RVO2D_ERROR when the agent
+ * defaults have not been set.
+ */
+ size_t addAgent(const Vector2 &position);
+
+ /**
+ * \brief Adds a new agent to the simulation.
+ * \param position The two-dimensional starting position of
+ * this agent.
+ * \param neighborDist The maximum distance (center point to
+ * center point) to other agents this agent
+ * takes into account in the navigation. The
+ * larger this number, the longer the running
+ * time of the simulation. If the number is too
+ * low, the simulation will not be safe.
+ * Must be non-negative.
+ * \param maxNeighbors The maximum number of other agents this
+ * agent takes into account in the navigation.
+ * The larger this number, the longer the
+ * running time of the simulation. If the
+ * number is too low, the simulation will not
+ * be safe.
+ * \param timeHorizon The minimal amount of time for which this
+ * agent's velocities that are computed by the
+ * simulation are safe with respect to other
+ * agents. The larger this number, the sooner
+ * this agent will respond to the presence of
+ * other agents, but the less freedom this
+ * agent has in choosing its velocities.
+ * Must be positive.
+ * \param timeHorizonObst The minimal amount of time for which this
+ * agent's velocities that are computed by the
+ * simulation are safe with respect to
+ * obstacles. The larger this number, the
+ * sooner this agent will respond to the
+ * presence of obstacles, but the less freedom
+ * this agent has in choosing its velocities.
+ * Must be positive.
+ * \param radius The radius of this agent.
+ * Must be non-negative.
+ * \param maxSpeed The maximum speed of this agent.
+ * Must be non-negative.
+ * \param velocity The initial two-dimensional linear velocity
+ * of this agent (optional).
+ * \return The number of the agent.
+ */
+ size_t addAgent(const Vector2 &position, float neighborDist,
+ size_t maxNeighbors, float timeHorizon,
+ float timeHorizonObst, float radius, float maxSpeed,
+ const Vector2 &velocity = Vector2());
+
+ /**
+ * \brief Adds a new obstacle to the simulation.
+ * \param vertices List of the vertices of the polygonal
+ * obstacle in counterclockwise order.
+ * \return The number of the first vertex of the obstacle,
+ * or RVO2D::RVO2D_ERROR when the number of vertices is less than two.
+ * \note To add a "negative" obstacle, e.g. a bounding polygon around
+ * the environment, the vertices should be listed in clockwise
+ * order.
+ */
+ size_t addObstacle(const std::vector<Vector2> &vertices);
+
+ /**
+ * \brief Lets the simulator perform a simulation step and updates the
+ * two-dimensional position and two-dimensional velocity of
+ * each agent.
+ */
+ void doStep();
+
+ /**
+ * \brief Returns the specified agent neighbor of the specified
+ * agent.
+ * \param agentNo The number of the agent whose agent
+ * neighbor is to be retrieved.
+ * \param neighborNo The number of the agent neighbor to be
+ * retrieved.
+ * \return The number of the neighboring agent.
+ */
+ size_t getAgentAgentNeighbor(size_t agentNo, size_t neighborNo) const;
+
+ /**
+ * \brief Returns the maximum neighbor count of a specified agent.
+ * \param agentNo The number of the agent whose maximum
+ * neighbor count is to be retrieved.
+ * \return The present maximum neighbor count of the agent.
+ */
+ size_t getAgentMaxNeighbors(size_t agentNo) const;
+
+ /**
+ * \brief Returns the maximum speed of a specified agent.
+ * \param agentNo The number of the agent whose maximum speed
+ * is to be retrieved.
+ * \return The present maximum speed of the agent.
+ */
+ float getAgentMaxSpeed(size_t agentNo) const;
+
+ /**
+ * \brief Returns the maximum neighbor distance of a specified
+ * agent.
+ * \param agentNo The number of the agent whose maximum
+ * neighbor distance is to be retrieved.
+ * \return The present maximum neighbor distance of the agent.
+ */
+ float getAgentNeighborDist(size_t agentNo) const;
+
+ /**
+ * \brief Returns the count of agent neighbors taken into account to
+ * compute the current velocity for the specified agent.
+ * \param agentNo The number of the agent whose count of agent
+ * neighbors is to be retrieved.
+ * \return The count of agent neighbors taken into account to compute
+ * the current velocity for the specified agent.
+ */
+ size_t getAgentNumAgentNeighbors(size_t agentNo) const;
+
+ /**
+ * \brief Returns the count of obstacle neighbors taken into account
+ * to compute the current velocity for the specified agent.
+ * \param agentNo The number of the agent whose count of
+ * obstacle neighbors is to be retrieved.
+ * \return The count of obstacle neighbors taken into account to
+ * compute the current velocity for the specified agent.
+ */
+ size_t getAgentNumObstacleNeighbors(size_t agentNo) const;
+
+
+ /**
+ * \brief Returns the count of ORCA constraints used to compute
+ * the current velocity for the specified agent.
+ * \param agentNo The number of the agent whose count of ORCA
+ * constraints is to be retrieved.
+ * \return The count of ORCA constraints used to compute the current
+ * velocity for the specified agent.
+ */
+ size_t getAgentNumORCALines(size_t agentNo) const;
+
+ /**
+ * \brief Returns the specified obstacle neighbor of the specified
+ * agent.
+ * \param agentNo The number of the agent whose obstacle
+ * neighbor is to be retrieved.
+ * \param neighborNo The number of the obstacle neighbor to be
+ * retrieved.
+ * \return The number of the first vertex of the neighboring obstacle
+ * edge.
+ */
+ size_t getAgentObstacleNeighbor(size_t agentNo, size_t neighborNo) const;
+
+ /**
+ * \brief Returns the specified ORCA constraint of the specified
+ * agent.
+ * \param agentNo The number of the agent whose ORCA
+ * constraint is to be retrieved.
+ * \param lineNo The number of the ORCA constraint to be
+ * retrieved.
+ * \return A line representing the specified ORCA constraint.
+ * \note The halfplane to the left of the line is the region of
+ * permissible velocities with respect to the specified
+ * ORCA constraint.
+ */
+ const Line &getAgentORCALine(size_t agentNo, size_t lineNo) const;
+
+ /**
+ * \brief Returns the two-dimensional position of a specified
+ * agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional position is to be retrieved.
+ * \return The present two-dimensional position of the (center of the)
+ * agent.
+ */
+ const Vector2 &getAgentPosition(size_t agentNo) const;
+
+ /**
+ * \brief Returns the two-dimensional preferred velocity of a
+ * specified agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional preferred velocity is to be
+ * retrieved.
+ * \return The present two-dimensional preferred velocity of the agent.
+ */
+ const Vector2 &getAgentPrefVelocity(size_t agentNo) const;
+
+ /**
+ * \brief Returns the radius of a specified agent.
+ * \param agentNo The number of the agent whose radius is to
+ * be retrieved.
+ * \return The present radius of the agent.
+ */
+ float getAgentRadius(size_t agentNo) const;
+
+ /**
+ * \brief Returns the time horizon of a specified agent.
+ * \param agentNo The number of the agent whose time horizon
+ * is to be retrieved.
+ * \return The present time horizon of the agent.
+ */
+ float getAgentTimeHorizon(size_t agentNo) const;
+
+ /**
+ * \brief Returns the time horizon with respect to obstacles of a
+ * specified agent.
+ * \param agentNo The number of the agent whose time horizon
+ * with respect to obstacles is to be
+ * retrieved.
+ * \return The present time horizon with respect to obstacles of the
+ * agent.
+ */
+ float getAgentTimeHorizonObst(size_t agentNo) const;
+
+ /**
+ * \brief Returns the two-dimensional linear velocity of a
+ * specified agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional linear velocity is to be
+ * retrieved.
+ * \return The present two-dimensional linear velocity of the agent.
+ */
+ const Vector2 &getAgentVelocity(size_t agentNo) const;
+
+ /**
+ * \brief Returns the global time of the simulation.
+ * \return The present global time of the simulation (zero initially).
+ */
+ float getGlobalTime() const;
+
+ /**
+ * \brief Returns the count of agents in the simulation.
+ * \return The count of agents in the simulation.
+ */
+ size_t getNumAgents() const;
+
+ /**
+ * \brief Returns the count of obstacle vertices in the simulation.
+ * \return The count of obstacle vertices in the simulation.
+ */
+ size_t getNumObstacleVertices() const;
+
+ /**
+ * \brief Returns the two-dimensional position of a specified obstacle
+ * vertex.
+ * \param vertexNo The number of the obstacle vertex to be
+ * retrieved.
+ * \return The two-dimensional position of the specified obstacle
+ * vertex.
+ */
+ const Vector2 &getObstacleVertex(size_t vertexNo) const;
+
+ /**
+ * \brief Returns the number of the obstacle vertex succeeding the
+ * specified obstacle vertex in its polygon.
+ * \param vertexNo The number of the obstacle vertex whose
+ * successor is to be retrieved.
+ * \return The number of the obstacle vertex succeeding the specified
+ * obstacle vertex in its polygon.
+ */
+ size_t getNextObstacleVertexNo(size_t vertexNo) const;
+
+ /**
+ * \brief Returns the number of the obstacle vertex preceding the
+ * specified obstacle vertex in its polygon.
+ * \param vertexNo The number of the obstacle vertex whose
+ * predecessor is to be retrieved.
+ * \return The number of the obstacle vertex preceding the specified
+ * obstacle vertex in its polygon.
+ */
+ size_t getPrevObstacleVertexNo(size_t vertexNo) const;
+
+ /**
+ * \brief Returns the time step of the simulation.
+ * \return The present time step of the simulation.
+ */
+ float getTimeStep() const;
+
+ /**
+ * \brief Processes the obstacles that have been added so that they
+ * are accounted for in the simulation.
+ * \note Obstacles added to the simulation after this function has
+ * been called are not accounted for in the simulation.
+ */
+ void processObstacles();
+
+ /**
+ * \brief Performs a visibility query between the two specified
+ * points with respect to the obstacles
+ * \param point1 The first point of the query.
+ * \param point2 The second point of the query.
+ * \param radius The minimal distance between the line
+ * connecting the two points and the obstacles
+ * in order for the points to be mutually
+ * visible (optional). Must be non-negative.
+ * \return A boolean specifying whether the two points are mutually
+ * visible. Returns true when the obstacles have not been
+ * processed.
+ */
+ bool queryVisibility(const Vector2 &point1, const Vector2 &point2,
+ float radius = 0.0f) const;
+
+ /**
+ * \brief Sets the default properties for any new agent that is
+ * added.
+ * \param neighborDist The default maximum distance (center point
+ * to center point) to other agents a new agent
+ * takes into account in the navigation. The
+ * larger this number, the longer he running
+ * time of the simulation. If the number is too
+ * low, the simulation will not be safe.
+ * Must be non-negative.
+ * \param maxNeighbors The default maximum number of other agents a
+ * new agent takes into account in the
+ * navigation. The larger this number, the
+ * longer the running time of the simulation.
+ * If the number is too low, the simulation
+ * will not be safe.
+ * \param timeHorizon The default minimal amount of time for which
+ * a new agent's velocities that are computed
+ * by the simulation are safe with respect to
+ * other agents. The larger this number, the
+ * sooner an agent will respond to the presence
+ * of other agents, but the less freedom the
+ * agent has in choosing its velocities.
+ * Must be positive.
+ * \param timeHorizonObst The default minimal amount of time for which
+ * a new agent's velocities that are computed
+ * by the simulation are safe with respect to
+ * obstacles. The larger this number, the
+ * sooner an agent will respond to the presence
+ * of obstacles, but the less freedom the agent
+ * has in choosing its velocities.
+ * Must be positive.
+ * \param radius The default radius of a new agent.
+ * Must be non-negative.
+ * \param maxSpeed The default maximum speed of a new agent.
+ * Must be non-negative.
+ * \param velocity The default initial two-dimensional linear
+ * velocity of a new agent (optional).
+ */
+ void setAgentDefaults(float neighborDist, size_t maxNeighbors,
+ float timeHorizon, float timeHorizonObst,
+ float radius, float maxSpeed,
+ const Vector2 &velocity = Vector2());
+
+ /**
+ * \brief Sets the maximum neighbor count of a specified agent.
+ * \param agentNo The number of the agent whose maximum
+ * neighbor count is to be modified.
+ * \param maxNeighbors The replacement maximum neighbor count.
+ */
+ void setAgentMaxNeighbors(size_t agentNo, size_t maxNeighbors);
+
+ /**
+ * \brief Sets the maximum speed of a specified agent.
+ * \param agentNo The number of the agent whose maximum speed
+ * is to be modified.
+ * \param maxSpeed The replacement maximum speed. Must be
+ * non-negative.
+ */
+ void setAgentMaxSpeed(size_t agentNo, float maxSpeed);
+
+ /**
+ * \brief Sets the maximum neighbor distance of a specified agent.
+ * \param agentNo The number of the agent whose maximum
+ * neighbor distance is to be modified.
+ * \param neighborDist The replacement maximum neighbor distance.
+ * Must be non-negative.
+ */
+ void setAgentNeighborDist(size_t agentNo, float neighborDist);
+
+ /**
+ * \brief Sets the two-dimensional position of a specified agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional position is to be modified.
+ * \param position The replacement of the two-dimensional
+ * position.
+ */
+ void setAgentPosition(size_t agentNo, const Vector2 &position);
+
+ /**
+ * \brief Sets the two-dimensional preferred velocity of a
+ * specified agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional preferred velocity is to be
+ * modified.
+ * \param prefVelocity The replacement of the two-dimensional
+ * preferred velocity.
+ */
+ void setAgentPrefVelocity(size_t agentNo, const Vector2 &prefVelocity);
+
+ /**
+ * \brief Sets the radius of a specified agent.
+ * \param agentNo The number of the agent whose radius is to
+ * be modified.
+ * \param radius The replacement radius.
+ * Must be non-negative.
+ */
+ void setAgentRadius(size_t agentNo, float radius);
+
+ /**
+ * \brief Sets the time horizon of a specified agent with respect
+ * to other agents.
+ * \param agentNo The number of the agent whose time horizon
+ * is to be modified.
+ * \param timeHorizon The replacement time horizon with respect
+ * to other agents. Must be positive.
+ */
+ void setAgentTimeHorizon(size_t agentNo, float timeHorizon);
+
+ /**
+ * \brief Sets the time horizon of a specified agent with respect
+ * to obstacles.
+ * \param agentNo The number of the agent whose time horizon
+ * with respect to obstacles is to be modified.
+ * \param timeHorizonObst The replacement time horizon with respect to
+ * obstacles. Must be positive.
+ */
+ void setAgentTimeHorizonObst(size_t agentNo, float timeHorizonObst);
+
+ /**
+ * \brief Sets the two-dimensional linear velocity of a specified
+ * agent.
+ * \param agentNo The number of the agent whose
+ * two-dimensional linear velocity is to be
+ * modified.
+ * \param velocity The replacement two-dimensional linear
+ * velocity.
+ */
+ void setAgentVelocity(size_t agentNo, const Vector2 &velocity);
+
+ /**
+ * \brief Sets the time step of the simulation.
+ * \param timeStep The time step of the simulation.
+ * Must be positive.
+ */
+ void setTimeStep(float timeStep);
+
+ public:
+ std::vector<Agent2D *> agents_;
+ Agent2D *defaultAgent_;
+ float globalTime_;
+ KdTree2D *kdTree_;
+ std::vector<Obstacle2D *> obstacles_;
+ float timeStep_;
+
+ friend class Agent2D;
+ friend class KdTree2D;
+ friend class Obstacle2D;
+ };
+}
+
+#endif /* RVO2D_RVO_SIMULATOR_H_ */
diff --git a/thirdparty/rvo2/rvo2_2d/Vector2.h b/thirdparty/rvo2/rvo2_2d/Vector2.h
new file mode 100644
index 0000000000..24353e09f3
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_2d/Vector2.h
@@ -0,0 +1,346 @@
+/*
+ * Vector2.h
+ * RVO2 Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO_VECTOR2_H_
+#define RVO_VECTOR2_H_
+
+/**
+ * \file Vector2.h
+ * \brief Contains the Vector2 class.
+ */
+
+#include <cmath>
+#include <ostream>
+
+namespace RVO2D {
+ /**
+ * \brief Defines a two-dimensional vector.
+ */
+ class Vector2 {
+ public:
+ /**
+ * \brief Constructs and initializes a two-dimensional vector instance
+ * to (0.0, 0.0).
+ */
+ inline Vector2() : x_(0.0f), y_(0.0f) { }
+
+ /**
+ * \brief Constructs and initializes a two-dimensional vector from
+ * the specified xy-coordinates.
+ * \param x The x-coordinate of the two-dimensional
+ * vector.
+ * \param y The y-coordinate of the two-dimensional
+ * vector.
+ */
+ inline Vector2(float x, float y) : x_(x), y_(y) { }
+
+ inline Vector2(const Vector2 &vector)
+ {
+ x_ = vector.x();
+ y_ = vector.y();
+ }
+
+ /**
+ * \brief Returns the x-coordinate of this two-dimensional vector.
+ * \return The x-coordinate of the two-dimensional vector.
+ */
+ inline float x() const { return x_; }
+
+ /**
+ * \brief Returns the y-coordinate of this two-dimensional vector.
+ * \return The y-coordinate of the two-dimensional vector.
+ */
+ inline float y() const { return y_; }
+
+ /**
+ * \brief Computes the negation of this two-dimensional vector.
+ * \return The negation of this two-dimensional vector.
+ */
+ inline Vector2 operator-() const
+ {
+ return Vector2(-x_, -y_);
+ }
+
+ /**
+ * \brief Computes the dot product of this two-dimensional vector with
+ * the specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which the
+ * dot product should be computed.
+ * \return The dot product of this two-dimensional vector with a
+ * specified two-dimensional vector.
+ */
+ inline float operator*(const Vector2 &vector) const
+ {
+ return x_ * vector.x() + y_ * vector.y();
+ }
+
+ /**
+ * \brief Computes the scalar multiplication of this
+ * two-dimensional vector with the specified scalar value.
+ * \param s The scalar value with which the scalar
+ * multiplication should be computed.
+ * \return The scalar multiplication of this two-dimensional vector
+ * with a specified scalar value.
+ */
+ inline Vector2 operator*(float s) const
+ {
+ return Vector2(x_ * s, y_ * s);
+ }
+
+ /**
+ * \brief Computes the scalar division of this two-dimensional vector
+ * with the specified scalar value.
+ * \param s The scalar value with which the scalar
+ * division should be computed.
+ * \return The scalar division of this two-dimensional vector with a
+ * specified scalar value.
+ */
+ inline Vector2 operator/(float s) const
+ {
+ const float invS = 1.0f / s;
+
+ return Vector2(x_ * invS, y_ * invS);
+ }
+
+ /**
+ * \brief Computes the vector sum of this two-dimensional vector with
+ * the specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which the
+ * vector sum should be computed.
+ * \return The vector sum of this two-dimensional vector with a
+ * specified two-dimensional vector.
+ */
+ inline Vector2 operator+(const Vector2 &vector) const
+ {
+ return Vector2(x_ + vector.x(), y_ + vector.y());
+ }
+
+ /**
+ * \brief Computes the vector difference of this two-dimensional
+ * vector with the specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which the
+ * vector difference should be computed.
+ * \return The vector difference of this two-dimensional vector with a
+ * specified two-dimensional vector.
+ */
+ inline Vector2 operator-(const Vector2 &vector) const
+ {
+ return Vector2(x_ - vector.x(), y_ - vector.y());
+ }
+
+ /**
+ * \brief Tests this two-dimensional vector for equality with the
+ * specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which to
+ * test for equality.
+ * \return True if the two-dimensional vectors are equal.
+ */
+ inline bool operator==(const Vector2 &vector) const
+ {
+ return x_ == vector.x() && y_ == vector.y();
+ }
+
+ /**
+ * \brief Tests this two-dimensional vector for inequality with the
+ * specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which to
+ * test for inequality.
+ * \return True if the two-dimensional vectors are not equal.
+ */
+ inline bool operator!=(const Vector2 &vector) const
+ {
+ return x_ != vector.x() || y_ != vector.y();
+ }
+
+ /**
+ * \brief Sets the value of this two-dimensional vector to the scalar
+ * multiplication of itself with the specified scalar value.
+ * \param s The scalar value with which the scalar
+ * multiplication should be computed.
+ * \return A reference to this two-dimensional vector.
+ */
+ inline Vector2 &operator*=(float s)
+ {
+ x_ *= s;
+ y_ *= s;
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this two-dimensional vector to the scalar
+ * division of itself with the specified scalar value.
+ * \param s The scalar value with which the scalar
+ * division should be computed.
+ * \return A reference to this two-dimensional vector.
+ */
+ inline Vector2 &operator/=(float s)
+ {
+ const float invS = 1.0f / s;
+ x_ *= invS;
+ y_ *= invS;
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this two-dimensional vector to the vector
+ * sum of itself with the specified two-dimensional vector.
+ * \param vector The two-dimensional vector with which the
+ * vector sum should be computed.
+ * \return A reference to this two-dimensional vector.
+ */
+ inline Vector2 &operator+=(const Vector2 &vector)
+ {
+ x_ += vector.x();
+ y_ += vector.y();
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this two-dimensional vector to the vector
+ * difference of itself with the specified two-dimensional
+ * vector.
+ * \param vector The two-dimensional vector with which the
+ * vector difference should be computed.
+ * \return A reference to this two-dimensional vector.
+ */
+ inline Vector2 &operator-=(const Vector2 &vector)
+ {
+ x_ -= vector.x();
+ y_ -= vector.y();
+
+ return *this;
+ }
+
+ inline Vector2 &operator=(const Vector2 &vector)
+ {
+ x_ = vector.x();
+ y_ = vector.y();
+
+ return *this;
+ }
+
+ private:
+ float x_;
+ float y_;
+ };
+
+ /**
+ * \relates Vector2
+ * \brief Computes the scalar multiplication of the specified
+ * two-dimensional vector with the specified scalar value.
+ * \param s The scalar value with which the scalar
+ * multiplication should be computed.
+ * \param vector The two-dimensional vector with which the scalar
+ * multiplication should be computed.
+ * \return The scalar multiplication of the two-dimensional vector with the
+ * scalar value.
+ */
+ inline Vector2 operator*(float s, const Vector2 &vector)
+ {
+ return Vector2(s * vector.x(), s * vector.y());
+ }
+
+ /**
+ * \relates Vector2
+ * \brief Inserts the specified two-dimensional vector into the specified
+ * output stream.
+ * \param os The output stream into which the two-dimensional
+ * vector should be inserted.
+ * \param vector The two-dimensional vector which to insert into
+ * the output stream.
+ * \return A reference to the output stream.
+ */
+ inline std::ostream &operator<<(std::ostream &os, const Vector2 &vector)
+ {
+ os << "(" << vector.x() << "," << vector.y() << ")";
+
+ return os;
+ }
+
+ /**
+ * \relates Vector2
+ * \brief Computes the length of a specified two-dimensional vector.
+ * \param vector The two-dimensional vector whose length is to be
+ * computed.
+ * \return The length of the two-dimensional vector.
+ */
+ inline float abs(const Vector2 &vector)
+ {
+ return std::sqrt(vector * vector);
+ }
+
+ /**
+ * \relates Vector2
+ * \brief Computes the squared length of a specified two-dimensional
+ * vector.
+ * \param vector The two-dimensional vector whose squared length
+ * is to be computed.
+ * \return The squared length of the two-dimensional vector.
+ */
+ inline float absSq(const Vector2 &vector)
+ {
+ return vector * vector;
+ }
+
+ /**
+ * \relates Vector2
+ * \brief Computes the determinant of a two-dimensional square matrix with
+ * rows consisting of the specified two-dimensional vectors.
+ * \param vector1 The top row of the two-dimensional square
+ * matrix.
+ * \param vector2 The bottom row of the two-dimensional square
+ * matrix.
+ * \return The determinant of the two-dimensional square matrix.
+ */
+ inline float det(const Vector2 &vector1, const Vector2 &vector2)
+ {
+ return vector1.x() * vector2.y() - vector1.y() * vector2.x();
+ }
+
+ /**
+ * \relates Vector2
+ * \brief Computes the normalization of the specified two-dimensional
+ * vector.
+ * \param vector The two-dimensional vector whose normalization
+ * is to be computed.
+ * \return The normalization of the two-dimensional vector.
+ */
+ inline Vector2 normalize(const Vector2 &vector)
+ {
+ return vector / abs(vector);
+ }
+}
+
+#endif /* RVO_VECTOR2_H_ */
diff --git a/thirdparty/rvo2/Agent.cpp b/thirdparty/rvo2/rvo2_3d/Agent3d.cpp
index b35eee9c12..bddf226db1 100644
--- a/thirdparty/rvo2/Agent.cpp
+++ b/thirdparty/rvo2/rvo2_3d/Agent3d.cpp
@@ -30,15 +30,15 @@
* <https://gamma.cs.unc.edu/RVO2/>
*/
-#include "Agent.h"
+#include "Agent3d.h"
#include <cmath>
#include <algorithm>
#include "Definitions.h"
-#include "KdTree.h"
+#include "KdTree3d.h"
-namespace RVO {
+namespace RVO3D {
/**
* \brief A sufficiently small positive number.
*/
@@ -47,7 +47,7 @@ namespace RVO {
/**
* \brief Defines a directed line.
*/
- class Line {
+ class Line3D {
public:
/**
* \brief The direction of the directed line.
@@ -71,7 +71,7 @@ namespace RVO {
* \param result A reference to the result of the linear program.
* \return True if successful.
*/
- bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);
+ bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line3D &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);
/**
* \brief Solves a two-dimensional linear program on a specified plane subject to linear constraints defined by planes and a spherical constraint.
@@ -105,42 +105,34 @@ namespace RVO {
*/
void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result);
- Agent::Agent() : id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f), ignore_y_(false) { }
+ Agent3D::Agent3D() : id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f) { }
- void Agent::computeNeighbors(KdTree *kdTree_)
+ void Agent3D::computeNeighbors(RVOSimulator3D *sim_)
{
agentNeighbors_.clear();
+
if (maxNeighbors_ > 0) {
- kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);
+ sim_->kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);
}
}
- void Agent::computeNewVelocity(float timeStep)
+ void Agent3D::computeNewVelocity(RVOSimulator3D *sim_)
{
orcaPlanes_.clear();
+
const float invTimeHorizon = 1.0f / timeHorizon_;
/* Create agent ORCA planes. */
for (size_t i = 0; i < agentNeighbors_.size(); ++i) {
- const Agent *const other = agentNeighbors_[i].second;
-
- Vector3 relativePosition = other->position_ - position_;
- Vector3 relativeVelocity = velocity_ - other->velocity_;
- const float combinedRadius = radius_ + other->radius_;
+ const Agent3D *const other = agentNeighbors_[i].second;
- // This is a Godot feature that allow the agents to avoid the collision
- // by moving only on the horizontal plane relative to the player velocity.
- if (ignore_y_) {
- // Skip if these are in two different heights
-#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
- if (ABS(relativePosition[1]) > combinedRadius * 2) {
- continue;
- }
- relativePosition[1] = 0;
- relativeVelocity[1] = 0;
- }
+ //const float timeHorizon_mod = (avoidance_priority_ - other->avoidance_priority_ + 1.0f) * 0.5f;
+ //const float invTimeHorizon = (1.0f / timeHorizon_) * timeHorizon_mod;
+ const Vector3 relativePosition = other->position_ - position_;
+ const Vector3 relativeVelocity = velocity_ - other->velocity_;
const float distSq = absSq(relativePosition);
+ const float combinedRadius = radius_ + other->radius_;
const float combinedRadiusSq = sqr(combinedRadius);
Plane plane;
@@ -168,17 +160,17 @@ namespace RVO {
const float b = relativePosition * relativeVelocity;
const float c = absSq(relativeVelocity) - absSq(cross(relativePosition, relativeVelocity)) / (distSq - combinedRadiusSq);
const float t = (b + std::sqrt(sqr(b) - a * c)) / a;
- const Vector3 ww = relativeVelocity - t * relativePosition;
- const float wwLength = abs(ww);
- const Vector3 unitWW = ww / wwLength;
+ const Vector3 w = relativeVelocity - t * relativePosition;
+ const float wLength = abs(w);
+ const Vector3 unitW = w / wLength;
- plane.normal = unitWW;
- u = (combinedRadius * t - wwLength) * unitWW;
+ plane.normal = unitW;
+ u = (combinedRadius * t - wLength) * unitW;
}
}
else {
/* Collision. */
- const float invTimeStep = 1.0f / timeStep;
+ const float invTimeStep = 1.0f / sim_->timeStep_;
const Vector3 w = relativeVelocity - invTimeStep * relativePosition;
const float wLength = abs(w);
const Vector3 unitW = w / wLength;
@@ -196,40 +188,52 @@ namespace RVO {
if (planeFail < orcaPlanes_.size()) {
linearProgram4(orcaPlanes_, planeFail, maxSpeed_, newVelocity_);
}
-
- if (ignore_y_) {
- // Not 100% necessary, but better to have.
- newVelocity_[1] = prefVelocity_[1];
- }
}
- void Agent::insertAgentNeighbor(const Agent *agent, float &rangeSq)
+ void Agent3D::insertAgentNeighbor(const Agent3D *agent, float &rangeSq)
{
- if (this != agent) {
- const float distSq = absSq(position_ - agent->position_);
+ // no point processing same agent
+ if (this == agent) {
+ return;
+ }
+ // ignore other agent if layers/mask bitmasks have no matching bit
+ if ((avoidance_mask_ & agent->avoidance_layers_) == 0) {
+ return;
+ }
- if (distSq < rangeSq) {
- if (agentNeighbors_.size() < maxNeighbors_) {
- agentNeighbors_.push_back(std::make_pair(distSq, agent));
- }
+ if (avoidance_priority_ > agent->avoidance_priority_) {
+ return;
+ }
- size_t i = agentNeighbors_.size() - 1;
+ const float distSq = absSq(position_ - agent->position_);
- while (i != 0 && distSq < agentNeighbors_[i - 1].first) {
- agentNeighbors_[i] = agentNeighbors_[i - 1];
- --i;
- }
+ if (distSq < rangeSq) {
+ if (agentNeighbors_.size() < maxNeighbors_) {
+ agentNeighbors_.push_back(std::make_pair(distSq, agent));
+ }
- agentNeighbors_[i] = std::make_pair(distSq, agent);
+ size_t i = agentNeighbors_.size() - 1;
- if (agentNeighbors_.size() == maxNeighbors_) {
- rangeSq = agentNeighbors_.back().first;
- }
+ while (i != 0 && distSq < agentNeighbors_[i - 1].first) {
+ agentNeighbors_[i] = agentNeighbors_[i - 1];
+ --i;
+ }
+
+ agentNeighbors_[i] = std::make_pair(distSq, agent);
+
+ if (agentNeighbors_.size() == maxNeighbors_) {
+ rangeSq = agentNeighbors_.back().first;
}
}
}
- bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)
+ void Agent3D::update(RVOSimulator3D *sim_)
+ {
+ velocity_ = newVelocity_;
+ position_ += velocity_ * sim_->timeStep_;
+ }
+
+ bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line3D &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result)
{
const float dotProduct = line.point * line.direction;
const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(line.point);
@@ -248,7 +252,7 @@ namespace RVO {
const float denominator = line.direction * planes[i].normal;
if (sqr(denominator) <= RVO3D_EPSILON) {
- /* Lines line is (almost) parallel to plane i. */
+ /* Lines3D line is (almost) parallel to plane i. */
if (numerator > 0.0f) {
return false;
}
@@ -352,7 +356,7 @@ namespace RVO {
return false;
}
- Line line;
+ Line3D line;
line.direction = normalize(crossProduct);
const Vector3 lineNormal = cross(line.direction, planes[planeNo].normal);
line.point = planes[planeNo].point + (((planes[i].point - planes[planeNo].point) * planes[i].normal) / (lineNormal * planes[i].normal)) * lineNormal;
diff --git a/thirdparty/rvo2/Agent.h b/thirdparty/rvo2/rvo2_3d/Agent3d.h
index 45fbead2f5..d99e3cac66 100644
--- a/thirdparty/rvo2/Agent.h
+++ b/thirdparty/rvo2/rvo2_3d/Agent3d.h
@@ -8,7 +8,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * https://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,7 +27,7 @@
* Chapel Hill, N.C. 27599-3175
* United States of America
*
- * <https://gamma.cs.unc.edu/RVO2/>
+ * <http://gamma.cs.unc.edu/RVO2/>
*/
/**
@@ -38,80 +38,68 @@
#define RVO3D_AGENT_H_
#include <cstddef>
+#include <cstdint>
#include <utility>
#include <vector>
+#include "RVOSimulator3d.h"
#include "Vector3.h"
-// Note: Slightly modified to work better in Godot.
-// - The agent can be created by anyone.
-// - The simulator pointer is removed.
-// - The update function is removed.
-// - The compute velocity function now need the timeStep.
-// - Moved the `Plane` class here.
-// - Added a new parameter `ignore_y_` in the `Agent`. This parameter is used to control a godot feature that allows to avoid collisions by moving on the horizontal plane.
-namespace RVO {
- /**
- * \brief Defines a plane.
- */
- class Plane {
- public:
- /**
- * \brief A point on the plane.
- */
- Vector3 point;
-
- /**
- * \brief The normal to the plane.
- */
- Vector3 normal;
- };
-
+namespace RVO3D {
/**
* \brief Defines an agent in the simulation.
*/
- class Agent {
+ class Agent3D {
public:
/**
* \brief Constructs an agent instance.
* \param sim The simulator instance.
*/
- explicit Agent();
+ explicit Agent3D();
/**
* \brief Computes the neighbors of this agent.
*/
- void computeNeighbors(class KdTree *kdTree_);
+ void computeNeighbors(RVOSimulator3D *sim_);
/**
* \brief Computes the new velocity of this agent.
*/
- void computeNewVelocity(float timeStep);
+ void computeNewVelocity(RVOSimulator3D *sim_);
/**
* \brief Inserts an agent neighbor into the set of neighbors of this agent.
* \param agent A pointer to the agent to be inserted.
* \param rangeSq The squared range around this agent.
*/
- void insertAgentNeighbor(const Agent *agent, float &rangeSq);
+ void insertAgentNeighbor(const Agent3D *agent, float &rangeSq);
+
+ /**
+ * \brief Updates the three-dimensional position and three-dimensional velocity of this agent.
+ */
+ void update(RVOSimulator3D *sim_);
Vector3 newVelocity_;
Vector3 position_;
Vector3 prefVelocity_;
Vector3 velocity_;
+ RVOSimulator3D *sim_;
size_t id_;
size_t maxNeighbors_;
float maxSpeed_;
float neighborDist_;
float radius_;
float timeHorizon_;
- std::vector<std::pair<float, const Agent *> > agentNeighbors_;
+ float timeHorizonObst_;
+ std::vector<std::pair<float, const Agent3D *> > agentNeighbors_;
std::vector<Plane> orcaPlanes_;
- /// This is a godot feature that allows the Agent to avoid collision by mooving
- /// on the horizontal plane.
- bool ignore_y_;
+ float height_ = 1.0;
+ uint32_t avoidance_layers_ = 1;
+ uint32_t avoidance_mask_ = 1;
+ float avoidance_priority_ = 1.0;
- friend class KdTree;
+ friend class KdTree3D;
+ friend class RVOSimulator3D;
};
}
diff --git a/thirdparty/rvo2/Definitions.h b/thirdparty/rvo2/rvo2_3d/Definitions.h
index 707d3c897f..34d1d06e0a 100644
--- a/thirdparty/rvo2/Definitions.h
+++ b/thirdparty/rvo2/rvo2_3d/Definitions.h
@@ -38,7 +38,7 @@
#ifndef RVO3D_DEFINITIONS_H_
#define RVO3D_DEFINITIONS_H_
-namespace RVO {
+namespace RVO3D {
/**
* \brief Computes the square of a float.
* \param scalar The float to be squared.
diff --git a/thirdparty/rvo2/KdTree.cpp b/thirdparty/rvo2/rvo2_3d/KdTree3d.cpp
index c857f299df..2534871db1 100644
--- a/thirdparty/rvo2/KdTree.cpp
+++ b/thirdparty/rvo2/rvo2_3d/KdTree3d.cpp
@@ -30,19 +30,20 @@
* <https://gamma.cs.unc.edu/RVO2/>
*/
-#include "KdTree.h"
+#include "KdTree3d.h"
#include <algorithm>
-#include "Agent.h"
+#include "Agent3d.h"
#include "Definitions.h"
+#include "RVOSimulator3d.h"
-namespace RVO {
+namespace RVO3D {
const size_t RVO3D_MAX_LEAF_SIZE = 10;
- KdTree::KdTree() { }
+ KdTree3D::KdTree3D(RVOSimulator3D *sim) : sim_(sim) { }
- void KdTree::buildAgentTree(std::vector<Agent *> agents)
+ void KdTree3D::buildAgentTree(std::vector<Agent3D *> agents)
{
agents_.swap(agents);
@@ -52,7 +53,7 @@ namespace RVO {
}
}
- void KdTree::buildAgentTreeRecursive(size_t begin, size_t end, size_t node)
+ void KdTree3D::buildAgentTreeRecursive(size_t begin, size_t end, size_t node)
{
agentTree_[node].begin = begin;
agentTree_[node].end = end;
@@ -120,12 +121,12 @@ namespace RVO {
}
}
- void KdTree::computeAgentNeighbors(Agent *agent, float rangeSq) const
+ void KdTree3D::computeAgentNeighbors(Agent3D *agent, float rangeSq) const
{
queryAgentTreeRecursive(agent, rangeSq, 0);
}
- void KdTree::queryAgentTreeRecursive(Agent *agent, float &rangeSq, size_t node) const
+ void KdTree3D::queryAgentTreeRecursive(Agent3D *agent, float &rangeSq, size_t node) const
{
if (agentTree_[node].end - agentTree_[node].begin <= RVO3D_MAX_LEAF_SIZE) {
for (size_t i = agentTree_[node].begin; i < agentTree_[node].end; ++i) {
diff --git a/thirdparty/rvo2/KdTree.h b/thirdparty/rvo2/rvo2_3d/KdTree3d.h
index 69d8920ce0..c018f98b23 100644
--- a/thirdparty/rvo2/KdTree.h
+++ b/thirdparty/rvo2/rvo2_3d/KdTree3d.h
@@ -41,22 +41,19 @@
#include "Vector3.h"
-// Note: Slightly modified to work better with Godot.
-// - Removed `sim_`.
-// - KdTree things are public
-namespace RVO {
- class Agent;
- class RVOSimulator;
+namespace RVO3D {
+ class Agent3D;
+ class RVOSimulator3D;
/**
* \brief Defines <i>k</i>d-trees for agents in the simulation.
*/
- class KdTree {
+ class KdTree3D {
public:
/**
* \brief Defines an agent <i>k</i>d-tree node.
*/
- class AgentTreeNode {
+ class AgentTreeNode3D {
public:
/**
* \brief The beginning node number.
@@ -93,12 +90,12 @@ namespace RVO {
* \brief Constructs a <i>k</i>d-tree instance.
* \param sim The simulator instance.
*/
- explicit KdTree();
+ explicit KdTree3D(RVOSimulator3D *sim);
/**
* \brief Builds an agent <i>k</i>d-tree.
*/
- void buildAgentTree(std::vector<Agent *> agents);
+ void buildAgentTree(std::vector<Agent3D *> agents);
void buildAgentTreeRecursive(size_t begin, size_t end, size_t node);
@@ -107,15 +104,16 @@ namespace RVO {
* \param agent A pointer to the agent for which agent neighbors are to be computed.
* \param rangeSq The squared range around the agent.
*/
- void computeAgentNeighbors(Agent *agent, float rangeSq) const;
+ void computeAgentNeighbors(Agent3D *agent, float rangeSq) const;
- void queryAgentTreeRecursive(Agent *agent, float &rangeSq, size_t node) const;
+ void queryAgentTreeRecursive(Agent3D *agent, float &rangeSq, size_t node) const;
- std::vector<Agent *> agents_;
- std::vector<AgentTreeNode> agentTree_;
+ std::vector<Agent3D *> agents_;
+ std::vector<AgentTreeNode3D> agentTree_;
+ RVOSimulator3D *sim_;
- friend class Agent;
- friend class RVOSimulator;
+ friend class Agent3D;
+ friend class RVOSimulator3D;
};
}
diff --git a/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.cpp b/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.cpp
new file mode 100644
index 0000000000..71e5aea9e2
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.cpp
@@ -0,0 +1,274 @@
+/*
+ * RVOSimulator.cpp
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "RVOSimulator3d.h"
+
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+#include "Agent3d.h"
+#include "KdTree3d.h"
+
+namespace RVO3D {
+ RVOSimulator3D::RVOSimulator3D() : defaultAgent_(NULL), kdTree_(NULL), globalTime_(0.0f), timeStep_(0.0f)
+ {
+ kdTree_ = new KdTree3D(this);
+ }
+
+ RVOSimulator3D::RVOSimulator3D(float timeStep, float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity) : defaultAgent_(NULL), kdTree_(NULL), globalTime_(0.0f), timeStep_(timeStep)
+ {
+ kdTree_ = new KdTree3D(this);
+ defaultAgent_ = new Agent3D();
+
+ defaultAgent_->maxNeighbors_ = maxNeighbors;
+ defaultAgent_->maxSpeed_ = maxSpeed;
+ defaultAgent_->neighborDist_ = neighborDist;
+ defaultAgent_->radius_ = radius;
+ defaultAgent_->timeHorizon_ = timeHorizon;
+ defaultAgent_->velocity_ = velocity;
+ }
+
+ RVOSimulator3D::~RVOSimulator3D()
+ {
+ if (defaultAgent_ != NULL) {
+ delete defaultAgent_;
+ }
+
+ for (size_t i = 0; i < agents_.size(); ++i) {
+ delete agents_[i];
+ }
+
+ if (kdTree_ != NULL) {
+ delete kdTree_;
+ }
+ }
+
+ size_t RVOSimulator3D::getAgentNumAgentNeighbors(size_t agentNo) const
+ {
+ return agents_[agentNo]->agentNeighbors_.size();
+ }
+
+ size_t RVOSimulator3D::getAgentAgentNeighbor(size_t agentNo, size_t neighborNo) const
+ {
+ return agents_[agentNo]->agentNeighbors_[neighborNo].second->id_;
+ }
+
+ size_t RVOSimulator3D::getAgentNumORCAPlanes(size_t agentNo) const
+ {
+ return agents_[agentNo]->orcaPlanes_.size();
+ }
+
+ const Plane &RVOSimulator3D::getAgentORCAPlane(size_t agentNo, size_t planeNo) const
+ {
+ return agents_[agentNo]->orcaPlanes_[planeNo];
+ }
+
+ void RVOSimulator3D::removeAgent(size_t agentNo)
+ {
+ delete agents_[agentNo];
+ agents_[agentNo] = agents_.back();
+ agents_.pop_back();
+ }
+
+ size_t RVOSimulator3D::addAgent(const Vector3 &position)
+ {
+ if (defaultAgent_ == NULL) {
+ return RVO3D_ERROR;
+ }
+
+ Agent3D *agent = new Agent3D();
+
+ agent->position_ = position;
+ agent->maxNeighbors_ = defaultAgent_->maxNeighbors_;
+ agent->maxSpeed_ = defaultAgent_->maxSpeed_;
+ agent->neighborDist_ = defaultAgent_->neighborDist_;
+ agent->radius_ = defaultAgent_->radius_;
+ agent->timeHorizon_ = defaultAgent_->timeHorizon_;
+ agent->velocity_ = defaultAgent_->velocity_;
+
+ agent->id_ = agents_.size();
+
+ agents_.push_back(agent);
+
+ return agents_.size() - 1;
+ }
+
+ size_t RVOSimulator3D::addAgent(const Vector3 &position, float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity)
+ {
+ Agent3D *agent = new Agent3D();
+
+ agent->position_ = position;
+ agent->maxNeighbors_ = maxNeighbors;
+ agent->maxSpeed_ = maxSpeed;
+ agent->neighborDist_ = neighborDist;
+ agent->radius_ = radius;
+ agent->timeHorizon_ = timeHorizon;
+ agent->velocity_ = velocity;
+
+ agent->id_ = agents_.size();
+
+ agents_.push_back(agent);
+
+ return agents_.size() - 1;
+ }
+
+ void RVOSimulator3D::doStep()
+ {
+ kdTree_->buildAgentTree(agents_);
+
+ for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
+ agents_[i]->computeNeighbors(this);
+ agents_[i]->computeNewVelocity(this);
+ }
+
+ for (int i = 0; i < static_cast<int>(agents_.size()); ++i) {
+ agents_[i]->update(this);
+ }
+
+ globalTime_ += timeStep_;
+ }
+
+ size_t RVOSimulator3D::getAgentMaxNeighbors(size_t agentNo) const
+ {
+ return agents_[agentNo]->maxNeighbors_;
+ }
+
+ float RVOSimulator3D::getAgentMaxSpeed(size_t agentNo) const
+ {
+ return agents_[agentNo]->maxSpeed_;
+ }
+
+ float RVOSimulator3D::getAgentNeighborDist(size_t agentNo) const
+ {
+ return agents_[agentNo]->neighborDist_;
+ }
+
+ const Vector3 &RVOSimulator3D::getAgentPosition(size_t agentNo) const
+ {
+ return agents_[agentNo]->position_;
+ }
+
+ const Vector3 &RVOSimulator3D::getAgentPrefVelocity(size_t agentNo) const
+ {
+ return agents_[agentNo]->prefVelocity_;
+ }
+
+ float RVOSimulator3D::getAgentRadius(size_t agentNo) const
+ {
+ return agents_[agentNo]->radius_;
+ }
+
+ float RVOSimulator3D::getAgentTimeHorizon(size_t agentNo) const
+ {
+ return agents_[agentNo]->timeHorizon_;
+ }
+
+ const Vector3 &RVOSimulator3D::getAgentVelocity(size_t agentNo) const
+ {
+ return agents_[agentNo]->velocity_;
+ }
+
+ float RVOSimulator3D::getGlobalTime() const
+ {
+ return globalTime_;
+ }
+
+ size_t RVOSimulator3D::getNumAgents() const
+ {
+ return agents_.size();
+ }
+
+ float RVOSimulator3D::getTimeStep() const
+ {
+ return timeStep_;
+ }
+
+ void RVOSimulator3D::setAgentDefaults(float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity)
+ {
+ if (defaultAgent_ == NULL) {
+ defaultAgent_ = new Agent3D();
+ }
+
+ defaultAgent_->maxNeighbors_ = maxNeighbors;
+ defaultAgent_->maxSpeed_ = maxSpeed;
+ defaultAgent_->neighborDist_ = neighborDist;
+ defaultAgent_->radius_ = radius;
+ defaultAgent_->timeHorizon_ = timeHorizon;
+ defaultAgent_->velocity_ = velocity;
+ }
+
+ void RVOSimulator3D::setAgentMaxNeighbors(size_t agentNo, size_t maxNeighbors)
+ {
+ agents_[agentNo]->maxNeighbors_ = maxNeighbors;
+ }
+
+ void RVOSimulator3D::setAgentMaxSpeed(size_t agentNo, float maxSpeed)
+ {
+ agents_[agentNo]->maxSpeed_ = maxSpeed;
+ }
+
+ void RVOSimulator3D::setAgentNeighborDist(size_t agentNo, float neighborDist)
+ {
+ agents_[agentNo]->neighborDist_ = neighborDist;
+ }
+
+ void RVOSimulator3D::setAgentPosition(size_t agentNo, const Vector3 &position)
+ {
+ agents_[agentNo]->position_ = position;
+ }
+
+ void RVOSimulator3D::setAgentPrefVelocity(size_t agentNo, const Vector3 &prefVelocity)
+ {
+ agents_[agentNo]->prefVelocity_ = prefVelocity;
+ }
+
+ void RVOSimulator3D::setAgentRadius(size_t agentNo, float radius)
+ {
+ agents_[agentNo]->radius_ = radius;
+ }
+
+ void RVOSimulator3D::setAgentTimeHorizon(size_t agentNo, float timeHorizon)
+ {
+ agents_[agentNo]->timeHorizon_ = timeHorizon;
+ }
+
+ void RVOSimulator3D::setAgentVelocity(size_t agentNo, const Vector3 &velocity)
+ {
+ agents_[agentNo]->velocity_ = velocity;
+ }
+
+ void RVOSimulator3D::setTimeStep(float timeStep)
+ {
+ timeStep_ = timeStep;
+ }
+}
diff --git a/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.h b/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.h
new file mode 100644
index 0000000000..4ea093d74c
--- /dev/null
+++ b/thirdparty/rvo2/rvo2_3d/RVOSimulator3d.h
@@ -0,0 +1,324 @@
+/*
+ * RVOSimulator.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+/**
+ * \file RVOSimulator.h
+ * \brief Contains the RVOSimulator class.
+ */
+#ifndef RVO3D_RVO_SIMULATOR_H_
+#define RVO3D_RVO_SIMULATOR_H_
+
+#include <cstddef>
+#include <limits>
+#include <vector>
+
+#include "Vector3.h"
+
+namespace RVO3D {
+ class Agent3D;
+ class KdTree3D;
+
+ /**
+ * \brief Error value.
+ *
+ * A value equal to the largest unsigned integer, which is returned in case of an error by functions in RVO3D::RVOSimulator.
+ */
+ const size_t RVO3D_ERROR = std::numeric_limits<size_t>::max();
+
+ /**
+ * \brief Defines a plane.
+ */
+ class Plane {
+ public:
+ /**
+ * \brief A point on the plane.
+ */
+ Vector3 point;
+
+ /**
+ * \brief The normal to the plane.
+ */
+ Vector3 normal;
+ };
+
+ /**
+ * \brief Defines the simulation.
+ *
+ * The main class of the library that contains all simulation functionality.
+ */
+ class RVOSimulator3D {
+ public:
+ /**
+ * \brief Constructs a simulator instance.
+ */
+ RVOSimulator3D();
+
+ /**
+ * \brief Constructs a simulator instance and sets the default properties for any new agent that is added.
+ * \param timeStep The time step of the simulation. Must be positive.
+ * \param neighborDist The default maximum distance (center point to center point) to other agents a new agent takes into account in the navigation. The larger this number, the longer he running time of the simulation. If the number is too low, the simulation will not be safe. Must be non-negative.
+ * \param maxNeighbors The default maximum number of other agents a new agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ * \param timeHorizon The default minimum amount of time for which a new agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner an agent will respond to the presence of other agents, but the less freedom the agent has in choosing its velocities. Must be positive.
+ * \param radius The default radius of a new agent. Must be non-negative.
+ * \param maxSpeed The default maximum speed of a new agent. Must be non-negative.
+ * \param velocity The default initial three-dimensional linear velocity of a new agent (optional).
+ */
+ RVOSimulator3D(float timeStep, float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity = Vector3());
+
+ /**
+ * \brief Destroys this simulator instance.
+ */
+ ~RVOSimulator3D();
+
+ /**
+ * \brief Adds a new agent with default properties to the simulation.
+ * \param position The three-dimensional starting position of this agent.
+ * \return The number of the agent, or RVO3D::RVO3D_ERROR when the agent defaults have not been set.
+ */
+ size_t addAgent(const Vector3 &position);
+
+ /**
+ * \brief Adds a new agent to the simulation.
+ * \param position The three-dimensional starting position of this agent.
+ * \param neighborDist The maximum distance (center point to center point) to other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe. Must be non-negative.
+ * \param maxNeighbors The maximum number of other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ * \param timeHorizon The minimum amount of time for which this agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ * \param radius The radius of this agent. Must be non-negative.
+ * \param maxSpeed The maximum speed of this agent. Must be non-negative.
+ * \param velocity The initial three-dimensional linear velocity of this agent (optional).
+ * \return The number of the agent.
+ */
+ size_t addAgent(const Vector3 &position, float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity = Vector3());
+
+ /**
+ * \brief Lets the simulator perform a simulation step and updates the three-dimensional position and three-dimensional velocity of each agent.
+ */
+ void doStep();
+
+ /**
+ * \brief Returns the specified agent neighbor of the specified agent.
+ * \param agentNo The number of the agent whose agent neighbor is to be retrieved.
+ * \param neighborNo The number of the agent neighbor to be retrieved.
+ * \return The number of the neighboring agent.
+ */
+ size_t getAgentAgentNeighbor(size_t agentNo, size_t neighborNo) const;
+
+ /**
+ * \brief Returns the maximum neighbor count of a specified agent.
+ * \param agentNo The number of the agent whose maximum neighbor count is to be retrieved.
+ * \return The present maximum neighbor count of the agent.
+ */
+ size_t getAgentMaxNeighbors(size_t agentNo) const;
+
+ /**
+ * \brief Returns the maximum speed of a specified agent.
+ * \param agentNo The number of the agent whose maximum speed is to be retrieved.
+ * \return The present maximum speed of the agent.
+ */
+ float getAgentMaxSpeed(size_t agentNo) const;
+
+ /**
+ * \brief Returns the maximum neighbor distance of a specified agent.
+ * \param agentNo The number of the agent whose maximum neighbor distance is to be retrieved.
+ * \return The present maximum neighbor distance of the agent.
+ */
+ float getAgentNeighborDist(size_t agentNo) const;
+
+ /**
+ * \brief Returns the count of agent neighbors taken into account to compute the current velocity for the specified agent.
+ * \param agentNo The number of the agent whose count of agent neighbors is to be retrieved.
+ * \return The count of agent neighbors taken into account to compute the current velocity for the specified agent.
+ */
+ size_t getAgentNumAgentNeighbors(size_t agentNo) const;
+
+ /**
+ * \brief Returns the count of ORCA constraints used to compute the current velocity for the specified agent.
+ * \param agentNo The number of the agent whose count of ORCA constraints is to be retrieved.
+ * \return The count of ORCA constraints used to compute the current velocity for the specified agent.
+ */
+ size_t getAgentNumORCAPlanes(size_t agentNo) const;
+
+ /**
+ * \brief Returns the specified ORCA constraint of the specified agent.
+ * \param agentNo The number of the agent whose ORCA constraint is to be retrieved.
+ * \param planeNo The number of the ORCA constraint to be retrieved.
+ * \return A plane representing the specified ORCA constraint.
+ * \note The halfspace to which the normal of the plane points is the region of permissible velocities with respect to the specified ORCA constraint.
+ */
+ const Plane &getAgentORCAPlane(size_t agentNo, size_t planeNo) const;
+
+ /**
+ * \brief Returns the three-dimensional position of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional position is to be retrieved.
+ * \return The present three-dimensional position of the (center of the) agent.
+ */
+ const Vector3 &getAgentPosition(size_t agentNo) const;
+
+ /**
+ * \brief Returns the three-dimensional preferred velocity of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional preferred velocity is to be retrieved.
+ * \return The present three-dimensional preferred velocity of the agent.
+ */
+ const Vector3 &getAgentPrefVelocity(size_t agentNo) const;
+
+ /**
+ * \brief Returns the radius of a specified agent.
+ * \param agentNo The number of the agent whose radius is to be retrieved.
+ * \return The present radius of the agent.
+ */
+ float getAgentRadius(size_t agentNo) const;
+
+ /**
+ * \brief Returns the time horizon of a specified agent.
+ * \param agentNo The number of the agent whose time horizon is to be retrieved.
+ * \return The present time horizon of the agent.
+ */
+ float getAgentTimeHorizon(size_t agentNo) const;
+
+ /**
+ * \brief Returns the three-dimensional linear velocity of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional linear velocity is to be retrieved.
+ * \return The present three-dimensional linear velocity of the agent.
+ */
+ const Vector3 &getAgentVelocity(size_t agentNo) const;
+
+ /**
+ * \brief Returns the global time of the simulation.
+ * \return The present global time of the simulation (zero initially).
+ */
+ float getGlobalTime() const;
+
+ /**
+ * \brief Returns the count of agents in the simulation.
+ * \return The count of agents in the simulation.
+ */
+ size_t getNumAgents() const;
+
+ /**
+ * \brief Returns the time step of the simulation.
+ * \return The present time step of the simulation.
+ */
+ float getTimeStep() const;
+
+ /**
+ * \brief Removes an agent from the simulation.
+ * \param agentNo The number of the agent that is to be removed.
+ * \note After the removal of the agent, the agent that previously had number getNumAgents() - 1 will now have number agentNo.
+ */
+ void removeAgent(size_t agentNo);
+
+ /**
+ * \brief Sets the default properties for any new agent that is added.
+ * \param neighborDist The default maximum distance (center point to center point) to other agents a new agent takes into account in the navigation. The larger this number, the longer he running time of the simulation. If the number is too low, the simulation will not be safe. Must be non-negative.
+ * \param maxNeighbors The default maximum number of other agents a new agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ * \param timeHorizon The default minimum amount of time for which a new agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner an agent will respond to the presence of other agents, but the less freedom the agent has in choosing its velocities. Must be positive.
+ * \param radius The default radius of a new agent. Must be non-negative.
+ * \param maxSpeed The default maximum speed of a new agent. Must be non-negative.
+ * \param velocity The default initial three-dimensional linear velocity of a new agent (optional).
+ */
+ void setAgentDefaults(float neighborDist, size_t maxNeighbors, float timeHorizon, float radius, float maxSpeed, const Vector3 &velocity = Vector3());
+
+ /**
+ * \brief Sets the maximum neighbor count of a specified agent.
+ * \param agentNo The number of the agent whose maximum neighbor count is to be modified.
+ * \param maxNeighbors The replacement maximum neighbor count.
+ */
+ void setAgentMaxNeighbors(size_t agentNo, size_t maxNeighbors);
+
+ /**
+ * \brief Sets the maximum speed of a specified agent.
+ * \param agentNo The number of the agent whose maximum speed is to be modified.
+ * \param maxSpeed The replacement maximum speed. Must be non-negative.
+ */
+ void setAgentMaxSpeed(size_t agentNo, float maxSpeed);
+
+ /**
+ * \brief Sets the maximum neighbor distance of a specified agent.
+ * \param agentNo The number of the agent whose maximum neighbor distance is to be modified.
+ * \param neighborDist The replacement maximum neighbor distance. Must be non-negative.
+ */
+ void setAgentNeighborDist(size_t agentNo, float neighborDist);
+
+ /**
+ * \brief Sets the three-dimensional position of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional position is to be modified.
+ * \param position The replacement of the three-dimensional position.
+ */
+ void setAgentPosition(size_t agentNo, const Vector3 &position);
+
+ /**
+ * \brief Sets the three-dimensional preferred velocity of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional preferred velocity is to be modified.
+ * \param prefVelocity The replacement of the three-dimensional preferred velocity.
+ */
+ void setAgentPrefVelocity(size_t agentNo, const Vector3 &prefVelocity);
+
+ /**
+ * \brief Sets the radius of a specified agent.
+ * \param agentNo The number of the agent whose radius is to be modified.
+ * \param radius The replacement radius. Must be non-negative.
+ */
+ void setAgentRadius(size_t agentNo, float radius);
+
+ /**
+ * \brief Sets the time horizon of a specified agent with respect to other agents.
+ * \param agentNo The number of the agent whose time horizon is to be modified.
+ * \param timeHorizon The replacement time horizon with respect to other agents. Must be positive.
+ */
+ void setAgentTimeHorizon(size_t agentNo, float timeHorizon);
+
+ /**
+ * \brief Sets the three-dimensional linear velocity of a specified agent.
+ * \param agentNo The number of the agent whose three-dimensional linear velocity is to be modified.
+ * \param velocity The replacement three-dimensional linear velocity.
+ */
+ void setAgentVelocity(size_t agentNo, const Vector3 &velocity);
+
+ /**
+ * \brief Sets the time step of the simulation.
+ * \param timeStep The time step of the simulation. Must be positive.
+ */
+ void setTimeStep(float timeStep);
+
+ public:
+ Agent3D *defaultAgent_;
+ KdTree3D *kdTree_;
+ float globalTime_;
+ float timeStep_;
+ std::vector<Agent3D *> agents_;
+
+ friend class Agent3D;
+ friend class KdTree3D;
+ };
+}
+
+#endif
diff --git a/thirdparty/rvo2/Vector3.h b/thirdparty/rvo2/rvo2_3d/Vector3.h
index f44e311f29..6fa4bb074c 100644
--- a/thirdparty/rvo2/Vector3.h
+++ b/thirdparty/rvo2/rvo2_3d/Vector3.h
@@ -41,13 +41,11 @@
#include <cstddef>
#include <ostream>
-#define RVO3D_EXPORT
-
-namespace RVO {
+namespace RVO3D {
/**
* \brief Defines a three-dimensional vector.
*/
- class RVO3D_EXPORT Vector3 {
+ class Vector3 {
public:
/**
* \brief Constructs and initializes a three-dimensional vector instance to zero.
@@ -60,6 +58,17 @@ namespace RVO {
}
/**
+ * \brief Constructs and initializes a three-dimensional vector from the specified three-dimensional vector.
+ * \param vector The three-dimensional vector containing the xyz-coordinates.
+ */
+ inline Vector3(const Vector3 &vector)
+ {
+ val_[0] = vector[0];
+ val_[1] = vector[1];
+ val_[2] = vector[2];
+ }
+
+ /**
* \brief Constructs and initializes a three-dimensional vector from the specified three-element array.
* \param val The three-element array containing the xyz-coordinates.
*/
@@ -255,6 +264,15 @@ namespace RVO {
return *this;
}
+ inline Vector3 &operator=(const Vector3 &vector)
+ {
+ val_[0] = vector[0];
+ val_[1] = vector[1];
+ val_[2] = vector[2];
+
+ return *this;
+ }
+
private:
float val_[3];
};
@@ -267,7 +285,7 @@ namespace RVO {
* \param vector The three-dimensional vector with which the scalar multiplication should be computed.
* \return The scalar multiplication of the three-dimensional vector with the scalar value.
*/
- RVO3D_EXPORT inline Vector3 operator*(float scalar, const Vector3 &vector)
+ inline Vector3 operator*(float scalar, const Vector3 &vector)
{
return Vector3(scalar * vector[0], scalar * vector[1], scalar * vector[2]);
}
@@ -279,7 +297,7 @@ namespace RVO {
* \param vector2 The second vector with which the cross product should be computed.
* \return The cross product of the two specified vectors.
*/
- RVO3D_EXPORT inline Vector3 cross(const Vector3 &vector1, const Vector3 &vector2)
+ inline Vector3 cross(const Vector3 &vector1, const Vector3 &vector2)
{
return Vector3(vector1[1] * vector2[2] - vector1[2] * vector2[1], vector1[2] * vector2[0] - vector1[0] * vector2[2], vector1[0] * vector2[1] - vector1[1] * vector2[0]);
}
@@ -291,7 +309,7 @@ namespace RVO {
* \param vector The three-dimensional vector which to insert into the output stream.
* \return A reference to the output stream.
*/
- RVO3D_EXPORT inline std::ostream &operator<<(std::ostream &os, const Vector3 &vector)
+ inline std::ostream &operator<<(std::ostream &os, const Vector3 &vector)
{
os << "(" << vector[0] << "," << vector[1] << "," << vector[2] << ")";
@@ -304,7 +322,7 @@ namespace RVO {
* \param vector The three-dimensional vector whose length is to be computed.
* \return The length of the three-dimensional vector.
*/
- RVO3D_EXPORT inline float abs(const Vector3 &vector)
+ inline float abs(const Vector3 &vector)
{
return std::sqrt(vector * vector);
}
@@ -315,7 +333,7 @@ namespace RVO {
* \param vector The three-dimensional vector whose squared length is to be computed.
* \return The squared length of the three-dimensional vector.
*/
- RVO3D_EXPORT inline float absSq(const Vector3 &vector)
+ inline float absSq(const Vector3 &vector)
{
return vector * vector;
}
@@ -326,7 +344,7 @@ namespace RVO {
* \param vector The three-dimensional vector whose normalization is to be computed.
* \return The normalization of the three-dimensional vector.
*/
- RVO3D_EXPORT inline Vector3 normalize(const Vector3 &vector)
+ inline Vector3 normalize(const Vector3 &vector)
{
return vector / abs(vector);
}
diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS
index 11f3f170a5..c5f8529da9 100644
--- a/thirdparty/thorvg/AUTHORS
+++ b/thirdparty/thorvg/AUTHORS
@@ -1,4 +1,4 @@
-Hermet Park <hermetpark@gmail.com>
+Hermet Park <hermet@lottiefiles.com>
Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
Junsu Choi <jsuya.choi@samsung.com>
Pranay Samanta <pranay.ks@samsung.com>
@@ -15,3 +15,8 @@ Michal Maciola <m.maciola@samsung.com>
Peter Vullings <peter@projectitis.com>
K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
Rémi Verschelde <rverschelde@gmail.com>
+Martin Liska <mliksa@suse.cz>
+Vincenzo Pupillo <vincenzo.pupillo@unimi.it>
+EunSik Jeong <rinechran@outlook.jp>
+Samsung Electronics Co., Ltd
+Rafał Mikrut <mikrutrafal@protonmail.com>
diff --git a/thirdparty/thorvg/LICENSE b/thirdparty/thorvg/LICENSE
index 2f0361a864..d056ff6cbf 100644
--- a/thirdparty/thorvg/LICENSE
+++ b/thirdparty/thorvg/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2020 - 2022 notice for the ThorVG Project (see AUTHORS)
+Copyright (c) 2020 - 2023 notice for the ThorVG Project (see AUTHORS)
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:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 78522d6d2d..89caf0161d 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -13,5 +13,5 @@
#define THORVG_JPG_LOADER_SUPPORT 1
-#define THORVG_VERSION_STRING "0.8.4"
+#define THORVG_VERSION_STRING "0.9.0"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index b1f2e9e286..bde045f76c 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -2,11 +2,12 @@
* @file thorvg.h
*
* The main APIs enabling the TVG initialization, preparation of the canvas and provisioning of its content:
- * - drawing shapes such as line, curve, arc, rectangle, circle or user-defined
- * - drawing pictures - SVG, PNG, JPG, RAW
- * - solid or gradient filling
- * - continuous and dashed stroking
- * - clipping and masking
+ * - drawing shapes: line, arc, curve, path, polygon...
+ * - drawing pictures: tvg, svg, png, jpg, bitmap...
+ * - drawing fillings: solid, linear and radial gradient...
+ * - drawing stroking: continuous stroking with arbitrary width, join, cap, dash styles.
+ * - drawing composition: blending, masking, path clipping...
+ * - drawing scene graph & affine transformation (translation, rotation, scale, ...)
* and finally drawing the canvas and TVG termination.
*/
@@ -14,20 +15,36 @@
#ifndef _THORVG_H_
#define _THORVG_H_
+#include <functional>
#include <memory>
#include <string>
-#ifdef TVG_BUILD
- #if defined(_WIN32) && !defined(__clang__)
- #define TVG_EXPORT __declspec(dllexport)
- #define TVG_DEPRECATED __declspec(deprecated)
+#ifdef TVG_API
+ #undef TVG_API
+#endif
+
+#if defined(_WIN32) && !defined(__clang__)
+ #if TVG_BUILD
+ #if TVG_EXPORT
+ #define TVG_API __declspec(dllexport)
+ #else
+ #define TVG_API
+ #endif
#else
- #define TVG_EXPORT __attribute__ ((visibility ("default")))
- #define TVG_DEPRECATED __attribute__ ((__deprecated__))
+ #define TVG_API
#endif
+ #define TVG_DEPRECATED __declspec(deprecated)
#else
- #define TVG_EXPORT
- #define TVG_DEPRECATED
+ #if TVG_BUILD
+ #if TVG_EXPORT
+ #define TVG_API __attribute__ ((visibility ("default")))
+ #else
+ #define TVG_API
+ #endif
+ #else
+ #define TVG_API
+ #endif
+ #define TVG_DEPRECATED __attribute__ ((__deprecated__))
#endif
#ifdef __cplusplus
@@ -147,7 +164,7 @@ enum class CompositeMethod
ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible.
InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible.
- LumaMask ///< @BETA_API The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible.
+ LumaMask ///< The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. @since 0.9
};
/**
@@ -183,6 +200,33 @@ struct Matrix
float e31, e32, e33;
};
+/**
+ * @brief A data structure representing a texture mesh vertex
+ *
+ * @param pt The vertex coordinate
+ * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0)
+ *
+ * @BETA_API
+ */
+struct Vertex
+{
+ Point pt;
+ Point uv;
+};
+
+
+/**
+ * @brief A data structure representing a triange in a texture mesh
+ *
+ * @param vertex The three vertices that make up the polygon
+ *
+ * @BETA_API
+ */
+struct Polygon
+{
+ Vertex vertex[3];
+};
+
/**
* @class Paint
@@ -193,7 +237,7 @@ struct Matrix
* Paint represents such a graphical object and its behaviors such as duplication, transformation and composition.
* TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them.
*/
-class TVG_EXPORT Paint
+class TVG_API Paint
{
public:
virtual ~Paint();
@@ -263,6 +307,7 @@ public:
* @return Result::Success when succeed.
*
* @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible.
+ * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath)
*/
Result opacity(uint8_t o) noexcept;
@@ -336,27 +381,11 @@ public:
CompositeMethod composite(const Paint** target) const noexcept;
/**
- * @brief Gets the composition source object and the composition method.
- *
- * @param[out] source The paint of the composition source object.
- * @param[out] method The method used to composite the source object with the target.
- *
- * @return Result::Success when the paint object used as a composition target, Result::InsufficientCondition otherwise.
- *
- * @warning Please do not use it, this API is not official one. It could be modified in the next version.
- *
- * @BETA_API
- */
- Result composite(const Paint** source, CompositeMethod* method) const noexcept;
-
- /**
* @brief Return the unique id value of the paint instance.
*
* This method can be called for checking the current concrete instance type.
*
* @return The type id of the Paint instance.
- *
- * @BETA_API
*/
uint32_t identifier() const noexcept;
@@ -376,7 +405,7 @@ public:
* It specifies the gradient behavior in case the area defined by the gradient bounds
* is smaller than the area to be filled.
*/
-class TVG_EXPORT Fill
+class TVG_API Fill
{
public:
/**
@@ -463,8 +492,6 @@ public:
* This method can be called for checking the current concrete instance type.
*
* @return The type id of the Fill instance.
- *
- * @BETA_API
*/
uint32_t identifier() const noexcept;
@@ -482,7 +509,7 @@ public:
* @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical.
* @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases.
*/
-class TVG_EXPORT Canvas
+class TVG_API Canvas
{
public:
Canvas(RenderMethod*);
@@ -578,7 +605,7 @@ public:
* Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds.
* The behavior outside the gradient bounds depends on the value specified in the spread API.
*/
-class TVG_EXPORT LinearGradient final : public Fill
+class TVG_API LinearGradient final : public Fill
{
public:
~LinearGradient();
@@ -630,8 +657,6 @@ public:
* This method can be referred for identifying the LinearGradient class type.
*
* @return The type id of the LinearGradient class.
- *
- * @BETA_API
*/
static uint32_t identifier() noexcept;
@@ -645,7 +670,7 @@ public:
* @brief A class representing the radial gradient fill of the Shape object.
*
*/
-class TVG_EXPORT RadialGradient final : public Fill
+class TVG_API RadialGradient final : public Fill
{
public:
~RadialGradient();
@@ -689,8 +714,6 @@ public:
* This method can be referred for identifying the RadialGradient class type.
*
* @return The type id of the RadialGradient class.
- *
- * @BETA_API
*/
static uint32_t identifier() noexcept;
@@ -710,7 +733,7 @@ public:
* The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders.
* It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context.
*/
-class TVG_EXPORT Shape final : public Paint
+class TVG_API Shape final : public Paint
{
public:
~Shape();
@@ -943,6 +966,7 @@ public:
* @return Result::Success when succeed.
*
* @note Either a solid color or a gradient fill is applied, depending on what was set as last.
+ * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath)
*/
Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
@@ -968,6 +992,18 @@ public:
*/
Result fill(FillRule r) noexcept;
+
+ /**
+ * @brief Sets the rendering order of the stroke and the fill.
+ *
+ * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option).
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @BETA_API
+ */
+ Result order(bool strokeFirst) noexcept;
+
+
/**
* @brief Gets the commands data of the path.
*
@@ -1074,8 +1110,6 @@ public:
* This method can be referred for identifying the Shape class type.
*
* @return The type id of the Shape class.
- *
- * @BETA_API
*/
static uint32_t identifier() noexcept;
@@ -1091,7 +1125,7 @@ public:
*
* @note Supported formats are depended on the available TVG loaders.
*/
-class TVG_EXPORT Picture final : public Paint
+class TVG_API Picture final : public Paint
{
public:
~Picture();
@@ -1185,11 +1219,50 @@ public:
/**
* @brief Loads a raw data from a memory block with a given size.
*
+ * @retval Result::Success When succeed, Result::InsufficientCondition otherwise.
+ * @retval Result::FailedAllocation An internal error possibly with memory allocation.
+ *
+ * @since 0.9
+ */
+ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
+
+ /**
+ * @brief Sets or removes the triangle mesh to deform the image.
+ *
+ * If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the
+ * image data will be used as the texture.
+ *
+ * If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed.
+ *
+ * Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support.
+ * mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh.
+ *
+ * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh.
+ * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh.
+ *
+ * @return Result::Success When succeed.
+ * @return Result::Unknown If fails
+ *
+ * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect.
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
*
* @BETA_API
*/
- Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
+ Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept;
+
+ /**
+ * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh.
+ *
+ * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh.
+ *
+ * @return uint32_t The number of polygons in the array.
+ *
+ * @note Modifying the triangles returned by this method will modify them directly within the mesh.
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ uint32_t mesh(const Polygon** triangles) const noexcept;
/**
* @brief Gets the position and the size of the loaded SVG picture.
@@ -1213,8 +1286,6 @@ public:
* This method can be referred for identifying the Picture class type.
*
* @return The type id of the Picture class.
- *
- * @BETA_API
*/
static uint32_t identifier() noexcept;
@@ -1233,7 +1304,7 @@ public:
* As a group, the scene can be transformed, made translucent and composited with other target paints,
* its children will be affected by the scene world.
*/
-class TVG_EXPORT Scene final : public Paint
+class TVG_API Scene final : public Paint
{
public:
~Scene();
@@ -1293,8 +1364,6 @@ public:
* This method can be referred for identifying the Scene class type.
*
* @return The type id of the Scene class.
- *
- * @BETA_API
*/
static uint32_t identifier() noexcept;
@@ -1307,7 +1376,7 @@ public:
*
* @brief A class for the rendering graphical elements with a software raster engine.
*/
-class TVG_EXPORT SwCanvas final : public Canvas
+class TVG_API SwCanvas final : public Canvas
{
public:
~SwCanvas();
@@ -1398,7 +1467,7 @@ public:
*
* @BETA_API
*/
-class TVG_EXPORT GlCanvas final : public Canvas
+class TVG_API GlCanvas final : public Canvas
{
public:
~GlCanvas();
@@ -1430,7 +1499,7 @@ public:
*
* @brief A class that enables initialization and termination of the TVG engines.
*/
-class TVG_EXPORT Initializer final
+class TVG_API Initializer final
{
public:
/**
@@ -1492,7 +1561,7 @@ public:
*
* @since 0.5
*/
-class TVG_EXPORT Saver final
+class TVG_API Saver final
{
public:
~Saver();
@@ -1562,13 +1631,13 @@ public:
*
* @BETA_API
*/
-class TVG_EXPORT Accessor final
+class TVG_API Accessor final
{
public:
~Accessor();
/**
- * @brief Access the Picture scene stree nodes.
+ * @brief Set the access function for traversing the Picture scene tree nodes.
*
* @param[in] picture The picture node to traverse the internal scene-tree.
* @param[in] func The callback function calling for every paint nodes of the Picture.
@@ -1579,7 +1648,7 @@ public:
*
* @BETA_API
*/
- std::unique_ptr<Picture> access(std::unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept;
+ std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept;
/**
* @brief Creates a new Accessor object.
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
index 157fdb8f82..0e9029bf73 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SW_COMMON_H_
#define _TVG_SW_COMMON_H_
@@ -222,11 +223,16 @@ struct SwImage
{
SwOutline* outline = nullptr;
SwRleData* rle = nullptr;
- uint32_t* data = nullptr;
+ union {
+ pixel_t* data; //system based data pointer
+ uint32_t* buf32; //for explicit 32bits channels
+ uint8_t* buf8; //for explicit 8bits grayscale
+ };
uint32_t w, h, stride;
int32_t ox = 0; //offset x
int32_t oy = 0; //offset y
float scale;
+ uint8_t channelSize;
bool direct = false; //draw image directly (with offset)
bool scaled = false; //draw scaled image
@@ -235,7 +241,7 @@ struct SwImage
struct SwBlender
{
uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
- uint32_t (*lumaValue)(uint32_t c);
+ uint8_t (*luma)(uint8_t* c);
};
struct SwCompositor;
@@ -301,12 +307,12 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
void shapeReset(SwShape* shape);
-bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
+bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepared(const SwShape* shape);
-bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
+bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
-void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
-bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform);
+bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
@@ -316,12 +322,12 @@ void shapeResetStrokeFill(SwShape* shape);
void shapeDelFill(SwShape* shape);
void shapeDelStrokeFill(SwShape* shape);
-void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
+void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform);
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke);
-bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
@@ -334,10 +340,12 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
+SwRleData* rleRender(const SwBBox* bbox);
void rleFree(SwRleData* rle);
void rleReset(SwRleData* rle);
-void rleClipPath(SwRleData *rle, const SwRleData *clip);
-void rleClipRect(SwRleData *rle, const SwBBox* clip);
+void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2);
+void rleClipPath(SwRleData* rle, const SwRleData* clip);
+void rleClipRect(SwRleData* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool);
@@ -350,11 +358,13 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
+bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
-bool rasterClear(SwSurface* surface);
+bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
-void rasterUnpremultiply(SwSurface* surface);
+void rasterUnpremultiply(Surface* surface);
+void rasterPremultiply(Surface* surface);
+bool rasterConvertCS(Surface* surface, ColorSpace to);
#endif /* _TVG_SW_COMMON_H_ */
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
index 04014a9ec3..694bc35231 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgSwCommon.h"
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
index c02e28b432..9e215dbefd 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgSwCommon.h"
@@ -33,7 +34,7 @@ static inline bool _onlyShifted(const Matrix* m)
}
-static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
+static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid)
{
image->outline = mpoolReqOutline(mpool, tid);
auto outline = image->outline;
@@ -51,10 +52,50 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool,
outline->closed[0] = true;
}
- auto w = static_cast<float>(image->w);
- auto h = static_cast<float>(image->h);
+ Point to[4];
+ if (mesh->triangleCnt > 0) {
+ // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple
+ // places. We should be able to re-use one we have already done? Also see:
+ // tvgPictureImpl.h --> bounds
+ // tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh
+ //
+ // TODO: Should we calculate the exact path(s) of the triangle mesh instead?
+ // i.e. copy tvgSwShape.capp -> _genOutline?
+ //
+ // TODO: Cntrs?
+ auto triangles = mesh->triangles;
+ auto min = triangles[0].vertex[0].pt;
+ auto max = triangles[0].vertex[0].pt;
+
+ for (uint32_t i = 0; i < mesh->triangleCnt; ++i) {
+ if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
+ else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
+ if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
+ else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
+
+ if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
+ else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
+ if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
+ else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
+
+ if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
+ else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
+ if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
+ else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
+ }
+ to[0] = {min.x, min.y};
+ to[1] = {max.x, min.y};
+ to[2] = {max.x, max.y};
+ to[3] = {min.x, max.y};
+ } else {
+ auto w = static_cast<float>(image->w);
+ auto h = static_cast<float>(image->h);
+ to[0] = {0, 0};
+ to[1] = {w, 0};
+ to[2] = {w, h};
+ to[3] = {0, h};
+ }
- Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}};
for (int i = 0; i < 4; i++) {
outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform);
outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
@@ -78,7 +119,7 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool,
/* External Class Implementation */
/************************************************************************/
-bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{
image->direct = _onlyShifted(transform);
@@ -96,7 +137,7 @@ bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipReg
else image->scaled = false;
}
- if (!_genOutline(image, transform, mpool, tid)) return false;
+ if (!_genOutline(image, mesh, transform, mpool, tid)) return false;
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
index 1027bb1f79..5a4f58d9a6 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <math.h>
#include "tvgSwCommon.h"
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
index d2962e6d8d..05ff9ddf0b 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgSwCommon.h"
@@ -59,16 +60,16 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
SwMpool* mpoolInit(unsigned threads)
{
- if (threads == 0) threads = 1;
+ auto allocSize = threads + 1;
auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
- mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
if (!mpool->outline) goto err;
- mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
if (!mpool->strokeOutline) goto err;
- mpool->allocSize = threads;
+ mpool->allocSize = allocSize;
return mpool;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
index ffd74bdd47..1f10afd9b3 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -37,8 +37,8 @@
/************************************************************************/
constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
-
-static inline uint32_t _multiplyAlpha(uint32_t c, uint32_t a)
+template<typename T>
+static inline T _multiply(T c, T a)
{
return ((c * a + 0xff) >> 8);
}
@@ -56,15 +56,29 @@ static inline uint32_t _ialpha(uint32_t c)
}
-static inline uint32_t _abgrLumaValue(uint32_t c)
+static inline uint8_t _alpha(uint8_t* a)
{
- return ((((c&0xff)*54) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B
+ return *a;
}
-static inline uint32_t _argbLumaValue(uint32_t c)
+static inline uint8_t _ialpha(uint8_t* a)
{
- return ((((c&0xff)*19) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R
+ return ~(*a);
+}
+
+
+static inline uint8_t _abgrLuma(uint8_t* c)
+{
+ auto v = *(uint32_t*)c;
+ return ((((v&0xff)*54) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B
+}
+
+
+static inline uint8_t _argbLuma(uint8_t* c)
+{
+ auto v = *(uint32_t*)c;
+ return ((((v&0xff)*19) + (((v>>8)&0xff)*183) + (((v>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R
}
@@ -148,65 +162,95 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
}
+void _rasterGrayscale8(uint8_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ cRasterPixels<uint8_t>(dst, val, offset, len);
+}
+
/************************************************************************/
/* Rect */
/************************************************************************/
-static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*))
{
- TVGLOG("SW_ENGINE", "Masked Rect");
-
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
-
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
-
- for (uint32_t y = 0; y < h; ++y) {
- auto dst = &buffer[y * surface->stride];
- auto cmp = &cbuffer[y * surface->stride];
- for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp) {
- auto tmp = ALPHA_BLEND(color, blendMethod(*cmp));
- *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8 + ((region.min.y * surface->compositor->image.stride + region.min.x) * csize); //compositor buffer
+
+ TVGLOG("SW_ENGINE", "Masked Rect [Region: %lu %lu %u %u]", region.min.x, region.min.y, w, h);
+
+ //32bits channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, a);
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ auto cmp = &cbuffer[y * surface->stride * csize];
+ for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) {
+ auto tmp = ALPHA_BLEND(color, blender(cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ //8bits grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ auto cmp = &cbuffer[y * surface->stride * csize];
+ for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) {
+ auto tmp = _multiply<uint8_t>(a, blender(cmp));
+ *dst = tmp + _multiply<uint8_t>(*dst, _ialpha(tmp));
+ }
}
}
return true;
}
-static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b)
{
- auto buffer = surface->buffer + (region.min.y * surface->stride);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- for (uint32_t y = 0; y < h; ++y) {
- rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w);
+ //32bits channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, 255);
+ auto buffer = surface->buf32 + (region.min.y * surface->stride);
+ for (uint32_t y = 0; y < h; ++y) {
+ rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w);
+ }
+ //8bits grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ auto buffer = surface->buf8 + (region.min.y * surface->stride);
+ for (uint32_t y = 0; y < h; ++y) {
+ _rasterGrayscale8(buffer + y * surface->stride, 255, region.min.x, w);
+ }
}
return true;
}
-static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint8_t opacity)
+static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (_compositing(surface)) {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
- return _rasterMaskedRect(surface, region, color, _alpha);
+ return _rasterMaskedRect(surface, region, r, g, b, a, _alpha);
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
- return _rasterMaskedRect(surface, region, color, _ialpha);
+ return _rasterMaskedRect(surface, region, r, g, b, a, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterMaskedRect(surface, region, color, surface->blender.lumaValue);
+ return _rasterMaskedRect(surface, region, r, g, b, a, surface->blender.luma);
}
} else {
- if (opacity == 255) {
- return _rasterSolidRect(surface, region, color);
+ if (a == 255) {
+ return _rasterSolidRect(surface, region, r, g, b);
} else {
#if defined(THORVG_AVX_VECTOR_SUPPORT)
- return avxRasterTranslucentRect(surface, region, color);
+ return avxRasterTranslucentRect(surface, region, r, g, b, a);
#elif defined(THORVG_NEON_VECTOR_SUPPORT)
- return neonRasterTranslucentRect(surface, region, color);
+ return neonRasterTranslucentRect(surface, region, r, g, b, a);
#else
- return cRasterTranslucentRect(surface, region, color);
+ return cRasterTranslucentRect(surface, region, r, g, b, a);
#endif
}
}
@@ -218,41 +262,74 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color
/* Rle */
/************************************************************************/
-static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Masked Rle");
auto span = rle->spans;
uint32_t src;
- auto cbuffer = surface->compositor->image.data;
-
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
- if (span->coverage == 255) src = color;
- else src = ALPHA_BLEND(color, span->coverage);
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) {
- auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
- *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ auto cbuffer = surface->compositor->image.buf8;
+ auto csize = surface->compositor->image.channelSize;
+
+ //32bit channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, a);
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
+ if (span->coverage == 255) src = color;
+ else src = ALPHA_BLEND(color, span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) {
+ auto tmp = ALPHA_BLEND(src, blender(cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ //8bit grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
+ if (span->coverage == 255) src = a;
+ else src = _multiply<uint8_t>(a, span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) {
+ auto tmp = _multiply<uint8_t>(src, blender(cmp));
+ *dst = tmp + _multiply<uint8_t>(*dst, _ialpha(tmp));
+ }
}
}
return true;
}
-static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b)
{
auto span = rle->spans;
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- if (span->coverage == 255) {
- rasterRGBA32(surface->buffer + span->y * surface->stride, color, span->x, span->len);
- } else {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto src = ALPHA_BLEND(color, span->coverage);
- auto ialpha = 255 - span->coverage;
- for (uint32_t x = 0; x < span->len; ++x, ++dst) {
- *dst = src + ALPHA_BLEND(*dst, ialpha);
+ //32bit channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, 255);
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ rasterRGBA32(surface->buf32 + span->y * surface->stride, color, span->x, span->len);
+ } else {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto src = ALPHA_BLEND(color, span->coverage);
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ }
+ //8bit grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ _rasterGrayscale8(surface->buf8 + span->y * surface->stride, 255, span->x, span->len);
+ } else {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = span->coverage;
+ }
}
}
}
@@ -260,28 +337,28 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t c
}
-static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8_t opacity)
+static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!rle) return false;
if (_compositing(surface)) {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
- return _rasterMaskedRle(surface, rle, color, _alpha);
+ return _rasterMaskedRle(surface, rle, r, g, b, a, _alpha);
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
- return _rasterMaskedRle(surface, rle, color, _ialpha);
+ return _rasterMaskedRle(surface, rle, r, g, b, a, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterMaskedRle(surface, rle, color, surface->blender.lumaValue);
+ return _rasterMaskedRle(surface, rle, r, g, b, a, surface->blender.luma);
}
} else {
- if (opacity == 255) {
- return _rasterSolidRle(surface, rle, color);
+ if (a == 255) {
+ return _rasterSolidRle(surface, rle, r, g, b);
} else {
#if defined(THORVG_AVX_VECTOR_SUPPORT)
- return avxRasterTranslucentRle(surface, rle, color);
+ return avxRasterTranslucentRle(surface, rle, r, g, b, a);
#elif defined(THORVG_NEON_VECTOR_SUPPORT)
- return neonRasterTranslucentRle(surface, rle, color);
+ return neonRasterTranslucentRle(surface, rle, r, g, b, a);
#else
- return cRasterTranslucentRle(surface, rle, color);
+ return cRasterTranslucentRle(surface, rle, r, g, b, a);
#endif
}
}
@@ -301,7 +378,7 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.lumaValue);
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.luma);
}
} else {
return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr);
@@ -313,25 +390,26 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c
/* RLE Scaled RGBA Image */
/************************************************************************/
-static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Scaled Masked Translucent Rle Image");
auto span = image->rle->spans;
+ auto csize = surface->compositor->image.channelSize;
//Center (Down-Scaled)
if (image->scale < DOWN_SCALE_TOLERANCE) {
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
if (sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
- auto alpha = _multiplyAlpha(span->coverage, opacity);
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
- auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ auto tmp = ALPHA_BLEND(src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -340,14 +418,14 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
- auto alpha = _multiplyAlpha(span->coverage, opacity);
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
- auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha);
+ auto tmp = ALPHA_BLEND(src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -356,32 +434,33 @@ static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const
}
-static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Scaled Masked Rle Image");
auto span = image->rle->spans;
+ auto csize = surface->compositor->image.channelSize;
//Center (Down-Scaled)
if (image->scale < DOWN_SCALE_TOLERANCE) {
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
if (sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
if (span->coverage == 255) {
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ auto tmp = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
- auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -391,21 +470,21 @@ static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* i
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
if (span->coverage == 255) {
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto tmp = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ auto tmp = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
- for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
- auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -424,12 +503,12 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
if (sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto alpha = _multiplyAlpha(span->coverage, opacity);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -438,12 +517,12 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto alpha = _multiplyAlpha(span->coverage, opacity);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -461,19 +540,19 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image,
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
if (sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -483,19 +562,19 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image,
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -522,7 +601,7 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue);
+ return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma);
}
} else {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
@@ -530,7 +609,7 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue);
+ return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma);
}
}
} else {
@@ -545,26 +624,27 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const
/* RLE Direct RGBA Image */
/************************************************************************/
-static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.data;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
- auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
- auto alpha = _multiplyAlpha(span->coverage, opacity);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
+ auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
if (alpha == 255) {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
- auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*img, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
- auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(alpha, blendMethod(*cmp)));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*img, _multiply<uint32_t>(alpha, blender(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -573,25 +653,26 @@ static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const
}
-static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.data;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
- auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
+ auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
if (span->coverage == 255) {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
- auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*img, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
- auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(span->coverage, blendMethod(*cmp)));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*img, _multiply<uint32_t>(span->coverage, blender(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -605,9 +686,9 @@ static bool _rasterDirectTranslucentRleRGBAImage(SwSurface* surface, const SwIma
auto span = image->rle->spans;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
- auto alpha = _multiplyAlpha(span->coverage, opacity);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiply<uint32_t>(span->coverage, opacity);
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
auto src = ALPHA_BLEND(*img, alpha);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
@@ -622,8 +703,8 @@ static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image)
auto span = image->rle->spans;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
if (span->coverage == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
*dst = *img + ALPHA_BLEND(*dst, _ialpha(*img));
@@ -648,7 +729,7 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.lumaValue);
+ return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.luma);
}
} else {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
@@ -656,7 +737,7 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.lumaValue);
+ return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.luma);
}
}
} else {
@@ -679,7 +760,7 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterTexmapPolygon(surface, image, transform, &region, opacity, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterTexmapPolygon(surface, image, transform, &region, opacity, surface->blender.lumaValue);
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, surface->blender.luma);
}
} else {
return _rasterTexmapPolygon(surface, image, transform, &region, opacity, nullptr);
@@ -687,18 +768,34 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons
return false;
}
+static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _ialpha);
+ } else if (surface->compositor->method == CompositeMethod::LumaMask) {
+ return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.luma);
+ }
+ } else {
+ return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, nullptr);
+ }
+ return false;
+}
+
/************************************************************************/
/*Scaled RGBA Image */
/************************************************************************/
-
-static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Scaled Masked Image");
- auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+ auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
// Down-Scaled
if (image->scale < DOWN_SCALE_TOLERANCE) {
@@ -707,15 +804,15 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw
if (sy >= image->h) continue;
auto dst = dbuffer;
auto cmp = cbuffer;
- for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ auto alpha = _multiply<uint32_t>(opacity, blender(cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
dbuffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
}
// Up-Scaled
} else {
@@ -724,27 +821,28 @@ static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const Sw
if ((uint32_t)sy >= image->h) continue;
auto dst = dbuffer;
auto cmp = cbuffer;
- for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ auto alpha = _multiply<uint32_t>(opacity, blender(cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
dbuffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
}
}
return true;
}
-static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t (*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Scaled Masked Image");
- auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+ auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
// Down-Scaled
if (image->scale < DOWN_SCALE_TOLERANCE) {
@@ -753,14 +851,14 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag
if (sy >= image->h) continue;
auto dst = dbuffer;
auto cmp = cbuffer;
- for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp));
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
dbuffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
}
// Up-Scaled
} else {
@@ -769,14 +867,14 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag
if ((uint32_t)sy >= image->h) continue;
auto dst = dbuffer;
auto cmp = cbuffer;
- for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp));
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
dbuffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
}
}
return true;
@@ -785,7 +883,7 @@ static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* imag
static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
{
- auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
// Down-Scaled
if (image->scale < DOWN_SCALE_TOLERANCE) {
@@ -796,7 +894,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage*
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), opacity);
+ auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), opacity);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -809,7 +907,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage*
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), opacity);
+ auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), opacity);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -820,7 +918,7 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage*
static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale)
{
- auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
// Down-Scaled
if (image->scale < DOWN_SCALE_TOLERANCE) {
@@ -831,7 +929,7 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
if (sx >= image->w) continue;
- auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -844,7 +942,7 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy);
*dst = src + ALPHA_BLEND(*dst, _ialpha(src));
}
}
@@ -870,7 +968,7 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue);
+ return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma);
}
} else {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
@@ -878,7 +976,7 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue);
+ return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma);
}
}
} else {
@@ -893,54 +991,56 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat
/* Direct RGBA Image */
/************************************************************************/
-static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t (*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Direct Masked Image");
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto csize = surface->compositor->image.channelSize;
- auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+ auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
for (uint32_t y = 0; y < h2; ++y) {
auto dst = buffer;
auto cmp = cbuffer;
auto src = sbuffer;
- for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
buffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride;
}
return true;
}
-static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint8_t (*blender)(uint8_t*))
{
TVGLOG("SW_ENGINE", "Direct Masked Translucent Image");
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto csize = surface->compositor->image.channelSize;
- auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+ auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
for (uint32_t y = 0; y < h2; ++y) {
auto dst = buffer;
auto cmp = cbuffer;
auto src = sbuffer;
- for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
- auto tmp = ALPHA_BLEND(*src, _multiplyAlpha(opacity, blendMethod(*cmp)));
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, _multiply<uint32_t>(opacity, blender(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
buffer += surface->stride;
- cbuffer += surface->compositor->image.stride;
+ cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride;
}
return true;
@@ -949,8 +1049,8 @@ static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const Sw
static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity)
{
- auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
- auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
@@ -968,8 +1068,8 @@ static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage*
static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
{
- auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
- auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
@@ -994,7 +1094,7 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.lumaValue);
+ return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.luma);
}
} else {
if (surface->compositor->method == CompositeMethod::AlphaMask) {
@@ -1002,7 +1102,7 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.lumaValue);
+ return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.luma);
}
}
} else {
@@ -1034,14 +1134,15 @@ static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* t
/* Rect Linear Gradient */
/************************************************************************/
-static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t (*blender)(uint8_t*))
{
if (fill->linear.len < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
if (!sbuffer) return false;
@@ -1051,12 +1152,12 @@ static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& re
auto dst = buffer;
auto cmp = cbuffer;
auto src = sbuffer;
- for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
buffer += surface->stride;
- cbuffer += surface->stride;
+ cbuffer += surface->stride * csize;
}
return true;
}
@@ -1066,7 +1167,7 @@ static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBo
{
if (fill->linear.len < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@@ -1089,7 +1190,7 @@ static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& reg
{
if (fill->linear.len < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
@@ -1108,7 +1209,7 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region,
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.lumaValue);
+ return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.luma);
}
} else {
if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill);
@@ -1122,29 +1223,30 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region,
/* Rle Linear Gradient */
/************************************************************************/
-static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t (*blender)(uint8_t*))
{
if (fill->linear.len < FLT_EPSILON) return false;
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.data;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8;
auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!buffer) return false;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
fillFetchLinear(fill, buffer, span->y, span->x, span->len);
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
auto src = buffer;
if (span->coverage == 255) {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
auto ialpha = 255 - span->coverage;
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha);
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
@@ -1163,7 +1265,7 @@ static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleD
if (!buffer) return false;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
fillFetchLinear(fill, buffer, span->y, span->x, span->len);
if (span->coverage == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
@@ -1191,10 +1293,10 @@ static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* r
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
if (span->coverage == 255) {
- fillFetchLinear(fill, surface->buffer + span->y * surface->stride + span->x, span->y, span->x, span->len);
+ fillFetchLinear(fill, surface->buf32 + span->y * surface->stride + span->x, span->y, span->x, span->len);
} else {
fillFetchLinear(fill, buf, span->y, span->x, span->len);
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
for (uint32_t x = 0; x < span->len; ++x) {
dst[x] = INTERPOLATE(span->coverage, buf[x], dst[x]);
}
@@ -1214,7 +1316,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue);
+ return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.luma);
}
} else {
if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill);
@@ -1228,14 +1330,15 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
/* Rect Radial Gradient */
/************************************************************************/
-static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t(*blender)(uint8_t*))
{
if (fill->radial.a < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
- auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
if (!sbuffer) return false;
@@ -1245,12 +1348,12 @@ static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& re
auto dst = buffer;
auto cmp = cbuffer;
auto src = sbuffer;
- for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
buffer += surface->stride;
- cbuffer += surface->stride;
+ cbuffer += surface->stride * csize;
}
return true;
}
@@ -1260,7 +1363,7 @@ static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBo
{
if (fill->radial.a < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@@ -1283,7 +1386,7 @@ static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& reg
{
if (fill->radial.a < FLT_EPSILON) return false;
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@@ -1303,7 +1406,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.lumaValue);
+ return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.luma);
}
} else {
if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill);
@@ -1317,28 +1420,29 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
/* RLE Radial Gradient */
/************************************************************************/
-static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t(*blender)(uint8_t*))
{
if (fill->radial.a < FLT_EPSILON) return false;
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.data;
+ auto csize = surface->compositor->image.channelSize;
+ auto cbuffer = surface->compositor->image.buf8;
auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
if (!buffer) return false;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
fillFetchRadial(fill, buffer, span->y, span->x, span->len);
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
- auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
auto src = buffer;
if (span->coverage == 255) {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
- auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, blender(cmp));
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
} else {
- for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
- auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blendMethod(*cmp)), *dst);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blender(cmp)), *dst);
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -1356,7 +1460,7 @@ static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleD
if (!buffer) return false;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
fillFetchRadial(fill, buffer, span->y, span->x, span->len);
if (span->coverage == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
@@ -1383,7 +1487,7 @@ static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* r
auto span = rle->spans;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage == 255) {
fillFetchRadial(fill, dst, span->y, span->x, span->len);
} else {
@@ -1408,7 +1512,7 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
} else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha);
} else if (surface->compositor->method == CompositeMethod::LumaMask) {
- return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue);
+ return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.luma);
}
} else {
if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill);
@@ -1417,7 +1521,6 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
return false;
}
-
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
@@ -1429,47 +1532,65 @@ void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
#elif defined(THORVG_NEON_VECTOR_SUPPORT)
neonRasterRGBA32(dst, val, offset, len);
#else
- cRasterRGBA32(dst, val, offset, len);
+ cRasterPixels<uint32_t>(dst, val, offset, len);
#endif
}
bool rasterCompositor(SwSurface* surface)
{
- if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) {
+ if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) {
surface->blender.join = _abgrJoin;
- surface->blender.lumaValue = _abgrLumaValue;
- } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ surface->blender.luma = _abgrLuma;
+ } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) {
surface->blender.join = _argbJoin;
- surface->blender.lumaValue = _argbLumaValue;
+ surface->blender.luma = _argbLuma;
} else {
- //What Color Space ???
+ TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs);
return false;
}
return true;
}
-bool rasterClear(SwSurface* surface)
+bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
- if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
+ if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false;
- if (surface->w == surface->stride) {
- rasterRGBA32(surface->buffer, 0x00000000, 0, surface->w * surface->h);
- } else {
- for (uint32_t i = 0; i < surface->h; i++) {
- rasterRGBA32(surface->buffer + surface->stride * i, 0x00000000, 0, surface->w);
+ //full clear
+ if (surface->channelSize == sizeof(uint32_t)) {
+ if (w == surface->stride) {
+ rasterRGBA32(surface->buf32 + (surface->stride * y), 0x00000000, 0, w * h);
+ } else {
+ auto buffer = surface->buf32 + (surface->stride * y + x);
+ for (uint32_t i = 0; i < h; i++) {
+ rasterRGBA32(buffer + (surface->stride * i), 0x00000000, 0, w);
+ }
+ }
+ //partial clear
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ if (w == surface->stride) {
+ _rasterGrayscale8(surface->buf8 + (surface->stride * y), 0x00, 0, w * h);
+ } else {
+ auto buffer = surface->buf8 + (surface->stride * y + x);
+ for (uint32_t i = 0; i < h; i++) {
+ _rasterGrayscale8(buffer + (surface->stride * i), 0x00, 0, w);
+ }
}
}
return true;
}
-void rasterUnpremultiply(SwSurface* surface)
+void rasterUnpremultiply(Surface* surface)
{
+ if (surface->channelSize != sizeof(uint32_t)) return;
+
+ TVGLOG("SW_ENGINE", "Unpremultiply [Size: %d x %d]", surface->w, surface->h);
+
//OPTIMIZE_ME: +SIMD
for (uint32_t y = 0; y < surface->h; y++) {
- auto buffer = surface->buffer + surface->stride * y;
+ auto buffer = surface->buf32 + surface->stride * y;
for (uint32_t x = 0; x < surface->w; ++x) {
uint8_t a = buffer[x] >> 24;
if (a == 255) {
@@ -1487,11 +1608,37 @@ void rasterUnpremultiply(SwSurface* surface)
}
}
}
+ surface->premultiplied = false;
+}
+
+
+void rasterPremultiply(Surface* surface)
+{
+ if (surface->channelSize != sizeof(uint32_t)) return;
+
+ TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h);
+
+ //OPTIMIZE_ME: +SIMD
+ auto buffer = surface->buf32;
+ for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) {
+ auto dst = buffer;
+ for (uint32_t x = 0; x < surface->w; ++x, ++dst) {
+ auto c = *dst;
+ auto a = (c >> 24);
+ *dst = (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
+ }
+ }
+ surface->premultiplied = true;
}
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
+ return false;
+ }
+
if (!shape->fill) return false;
if (shape->fastTrack) {
@@ -1507,6 +1654,11 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
+ return false;
+ }
+
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
@@ -1519,40 +1671,60 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (a < 255) {
- r = _multiplyAlpha(r, a);
- g = _multiplyAlpha(g, a);
- b = _multiplyAlpha(b, a);
+ r = _multiply<uint32_t>(r, a);
+ g = _multiply<uint32_t>(g, a);
+ b = _multiply<uint32_t>(b, a);
}
- auto color = surface->blender.join(r, g, b, a);
-
- if (shape->fastTrack) return _rasterRect(surface, shape->bbox, color, a);
- else return _rasterRle(surface, shape->rle, color, a);
+ if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a);
+ else return _rasterRle(surface, shape->rle, r, g, b, a);
}
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (a < 255) {
- r = _multiplyAlpha(r, a);
- g = _multiplyAlpha(g, a);
- b = _multiplyAlpha(b, a);
+ r = _multiply<uint32_t>(r, a);
+ g = _multiply<uint32_t>(g, a);
+ b = _multiply<uint32_t>(b, a);
}
- auto color = surface->blender.join(r, g, b, a);
-
- return _rasterRle(surface, shape->strokeRle, color, a);
+ return _rasterRle(surface, shape->strokeRle, r, g, b, a);
}
-bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
+bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale image!");
+ return false;
+ }
+
//Verify Boundary
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return false;
//TOOD: switch (image->format)
- //TODO: case: _rasterRGBImage()
- //TODO: case: _rasterGrayscaleImage()
- //TODO: case: _rasterAlphaImage()
- return _rasterRGBAImage(surface, image, transform, bbox, opacity);
+ //TODO: case: _rasterRGBImageMesh()
+ //TODO: case: _rasterGrayscaleImageMesh()
+ //TODO: case: _rasterAlphaImageMesh()
+ if (mesh && mesh->triangleCnt > 0) return _transformedRGBAImageMesh(surface, image, mesh, transform, &bbox, opacity);
+ else return _rasterRGBAImage(surface, image, transform, bbox, opacity);
+}
+
+
+bool rasterConvertCS(Surface* surface, ColorSpace to)
+{
+ //TOOD: Support SIMD accelerations
+ auto from = surface->cs;
+
+ if ((from == ColorSpace::ABGR8888 && to == ColorSpace::ARGB8888) || (from == ColorSpace::ABGR8888S && to == ColorSpace::ARGB8888S)) {
+ surface->cs = to;
+ return cRasterABGRtoARGB(surface);
+ }
+ if ((from == ColorSpace::ARGB8888 && to == ColorSpace::ABGR8888) || (from == ColorSpace::ARGB8888S && to == ColorSpace::ABGR8888S)) {
+ surface->cs = to;
+ return cRasterARGBtoABGR(surface);
+ }
+
+ return false;
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
index 7a129a3a80..cf658a6abb 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -82,9 +82,15 @@ static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_
}
-static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ if (surface->channelSize != sizeof(uint32_t)) {
+ TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize);
+ return false;
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@@ -125,13 +131,19 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u
}
-static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
+ if (surface->channelSize != sizeof(uint32_t)) {
+ TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize);
+ return false;
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
auto span = rle->spans;
uint32_t src;
for (uint32_t i = 0; i < rle->size; ++i) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
index de6b35fd64..ad2fc57f24 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,45 +20,95 @@
* SOFTWARE.
*/
-
-static void inline cRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+template<typename PIXEL_T>
+static void inline cRasterPixels(PIXEL_T* dst, uint32_t val, uint32_t offset, int32_t len)
{
dst += offset;
while (len--) *dst++ = val;
}
-static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
- uint32_t src;
-
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
-
- if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
- else src = color;
- for (uint32_t x = 0; x < span->len; ++x, ++dst) {
- *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ //32bit channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, a);
+ uint32_t src;
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ //8bit grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ uint8_t src;
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ if (span->coverage < 255) src = _multiply<uint8_t>(span->coverage, a);
+ else src = a;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + _multiply<uint8_t>(*dst, ~src);
+ }
}
}
return true;
}
-static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
- auto ialpha = _ialpha(color);
- for (uint32_t y = 0; y < h; ++y) {
- auto dst = &buffer[y * surface->stride];
- for (uint32_t x = 0; x < w; ++x, ++dst) {
- *dst = color + ALPHA_BLEND(*dst, ialpha);
+ //32bits channels
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto color = surface->blender.join(r, g, b, a);
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+ auto ialpha = _ialpha(color);
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ //8bit grayscale
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = a + _multiply<uint8_t>(*dst, ~a);
+ }
}
}
return true;
}
+
+
+static bool inline cRasterABGRtoARGB(Surface* surface)
+{
+ TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
+
+ auto buffer = surface->buf32;
+ for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) {
+ auto dst = buffer;
+ for (uint32_t x = 0; x < surface->w; ++x, ++dst) {
+ auto c = *dst;
+ //flip Blue, Red channels
+ *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16);
+ }
+ }
+ return true;
+}
+
+
+static bool inline cRasterARGBtoABGR(Surface* surface)
+{
+ //exactly same with ABGRtoARGB
+ return cRasterABGRtoARGB(surface);
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
index a4b3cdaeb2..33c3d181b5 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -49,8 +49,14 @@ static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32
}
-static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
+ if (surface->channelSize != sizeof(uint32_t)) {
+ TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize);
+ return false;
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
auto span = rle->spans;
uint32_t src;
uint8x8_t *vDst = nullptr;
@@ -60,7 +66,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color;
- auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto ialpha = 255 - _alpha(src);
if ((((uint32_t) dst) & 0x7) != 0) {
@@ -88,9 +94,15 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
}
-static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ if (surface->channelSize != sizeof(uint32_t)) {
+ TVGERR("SW_ENGINE", "Unsupported Channel Size = %d", surface->channelSize);
+ return false;
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto ialpha = 255 - _alpha(color);
@@ -116,7 +128,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
for (uint32_t x = 0; x < (w - align) / 2; ++x)
vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha));
-
+
auto leftovers = (w - align) % 2;
if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha);
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
index abb57d7c45..52585162fc 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,17 +20,6 @@
* SOFTWARE.
*/
-struct Vertex
-{
- Point pt;
- Point uv;
-};
-
-struct Polygon
-{
- Vertex vertex[3];
-};
-
struct AALine
{
int32_t x[2];
@@ -80,7 +69,7 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
}
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
{
#define TEXMAP_TRANSLUCENT
#define TEXMAP_MASKING
@@ -90,7 +79,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
}
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
{
#define TEXMAP_MASKING
#include "tvgSwRasterTexmapInternal.h"
@@ -113,7 +102,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */
-static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
{
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@@ -201,9 +190,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
dxdyb = dxdy[0];
xb = x[0] + dy * dxdyb + (off_y * dxdyb);
- if (blendMethod) {
- if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
- else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ if (blender) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans);
} else {
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
@@ -222,9 +211,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
// Set right edge X-slope and perform subpixel pre-stepping
dxdyb = dxdy[2];
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
- if (blendMethod) {
- if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
- else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ if (blender) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans);
} else {
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
@@ -251,9 +240,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
ua = u[0] + dy * dudya + (off_y * dudya);
va = v[0] + dy * dvdya + (off_y * dvdya);
- if (blendMethod) {
- if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
- else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ if (blender) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans);
} else {
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
@@ -275,9 +264,9 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
ua = u[1] + dy * dudya + (off_y * dudya);
va = v[1] + dy * dvdya + (off_y * dvdya);
- if (blendMethod) {
- if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
- else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ if (blender) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans);
} else {
if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
@@ -287,18 +276,10 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
}
-static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region)
+static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
{
- //Initialize Y range
- float ys = FLT_MAX, ye = -1.0f;
-
- for (int i = 0; i < 4; i++) {
- if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
- if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
- }
-
- auto yStart = static_cast<int32_t>(ys);
- auto yEnd = static_cast<int32_t>(ye);
+ auto yStart = static_cast<int32_t>(ymin);
+ auto yEnd = static_cast<int32_t>(ymax);
if (!_arrange(image, region, yStart, yEnd)) return nullptr;
@@ -521,7 +502,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
auto offset = y * surface->stride;
//Left edge
- dst = surface->buffer + (offset + line->x[0]);
+ dst = surface->buf32 + (offset + line->x[0]);
if (line->x[0] > 1) pixel = *(dst - 1);
else pixel = *dst;
@@ -533,10 +514,10 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
}
//Right edge
- dst = surface->buffer + (offset + line->x[1] - 1);
+ dst = surface->buf32 + (offset + line->x[1] - 1);
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
else pixel = *dst;
-
+
pos = width;
while ((int32_t)(width - line->length[1]) < pos) {
*dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
@@ -562,12 +543,12 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
0 -- 1
| / |
| / |
- 3 -- 2
+ 3 -- 2
*/
-static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*))
{
//Exceptions: No dedicated drawing area?
- if (!region && image->rle->size == 0) return false;
+ if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
/* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */
@@ -577,9 +558,15 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
- for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform);
+ float ys = FLT_MAX, ye = -1.0f;
+ for (int i = 0; i < 4; i++) {
+ mathMultiply(&vertices[i].pt, transform);
- auto aaSpans = _AASpans(vertices, image, region);
+ if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
+ if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
+ }
+
+ auto aaSpans = _AASpans(ys, ye, image, region);
if (!aaSpans) return true;
Polygon polygon;
@@ -589,14 +576,72 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
polygon.vertex[1] = vertices[1];
polygon.vertex[2] = vertices[3];
- _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans);
//Draw the second polygon
polygon.vertex[0] = vertices[1];
polygon.vertex[1] = vertices[2];
polygon.vertex[2] = vertices[3];
- _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans);
return _apply(surface, aaSpans);
}
+
+
+/*
+ Provide any number of triangles to draw a mesh using the supplied image.
+ Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
+ Example:
+
+ 0 -- 1 0 -- 1 0
+ | / | --> | / / |
+ | / | | / / |
+ 2 -- 3 2 1 -- 2
+
+ Should provide two Polygons, one for each triangle.
+ // TODO: region?
+*/
+static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*))
+{
+ //Exceptions: No dedicated drawing area?
+ if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
+
+ // Step polygons once to transform
+ auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
+ float ys = FLT_MAX, ye = -1.0f;
+ for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
+ transformedTris[i] = mesh->triangles[i];
+ mathMultiply(&transformedTris[i].vertex[0].pt, transform);
+ mathMultiply(&transformedTris[i].vertex[1].pt, transform);
+ mathMultiply(&transformedTris[i].vertex[2].pt, transform);
+
+ if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
+ else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
+ if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
+ else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
+ if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
+ else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
+
+ // Convert normalized UV coordinates to image coordinates
+ transformedTris[i].vertex[0].uv.x *= (float)image->w;
+ transformedTris[i].vertex[0].uv.y *= (float)image->h;
+ transformedTris[i].vertex[1].uv.x *= (float)image->w;
+ transformedTris[i].vertex[1].uv.y *= (float)image->h;
+ transformedTris[i].vertex[2].uv.x *= (float)image->w;
+ transformedTris[i].vertex[2].uv.y *= (float)image->h;
+ }
+
+ // Get AA spans and step polygons again to draw
+ auto aaSpans = _AASpans(ys, ye, image, region);
+ if (aaSpans) {
+ for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
+ _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blender, aaSpans);
+ }
+ // Apply to surface (note: frees the AA spans)
+ _apply(surface, aaSpans);
+ }
+ free(transformedTris);
+
+ return true;
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
index 50536299b1..51685fe6e8 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,12 +19,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
{
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
- auto sbuf = image->data;
- auto dbuf = surface->buffer;
+ auto sbuf = image->buf32;
+ auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t dw = surface->stride;
@@ -36,7 +37,8 @@
SwSpan* span = nullptr; //used only when rle based.
#ifdef TEXMAP_MASKING
- uint32_t* cmp;
+ uint8_t* cmp;
+ auto csize = surface->compositor->image.channelSize;
#endif
if (!_arrange(image, region, yStart, yEnd)) return;
@@ -93,7 +95,7 @@
x = x1;
#ifdef TEXMAP_MASKING
- cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1];
+ cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
#endif
//Draw horizontal line
while (x++ < x2) {
@@ -104,6 +106,9 @@
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
+
+ if (vv >= sh) continue;
+
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
@@ -126,9 +131,9 @@
px = INTERPOLATE(ab, px, px2);
}
#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT)
- auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp)));
+ auto src = ALPHA_BLEND(px, _multiply<uint32_t>(opacity, blender(cmp)));
#elif defined(TEXMAP_MASKING)
- auto src = ALPHA_BLEND(px, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(px, blender(cmp));
#elif defined(TEXMAP_TRANSLUCENT)
auto src = ALPHA_BLEND(px, opacity);
#else
@@ -137,7 +142,7 @@
*buf = src + ALPHA_BLEND(*buf, _ialpha(src));
++buf;
#ifdef TEXMAP_MASKING
- ++cmp;
+ cmp += csize;
#endif
//Step UV horizontally
u += _dudx;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
index a1c0032a2e..6223bc8722 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgSwCommon.h"
#include "tvgTaskScheduler.h"
@@ -60,6 +61,8 @@ struct SwTask : Task
}
virtual bool dispose() = 0;
+ virtual bool clip(SwRleData* target) = 0;
+ virtual SwRleData* rle() = 0;
virtual ~SwTask()
{
@@ -71,23 +74,39 @@ struct SwTask : Task
struct SwShapeTask : SwTask
{
SwShape shape;
- const Shape* sdata = nullptr;
+ const RenderShape* rshape = nullptr;
bool cmpStroking = false;
+ bool clipper = false;
+
+ bool clip(SwRleData* target) override
+ {
+ if (shape.fastTrack) rleClipRect(target, &bbox);
+ else if (shape.rle) rleClipPath(target, shape.rle);
+ else return false;
+
+ return true;
+ }
+
+ SwRleData* rle() override
+ {
+ if (!shape.rle && shape.fastTrack) {
+ shape.rle = rleRender(&shape.bbox);
+ }
+ return shape.rle;
+ }
void run(unsigned tid) override
{
- auto compMethod = CompositeMethod::None;
- auto usedAsClip = (sdata->composite(nullptr, &compMethod) == Result::Success) && (compMethod == CompositeMethod::ClipPath);
- if (opacity == 0 && !usedAsClip) return; //Invisible
+ if (opacity == 0 && !clipper) return; //Invisible
uint8_t strokeAlpha = 0;
auto visibleStroke = false;
bool visibleFill = false;
auto clipRegion = bbox;
- if (HALF_STROKE(sdata->strokeWidth()) > 0) {
- sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
- visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
+ if (HALF_STROKE(rshape->strokeWidth()) > 0) {
+ rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
+ visibleStroke = rshape->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
}
//This checks also for the case, if the invisible shape turned to visible by alpha.
@@ -97,12 +116,12 @@ struct SwShapeTask : SwTask
//Shape
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
uint8_t alpha = 0;
- sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
+ rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
- visibleFill = (alpha > 0 || sdata->fill());
- if (visibleFill || visibleStroke || usedAsClip) {
+ visibleFill = (alpha > 0 || rshape->fill);
+ if (visibleFill || visibleStroke || clipper) {
shapeReset(&shape);
- if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
+ if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
}
}
@@ -112,16 +131,16 @@ struct SwShapeTask : SwTask
//Fill
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
- if (visibleFill || usedAsClip) {
+ if (visibleFill || clipper) {
/* We assume that if stroke width is bigger than 2,
shape outline below stroke could be full covered by stroke drawing.
Thus it turns off antialising in that condition.
Also, it shouldn't be dash style. */
- auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
+ auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
- if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
+ if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
}
- if (auto fill = sdata->fill()) {
+ if (auto fill = rshape->fill) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
if (ctable) shapeResetFill(&shape);
if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
@@ -133,10 +152,10 @@ struct SwShapeTask : SwTask
//Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (visibleStroke) {
- shapeResetStroke(&shape, sdata, transform);
- if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
+ shapeResetStroke(&shape, rshape, transform);
+ if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
- if (auto fill = sdata->strokeFill()) {
+ if (auto fill = rshape->strokeFill()) {
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape);
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
@@ -148,27 +167,21 @@ struct SwShapeTask : SwTask
}
}
+ //Clear current task memorypool here if the clippers would use the same memory pool
+ shapeDelOutline(&shape, mpool, tid);
+
//Clip Path
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
- auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
+ auto clipper = static_cast<SwTask*>(*clip);
//Clip shape rle
- if (shape.rle) {
- if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
- else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
- else goto err;
- }
+ if (shape.rle && !clipper->clip(shape.rle)) goto err;
//Clip stroke rle
- if (shape.strokeRle) {
- if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
- else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
- else goto err;
- }
+ if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
}
- goto end;
+ return;
err:
shapeReset(&shape);
- end:
shapeDelOutline(&shape, mpool, tid);
}
@@ -180,35 +193,116 @@ struct SwShapeTask : SwTask
};
+struct SwSceneTask : SwTask
+{
+ Array<RenderData> scene; //list of paints render data (SwTask)
+ SwRleData* sceneRle = nullptr;
+
+ bool clip(SwRleData* target) override
+ {
+ //Only one shape
+ if (scene.count == 1) {
+ return static_cast<SwTask*>(*scene.data)->clip(target);
+ }
+
+ //More than one shapes
+ if (sceneRle) rleClipPath(target, sceneRle);
+ else TVGLOG("SW_ENGINE", "No clippers in a scene?");
+
+ return true;
+ }
+
+ SwRleData* rle() override
+ {
+ return sceneRle;
+ }
+
+ void run(unsigned tid) override
+ {
+ //TODO: Skip the run if the scene hans't changed.
+ if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
+ else rleReset(sceneRle);
+
+ //Merge shapes if it has more than one shapes
+ if (scene.count > 1) {
+ //Merge first two clippers
+ auto clipper1 = static_cast<SwTask*>(*scene.data);
+ auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
+
+ rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
+
+ //Unify the remained clippers
+ for (auto rd = scene.data + 2; rd < (scene.data + scene.count); ++rd) {
+ auto clipper = static_cast<SwTask*>(*rd);
+ rleMerge(sceneRle, sceneRle, clipper->rle());
+ }
+ }
+ }
+
+ bool dispose() override
+ {
+ rleFree(sceneRle);
+ return true;
+ }
+};
+
+
struct SwImageTask : SwTask
{
SwImage image;
+ Surface* source; //Image source
+ const RenderMesh* mesh = nullptr; //Should be valid ptr in action
+
+ bool clip(SwRleData* target) override
+ {
+ TVGERR("SW_ENGINE", "Image is used as ClipPath?");
+ return true;
+ }
+
+ SwRleData* rle() override
+ {
+ TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
+ return nullptr;
+ }
void run(unsigned tid) override
{
auto clipRegion = bbox;
+ //Convert colorspace if it's not aligned.
+ if (source->owner) {
+ if (source->cs != surface->cs) rasterConvertCS(source, surface->cs);
+ if (!source->premultiplied) rasterPremultiply(source);
+ }
+
+ image.data = source->data;
+ image.w = source->w;
+ image.h = source->h;
+ image.stride = source->stride;
+ image.channelSize = source->channelSize;
+
//Invisible shape turned to visible by alpha.
if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end;
- if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
+ if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end;
- if (clips.count > 0) {
+ // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
+ if (mesh->triangleCnt == 0 && clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) {
+ //Clear current task memorypool here if the clippers would use the same memory pool
+ imageDelOutline(&image, mpool, tid);
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
- auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
- if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
- else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
- else goto err;
+ auto clipper = static_cast<SwTask*>(*clip);
+ if (!clipper->clip(image.rle)) goto err;
}
+ return;
}
}
}
goto end;
-
err:
rleReset(image.rle);
end:
@@ -232,6 +326,31 @@ static void _termEngine()
}
+static void _renderFill(SwShapeTask* task, SwSurface* surface, uint32_t opacity)
+{
+ uint8_t r, g, b, a;
+ if (auto fill = task->rshape->fill) {
+ rasterGradientShape(surface, &task->shape, fill->identifier());
+ } else {
+ task->rshape->fillColor(&r, &g, &b, &a);
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
+ }
+}
+
+static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint32_t opacity)
+{
+ uint8_t r, g, b, a;
+ if (auto strokeFill = task->rshape->strokeFill()) {
+ rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
+ } else {
+ if (task->rshape->strokeColor(&r, &g, &b, &a)) {
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
+ }
+ }
+}
+
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
@@ -293,17 +412,20 @@ bool SwRenderer::viewport(const RenderRegion& vp)
}
-bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace)
+bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs)
{
- if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
+ if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false;
if (!surface) surface = new SwSurface;
- surface->buffer = buffer;
+ surface->data = data;
surface->stride = stride;
surface->w = w;
surface->h = h;
- surface->cs = colorSpace;
+ surface->cs = cs;
+ surface->channelSize = CHANNEL_SIZE(cs);
+ surface->premultiplied = true;
+ surface->owner = true;
vport.x = vport.y = 0;
vport.w = surface->w;
@@ -315,9 +437,10 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
bool SwRenderer::preRender()
{
- return rasterClear(surface);
+ return rasterClear(surface, 0, 0, surface->w, surface->h);
}
+
void SwRenderer::clearCompositors()
{
//Free Composite Caches
@@ -333,7 +456,7 @@ void SwRenderer::clearCompositors()
bool SwRenderer::postRender()
{
//Unmultiply alpha if needed
- if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) {
rasterUnpremultiply(surface);
}
@@ -354,7 +477,7 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true;
- return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
+ return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
}
@@ -373,7 +496,7 @@ bool SwRenderer::renderShape(RenderData data)
//Do Stroking Composition
if (task->cmpStroking) {
opacity = 255;
- cmp = target(task->bounds());
+ cmp = target(task->bounds(), colorSpace());
beginComposite(cmp, CompositeMethod::None, task->opacity);
//No Stroking Composition
} else {
@@ -381,23 +504,12 @@ bool SwRenderer::renderShape(RenderData data)
}
//Main raster stage
- uint8_t r, g, b, a;
-
- if (auto fill = task->sdata->fill()) {
- rasterGradientShape(surface, &task->shape, fill->identifier());
- } else {
- task->sdata->fillColor(&r, &g, &b, &a);
- a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
- if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
- }
-
- if (auto strokeFill = task->sdata->strokeFill()) {
- rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
+ if (task->rshape->stroke && task->rshape->stroke->strokeFirst) {
+ _renderStroke(task, surface, opacity);
+ _renderFill(task, surface, opacity);
} else {
- if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
- a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
- if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
- }
+ _renderFill(task, surface, opacity);
+ _renderStroke(task, surface, opacity);
}
if (task->cmpStroking) endComposite(cmp);
@@ -450,7 +562,7 @@ bool SwRenderer::mempool(bool shared)
}
-Compositor* SwRenderer::target(const RenderRegion& region)
+Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
{
auto x = region.x;
auto y = region.y;
@@ -460,13 +572,15 @@ Compositor* SwRenderer::target(const RenderRegion& region)
auto sh = static_cast<int32_t>(surface->h);
//Out of boundary
- if (x > sw || y > sh) return nullptr;
+ if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
SwSurface* cmp = nullptr;
+ auto reqChannelSize = CHANNEL_SIZE(cs);
+
//Use cached data
for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
- if ((*p)->compositor->valid) {
+ if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
cmp = *p;
break;
}
@@ -475,17 +589,16 @@ Compositor* SwRenderer::target(const RenderRegion& region)
//New Composition
if (!cmp) {
cmp = new SwSurface;
- if (!cmp) goto err;
//Inherits attributes from main surface
*cmp = *surface;
cmp->compositor = new SwCompositor;
- if (!cmp->compositor) goto err;
- //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
- cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h);
- if (!cmp->compositor->image.data) goto err;
+ //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h)
+ cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h);
+ cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize;
+
compositors.push(cmp);
}
@@ -507,30 +620,16 @@ Compositor* SwRenderer::target(const RenderRegion& region)
cmp->compositor->image.h = surface->h;
cmp->compositor->image.direct = true;
- //We know partial clear region
- cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x);
- cmp->w = w;
- cmp->h = h;
-
- rasterClear(cmp);
-
- //Recover context
- cmp->buffer = cmp->compositor->image.data;
+ cmp->data = cmp->compositor->image.data;
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
+ rasterClear(cmp, x, y, w, h);
+
//Switch render target
surface = cmp;
return cmp->compositor;
-
-err:
- if (cmp) {
- if (cmp->compositor) delete(cmp->compositor);
- delete(cmp);
- }
-
- return nullptr;
}
@@ -547,13 +646,20 @@ bool SwRenderer::endComposite(Compositor* cmp)
//Default is alpha blending
if (p->method == CompositeMethod::None) {
- return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity);
+ return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity);
}
return true;
}
+ColorSpace SwRenderer::colorSpace()
+{
+ if (surface) return surface->cs;
+ else return ColorSpace::Unsupported;
+}
+
+
bool SwRenderer::dispose(RenderData data)
{
auto task = static_cast<SwTask*>(data);
@@ -576,14 +682,15 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
//Finish previous task if it has duplicated request.
task->done();
- if (clips.count > 0) {
- //Guarantee composition targets get ready.
- for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
- static_cast<SwShapeTask*>(*clip)->done();
- }
- task->clips = clips;
+ //TODO: Failed threading them. It would be better if it's possible.
+ //See: https://github.com/thorvg/thorvg/issues/1409
+ //Guarantee composition targets get ready.
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ static_cast<SwTask*>(*clip)->done();
}
+ task->clips = clips;
+
if (transform) {
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
*task->transform = transform->m;
@@ -612,44 +719,52 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
}
-RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwImageTask*>(data);
- if (!task) {
- task = new SwImageTask;
- if (flags & RenderUpdateFlag::Image) {
- task->image.data = image->buffer;
- task->image.w = image->w;
- task->image.h = image->h;
- task->image.stride = image->stride;
- }
+ if (!task) task = new SwImageTask;
+ if (flags & RenderUpdateFlag::Image) {
+ task->source = surface;
+ task->mesh = mesh;
}
return prepareCommon(task, transform, opacity, clips, flags);
}
-RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
{
//prepare task
- auto task = static_cast<SwShapeTask*>(data);
- if (!task) {
- task = new SwShapeTask;
- task->sdata = &sdata;
+ auto task = static_cast<SwSceneTask*>(data);
+ if (!task) task = new SwSceneTask;
+ task->scene = scene;
+
+ //TODO: Failed threading them. It would be better if it's possible.
+ //See: https://github.com/thorvg/thorvg/issues/1409
+ //Guarantee composition targets get ready.
+ for (auto task = scene.data; task < (scene.data + scene.count); ++task) {
+ static_cast<SwTask*>(*task)->done();
}
return prepareCommon(task, transform, opacity, clips, flags);
}
-SwRenderer::SwRenderer():mpool(globalMpool)
+RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper)
{
+ //prepare task
+ auto task = static_cast<SwShapeTask*>(data);
+ if (!task) {
+ task = new SwShapeTask;
+ task->rshape = &rshape;
+ }
+ task->clipper = clipper;
+
+ return prepareCommon(task, transform, opacity, clips, flags);
}
-uint32_t SwRenderer::colorSpace()
+SwRenderer::SwRenderer():mpool(globalMpool)
{
- if (surface) return surface->cs;
- return tvg::SwCanvas::ARGB8888;
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
index cab93f9e1c..36614fa1c1 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SW_RENDERER_H_
#define _TVG_SW_RENDERER_H_
@@ -35,8 +36,9 @@ namespace tvg
class SwRenderer : public RenderMethod
{
public:
- RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
- RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
+ RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;
@@ -45,19 +47,18 @@ public:
RenderRegion region(RenderData data) override;
RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override;
+ ColorSpace colorSpace() override;
bool clear() override;
bool sync() override;
- bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace);
+ bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
bool mempool(bool shared);
- Compositor* target(const RenderRegion& region) override;
+ Compositor* target(const RenderRegion& region, ColorSpace cs) override;
bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
bool endComposite(Compositor* cmp) override;
void clearCompositors();
- uint32_t colorSpace() override;
-
static SwRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
index 63e7fc9447..50dec208b2 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -773,60 +773,59 @@ static int _genRle(RleWorker& rw)
}
-SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt)
{
auto out = outSpans;
- auto spans = targetRle->spans;
- auto end = targetRle->spans + targetRle->size;
+ auto spans = target->spans;
+ auto end = target->spans + target->size;
auto clipSpans = clip->spans;
auto clipEnd = clip->spans + clip->size;
- while (spanCnt > 0 && spans < end) {
- if (clipSpans == clipEnd) {
- spans = end;
- break;
- }
+ while (spans < end && clipSpans < clipEnd) {
+ //align y cooridnates.
if (clipSpans->y > spans->y) {
++spans;
continue;
}
- if (spans->y != clipSpans->y) {
+ if (spans->y > clipSpans->y) {
++clipSpans;
continue;
}
- auto sx1 = spans->x;
- auto sx2 = sx1 + spans->len;
- auto cx1 = clipSpans->x;
- auto cx2 = cx1 + clipSpans->len;
- if (cx1 < sx1 && cx2 < sx1) {
- ++clipSpans;
- continue;
- }
- else if (sx1 < cx1 && sx2 < cx1) {
- ++spans;
- continue;
- }
- auto x = sx1 > cx1 ? sx1 : cx1;
- auto len = (sx2 < cx2 ? sx2 : cx2) - x;
- if (len) {
- auto spansCorverage = spans->coverage;
- auto clipSpansCoverage = clipSpans->coverage;
- out->x = sx1 > cx1 ? sx1 : cx1;
- out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
- out->y = spans->y;
- out->coverage = (uint8_t)(((spansCorverage * clipSpansCoverage) + 0xff) >> 8);
- ++out;
- --spanCnt;
+ //Try clipping with all clip spans which have a same y coordinate.
+ auto temp = clipSpans;
+ while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
+ auto sx1 = spans->x;
+ auto sx2 = sx1 + spans->len;
+ auto cx1 = temp->x;
+ auto cx2 = cx1 + temp->len;
+
+ //The span must be left(x1) to right(x2) direction. Not intersected.
+ if (cx2 < sx1 || sx2 < cx1) {
+ ++temp;
+ continue;
+ }
+
+ //clip span region.
+ auto x = sx1 > cx1 ? sx1 : cx1;
+ auto len = (sx2 < cx2 ? sx2 : cx2) - x;
+ if (len > 0) {
+ out->x = x;
+ out->y = temp->y;
+ out->len = len;
+ out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8);
+ ++out;
+ --outSpansCnt;
+ }
+ ++temp;
}
- if (sx2 < cx2) ++spans;
- else ++clipSpans;
+ ++spans;
}
return out;
}
-SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
{
auto out = outSpans;
auto spans = targetRle->spans;
@@ -836,7 +835,7 @@ SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSp
auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
- while (spanCnt && spans < end) {
+ while (outSpansCnt > 0 && spans < end) {
if (spans->y > maxy) {
spans = end;
break;
@@ -853,18 +852,58 @@ SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSp
out->x = spans->x;
out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
}
- if (out->len != 0) {
+ if (out->len > 0) {
out->y = spans->y;
out->coverage = spans->coverage;
++out;
+ --outSpansCnt;
}
++spans;
- --spanCnt;
}
return out;
}
+static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
+{
+ auto out = outSpans;
+ auto spans1 = clip1->spans;
+ auto end1 = clip1->spans + clip1->size;
+ auto spans2 = clip2->spans;
+ auto end2 = clip2->spans + clip2->size;
+
+ //list two spans up in y order
+ //TODO: Remove duplicated regions?
+ while (spans1 < end1 && spans2 < end2) {
+ while (spans1 < end1 && spans1->y <= spans2->y) {
+ *out = *spans1;
+ ++spans1;
+ ++out;
+ }
+ if (spans1 >= end1) break;
+ while (spans2 < end2 && spans2->y <= spans1->y) {
+ *out = *spans2;
+ ++spans2;
+ ++out;
+ }
+ }
+
+ //Leftovers
+ while (spans1 < end1) {
+ *out = *spans1;
+ ++spans1;
+ ++out;
+ }
+ while (spans2 < end2) {
+ *out = *spans2;
+ ++spans2;
+ ++out;
+ }
+
+ return out;
+}
+
+
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
{
free(rle->spans);
@@ -1001,6 +1040,28 @@ error:
}
+SwRleData* rleRender(const SwBBox* bbox)
+{
+ auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
+ auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
+
+ auto rle = static_cast<SwRleData*>(malloc(sizeof(SwRleData)));
+ rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
+ rle->size = height;
+ rle->alloc = height;
+
+ auto span = rle->spans;
+ for (uint16_t i = 0; i < height; ++i, ++span) {
+ span->x = bbox->min.x;
+ span->y = bbox->min.y + i;
+ span->len = width;
+ span->coverage = 255;
+ }
+
+ return rle;
+}
+
+
void rleReset(SwRleData* rle)
{
if (!rle) return;
@@ -1016,12 +1077,50 @@ void rleFree(SwRleData* rle)
}
+void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
+{
+ if (!rle || (!clip1 && !clip2)) return;
+ if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return;
+
+ TVGLOG("SW_ENGINE", "Unifying Rle!");
+
+ //clip1 is empty, just copy clip2
+ if (!clip1 || clip1->size == 0) {
+ if (clip2) {
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip2->size)));
+ memcpy(spans, clip2->spans, clip2->size);
+ _replaceClipSpan(rle, spans, clip2->size);
+ } else {
+ _replaceClipSpan(rle, nullptr, 0);
+ }
+ return;
+ }
+
+ //clip2 is empty, just copy clip1
+ if (!clip2 || clip2->size == 0) {
+ if (clip1) {
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip1->size)));
+ memcpy(spans, clip1->spans, clip1->size);
+ _replaceClipSpan(rle, spans, clip1->size);
+ } else {
+ _replaceClipSpan(rle, nullptr, 0);
+ }
+ return;
+ }
+
+ auto spanCnt = clip1->size + clip2->size;
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * spanCnt));
+ auto spansEnd = _mergeSpansRegion(clip1, clip2, spans);
+
+ _replaceClipSpan(rle, spans, spansEnd - spans);
+}
+
+
void rleClipPath(SwRleData *rle, const SwRleData *clip)
{
if (rle->size == 0 || clip->size == 0) return;
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
- if (!spans) return;
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
_replaceClipSpan(rle, spans, spansEnd - spans);
@@ -1034,7 +1133,6 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip)
{
if (rle->size == 0) return;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
- if (!spans) return;
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_replaceClipSpan(rle, spans, spansEnd - spans);
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
index e5b540bcc3..7462a7b7ea 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgSwCommon.h"
#include "tvgBezier.h"
#include <float.h>
@@ -266,13 +267,13 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
}
-static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
{
- const PathCommand* cmds = nullptr;
- auto cmdCnt = sdata->pathCommands(&cmds);
+ const PathCommand* cmds = rshape->path.cmds;
+ auto cmdCnt = rshape->path.cmdCnt;
- const Point* pts = nullptr;
- auto ptsCnt = sdata->pathCoords(&pts);
+ const Point* pts = rshape->path.pts;
+ auto ptsCnt = rshape->path.ptsCnt;
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
@@ -285,7 +286,7 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
dash.curOpGap = false;
const float* pattern;
- dash.cnt = sdata->strokeDash(&pattern);
+ dash.cnt = rshape->strokeDash(&pattern);
if (dash.cnt == 0) return nullptr;
//OPTMIZE ME: Use mempool???
@@ -380,13 +381,13 @@ static bool _axisAlignedRect(const SwOutline* outline)
-static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
+static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
{
- const PathCommand* cmds = nullptr;
- auto cmdCnt = sdata->pathCommands(&cmds);
+ const PathCommand* cmds = rshape->path.cmds;
+ auto cmdCnt = rshape->path.cmdCnt;
- const Point* pts = nullptr;
- auto ptsCnt = sdata->pathCoords(&pts);
+ const Point* pts = rshape->path.pts;
+ auto ptsCnt = rshape->path.ptsCnt;
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return false;
@@ -466,7 +467,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
_outlineEnd(*outline);
- outline->fillRule = sdata->fillRule();
+ outline->fillRule = rshape->rule;
shape->outline = outline;
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
@@ -478,9 +479,9 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
/* External Class Implementation */
/************************************************************************/
-bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
+bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
{
- if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
+ if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
//Keep it for Rasterization Region
@@ -503,7 +504,7 @@ bool shapePrepared(const SwShape* shape)
}
-bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
+bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias)
{
//FIXME: Should we draw it?
//Case: Stroke Line
@@ -557,18 +558,18 @@ void shapeDelStroke(SwShape* shape)
}
-void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
+void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform)
{
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = shape->stroke;
if (!stroke) return;
- strokeReset(stroke, sdata, transform);
+ strokeReset(stroke, rshape, transform);
rleReset(shape->strokeRle);
}
-bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{
SwOutline* shapeOutline = nullptr;
SwOutline* strokeOutline = nullptr;
@@ -576,14 +577,14 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo
bool ret = true;
//Dash Style Stroke
- if (sdata->strokeDash(nullptr) > 0) {
- shapeOutline = _genDashOutline(sdata, transform);
+ if (rshape->strokeDash(nullptr) > 0) {
+ shapeOutline = _genDashOutline(rshape, transform);
if (!shapeOutline) return false;
freeOutline = true;
//Normal Style stroke
} else {
if (!shape->outline) {
- if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
+ if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
}
shapeOutline = shape->outline;
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
index fa213cc5d3..2f41d5f021 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+/*
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <string.h>
#include <math.h>
#include "tvgSwCommon.h"
@@ -301,7 +302,7 @@ static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
bool intersect = false;
/* Only intersect borders if between two line_to's and both
- lines are long enough (line length is zero fur curves). */
+ lines are long enough (line length is zero for curves). */
if (border->movable && lineLength > 0) {
//compute minimum required length of lines
SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
@@ -381,9 +382,16 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
if (delta.zero()) return;
//compute length of line
- auto lineLength = mathLength(delta);
auto angle = mathAtan(delta);
+ /* The lineLength is used to determine the intersection of strokes outlines.
+ The scale needs to be reverted since the stroke width has not been scaled.
+ An alternative option is to scale the width of the stroke properly by
+ calculating the mixture of the sx/sy rating on the stroke direction. */
+ delta.x = static_cast<SwCoord>(delta.x / stroke.sx);
+ delta.y = static_cast<SwCoord>(delta.y / stroke.sy);
+ auto lineLength = mathLength(delta);
+
delta = {static_cast<SwCoord>(stroke.width), 0};
mathRotate(delta, angle + SW_ANGLE_PI2);
SCALE(stroke, delta);
@@ -825,7 +833,7 @@ void strokeFree(SwStroke* stroke)
}
-void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
+void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform)
{
if (transform) {
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
@@ -834,11 +842,11 @@ void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
stroke->sx = stroke->sy = 1.0f;
}
- stroke->width = HALF_STROKE(sdata->strokeWidth());
- stroke->cap = sdata->strokeCap();
+ stroke->width = HALF_STROKE(rshape->strokeWidth());
+ stroke->cap = rshape->strokeCap();
//Save line join: it can be temporarily changed when stroking curves...
- stroke->joinSaved = stroke->join = sdata->strokeJoin();
+ stroke->joinSaved = stroke->join = rshape->strokeJoin();
stroke->borders[0].ptsCnt = 0;
stroke->borders[0].start = -1;
diff --git a/thirdparty/thorvg/src/lib/tvgAccessor.cpp b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
index 5ad24f4acf..0c636979b5 100644
--- a/thirdparty/thorvg/src/lib/tvgAccessor.cpp
+++ b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
@@ -1,13 +1,16 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
+
* 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
@@ -23,15 +26,15 @@
/* Internal Class Implementation */
/************************************************************************/
-static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor)
+static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func)
{
while (auto child = it->next()) {
//Access the child
if (!func(child)) return false;
//Access the children of the child
- if (auto it2 = itrAccessor.iterator(child)) {
- if (!accessChildren(it2, func, itrAccessor)) {
+ if (auto it2 = IteratorAccessor::iterator(child)) {
+ if (!accessChildren(it2, func)) {
delete(it2);
return false;
}
@@ -41,12 +44,11 @@ static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), Iterat
return true;
}
-
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
-unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept
+unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept
{
auto p = picture.get();
if (!p || !func) return picture;
@@ -57,9 +59,8 @@ unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(co
if (!func(p)) return picture;
//Children
- IteratorAccessor itrAccessor;
- if (auto it = itrAccessor.iterator(p)) {
- accessChildren(it, func, itrAccessor);
+ if (auto it = IteratorAccessor::iterator(p)) {
+ accessChildren(it, func);
delete(it);
}
return picture;
diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/lib/tvgArray.h
index 04ddbaee2b..8d67753ae3 100644
--- a/thirdparty/thorvg/src/lib/tvgArray.h
+++ b/thirdparty/thorvg/src/lib/tvgArray.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_ARRAY_H_
#define _TVG_ARRAY_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/lib/tvgBezier.cpp
index 95e2055943..6162252235 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.cpp
+++ b/thirdparty/thorvg/src/lib/tvgBezier.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <float.h>
#include <math.h>
#include "tvgBezier.h"
@@ -114,7 +115,7 @@ float bezAt(const Bezier& bz, float at)
auto t = 0.5f;
//just in case to prevent an infinite loop
- if (at <= 0) return 0.0f;
+ if (at <= 0) return 0.0f;
if (at >= len) return 1.0f;
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/lib/tvgBezier.h
index 7d7c84e34e..aa309b098e 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.h
+++ b/thirdparty/thorvg/src/lib/tvgBezier.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_BEZIER_H_
#define _TVG_BEZIER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
index f139def470..3b44db2829 100644
--- a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
+++ b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_BINARY_DESC_H_
#define _TVG_BINARY_DESC_H_
@@ -42,7 +43,7 @@ using TvgBinFlag = TvgBinByte;
#define TVG_HEADER_VERSION_LENGTH 6
#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
-//Compress Size
+//Compress Size
#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
@@ -92,5 +93,6 @@ using TvgBinFlag = TvgBinByte;
//Picture
#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
+#define TVG_TAG_PICTURE_MESH (TvgBinTag)0x71
#endif //_TVG_BINARY_DESC_H_
diff --git a/thirdparty/thorvg/src/lib/tvgCanvas.cpp b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
index e8529e47c2..95c66b94df 100644
--- a/thirdparty/thorvg/src/lib/tvgCanvas.cpp
+++ b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgCanvasImpl.h"
/************************************************************************/
@@ -57,13 +58,21 @@ Result Canvas::clear(bool free) noexcept
Result Canvas::draw() noexcept
{
- return pImpl->draw();
+ TVGLOG("COMMON", "Draw S. -------------------------------- Canvas(%p)", this);
+ auto ret = pImpl->draw();
+ TVGLOG("COMMON", "Draw E. -------------------------------- Canvas(%p)", this);
+
+ return ret;
}
Result Canvas::update(Paint* paint) noexcept
{
- return pImpl->update(paint, false);
+ TVGLOG("COMMON", "Update S. ------------------------------ Canvas(%p)", this);
+ auto ret = pImpl->update(paint, false);
+ TVGLOG("COMMON", "Update E. ------------------------------ Canvas(%p)", this);
+
+ return ret;
}
diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
index 0adec8fa52..6f5760f53c 100644
--- a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_CANVAS_IMPL_H_
#define _TVG_CANVAS_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h
index 1d66f2e1a2..1731647c00 100644
--- a/thirdparty/thorvg/src/lib/tvgCommon.h
+++ b/thirdparty/thorvg/src/lib/tvgCommon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_COMMON_H_
#define _TVG_COMMON_H_
@@ -64,8 +65,14 @@ using namespace tvg;
enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
#ifdef THORVG_LOG_ENABLED
- #define TVGLOG(tag, fmt, ...) fprintf(stdout, tag ": " fmt "\n", ##__VA_ARGS__) //Log Message for notifying user some useful info
- #define TVGERR(tag, fmt, ...) fprintf(stderr, tag ": " fmt "\n", ##__VA_ARGS__) //Error Message for us to fix it
+ constexpr auto ErrorColor = "\033[31m"; //red
+ constexpr auto ErrorBgColor = "\033[41m";//bg red
+ constexpr auto LogColor = "\033[32m"; //green
+ constexpr auto LogBgColor = "\033[42m"; //bg green
+ constexpr auto GreyColor = "\033[90m"; //grey
+ constexpr auto ResetColors = "\033[0m"; //default
+ #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
+ #define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#else
#define TVGERR(...)
#define TVGLOG(...)
diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp
index eecf239cee..cc7e1ccaed 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.cpp
+++ b/thirdparty/thorvg/src/lib/tvgFill.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgFill.h"
/************************************************************************/
diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h
index fff2475c4f..e90991c9df 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.h
+++ b/thirdparty/thorvg/src/lib/tvgFill.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_FILL_H_
#define _TVG_FILL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
index 56feb69541..dbbab516b2 100644
--- a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
+++ b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgCanvasImpl.h"
#ifdef THORVG_GL_RASTER_SUPPORT
diff --git a/thirdparty/thorvg/src/lib/tvgInitializer.cpp b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
index 42997c3493..b7326a9fbc 100644
--- a/thirdparty/thorvg/src/lib/tvgInitializer.cpp
+++ b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgCommon.h"
#include "tvgTaskScheduler.h"
#include "tvgLoader.h"
@@ -43,6 +44,11 @@
static int _initCnt = 0;
static uint16_t _version = 0;
+//enum class operation helper
+static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
+{
+ return int(a) & int(b);
+}
static bool _buildVersionInfo()
{
@@ -86,12 +92,12 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
{
auto nonSupport = true;
- if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ if (engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
- } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ } else if (engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
@@ -120,12 +126,12 @@ Result Initializer::term(CanvasEngine engine) noexcept
auto nonSupport = true;
- if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ if (engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
- } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ } else if (engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
diff --git a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
index 8e566acb72..2347613ed8 100644
--- a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
+++ b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_ITERATOR_ACCESSOR_H_
#define _TVG_ITERATOR_ACCESSOR_H_
@@ -31,7 +32,7 @@ class IteratorAccessor
{
public:
//Utility Method: Iterator Accessor
- Iterator* iterator(const Paint* paint)
+ static Iterator* iterator(const Paint* paint)
{
return paint->pImpl->iterator();
}
diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
index df34af3aa1..3e040c08f1 100644
--- a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
+++ b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <float.h>
#include <math.h>
#include "tvgFill.h"
diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h
index 0a154aa47d..ee7c0c1193 100644
--- a/thirdparty/thorvg/src/lib/tvgLoadModule.h
+++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_LOAD_MODULE_H_
#define _TVG_LOAD_MODULE_H_
@@ -35,8 +36,8 @@ public:
float vy = 0;
float vw = 0;
float vh = 0;
- float w = 0, h = 0; //default image size
- uint32_t colorSpace = SwCanvas::ARGB8888;
+ float w = 0, h = 0; //default image size
+ ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open()
virtual ~LoadModule() {}
@@ -49,7 +50,7 @@ public:
virtual bool read() = 0;
virtual bool close() = 0;
- virtual unique_ptr<Surface> bitmap(uint32_t colorSpace) { return nullptr; }
+ virtual unique_ptr<Surface> bitmap() { return nullptr; }
virtual unique_ptr<Paint> paint() { return nullptr; }
};
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp
index 991c260159..2bbe177844 100644
--- a/thirdparty/thorvg/src/lib/tvgLoader.cpp
+++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgLoader.h"
#ifdef THORVG_SVG_LOADER_SUPPORT
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.h b/thirdparty/thorvg/src/lib/tvgLoader.h
index ab32f89a2f..17ff9e2637 100644
--- a/thirdparty/thorvg/src/lib/tvgLoader.h
+++ b/thirdparty/thorvg/src/lib/tvgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_LOADER_H_
#define _TVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/lib/tvgLzw.cpp
index 1aaf37831a..52f4ed6716 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.cpp
+++ b/thirdparty/thorvg/src/lib/tvgLzw.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -258,8 +258,8 @@ struct Dictionary
Dictionary()
{
- /* First 256 dictionary entries are reserved to the byte/ASCII range.
- Additional entries follow for the character sequences found in the input.
+ /* First 256 dictionary entries are reserved to the byte/ASCII range.
+ Additional entries follow for the character sequences found in the input.
Up to 4096 - 256 (MaxDictEntries - FirstCode). */
size = FirstCode;
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/lib/tvgLzw.h
index 8e165bdb34..bd4e783fbf 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.h
+++ b/thirdparty/thorvg/src/lib/tvgLzw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_LZW_H_
#define _TVG_LZW_H_
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
index 74f34fb744..9ab8291b75 100644
--- a/thirdparty/thorvg/src/lib/tvgMath.h
+++ b/thirdparty/thorvg/src/lib/tvgMath.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_MATH_H_
#define _TVG_MATH_H_
@@ -118,7 +119,7 @@ static inline void mathScale(Matrix* m, float scale)
static inline void mathTranslate(Matrix* m, float x, float y)
{
m->e13 = x;
- m->e23 = y;
+ m->e23 = y;
}
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
index c90e95cd33..c7030aaccf 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgPaint.h"
@@ -161,16 +162,15 @@ bool Paint::Impl::render(RenderMethod& renderer)
{
Compositor* cmp = nullptr;
- //OPTIMIZE_ME: Can we replace the simple AlphaMasking with ClipPath?
-
/* Note: only ClipPath is processed in update() step.
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
auto region = smethod->bounds(renderer);
if (region.w == 0 || region.h == 0) return true;
- cmp = renderer.target(region);
- renderer.beginComposite(cmp, CompositeMethod::None, 255);
- compData->target->pImpl->render(renderer);
+ cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
+ if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
+ compData->target->pImpl->render(renderer);
+ }
}
if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
@@ -183,7 +183,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
}
-void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag)
+RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag, bool clipper)
{
if (renderFlag & RenderUpdateFlag::Transform) {
if (!rTransform) return nullptr;
@@ -194,9 +194,10 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf
}
/* 1. Composition Pre Processing */
- void *tdata = nullptr;
+ RenderData trd = nullptr; //composite target render data
RenderRegion viewport;
bool compFastTrack = false;
+ bool childClipper = false;
if (compData) {
auto target = compData->target;
@@ -206,47 +207,50 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf
/* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
auto tryFastTrack = false;
- if (method == CompositeMethod::ClipPath) tryFastTrack = true;
- else if (method == CompositeMethod::AlphaMask && target->identifier() == TVG_CLASS_ID_SHAPE) {
- auto shape = static_cast<Shape*>(target);
- uint8_t a;
- shape->fillColor(nullptr, nullptr, nullptr, &a);
- if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
- }
- if (tryFastTrack) {
- RenderRegion viewport2;
- if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
- viewport = renderer.viewport();
- viewport2.intersect(viewport);
- renderer.viewport(viewport2);
- target->pImpl->ctxFlag |= ContextFlag::FastTrack;
+ if (target->identifier() == TVG_CLASS_ID_SHAPE) {
+ if (method == CompositeMethod::ClipPath) tryFastTrack = true;
+ else if (method == CompositeMethod::AlphaMask) {
+ auto shape = static_cast<Shape*>(target);
+ uint8_t a;
+ shape->fillColor(nullptr, nullptr, nullptr, &a);
+ if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
+ }
+ if (tryFastTrack) {
+ RenderRegion viewport2;
+ if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
+ viewport = renderer.viewport();
+ viewport2.intersect(viewport);
+ renderer.viewport(viewport2);
+ target->pImpl->ctxFlag |= ContextFlag::FastTrack;
+ }
}
}
if (!compFastTrack) {
- tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag);
- if (method == CompositeMethod::ClipPath) clips.push(tdata);
+ childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
+ trd = target->pImpl->update(renderer, pTransform, 255, clips, pFlag, childClipper);
+ if (childClipper) clips.push(trd);
}
}
/* 2. Main Update */
- void *edata = nullptr;
+ RenderData rd = nullptr;
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
renderFlag = RenderUpdateFlag::None;
opacity = (opacity * this->opacity) / 255;
if (rTransform && pTransform) {
RenderTransform outTransform(pTransform, rTransform);
- edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag);
+ rd = smethod->update(renderer, &outTransform, opacity, clips, newFlag, clipper);
} else {
auto outTransform = pTransform ? pTransform : rTransform;
- edata = smethod->update(renderer, outTransform, opacity, clips, newFlag);
+ rd = smethod->update(renderer, outTransform, opacity, clips, newFlag, clipper);
}
/* 3. Composition Post Processing */
if (compFastTrack) renderer.viewport(viewport);
- else if (tdata && compData->method == CompositeMethod::ClipPath) clips.pop();
+ else if (childClipper) clips.pop();
- return edata;
+ return rd;
}
@@ -384,19 +388,6 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept
}
-Result Paint::composite(const Paint** source, CompositeMethod* method) const noexcept
-{
- if (source) *source = pImpl->compSource;
- auto met = (pImpl->compSource && pImpl->compSource->pImpl->compData ?
- pImpl->compSource->pImpl->compData->method : CompositeMethod::None);
- if (method) *method = met;
-
- if (pImpl->compSource != nullptr && met != CompositeMethod::None)
- return Result::Success;
- return Result::InsufficientCondition;
-}
-
-
Result Paint::opacity(uint8_t o) noexcept
{
if (pImpl->opacity == o) return Result::Success;
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
index 94239c30a1..d70490c649 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.h
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_PAINT_H_
#define _TVG_PAINT_H_
@@ -42,7 +43,7 @@ namespace tvg
virtual ~StrategyMethod() {}
virtual bool dispose(RenderMethod& renderer) = 0;
- virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
+ virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has.
virtual bool render(RenderMethod& renderer) = 0;
virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
@@ -62,7 +63,6 @@ namespace tvg
StrategyMethod* smethod = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
- Paint* compSource = nullptr;
uint32_t renderFlag = RenderUpdateFlag::None;
uint32_t ctxFlag = ContextFlag::Invalid;
uint32_t id;
@@ -137,7 +137,6 @@ namespace tvg
if (!target && method == CompositeMethod::None) return true;
compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
}
- target->pImpl->compSource = source;
compData->target = target;
compData->source = source;
compData->method = method;
@@ -148,7 +147,7 @@ namespace tvg
bool scale(float factor);
bool translate(float x, float y);
bool bounds(float* x, float* y, float* w, float* h, bool transformed);
- void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
+ RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag, bool clipper = false);
bool render(RenderMethod& renderer);
Paint* duplicate();
};
@@ -177,9 +176,9 @@ namespace tvg
return inst->dispose(renderer);
}
- void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag) override
+ RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag, bool clipper) override
{
- return inst->update(renderer, transform, opacity, clips, renderFlag);
+ return inst->update(renderer, transform, opacity, clips, renderFlag, clipper);
}
bool render(RenderMethod& renderer) override
diff --git a/thirdparty/thorvg/src/lib/tvgPicture.cpp b/thirdparty/thorvg/src/lib/tvgPicture.cpp
index 1e04e25435..ad9db96245 100644
--- a/thirdparty/thorvg/src/lib/tvgPicture.cpp
+++ b/thirdparty/thorvg/src/lib/tvgPicture.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -107,7 +107,7 @@ Result Picture::size(float* w, float* h) const noexcept
const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
{
//Try it, If not loaded yet.
- pImpl->reload();
+ pImpl->load();
if (pImpl->loader) {
if (w) *w = static_cast<uint32_t>(pImpl->loader->w);
@@ -116,6 +116,23 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
if (w) *w = 0;
if (h) *h = 0;
}
- if (pImpl->surface) return pImpl->surface->buffer;
+ if (pImpl->surface) return pImpl->surface->buf32;
else return nullptr;
}
+
+
+Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept
+{
+ if (!triangles && triangleCnt > 0) return Result::InvalidArguments;
+ if (triangles && triangleCnt == 0) return Result::InvalidArguments;
+
+ pImpl->mesh(triangles, triangleCnt);
+ return Result::Success;
+}
+
+
+uint32_t Picture::mesh(const Polygon** triangles) const noexcept
+{
+ if (triangles) *triangles = pImpl->rm.triangles;
+ return pImpl->rm.triangleCnt;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
index b6bc19fa9c..6af7a732e5 100644
--- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_PICTURE_IMPL_H_
#define _TVG_PICTURE_IMPL_H_
@@ -63,30 +64,28 @@ struct Picture::Impl
Paint* paint = nullptr; //vector picture uses
Surface* surface = nullptr; //bitmap picture uses
- void* rdata = nullptr; //engine data
+ RenderData rd = nullptr; //engine data
float w = 0, h = 0;
+ RenderMesh rm; //mesh data
bool resizing = false;
- uint32_t rendererColorSpace = 0;
~Impl()
{
if (paint) delete(paint);
- free(surface);
+ delete(surface);
}
bool dispose(RenderMethod& renderer)
{
bool ret = true;
- if (paint) {
- ret = paint->pImpl->dispose(renderer);
- } else if (surface) {
- ret = renderer.dispose(rdata);
- rdata = nullptr;
- }
+ if (paint) ret = paint->pImpl->dispose(renderer);
+ else if (surface) ret = renderer.dispose(rd);
+ rd = nullptr;
+
return ret;
}
- uint32_t reload()
+ uint32_t load()
{
if (loader) {
if (!paint) {
@@ -94,16 +93,21 @@ struct Picture::Impl
paint = p.release();
loader->close();
if (w != loader->w || h != loader->h) {
+ if (!resizing) {
+ w = loader->w;
+ h = loader->h;
+ }
loader->resize(paint, w, h);
resizing = false;
}
if (paint) return RenderUpdateFlag::None;
}
}
- free(surface);
- if ((surface = loader->bitmap(rendererColorSpace).release())) {
- loader->close();
- return RenderUpdateFlag::Image;
+ if (!surface) {
+ if ((surface = loader->bitmap().release())) {
+ loader->close();
+ return RenderUpdateFlag::Image;
+ }
}
}
return RenderUpdateFlag::None;
@@ -123,32 +127,31 @@ struct Picture::Impl
else return RenderTransform(pTransform, &tmp);
}
- void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
+ RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
{
- rendererColorSpace = renderer.colorSpace();
- auto flag = reload();
+ auto flag = load();
if (surface) {
auto transform = resizeTransform(pTransform);
- rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ rd = renderer.prepare(surface, &rm, rd, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
} else if (paint) {
if (resizing) {
loader->resize(paint, w, h);
resizing = false;
}
- rdata = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ rd = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
}
- return rdata;
+ return rd;
}
bool render(RenderMethod &renderer)
{
- if (surface) return renderer.renderImage(rdata);
+ if (surface) return renderer.renderImage(rd);
else if (paint) return paint->pImpl->render(renderer);
return false;
}
- bool viewbox(float* x, float* y, float* w, float* h) const
+ bool viewbox(float* x, float* y, float* w, float* h)
{
if (!loader) return false;
if (x) *x = loader->vx;
@@ -168,17 +171,43 @@ struct Picture::Impl
bool bounds(float* x, float* y, float* w, float* h)
{
- if (x) *x = 0;
- if (y) *y = 0;
- if (w) *w = this->w;
- if (h) *h = this->h;
-
+ if (rm.triangleCnt > 0) {
+ auto triangles = rm.triangles;
+ auto min = triangles[0].vertex[0].pt;
+ auto max = triangles[0].vertex[0].pt;
+
+ for (uint32_t i = 0; i < rm.triangleCnt; ++i) {
+ if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
+ else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
+ if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
+ else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
+
+ if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
+ else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
+ if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
+ else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
+
+ if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
+ else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
+ if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
+ else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
+ }
+ if (x) *x = min.x;
+ if (y) *y = min.y;
+ if (w) *w = max.x - min.x;
+ if (h) *h = max.y - min.y;
+ } else {
+ if (x) *x = 0;
+ if (y) *y = 0;
+ if (w) *w = this->w;
+ if (h) *h = this->h;
+ }
return true;
}
RenderRegion bounds(RenderMethod& renderer)
{
- if (rdata) return renderer.region(rdata);
+ if (rd) return renderer.region(rd);
if (paint) return paint->pImpl->bounds(renderer);
return {0, 0, 0, 0};
}
@@ -216,15 +245,28 @@ struct Picture::Impl
if (paint || surface) return Result::InsufficientCondition;
if (loader) loader->close();
loader = LoaderMgr::loader(data, w, h, copy);
- if (!loader) return Result::NonSupport;
+ if (!loader) return Result::FailedAllocation;
this->w = loader->w;
this->h = loader->h;
return Result::Success;
}
+ void mesh(const Polygon* triangles, const uint32_t triangleCnt)
+ {
+ if (triangles && triangleCnt > 0) {
+ this->rm.triangleCnt = triangleCnt;
+ this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
+ memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
+ } else {
+ free(this->rm.triangles);
+ this->rm.triangles = nullptr;
+ this->rm.triangleCnt = 0;
+ }
+ }
+
Paint* duplicate()
{
- reload();
+ load();
auto ret = Picture::gen();
@@ -233,19 +275,27 @@ struct Picture::Impl
dup->loader = loader;
if (surface) {
- dup->surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ dup->surface = new Surface;
*dup->surface = *surface;
+ //TODO: A dupilcation is not a proxy... it needs copy of the pixel data?
+ dup->surface->owner = false;
}
dup->w = w;
dup->h = h;
dup->resizing = resizing;
+ if (rm.triangleCnt > 0) {
+ dup->rm.triangleCnt = rm.triangleCnt;
+ dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
+ memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
+ }
+
return ret.release();
}
Iterator* iterator()
{
- reload();
+ load();
return new PictureIterator(paint);
}
};
diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
index 7f4e1d7e17..a85f60e5d0 100644
--- a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
+++ b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <float.h>
#include "tvgFill.h"
diff --git a/thirdparty/thorvg/src/lib/tvgRender.cpp b/thirdparty/thorvg/src/lib/tvgRender.cpp
index 90f0917e6b..fb9270afe6 100644
--- a/thirdparty/thorvg/src/lib/tvgRender.cpp
+++ b/thirdparty/thorvg/src/lib/tvgRender.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgRender.h"
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
index f474d87895..6270fa5316 100644
--- a/thirdparty/thorvg/src/lib/tvgRender.h
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_
@@ -28,18 +29,38 @@
namespace tvg
{
+using RenderData = void*;
+using pixel_t = uint32_t;
+
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
-struct Surface
+struct Surface;
+
+enum ColorSpace
{
- //TODO: Union for multiple types
- uint32_t* buffer;
- uint32_t stride;
- uint32_t w, h;
- uint32_t cs;
+ ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
+ ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
+ ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
+ ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
+ Grayscale8, //One single channel data.
+ Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
};
-using RenderData = void*;
+struct Surface
+{
+ union {
+ pixel_t* data; //system based data pointer
+ uint32_t* buf32; //for explicit 32bits channels
+ uint8_t* buf8; //for explicit 8bits grayscale
+ };
+ uint32_t stride;
+ uint32_t w, h;
+ ColorSpace cs;
+ uint8_t channelSize;
+
+ bool premultiplied; //Alpha-premultiplied
+ bool owner; //Only owner could modify the buffer
+};
struct Compositor
{
@@ -47,6 +68,17 @@ struct Compositor
uint32_t opacity;
};
+struct RenderMesh
+{
+ Polygon* triangles = nullptr;
+ uint32_t triangleCnt = 0;
+
+ ~RenderMesh()
+ {
+ free(triangles);
+ }
+};
+
struct RenderRegion
{
int32_t x, y, w, h;
@@ -84,13 +116,110 @@ struct RenderTransform
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};
+struct RenderStroke
+{
+ float width = 0.0f;
+ uint8_t color[4] = {0, 0, 0, 0};
+ Fill *fill = nullptr;
+ float* dashPattern = nullptr;
+ uint32_t dashCnt = 0;
+ StrokeCap cap = StrokeCap::Square;
+ StrokeJoin join = StrokeJoin::Bevel;
+ bool strokeFirst = false;
+
+ ~RenderStroke()
+ {
+ free(dashPattern);
+ if (fill) delete(fill);
+ }
+};
+
+struct RenderShape
+{
+ struct
+ {
+ PathCommand* cmds = nullptr;
+ uint32_t cmdCnt = 0;
+ uint32_t reservedCmdCnt = 0;
+
+ Point *pts = nullptr;
+ uint32_t ptsCnt = 0;
+ uint32_t reservedPtsCnt = 0;
+ } path;
+
+ Fill *fill = nullptr;
+ RenderStroke *stroke = nullptr;
+ uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
+ FillRule rule = FillRule::Winding;
+
+ ~RenderShape()
+ {
+ free(path.cmds);
+ free(path.pts);
+
+ if (fill) delete(fill);
+ if (stroke) delete(stroke);
+ }
+
+ void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
+ {
+ if (r) *r = color[0];
+ if (g) *g = color[1];
+ if (b) *b = color[2];
+ if (a) *a = color[3];
+ }
+
+ float strokeWidth() const
+ {
+ if (!stroke) return 0;
+ return stroke->width;
+ }
+
+ bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
+ {
+ if (!stroke) return false;
+
+ if (r) *r = stroke->color[0];
+ if (g) *g = stroke->color[1];
+ if (b) *b = stroke->color[2];
+ if (a) *a = stroke->color[3];
+
+ return true;
+ }
+
+ const Fill* strokeFill() const
+ {
+ if (!stroke) return nullptr;
+ return stroke->fill;
+ }
+
+ uint32_t strokeDash(const float** dashPattern) const
+ {
+ if (!stroke) return 0;
+ if (dashPattern) *dashPattern = stroke->dashPattern;
+ return stroke->dashCnt;
+ }
+
+ StrokeCap strokeCap() const
+ {
+ if (!stroke) return StrokeCap::Square;
+ return stroke->cap;
+ }
+
+ StrokeJoin strokeJoin() const
+ {
+ if (!stroke) return StrokeJoin::Bevel;
+ return stroke->join;
+ }
+};
class RenderMethod
{
public:
virtual ~RenderMethod() {}
- virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
- virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) = 0;
+ virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0;
virtual bool renderImage(RenderData data) = 0;
@@ -99,17 +228,47 @@ public:
virtual RenderRegion region(RenderData data) = 0;
virtual RenderRegion viewport() = 0;
virtual bool viewport(const RenderRegion& vp) = 0;
+ virtual ColorSpace colorSpace() = 0;
virtual bool clear() = 0;
virtual bool sync() = 0;
- virtual Compositor* target(const RenderRegion& region) = 0;
+ virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
virtual bool endComposite(Compositor* cmp) = 0;
-
- virtual uint32_t colorSpace() = 0;
};
+static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
+{
+ switch(cs) {
+ case ColorSpace::ABGR8888:
+ case ColorSpace::ABGR8888S:
+ case ColorSpace::ARGB8888:
+ case ColorSpace::ARGB8888S:
+ return sizeof(uint32_t);
+ case ColorSpace::Grayscale8:
+ return sizeof(uint8_t);
+ case ColorSpace::Unsupported:
+ default:
+ TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs);
+ return 0;
+ }
+}
+
+static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, CompositeMethod method)
+{
+ switch(method) {
+ case CompositeMethod::AlphaMask:
+ case CompositeMethod::InvAlphaMask:
+ return ColorSpace::Grayscale8;
+ case CompositeMethod::LumaMask:
+ return renderer.colorSpace();
+ default:
+ TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+ return ColorSpace::Unsupported;
+ }
+}
+
}
#endif //_TVG_RENDER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSaveModule.h b/thirdparty/thorvg/src/lib/tvgSaveModule.h
index 3531662fdd..a997b644a7 100644
--- a/thirdparty/thorvg/src/lib/tvgSaveModule.h
+++ b/thirdparty/thorvg/src/lib/tvgSaveModule.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SAVE_MODULE_H_
#define _TVG_SAVE_MODULE_H_
@@ -27,7 +28,7 @@
namespace tvg
{
-class SaveModule : public IteratorAccessor
+class SaveModule
{
public:
virtual ~SaveModule() {}
diff --git a/thirdparty/thorvg/src/lib/tvgSaver.cpp b/thirdparty/thorvg/src/lib/tvgSaver.cpp
index e71953700c..85b5a37855 100644
--- a/thirdparty/thorvg/src/lib/tvgSaver.cpp
+++ b/thirdparty/thorvg/src/lib/tvgSaver.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgCommon.h"
#include "tvgSaveModule.h"
diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp
index 9ed7d45d5c..feb45a9d22 100644
--- a/thirdparty/thorvg/src/lib/tvgScene.cpp
+++ b/thirdparty/thorvg/src/lib/tvgScene.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgSceneImpl.h"
/************************************************************************/
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
index b6c68262c6..9ce4d0b37d 100644
--- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SCENE_IMPL_H_
#define _TVG_SCENE_IMPL_H_
@@ -60,6 +61,7 @@ struct Scene::Impl
Array<Paint*> paints;
uint8_t opacity; //for composition
RenderMethod* renderer = nullptr; //keep it for explicit clear
+ RenderData rd = nullptr;
Scene* scene = nullptr;
Impl(Scene* s) : scene(s)
@@ -79,9 +81,11 @@ struct Scene::Impl
(*paint)->pImpl->dispose(renderer);
}
+ auto ret = renderer.dispose(rd);
this->renderer = nullptr;
+ this->rd = nullptr;
- return true;
+ return ret;
}
bool needComposition(uint32_t opacity)
@@ -101,23 +105,29 @@ struct Scene::Impl
return false;
}
- void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag)
+ RenderData update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag, bool clipper)
{
/* Overriding opacity value. If this scene is half-translucent,
It must do intermeidate composition with that opacity value. */
this->opacity = static_cast<uint8_t>(opacity);
if (needComposition(opacity)) opacity = 255;
- for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
- (*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast<uint32_t>(flag));
- }
-
- /* FXIME: it requires to return list of children engine data
- This is necessary for scene composition */
-
this->renderer = &renderer;
- return nullptr;
+ if (clipper) {
+ Array<RenderData> rds;
+ rds.reserve(paints.count);
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ rds.push((*paint)->pImpl->update(renderer, transform, opacity, clips, flag, true));
+ }
+ rd = renderer.prepare(rds, rd, transform, opacity, clips, flag);
+ return rd;
+ } else {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->update(renderer, transform, opacity, clips, flag, false);
+ }
+ return nullptr;
+ }
}
bool render(RenderMethod& renderer)
@@ -125,7 +135,7 @@ struct Scene::Impl
Compositor* cmp = nullptr;
if (needComposition(opacity)) {
- cmp = renderer.target(bounds(renderer));
+ cmp = renderer.target(bounds(renderer), renderer.colorSpace());
renderer.beginComposite(cmp, CompositeMethod::None, opacity);
}
diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp
index e57f2eafb2..a8354375c9 100644
--- a/thirdparty/thorvg/src/lib/tvgShape.cpp
+++ b/thirdparty/thorvg/src/lib/tvgShape.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
/* External Class Implementation */
/************************************************************************/
-Shape :: Shape() : pImpl(new Impl(this))
+Shape :: Shape() : pImpl(new Impl())
{
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
@@ -59,8 +59,7 @@ uint32_t Shape::identifier() noexcept
Result Shape::reset() noexcept
{
- pImpl->path.reset();
- pImpl->flag = RenderUpdateFlag::Path;
+ pImpl->reset();
return Result::Success;
}
@@ -70,9 +69,9 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
{
if (!cmds) return 0;
- *cmds = pImpl->path.cmds;
+ *cmds = pImpl->rs.path.cmds;
- return pImpl->path.cmdCnt;
+ return pImpl->rs.path.cmdCnt;
}
@@ -80,9 +79,9 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept
{
if (!pts) return 0;
- *pts = pImpl->path.pts;
+ *pts = pImpl->rs.path.pts;
- return pImpl->path.ptsCnt;
+ return pImpl->rs.path.ptsCnt;
}
@@ -90,10 +89,8 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
{
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
- pImpl->path.grow(cmdCnt, ptsCnt);
- pImpl->path.append(cmds, cmdCnt, pts, ptsCnt);
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->grow(cmdCnt, ptsCnt);
+ pImpl->append(cmds, cmdCnt, pts, ptsCnt);
return Result::Success;
}
@@ -101,9 +98,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
Result Shape::moveTo(float x, float y) noexcept
{
- pImpl->path.moveTo(x, y);
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->moveTo(x, y);
return Result::Success;
}
@@ -111,9 +106,7 @@ Result Shape::moveTo(float x, float y) noexcept
Result Shape::lineTo(float x, float y) noexcept
{
- pImpl->path.lineTo(x, y);
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->lineTo(x, y);
return Result::Success;
}
@@ -121,9 +114,7 @@ Result Shape::lineTo(float x, float y) noexcept
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
{
- pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y);
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
return Result::Success;
}
@@ -131,9 +122,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float
Result Shape::close() noexcept
{
- pImpl->path.close();
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->close();
return Result::Success;
}
@@ -144,15 +133,13 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
auto rxKappa = rx * PATH_KAPPA;
auto ryKappa = ry * PATH_KAPPA;
- pImpl->path.grow(6, 13);
- pImpl->path.moveTo(cx, cy - ry);
- pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
- pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
- pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
- pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
- pImpl->path.close();
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ pImpl->grow(6, 13);
+ pImpl->moveTo(cx, cy - ry);
+ pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
+ pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
+ pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
+ pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
+ pImpl->close();
return Result::Success;
}
@@ -174,10 +161,10 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
if (pie) {
- pImpl->path.moveTo(cx, cy);
- pImpl->path.lineTo(start.x + cx, start.y + cy);
+ pImpl->moveTo(cx, cy);
+ pImpl->lineTo(start.x + cx, start.y + cy);
} else {
- pImpl->path.moveTo(start.x + cx, start.y + cy);
+ pImpl->moveTo(start.x + cx, start.y + cy);
}
for (int i = 0; i < nCurves; ++i) {
@@ -204,14 +191,12 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
- pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
+ pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
startAngle = endAngle;
}
- if (pie) pImpl->path.close();
-
- pImpl->flag |= RenderUpdateFlag::Path;
+ if (pie) pImpl->close();
return Result::Success;
}
@@ -228,48 +213,47 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
//rectangle
if (rx == 0 && ry == 0) {
- pImpl->path.grow(5, 4);
- pImpl->path.moveTo(x, y);
- pImpl->path.lineTo(x + w, y);
- pImpl->path.lineTo(x + w, y + h);
- pImpl->path.lineTo(x, y + h);
- pImpl->path.close();
+ pImpl->grow(5, 4);
+ pImpl->moveTo(x, y);
+ pImpl->lineTo(x + w, y);
+ pImpl->lineTo(x + w, y + h);
+ pImpl->lineTo(x, y + h);
+ pImpl->close();
//circle
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
} else {
auto hrx = rx * 0.5f;
auto hry = ry * 0.5f;
- pImpl->path.grow(10, 17);
- pImpl->path.moveTo(x + rx, y);
- pImpl->path.lineTo(x + w - rx, y);
- pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
- pImpl->path.lineTo(x + w, y + h - ry);
- pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
- pImpl->path.lineTo(x + rx, y + h);
- pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
- pImpl->path.lineTo(x, y + ry);
- pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
- pImpl->path.close();
+ pImpl->grow(10, 17);
+ pImpl->moveTo(x + rx, y);
+ pImpl->lineTo(x + w - rx, y);
+ pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
+ pImpl->lineTo(x + w, y + h - ry);
+ pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
+ pImpl->lineTo(x + rx, y + h);
+ pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
+ pImpl->lineTo(x, y + ry);
+ pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
+ pImpl->close();
}
- pImpl->flag |= RenderUpdateFlag::Path;
-
return Result::Success;
}
+//TODO: kill alpha at TVG 1.0, because we also have opacity
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
- pImpl->color[0] = r;
- pImpl->color[1] = g;
- pImpl->color[2] = b;
- pImpl->color[3] = a;
+ pImpl->rs.color[0] = r;
+ pImpl->rs.color[1] = g;
+ pImpl->rs.color[2] = b;
+ pImpl->rs.color[3] = a;
pImpl->flag |= RenderUpdateFlag::Color;
- if (pImpl->fill) {
- delete(pImpl->fill);
- pImpl->fill = nullptr;
+ if (pImpl->rs.fill) {
+ delete(pImpl->rs.fill);
+ pImpl->rs.fill = nullptr;
pImpl->flag |= RenderUpdateFlag::Gradient;
}
@@ -282,8 +266,8 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
auto p = f.release();
if (!p) return Result::MemoryCorruption;
- if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill);
- pImpl->fill = p;
+ if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
+ pImpl->rs.fill = p;
pImpl->flag |= RenderUpdateFlag::Gradient;
return Result::Success;
@@ -292,17 +276,23 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{
- if (r) *r = pImpl->color[0];
- if (g) *g = pImpl->color[1];
- if (b) *b = pImpl->color[2];
- if (a) *a = pImpl->color[3];
+ pImpl->rs.fillColor(r, g, b, a);
return Result::Success;
}
+
const Fill* Shape::fill() const noexcept
{
- return pImpl->fill;
+ return pImpl->rs.fill;
+}
+
+
+Result Shape::order(bool strokeFirst) noexcept
+{
+ if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation;
+
+ return Result::Success;
}
@@ -316,8 +306,7 @@ Result Shape::stroke(float width) noexcept
float Shape::strokeWidth() const noexcept
{
- if (!pImpl->stroke) return 0;
- return pImpl->stroke->width;
+ return pImpl->rs.strokeWidth();
}
@@ -331,12 +320,7 @@ Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{
- if (!pImpl->stroke) return Result::InsufficientCondition;
-
- if (r) *r = pImpl->stroke->color[0];
- if (g) *g = pImpl->stroke->color[1];
- if (b) *b = pImpl->stroke->color[2];
- if (a) *a = pImpl->stroke->color[3];
+ if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition;
return Result::Success;
}
@@ -350,9 +334,7 @@ Result Shape::stroke(unique_ptr<Fill> f) noexcept
const Fill* Shape::strokeFill() const noexcept
{
- if (!pImpl->stroke) return nullptr;
-
- return pImpl->stroke->fill;
+ return pImpl->rs.strokeFill();
}
@@ -373,11 +355,7 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
{
- if (!pImpl->stroke) return 0;
-
- if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
-
- return pImpl->stroke->dashCnt;
+ return pImpl->rs.strokeDash(dashPattern);
}
@@ -399,23 +377,19 @@ Result Shape::stroke(StrokeJoin join) noexcept
StrokeCap Shape::strokeCap() const noexcept
{
- if (!pImpl->stroke) return StrokeCap::Square;
-
- return pImpl->stroke->cap;
+ return pImpl->rs.strokeCap();
}
StrokeJoin Shape::strokeJoin() const noexcept
{
- if (!pImpl->stroke) return StrokeJoin::Bevel;
-
- return pImpl->stroke->join;
+ return pImpl->rs.strokeJoin();
}
Result Shape::fill(FillRule r) noexcept
{
- pImpl->rule = r;
+ pImpl->rs.rule = r;
return Result::Success;
}
@@ -423,5 +397,5 @@ Result Shape::fill(FillRule r) noexcept
FillRule Shape::fillRule() const noexcept
{
- return pImpl->rule;
+ return pImpl->rs.rule;
}
diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
index 738b21ed70..da288756fb 100644
--- a/thirdparty/thorvg/src/lib/tvgShapeImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SHAPE_IMPL_H_
#define _TVG_SHAPE_IMPL_H_
@@ -29,243 +30,155 @@
/* Internal Class Implementation */
/************************************************************************/
-struct ShapeStroke
+struct Shape::Impl
{
- float width;
- uint8_t color[4];
- Fill *fill;
- float* dashPattern;
- uint32_t dashCnt;
- StrokeCap cap;
- StrokeJoin join;
-
- void copy(const ShapeStroke* src)
+ RenderShape rs; //shape data
+ RenderData rd = nullptr; //engine data
+ uint32_t flag = RenderUpdateFlag::None;
+
+ bool dispose(RenderMethod& renderer)
{
- width = src->width;
- dashCnt = src->dashCnt;
- cap = src->cap;
- join = src->join;
-
- memcpy(color, src->color, sizeof(color));
- if (dashCnt > 0) {
- dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
- memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
- }
- if (src->fill) fill = src->fill->duplicate();
+ auto ret = renderer.dispose(rd);
+ rd = nullptr;
+ return ret;
}
- void clear()
+ bool render(RenderMethod& renderer)
{
- if (dashPattern) free(dashPattern);
- if (fill) delete(fill);
+ return renderer.renderShape(rd);
}
-};
-
-struct ShapePath
-{
- PathCommand* cmds = nullptr;
- uint32_t cmdCnt = 0;
- uint32_t reservedCmdCnt = 0;
-
- Point *pts = nullptr;
- uint32_t ptsCnt = 0;
- uint32_t reservedPtsCnt = 0;
-
- ~ShapePath()
+ RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
{
- if (cmds) free(cmds);
- if (pts) free(pts);
+ rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+ flag = RenderUpdateFlag::None;
+ return rd;
}
- ShapePath()
+ RenderRegion bounds(RenderMethod& renderer)
{
+ return renderer.region(rd);
}
- void duplicate(const ShapePath* src)
+ bool bounds(float* x, float* y, float* w, float* h)
{
- if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
-
- cmdCnt = src->cmdCnt;
- reservedCmdCnt = src->reservedCmdCnt;
- ptsCnt = src->ptsCnt;
- reservedPtsCnt = src->reservedPtsCnt;
+ //Path bounding size
+ if (rs.path.ptsCnt > 0 ) {
+ Point min = { rs.path.pts[0].x, rs.path.pts[0].y };
+ Point max = { rs.path.pts[0].x, rs.path.pts[0].y };
+
+ for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) {
+ if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x;
+ if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y;
+ if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x;
+ if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y;
+ }
- cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
- if (!cmds) return;
- memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
+ if (x) *x = min.x;
+ if (y) *y = min.y;
+ if (w) *w = max.x - min.x;
+ if (h) *h = max.y - min.y;
+ }
- pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
- if (!pts) {
- free(cmds);
- return;
+ //Stroke feathering
+ if (rs.stroke) {
+ if (x) *x -= rs.stroke->width * 0.5f;
+ if (y) *y -= rs.stroke->width * 0.5f;
+ if (w) *w += rs.stroke->width;
+ if (h) *h += rs.stroke->width;
}
- memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
+ return rs.path.ptsCnt > 0 ? true : false;
}
void reserveCmd(uint32_t cmdCnt)
{
- if (cmdCnt <= reservedCmdCnt) return;
- reservedCmdCnt = cmdCnt;
- cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
+ if (cmdCnt <= rs.path.reservedCmdCnt) return;
+ rs.path.reservedCmdCnt = cmdCnt;
+ rs.path.cmds = static_cast<PathCommand*>(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt));
}
void reservePts(uint32_t ptsCnt)
{
- if (ptsCnt <= reservedPtsCnt) return;
- reservedPtsCnt = ptsCnt;
- pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
+ if (ptsCnt <= rs.path.reservedPtsCnt) return;
+ rs.path.reservedPtsCnt = ptsCnt;
+ rs.path.pts = static_cast<Point*>(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt));
}
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
{
- reserveCmd(this->cmdCnt + cmdCnt);
- reservePts(this->ptsCnt + ptsCnt);
+ reserveCmd(rs.path.cmdCnt + cmdCnt);
+ reservePts(rs.path.ptsCnt + ptsCnt);
}
void reset()
{
- cmdCnt = 0;
- ptsCnt = 0;
- }
-
- void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
- {
- memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
- memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
- this->cmdCnt += cmdCnt;
- this->ptsCnt += ptsCnt;
- }
+ rs.path.cmdCnt = 0;
+ rs.path.ptsCnt = 0;
- void moveTo(float x, float y)
- {
- if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
- if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
-
- cmds[cmdCnt++] = PathCommand::MoveTo;
- pts[ptsCnt++] = {x, y};
+ flag = RenderUpdateFlag::Path;
}
- void lineTo(float x, float y)
+ void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
{
- if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
- if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+ memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
+ memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt);
+ rs.path.cmdCnt += cmdCnt;
+ rs.path.ptsCnt += ptsCnt;
- cmds[cmdCnt++] = PathCommand::LineTo;
- pts[ptsCnt++] = {x, y};
+ flag |= RenderUpdateFlag::Path;
}
- void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
+ void moveTo(float x, float y)
{
- if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
- if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
+ if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
+ if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
- cmds[cmdCnt++] = PathCommand::CubicTo;
- pts[ptsCnt++] = {cx1, cy1};
- pts[ptsCnt++] = {cx2, cy2};
- pts[ptsCnt++] = {x, y};
- }
+ rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo;
+ rs.path.pts[rs.path.ptsCnt++] = {x, y};
- void close()
- {
- if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
-
- if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
- cmds[cmdCnt++] = PathCommand::Close;
+ flag |= RenderUpdateFlag::Path;
}
- bool bounds(float* x, float* y, float* w, float* h) const
+ void lineTo(float x, float y)
{
- if (ptsCnt == 0) return false;
+ if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
+ if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
- Point min = { pts[0].x, pts[0].y };
- Point max = { pts[0].x, pts[0].y };
+ rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo;
+ rs.path.pts[rs.path.ptsCnt++] = {x, y};
- for (uint32_t i = 1; i < ptsCnt; ++i) {
- if (pts[i].x < min.x) min.x = pts[i].x;
- if (pts[i].y < min.y) min.y = pts[i].y;
- if (pts[i].x > max.x) max.x = pts[i].x;
- if (pts[i].y > max.y) max.y = pts[i].y;
- }
-
- if (x) *x = min.x;
- if (y) *y = min.y;
- if (w) *w = max.x - min.x;
- if (h) *h = max.y - min.y;
-
- return true;
+ flag |= RenderUpdateFlag::Path;
}
-};
-
-struct Shape::Impl
-{
- ShapePath path;
- Fill *fill = nullptr;
- ShapeStroke *stroke = nullptr;
- uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
- FillRule rule = FillRule::Winding;
- RenderData rdata = nullptr; //engine data
- Shape *shape = nullptr;
- uint32_t flag = RenderUpdateFlag::None;
-
- Impl(Shape* s) : shape(s)
- {
- }
-
- ~Impl()
- {
- if (fill) delete(fill);
- if (stroke) {
- stroke->clear();
- free (stroke);
- }
- }
-
- bool dispose(RenderMethod& renderer)
+ void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
{
- auto ret = renderer.dispose(rdata);
- rdata = nullptr;
- return ret;
- }
+ if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
+ if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2);
- bool render(RenderMethod& renderer)
- {
- return renderer.renderShape(rdata);
- }
+ rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo;
+ rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1};
+ rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2};
+ rs.path.pts[rs.path.ptsCnt++] = {x, y};
- void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
- {
- this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
- flag = RenderUpdateFlag::None;
- return this->rdata;
+ flag |= RenderUpdateFlag::Path;
}
- RenderRegion bounds(RenderMethod& renderer)
+ void close()
{
- return renderer.region(rdata);
- }
+ if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return;
- bool bounds(float* x, float* y, float* w, float* h)
- {
- auto ret = path.bounds(x, y, w, h);
+ if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
+ rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close;
- //Stroke feathering
- if (stroke) {
- if (x) *x -= stroke->width * 0.5f;
- if (y) *y -= stroke->width * 0.5f;
- if (w) *w += stroke->width;
- if (h) *h += stroke->width;
- }
- return ret;
+ flag |= RenderUpdateFlag::Path;
}
bool strokeWidth(float width)
{
//TODO: Size Exception?
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- stroke->width = width;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
return true;
@@ -273,8 +186,8 @@ struct Shape::Impl
bool strokeCap(StrokeCap cap)
{
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- stroke->cap = cap;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
return true;
@@ -282,8 +195,8 @@ struct Shape::Impl
bool strokeJoin(StrokeJoin join)
{
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- stroke->join = join;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
return true;
@@ -291,17 +204,17 @@ struct Shape::Impl
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- if (stroke->fill) {
- delete(stroke->fill);
- stroke->fill = nullptr;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ if (rs.stroke->fill) {
+ delete(rs.stroke->fill);
+ rs.stroke->fill = nullptr;
flag |= RenderUpdateFlag::GradientStroke;
}
- stroke->color[0] = r;
- stroke->color[1] = g;
- stroke->color[2] = b;
- stroke->color[3] = a;
+ rs.stroke->color[0] = r;
+ rs.stroke->color[1] = g;
+ rs.stroke->color[2] = b;
+ rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
@@ -313,9 +226,9 @@ struct Shape::Impl
auto p = f.release();
if (!p) return Result::MemoryCorruption;
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- if (stroke->fill && stroke->fill != p) delete(stroke->fill);
- stroke->fill = p;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
+ rs.stroke->fill = p;
flag |= RenderUpdateFlag::Stroke;
flag |= RenderUpdateFlag::GradientStroke;
@@ -327,23 +240,32 @@ struct Shape::Impl
{
//Reset dash
if (!pattern && cnt == 0) {
- free(stroke->dashPattern);
- stroke->dashPattern = nullptr;
+ free(rs.stroke->dashPattern);
+ rs.stroke->dashPattern = nullptr;
} else {
- if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- if (stroke->dashCnt != cnt) {
- free(stroke->dashPattern);
- stroke->dashPattern = nullptr;
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ if (rs.stroke->dashCnt != cnt) {
+ free(rs.stroke->dashPattern);
+ rs.stroke->dashPattern = nullptr;
}
- if (!stroke->dashPattern) {
- stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
- if (!stroke->dashPattern) return false;
+ if (!rs.stroke->dashPattern) {
+ rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
+ if (!rs.stroke->dashPattern) return false;
}
for (uint32_t i = 0; i < cnt; ++i) {
- stroke->dashPattern[i] = pattern[i];
+ rs.stroke->dashPattern[i] = pattern[i];
}
}
- stroke->dashCnt = cnt;
+ rs.stroke->dashCnt = cnt;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeFirst(bool strokeFirst)
+ {
+ if (!rs.stroke) rs.stroke = new RenderStroke();
+ rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke;
return true;
@@ -354,29 +276,53 @@ struct Shape::Impl
auto ret = Shape::gen();
auto dup = ret.get()->pImpl;
- dup->rule = rule;
+ dup->rs.rule = rs.rule;
//Color
- memcpy(dup->color, color, sizeof(color));
+ memcpy(dup->rs.color, rs.color, sizeof(rs.color));
dup->flag = RenderUpdateFlag::Color;
//Path
- dup->path.duplicate(&path);
+ if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) {
+ dup->rs.path.cmdCnt = rs.path.cmdCnt;
+ dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt;
+ dup->rs.path.ptsCnt = rs.path.ptsCnt;
+ dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt;
+
+ dup->rs.path.cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt));
+ if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt);
+
+ dup->rs.path.pts = static_cast<Point*>(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt));
+ if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt);
+ }
dup->flag |= RenderUpdateFlag::Path;
//Stroke
- if (stroke) {
- dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
- dup->stroke->copy(stroke);
+ if (rs.stroke) {
+ dup->rs.stroke = new RenderStroke();
+ dup->rs.stroke->width = rs.stroke->width;
+ dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
+ dup->rs.stroke->cap = rs.stroke->cap;
+ dup->rs.stroke->join = rs.stroke->join;
+ dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
+ memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
+
+ if (rs.stroke->dashCnt > 0) {
+ dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
+ memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
+ }
+
dup->flag |= RenderUpdateFlag::Stroke;
- if (stroke->fill)
+ if (rs.stroke->fill) {
+ dup->rs.stroke->fill = rs.stroke->fill->duplicate();
dup->flag |= RenderUpdateFlag::GradientStroke;
+ }
}
//Fill
- if (fill) {
- dup->fill = fill->duplicate();
+ if (rs.fill) {
+ dup->rs.fill = rs.fill->duplicate();
dup->flag |= RenderUpdateFlag::Gradient;
}
diff --git a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
index b5cae424f7..626bd51a2e 100644
--- a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
+++ b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgCanvasImpl.h"
#ifdef THORVG_SW_RASTER_SUPPORT
@@ -84,7 +85,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
if (!renderer) return Result::MemoryCorruption;
- if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
+ if (!renderer->target(buffer, stride, w, h, static_cast<ColorSpace>(cs))) return Result::InvalidArguments;
//Paints must be updated again with this new target.
Canvas::pImpl->needRefresh();
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
index 8c07dc3859..019468083d 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <deque>
#include <thread>
#include <vector>
@@ -99,13 +100,12 @@ struct TaskQueue {
};
-class TaskSchedulerImpl
+struct TaskSchedulerImpl
{
-public:
- unsigned threadCnt;
+ uint32_t threadCnt;
vector<thread> threads;
vector<TaskQueue> taskQueues;
- atomic<unsigned> idx{0};
+ atomic<uint32_t> idx{0};
TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
{
@@ -135,7 +135,7 @@ public:
}
if (!success && !taskQueues[i].pop(&task)) break;
- (*task)(i);
+ (*task)(i + 1);
}
}
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
index 163e387f29..ce2016f561 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_TASK_SCHEDULER_H_
#define _TVG_TASK_SCHEDULER_H_
@@ -44,8 +45,8 @@ struct Task
private:
mutex mtx;
condition_variable cv;
- bool ready{true};
- bool pending{false};
+ bool ready = true;
+ bool pending = false;
public:
virtual ~Task() = default;
@@ -78,11 +79,9 @@ private:
pending = true;
}
- friend class TaskSchedulerImpl;
+ friend struct TaskSchedulerImpl;
};
-
-
}
#endif //_TVG_TASK_SCHEDULER_H_
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
index 1fb0681814..b0a9fdd579 100644
--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,42 +23,14 @@
#include "tvgLoader.h"
#include "tvgPngLoader.h"
-static inline uint32_t PREMULTIPLY(uint32_t c)
-{
- auto a = (c >> 24);
- return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
-}
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
-static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
-{
- auto buffer = data;
- for (uint32_t y = 0; y < h; ++y, buffer += w) {
- auto src = buffer;
- for (uint32_t x = 0; x < w; ++x, ++src) {
- *src = PREMULTIPLY(*src);
- }
- }
-}
-
-
-static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
-{
- return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
-}
-
-
-static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
-{
- auto buffer = data;
- for (uint32_t y = 0; y < h; ++y, buffer += w) {
- auto src = buffer;
- for (uint32_t x = 0; x < w; ++x, ++src) {
- *src = CHANGE_COLORSPACE(*src);
- }
- }
-}
-
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
PngLoader::PngLoader()
{
@@ -84,6 +56,7 @@ bool PngLoader::open(const string& path)
w = (float)image->width;
h = (float)image->height;
+ cs = ColorSpace::ARGB8888;
return true;
}
@@ -96,6 +69,7 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
w = (float)image->width;
h = (float)image->height;
+ cs = ColorSpace::ARGB8888;
return true;
}
@@ -117,8 +91,6 @@ bool PngLoader::read()
}
content = reinterpret_cast<uint32_t*>(buffer);
- _premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
-
return true;
}
@@ -128,20 +100,20 @@ bool PngLoader::close()
return true;
}
-unique_ptr<Surface> PngLoader::bitmap(uint32_t colorSpace)
+unique_ptr<Surface> PngLoader::bitmap()
{
if (!content) return nullptr;
- if (this->colorSpace != colorSpace) {
- this->colorSpace = colorSpace;
- _changeColorSpace(content, w, h);
- }
- auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = content;
+ //TODO: It's better to keep this surface instance in the loader side
+ auto surface = new Surface;
+ surface->buf32 = content;
surface->stride = w;
surface->w = w;
surface->h = h;
- surface->cs = colorSpace;
+ surface->cs = cs;
+ surface->channelSize = sizeof(uint32_t);
+ surface->owner = true;
+ surface->premultiplied = false;
return unique_ptr<Surface>(surface);
}
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
index f8c0daa61c..5354e1bdd6 100644
--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_PNG_LOADER_H_
#define _TVG_PNG_LOADER_H_
@@ -36,7 +37,7 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
+ unique_ptr<Surface> bitmap() override;
private:
png_imagep image = nullptr;
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
index f64b7110fe..6edda86cc1 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,24 +28,6 @@
/* Internal Class Implementation */
/************************************************************************/
-static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
-{
- return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
-}
-
-
-static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
-{
- auto buffer = data;
- for (uint32_t y = 0; y < h; ++y, buffer += w) {
- auto src = buffer;
- for (uint32_t x = 0; x < w; ++x, ++src) {
- *src = CHANGE_COLORSPACE(*src);
- }
- }
-}
-
-
void JpgLoader::clear()
{
jpgdDelete(decoder);
@@ -79,6 +61,7 @@ bool JpgLoader::open(const string& path)
w = static_cast<float>(width);
h = static_cast<float>(height);
+ cs = ColorSpace::ARGB8888;
return true;
}
@@ -104,6 +87,7 @@ bool JpgLoader::open(const char* data, uint32_t size, bool copy)
w = static_cast<float>(width);
h = static_cast<float>(height);
+ cs = ColorSpace::ARGB8888;
return true;
}
@@ -128,22 +112,22 @@ bool JpgLoader::close()
}
-unique_ptr<Surface> JpgLoader::bitmap(uint32_t colorSpace)
+unique_ptr<Surface> JpgLoader::bitmap()
{
this->done();
if (!image) return nullptr;
- if (this->colorSpace != colorSpace) {
- this->colorSpace = colorSpace;
- _changeColorSpace(reinterpret_cast<uint32_t*>(image), w, h);
- }
- auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = reinterpret_cast<uint32_t*>(image);
+ //TODO: It's better to keep this surface instance in the loader side
+ auto surface = new Surface;
+ surface->buf8 = image;
surface->stride = static_cast<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
- surface->cs = colorSpace;
+ surface->cs = cs;
+ surface->channelSize = sizeof(uint32_t);
+ surface->premultiplied = true;
+ surface->owner = true;
return unique_ptr<Surface>(surface);
}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
index c47cb6704f..6d2febe94f 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_JPG_LOADER_H_
#define _TVG_JPG_LOADER_H_
@@ -44,7 +45,7 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
+ unique_ptr<Surface> bitmap() override;
void run(unsigned tid) override;
};
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
index 56b40acf0b..6ea2efb054 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -80,7 +80,7 @@ enum jpgd_status
enum
{
JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
- JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
+ JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
};
// Input stream interface.
@@ -151,7 +151,7 @@ public:
// If JPGD_SUCCESS is returned you may then call decode() on each scanline.
int begin_decoding();
// Returns the next scan line.
- // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
+ // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
// Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
// Returns JPGD_SUCCESS if a scan line has been returned.
// Returns JPGD_DONE if all scan lines have been returned.
@@ -1246,7 +1246,7 @@ void jpeg_decoder::read_sof_marker()
uint32_t num_left = get_bits(16);
if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
-
+
m_image_y_size = get_bits(16);
if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
@@ -1326,7 +1326,7 @@ void jpeg_decoder::read_sos_marker()
}
num_left -= 3;
- while (num_left) { /* read past whatever is num_left */
+ while (num_left) { /* read past whatever is num_left */
get_bits(8);
num_left--;
}
@@ -1411,7 +1411,7 @@ int jpeg_decoder::process_markers()
stop_decoding(JPGD_UNEXPECTED_MARKER);
break;
}
- default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
+ default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
skip_variable_marker();
break;
}
@@ -1441,7 +1441,7 @@ void jpeg_decoder::locate_soi_marker()
if (lastchar == 0xFF) {
if (thischar == M_SOI) break;
- else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
+ else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
}
}
@@ -1460,7 +1460,7 @@ void jpeg_decoder::locate_sof_marker()
switch (c) {
case M_SOF2: m_progressive_flag = true;
case M_SOF0: /* baseline DCT */
- case M_SOF1: { /* extended sequential DCT */
+ case M_SOF1: { /* extended sequential DCT */
read_sof_marker();
break;
}
@@ -1671,7 +1671,7 @@ void jpeg_decoder::transform_mcu_expand(int mcu_row)
JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
- int max_zag = m_mcu_block_max_zag[mcu_block++] - 1;
+ int max_zag = m_mcu_block_max_zag[mcu_block++] - 1;
if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis
switch (s_max_rc[max_zag]) {
@@ -1789,7 +1789,7 @@ void jpeg_decoder::load_next_row()
p[0] = pDC[0];
memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
- for (i = 63; i > 0; i--) {
+ for (i = 63; i > 0; i--) {
if (p[g_ZAG[i]]) break;
}
@@ -1809,7 +1809,7 @@ void jpeg_decoder::load_next_row()
if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
block_y_mcu_ofs = 0;
block_x_mcu[component_id] += m_comp_h_samp[component_id];
- }
+ }
}
}
if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
@@ -1865,7 +1865,7 @@ void jpeg_decoder::process_restart()
static inline int dequantize_ac(int c, int q)
-{
+{
c *= q;
return c;
}
@@ -1910,7 +1910,7 @@ void jpeg_decoder::decode_next_row()
while (n--) p[g_ZAG[kt++]] = 0;
}
k += r;
- }
+ }
s = JPGD_HUFF_EXTEND(extra_bits, s);
JPGD_ASSERT(k < 64);
p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
@@ -2204,7 +2204,7 @@ int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
}
else *pScan_line = m_pScan_line_1;
break;
- }
+ }
case JPGD_YH2V1: {
H2V1Convert();
*pScan_line = m_pScan_line_0;
@@ -2609,11 +2609,11 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
int p1 = 1 << pD->m_successive_low;
int m1 = static_cast<unsigned int>(-1) << pD->m_successive_low;
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
-
+
JPGD_ASSERT(pD->m_spectral_end <= 63);
-
+
k = pD->m_spectral_start;
-
+
if (pD->m_eob_run == 0) {
for ( ; k <= pD->m_spectral_end; k++) {
s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
index ca9cb35c32..030fdc2946 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
index 19c1dd6668..5f5e72b0dd 100644
--- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <fstream>
#include <string.h>
#include "tvgLoader.h"
@@ -28,22 +29,6 @@
/* Internal Class Implementation */
/************************************************************************/
-static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
-{
- return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
-}
-
-
-static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
-{
- auto buffer = data;
- for (uint32_t y = 0; y < h; ++y, buffer += w) {
- auto src = buffer;
- for (uint32_t x = 0; x < w; ++x, ++src) {
- *src = CHANGE_COLORSPACE(*src);
- }
- }
-}
/************************************************************************/
/* External Class Implementation */
@@ -73,6 +58,8 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
}
else content = const_cast<uint32_t*>(data);
+ cs = ColorSpace::ARGB8888;
+
return true;
}
@@ -89,20 +76,20 @@ bool RawLoader::close()
}
-unique_ptr<Surface> RawLoader::bitmap(uint32_t colorSpace)
+unique_ptr<Surface> RawLoader::bitmap()
{
if (!content) return nullptr;
- if (this->colorSpace != colorSpace) {
- this->colorSpace = colorSpace;
- _changeColorSpace(content, w, h);
- }
- auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = content;
+ //TODO: It's better to keep this surface instance in the loader side
+ auto surface = new Surface;
+ surface->buf32 = content;
surface->stride = static_cast<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
- surface->cs = colorSpace;
+ surface->cs = cs;
+ surface->channelSize = sizeof(uint32_t);
+ surface->premultiplied = true;
+ surface->owner = true;
return unique_ptr<Surface>(surface);
}
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
index 8789b0cf51..69f9bdc47a 100644
--- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_RAW_LOADER_H_
#define _TVG_RAW_LOADER_H_
@@ -35,7 +36,7 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
+ unique_ptr<Surface> bitmap() override;
};
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
index 478ba5d3d1..694e6d1ebf 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2022 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,13 +32,13 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
{
if (from == nullptr) return;
//Copy the properties of 'from' only if they were explicitly set (not the default ones).
- if (from->curColorSet && !((int)to->flags & (int)SvgStyleFlags::Color)) {
+ if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) {
to->color = from->color;
to->curColorSet = true;
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color);
+ to->flags = (to->flags | SvgStyleFlags::Color);
}
//Fill
- if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) {
+ if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) {
to->fill.paint.color = from->fill.paint.color;
to->fill.paint.none = from->fill.paint.none;
to->fill.paint.curColor = from->fill.paint.curColor;
@@ -46,21 +46,21 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
if (to->fill.paint.url) free(to->fill.paint.url);
to->fill.paint.url = strdup(from->fill.paint.url);
}
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
+ to->flags = (to->flags | SvgStyleFlags::Fill);
}
- if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) {
+ if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) {
to->fill.opacity = from->fill.opacity;
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Opacity);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::FillOpacity);
}
- if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) {
+ if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) {
to->fill.fillRule = from->fill.fillRule;
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule);
+ to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
+ to->flags = (to->flags | SvgStyleFlags::FillRule);
}
//Stroke
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) {
to->stroke.paint.color = from->stroke.paint.color;
to->stroke.paint.none = from->stroke.paint.none;
to->stroke.paint.curColor = from->stroke.paint.curColor;
@@ -68,45 +68,45 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
if (to->stroke.paint.url) free(to->stroke.paint.url);
to->stroke.paint.url = strdup(from->stroke.paint.url);
}
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
+ to->flags = (to->flags | SvgStyleFlags::Stroke);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) {
to->stroke.opacity = from->stroke.opacity;
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Opacity);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) {
to->stroke.width = from->stroke.width;
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
+ to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) {
if (from->stroke.dash.array.count > 0) {
to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
}
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
+ to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
}
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) {
to->stroke.cap = from->stroke.cap;
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Cap);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
+ to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
}
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) {
+ if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) {
to->stroke.join = from->stroke.join;
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join);
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin);
+ to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
+ to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
}
//Opacity
//TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
- if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) {
+ if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) {
to->opacity = from->opacity;
- to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity);
+ to->flags = (to->flags | SvgStyleFlags::Opacity);
}
}
@@ -118,11 +118,11 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
{
//Copy matrix attribute
- if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) {
+ if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) {
to->transform = (Matrix*)malloc(sizeof(Matrix));
if (to->transform) {
*to->transform = *from->transform;
- to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform);
+ to->style->flags = (to->style->flags | SvgStyleFlags::Transform);
}
}
//Copy style attribute
@@ -155,12 +155,12 @@ SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType t
SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
{
- if (!style) return nullptr;
+ if (!style || !title) return nullptr;
auto child = style->child.data;
for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
if ((*child)->type == SvgNodeType::CssStyle) {
- if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child);
+ if ((*child)->id && !strcmp((*child)->id, title)) return (*child);
}
}
return nullptr;
@@ -175,9 +175,6 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) {
cssCopyStyleAttr(*child, cssNode);
}
- if (auto cssNode = cssFindStyleNode(style, nullptr)) {
- cssCopyStyleAttr(*child, cssNode);
- }
cssUpdateStyle(*child, style);
}
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
index 66477c1a32..228c5996da 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2022 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index bc350a0eb8..f9f08cdb81 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -137,11 +137,11 @@ static constexpr struct
};
-static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
+static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
{
if (!strcmp(*content, "none")) {
*align = AspectRatioAlign::None;
- return true;
+ return;
}
for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
@@ -158,8 +158,6 @@ static bool _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
} else if (!strcmp(*content, "slice")) {
*meetOrSlice = AspectRatioMeetOrSlice::Slice;
}
-
- return true;
}
@@ -254,6 +252,36 @@ static SvgMaskType _toMaskType(const char* str)
}
+//The default rendering order: fill, stroke, markers
+//If any is omitted, will be rendered in its default order after the specified ones.
+static bool _toPaintOrder(const char* str)
+{
+ uint8_t position = 1;
+ uint8_t strokePosition = 0;
+ uint8_t fillPosition = 0;
+
+ while (*str != '\0') {
+ str = _skipSpace(str, nullptr);
+ if (!strncmp(str, "fill", 4)) {
+ fillPosition = position++;
+ str += 4;
+ } else if (!strncmp(str, "stroke", 6)) {
+ strokePosition = position++;
+ str += 6;
+ } else if (!strncmp(str, "markers", 7)) {
+ str += 7;
+ } else {
+ return _toPaintOrder("fill stroke");
+ }
+ }
+
+ if (fillPosition == 0) fillPosition = position++;
+ if (strokePosition == 0) strokePosition = position++;
+
+ return fillPosition < strokePosition;
+}
+
+
#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
static Type _to##Name1(const char* str) \
{ \
@@ -833,20 +861,40 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
if (!strcmp(key, "width")) {
doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
+ }
} else if (!strcmp(key, "height")) {
doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
+ } else {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
+ }
} else if (!strcmp(key, "viewBox")) {
if (_parseNumber(&value, &doc->vx)) {
if (_parseNumber(&value, &doc->vy)) {
if (_parseNumber(&value, &doc->vw)) {
- _parseNumber(&value, &doc->vh);
- loader->svgParse->global.h = doc->vh;
+ if (_parseNumber(&value, &doc->vh)) {
+ doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
+ loader->svgParse->global.h = doc->vh;
+ }
+ loader->svgParse->global.w = doc->vw;
}
- loader->svgParse->global.w = doc->vw;
+ loader->svgParse->global.y = doc->vy;
}
- loader->svgParse->global.y = doc->vy;
+ loader->svgParse->global.x = doc->vx;
+ }
+ if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
+ doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
+ TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
+ }
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
+ loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
}
- loader->svgParse->global.x = doc->vx;
} else if (!strcmp(key, "preserveAspectRatio")) {
_parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (!strcmp(key, "style")) {
@@ -890,7 +938,7 @@ static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, co
static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
+ style->fill.flags = (style->fill.flags | SvgFillFlags::Paint);
_handlePaintAttr(&style->fill.paint, value);
}
@@ -898,47 +946,47 @@ static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con
static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
SvgStyleProperty* style = node->style;
- style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
+ style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint);
_handlePaintAttr(&style->stroke.paint, value);
}
static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity);
node->style->stroke.opacity = _toOpacity(value);
}
static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash);
_parseDashArray(loader, value, &node->style->stroke.dash);
}
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
}
static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap);
node->style->stroke.cap = _toLineCap(value);
}
static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join);
node->style->stroke.join = _toLineJoin(value);
}
static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule);
node->style->fill.fillRule = _toFillRule(value);
}
@@ -951,7 +999,7 @@ static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
{
- node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
+ node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity);
node->style->fill.opacity = _toOpacity(value);
}
@@ -1001,6 +1049,13 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
}
+static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder);
+ node->style->paintOrder = _toPaintOrder(value);
+}
+
+
static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
auto cssClass = &node->style->cssClass;
@@ -1051,7 +1106,8 @@ static constexpr struct
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
- STYLE_DEF(display, Display, SvgStyleFlags::Display)
+ STYLE_DEF(display, Display, SvgStyleFlags::Display),
+ STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
};
@@ -1071,8 +1127,8 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
if (style) {
styleTags[i].tagHandler(loader, node, value);
- node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
- } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
+ node->style->flags = (node->style->flags | styleTags[i].flag);
+ } else if (!(node->style->flags & styleTags[i].flag)) {
styleTags[i].tagHandler(loader, node, value);
}
return true;
@@ -1253,6 +1309,8 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
node->style->stroke.join = StrokeJoin::Miter;
node->style->stroke.scale = 1.0;
+ node->style->paintOrder = _toPaintOrder("fill stroke");
+
//Default display is true("inline").
node->display = true;
@@ -1291,22 +1349,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
if (!loader->svgParse->node) return nullptr;
SvgDocNode* doc = &(loader->svgParse->node->node.doc);
- loader->svgParse->global.w = 0;
- loader->svgParse->global.h = 0;
+ loader->svgParse->global.w = 1.0f;
+ loader->svgParse->global.h = 1.0f;
doc->align = AspectRatioAlign::XMidYMid;
doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
+ doc->viewFlag = SvgViewFlag::None;
func(buf, bufLength, _attrParseSvgNode, loader);
- if (loader->svgParse->global.w == 0) {
- if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
- else loader->svgParse->global.w = doc->w;
- }
- if (loader->svgParse->global.h == 0) {
- if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
- else loader->svgParse->global.h = doc->h;
+ if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
+ if (doc->viewFlag & SvgViewFlag::Width) {
+ loader->svgParse->global.w = doc->w;
+ }
+ if (doc->viewFlag & SvgViewFlag::Height) {
+ loader->svgParse->global.h = doc->h;
+ }
}
-
return loader->svgParse->node;
}
@@ -1868,309 +1926,6 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id)
}
-static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
-{
- for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src.data[i]);
- }
-}
-
-
-static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
-{
- if (!from) return nullptr;
-
- auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
- if (!grad) return nullptr;
-
- grad->type = from->type;
- grad->id = from->id ? _copyId(from->id) : nullptr;
- grad->ref = from->ref ? _copyId(from->ref) : nullptr;
- grad->spread = from->spread;
- grad->userSpace = from->userSpace;
-
- if (from->transform) {
- grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
- if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
- }
-
- if (grad->type == SvgGradientType::Linear) {
- grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
- if (!grad->linear) goto error_grad_alloc;
- memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
- } else if (grad->type == SvgGradientType::Radial) {
- grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
- if (!grad->radial) goto error_grad_alloc;
- memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
- }
-
- _cloneGradStops(grad->stops, from->stops);
-
- return grad;
-
-error_grad_alloc:
- if (grad) {
- grad->clear();
- free(grad);
- }
- return nullptr;
-}
-
-
-static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
-{
- if (parent == nullptr) return;
- //Inherit the property of parent if not present in child.
- if (!child->curColorSet) {
- child->color = parent->color;
- child->curColorSet = parent->curColorSet;
- }
- //Fill
- if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
- child->fill.paint.color = parent->fill.paint.color;
- child->fill.paint.none = parent->fill.paint.none;
- child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) {
- if (child->fill.paint.url) free(child->fill.paint.url);
- child->fill.paint.url = _copyId(parent->fill.paint.url);
- }
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
- child->fill.opacity = parent->fill.opacity;
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
- child->fill.fillRule = parent->fill.fillRule;
- }
- //Stroke
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- child->stroke.paint.color = parent->stroke.paint.color;
- child->stroke.paint.none = parent->stroke.paint.none;
- child->stroke.paint.curColor = parent->stroke.paint.curColor;
- if (parent->stroke.paint.url) {
- if (child->stroke.paint.url) free(child->stroke.paint.url);
- child->stroke.paint.url = _copyId(parent->stroke.paint.url);
- } else {
- child->stroke.paint.url = nullptr;
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- child->stroke.opacity = parent->stroke.opacity;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
- child->stroke.width = parent->stroke.width;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (parent->stroke.dash.array.count > 0) {
- child->stroke.dash.array.clear();
- child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
- for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
- }
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- child->stroke.cap = parent->stroke.cap;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
- child->stroke.join = parent->stroke.join;
- }
-}
-
-
-static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
-{
- if (from == nullptr) return;
- //Copy the properties of 'from' only if they were explicitly set (not the default ones).
- if (from->curColorSet) {
- to->color = from->color;
- to->curColorSet = true;
- }
- //Fill
- to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
- if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
- to->fill.paint.color = from->fill.paint.color;
- to->fill.paint.none = from->fill.paint.none;
- to->fill.paint.curColor = from->fill.paint.curColor;
- if (from->fill.paint.url) {
- if (to->fill.paint.url) free(to->fill.paint.url);
- to->fill.paint.url = _copyId(from->fill.paint.url);
- }
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
- to->fill.opacity = from->fill.opacity;
- }
- if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
- to->fill.fillRule = from->fill.fillRule;
- }
- //Stroke
- to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- to->stroke.paint.color = from->stroke.paint.color;
- to->stroke.paint.none = from->stroke.paint.none;
- to->stroke.paint.curColor = from->stroke.paint.curColor;
- if (from->stroke.paint.url) {
- if (to->stroke.paint.url) free(to->stroke.paint.url);
- to->stroke.paint.url = _copyId(from->stroke.paint.url);
- } else {
- to->stroke.paint.url = nullptr;
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- to->stroke.opacity = from->stroke.opacity;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
- to->stroke.width = from->stroke.width;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (from->stroke.dash.array.count > 0) {
- to->stroke.dash.array.clear();
- to->stroke.dash.array.reserve(from->stroke.dash.array.count);
- for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
- }
- }
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- to->stroke.cap = from->stroke.cap;
- }
- if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
- to->stroke.join = from->stroke.join;
- }
-}
-
-
-static void _copyAttr(SvgNode* to, const SvgNode* from)
-{
- //Copy matrix attribute
- if (from->transform) {
- to->transform = (Matrix*)malloc(sizeof(Matrix));
- if (to->transform) *to->transform = *from->transform;
- }
- //Copy style attribute
- _styleCopy(to->style, from->style);
- to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
- if (from->style->clipPath.url) {
- if (to->style->clipPath.url) free(to->style->clipPath.url);
- to->style->clipPath.url = strdup(from->style->clipPath.url);
- }
- if (from->style->mask.url) {
- if (to->style->mask.url) free(to->style->mask.url);
- to->style->mask.url = strdup(from->style->mask.url);
- }
-
- //Copy node attribute
- switch (from->type) {
- case SvgNodeType::Circle: {
- to->node.circle.cx = from->node.circle.cx;
- to->node.circle.cy = from->node.circle.cy;
- to->node.circle.r = from->node.circle.r;
- break;
- }
- case SvgNodeType::Ellipse: {
- to->node.ellipse.cx = from->node.ellipse.cx;
- to->node.ellipse.cy = from->node.ellipse.cy;
- to->node.ellipse.rx = from->node.ellipse.rx;
- to->node.ellipse.ry = from->node.ellipse.ry;
- break;
- }
- case SvgNodeType::Rect: {
- to->node.rect.x = from->node.rect.x;
- to->node.rect.y = from->node.rect.y;
- to->node.rect.w = from->node.rect.w;
- to->node.rect.h = from->node.rect.h;
- to->node.rect.rx = from->node.rect.rx;
- to->node.rect.ry = from->node.rect.ry;
- to->node.rect.hasRx = from->node.rect.hasRx;
- to->node.rect.hasRy = from->node.rect.hasRy;
- break;
- }
- case SvgNodeType::Line: {
- to->node.line.x1 = from->node.line.x1;
- to->node.line.y1 = from->node.line.y1;
- to->node.line.x2 = from->node.line.x2;
- to->node.line.y2 = from->node.line.y2;
- break;
- }
- case SvgNodeType::Path: {
- if (from->node.path.path) {
- if (to->node.path.path) free(to->node.path.path);
- to->node.path.path = strdup(from->node.path.path);
- }
- break;
- }
- case SvgNodeType::Polygon: {
- if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
- to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
- memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Polyline: {
- if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
- to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
- memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
- }
- break;
- }
- case SvgNodeType::Image: {
- to->node.image.x = from->node.image.x;
- to->node.image.y = from->node.image.y;
- to->node.image.w = from->node.image.w;
- to->node.image.h = from->node.image.h;
- if (from->node.image.href) {
- if (to->node.image.href) free(to->node.image.href);
- to->node.image.href = strdup(from->node.image.href);
- }
- break;
- }
- default: {
- break;
- }
- }
-}
-
-
-static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
-{
- /* Exception handling: Prevent invalid SVG data input.
- The size is the arbitrary value, we need an experimental size. */
- if (depth == 8192) {
- TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
- return;
- }
-
- SvgNode* newNode;
- if (!from || !parent || from == parent) return;
-
- newNode = _createNode(parent, from->type);
- if (!newNode) return;
-
- _styleInherit(newNode->style, parent->style);
- _copyAttr(newNode, from);
-
- auto child = from->child.data;
- for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
- _cloneNode(*child, newNode, depth + 1);
- }
-}
-
-
-static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
-{
- for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = cloneNodes->data[i];
- auto defs = _getDefsNode(nodeIdPair.node);
- auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
- if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
- _cloneNode(nodeFrom, nodeIdPair.node, 0);
- if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
- nodeIdPair.node->node.use.symbol = nodeFrom;
- }
- free(nodeIdPair.id);
- }
-}
-
-
static constexpr struct
{
const char* tag;
@@ -2185,6 +1940,7 @@ static constexpr struct
};
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth);
static bool _attrParseUseNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@@ -2379,13 +2135,99 @@ static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial,
}
+static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCxPercentage) {
+ if (userSpace) radial->cx /= loader->svgParse->global.w;
+ else radial->cx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isCyPercentage) {
+ if (userSpace) radial->cy /= loader->svgParse->global.h;
+ else radial->cy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFxPercentage) {
+ if (userSpace) radial->fx /= loader->svgParse->global.w;
+ else radial->fx *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFyPercentage) {
+ if (userSpace) radial->fy /= loader->svgParse->global.h;
+ else radial->fy *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isRPercentage) {
+ if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ }
+}
+
+
+static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cx = from->radial->cx;
+ to->radial->isCxPercentage = from->radial->isCxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cx);
+}
+
+
+static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->cy = from->radial->cy;
+ to->radial->isCyPercentage = from->radial->isCyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Cy);
+}
+
+
+static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fx = from->radial->fx;
+ to->radial->isFxPercentage = from->radial->isFxPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fx);
+}
+
+
+static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fy = from->radial->fy;
+ to->radial->isFyPercentage = from->radial->isFyPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fy);
+}
+
+
+static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->r = from->radial->r;
+ to->radial->isRPercentage = from->radial->isRPercentage;
+ to->flags = (to->flags | SvgGradientFlags::R);
+}
+
+
typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
+typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
-#define RADIAL_DEF(Name, Name1) \
+#define RADIAL_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
+#Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \
}
@@ -2394,13 +2236,16 @@ static constexpr struct
const char* tag;
int sz;
radialMethod tagHandler;
+ radialInheritMethod tagInheritHandler;
radialMethodRecalc tagRecalc;
+ radialMethodRecalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} radialTags[] = {
- RADIAL_DEF(cx, Cx),
- RADIAL_DEF(cy, Cy),
- RADIAL_DEF(fx, Fx),
- RADIAL_DEF(fy, Fy),
- RADIAL_DEF(r, R)
+ RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx),
+ RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
+ RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
+ RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
+ RADIAL_DEF(r, R, SvgGradientFlags::R)
};
@@ -2414,6 +2259,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
radialTags[i].tagHandler(loader, radial, value);
+ grad->flags = (grad->flags | radialTags[i].flag);
return true;
}
}
@@ -2423,11 +2269,13 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2443,6 +2291,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Radial;
grad->userSpace = false;
grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
@@ -2485,10 +2334,10 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
if (!strcmp(key, "stop-opacity")) {
stop->a = _toOpacity(value);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
} else if (!strcmp(key, "stop-color")) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
- loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
+ loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
} else {
return false;
}
@@ -2505,11 +2354,11 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
if (!strcmp(key, "offset")) {
stop->offset = _toOffset(value);
} else if (!strcmp(key, "stop-opacity")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) {
stop->a = _toOpacity(value);
}
} else if (!strcmp(key, "stop-color")) {
- if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
+ if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
}
} else if (!strcmp(key, "style")) {
@@ -2570,13 +2419,82 @@ static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear
}
+static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX1Percentage) {
+ if (userSpace) linear->x1 /= loader->svgParse->global.w;
+ else linear->x1 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isX2Percentage) {
+ if (userSpace) linear->x2 /= loader->svgParse->global.w;
+ else linear->x2 *= loader->svgParse->global.w;
+ }
+}
+
+
+static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY1Percentage) {
+ if (userSpace) linear->y1 /= loader->svgParse->global.h;
+ else linear->y1 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (!linear->isY2Percentage) {
+ if (userSpace) linear->y2 /= loader->svgParse->global.h;
+ else linear->y2 *= loader->svgParse->global.h;
+ }
+}
+
+
+static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x1 = from->linear->x1;
+ to->linear->isX1Percentage = from->linear->isX1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X1);
+}
+
+
+static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->x2 = from->linear->x2;
+ to->linear->isX2Percentage = from->linear->isX2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::X2);
+}
+
+
+static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y1 = from->linear->y1;
+ to->linear->isY1Percentage = from->linear->isY1Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y1);
+}
+
+
+static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->linear->y2 = from->linear->y2;
+ to->linear->isY2Percentage = from->linear->isY2Percentage;
+ to->flags = (to->flags | SvgGradientFlags::Y2);
+}
+
+
typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
+typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from);
typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
-#define LINEAR_DEF(Name, Name1) \
+#define LINEAR_DEF(Name, Name1, Flag) \
{ \
-#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
+#Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \
}
@@ -2585,12 +2503,15 @@ static constexpr struct
const char* tag;
int sz;
Linear_Method tagHandler;
+ Linear_Inherit_Method tagInheritHandler;
Linear_Method_Recalc tagRecalc;
+ Linear_Method_Recalc tagInheritedRecalc;
+ SvgGradientFlags flag;
} linear_tags[] = {
- LINEAR_DEF(x1, X1),
- LINEAR_DEF(y1, Y1),
- LINEAR_DEF(x2, X2),
- LINEAR_DEF(y2, Y2)
+ LINEAR_DEF(x1, X1, SvgGradientFlags::X1),
+ LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1),
+ LINEAR_DEF(x2, X2, SvgGradientFlags::X2),
+ LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2)
};
@@ -2604,6 +2525,7 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
linear_tags[i].tagHandler(loader, linear, value);
+ grad->flags = (grad->flags | linear_tags[i].flag);
return true;
}
}
@@ -2613,11 +2535,13 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
+ grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits")) {
if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
} else if (!strcmp(key, "gradientTransform")) {
grad->transform = _parseTransformationMatrix(value);
} else {
@@ -2633,6 +2557,7 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
loader->svgParse->styleGrad = grad;
+ grad->flags = SvgGradientFlags::None;
grad->type = SvgGradientType::Linear;
grad->userSpace = false;
grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
@@ -2692,6 +2617,371 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
}
+static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
+{
+ for (uint32_t i = 0; i < src.count; ++i) {
+ dst.push(src.data[i]);
+ }
+}
+
+
+static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ if (!to || !from) return;
+
+ if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) {
+ to->spread = from->spread;
+ to->flags = (to->flags | SvgGradientFlags::SpreadMethod);
+ }
+ bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits);
+ if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) {
+ to->userSpace = from->userSpace;
+ to->flags = (to->flags | SvgGradientFlags::GradientUnits);
+ }
+
+ if (!to->transform && from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) {
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ bool coordSet = to->flags & linear_tags[i].flag;
+ if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) {
+ linear_tags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ linear_tags[i].tagRecalc(loader, to->linear, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace);
+ }
+ }
+ } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) {
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ bool coordSet = (to->flags & radialTags[i].flag);
+ if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) {
+ radialTags[i].tagInheritHandler(to, from);
+ }
+
+ //GradUnits not set directly, coord set
+ if (!gradUnitSet && coordSet) {
+ radialTags[i].tagRecalc(loader, to->radial, to->userSpace);
+ }
+ //GradUnits set, coord not set directly
+ if (to->userSpace == from->userSpace) continue;
+ if (gradUnitSet && !coordSet) {
+ radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace);
+ }
+ }
+ }
+
+ if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops);
+}
+
+
+static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
+{
+ if (!from) return nullptr;
+
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ if (!grad) return nullptr;
+
+ grad->type = from->type;
+ grad->id = from->id ? _copyId(from->id) : nullptr;
+ grad->ref = from->ref ? _copyId(from->ref) : nullptr;
+ grad->spread = from->spread;
+ grad->userSpace = from->userSpace;
+ grad->flags = from->flags;
+
+ if (from->transform) {
+ grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
+ if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (grad->type == SvgGradientType::Linear) {
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) goto error_grad_alloc;
+ memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
+ } else if (grad->type == SvgGradientType::Radial) {
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) goto error_grad_alloc;
+ memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
+ }
+
+ _cloneGradStops(grad->stops, from->stops);
+
+ return grad;
+
+ error_grad_alloc:
+ if (grad) {
+ grad->clear();
+ free(grad);
+ }
+ return nullptr;
+}
+
+
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ if (!(child->flags & SvgStyleFlags::PaintOrder)) {
+ child->paintOrder = parent->paintOrder;
+ }
+ //Fill
+ if (!(child->fill.flags & SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) {
+ if (child->fill.paint.url) free(child->fill.paint.url);
+ child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ }
+ if (!(child->fill.flags & SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!(child->fill.flags & SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!(child->stroke.flags & SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ if (parent->stroke.paint.url) {
+ if (child->stroke.paint.url) free(child->stroke.paint.url);
+ child->stroke.paint.url = _copyId(parent->stroke.paint.url);
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ if (from->flags & SvgStyleFlags::PaintOrder) {
+ to->paintOrder = from->paintOrder;
+ }
+ //Fill
+ to->fill.flags = (to->fill.flags | from->fill.flags);
+ if (from->fill.flags & SvgFillFlags::Paint) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) {
+ if (to->fill.paint.url) free(to->fill.paint.url);
+ to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ }
+ if (from->fill.flags & SvgFillFlags::Opacity) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (from->fill.flags & SvgFillFlags::FillRule) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (to->stroke.flags | from->stroke.flags);
+ if (from->stroke.flags & SvgStrokeFlags::Paint) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ if (from->stroke.paint.url) {
+ if (to->stroke.paint.url) free(to->stroke.paint.url);
+ to->stroke.paint.url = _copyId(from->stroke.paint.url);
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Opacity) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Width) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Dash) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Cap) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (from->stroke.flags & SvgStrokeFlags::Join) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
+static void _copyAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) *to->transform = *from->transform;
+ }
+ //Copy style attribute
+ _styleCopy(to->style, from->style);
+ to->style->flags = (to->style->flags | from->style->flags);
+ if (from->style->clipPath.url) {
+ if (to->style->clipPath.url) free(to->style->clipPath.url);
+ to->style->clipPath.url = strdup(from->style->clipPath.url);
+ }
+ if (from->style->mask.url) {
+ if (to->style->mask.url) free(to->style->mask.url);
+ to->style->mask.url = strdup(from->style->mask.url);
+ }
+
+ //Copy node attribute
+ switch (from->type) {
+ case SvgNodeType::Circle: {
+ to->node.circle.cx = from->node.circle.cx;
+ to->node.circle.cy = from->node.circle.cy;
+ to->node.circle.r = from->node.circle.r;
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ to->node.ellipse.cx = from->node.ellipse.cx;
+ to->node.ellipse.cy = from->node.ellipse.cy;
+ to->node.ellipse.rx = from->node.ellipse.rx;
+ to->node.ellipse.ry = from->node.ellipse.ry;
+ break;
+ }
+ case SvgNodeType::Rect: {
+ to->node.rect.x = from->node.rect.x;
+ to->node.rect.y = from->node.rect.y;
+ to->node.rect.w = from->node.rect.w;
+ to->node.rect.h = from->node.rect.h;
+ to->node.rect.rx = from->node.rect.rx;
+ to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.hasRx = from->node.rect.hasRx;
+ to->node.rect.hasRy = from->node.rect.hasRy;
+ break;
+ }
+ case SvgNodeType::Line: {
+ to->node.line.x1 = from->node.line.x1;
+ to->node.line.y1 = from->node.line.y1;
+ to->node.line.x2 = from->node.line.x2;
+ to->node.line.y2 = from->node.line.y2;
+ break;
+ }
+ case SvgNodeType::Path: {
+ if (from->node.path.path) {
+ if (to->node.path.path) free(to->node.path.path);
+ to->node.path.path = strdup(from->node.path.path);
+ }
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Image: {
+ to->node.image.x = from->node.image.x;
+ to->node.image.y = from->node.image.y;
+ to->node.image.w = from->node.image.w;
+ to->node.image.h = from->node.image.h;
+ if (from->node.image.href) {
+ if (to->node.image.href) free(to->node.image.href);
+ to->node.image.href = strdup(from->node.image.href);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
+{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
+ SvgNode* newNode;
+ if (!from || !parent || from == parent) return;
+
+ newNode = _createNode(parent, from->type);
+ if (!newNode) return;
+
+ _styleInherit(newNode->style, parent->style);
+ _copyAttr(newNode, from);
+
+ auto child = from->child.data;
+ for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
+ _cloneNode(*child, newNode, depth + 1);
+ }
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
+ for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+ auto nodeIdPair = cloneNodes->data[i];
+ auto defs = _getDefsNode(nodeIdPair.node);
+ auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
+ _cloneNode(nodeFrom, nodeIdPair.node, 0);
+ if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
+ nodeIdPair.node->node.use.symbol = nodeFrom;
+ }
+ free(nodeIdPair.id);
+ }
+}
+
+
static constexpr struct
{
const char* tag;
@@ -2738,6 +3028,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
//Parse the empty tag
attrs = content;
while ((attrs != nullptr) && *attrs != '>') attrs++;
+ if (empty) attrs--;
}
if (attrs) {
@@ -2942,7 +3233,7 @@ static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
}
-static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const char* id)
+static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
{
SvgStyleGradient* result = nullptr;
@@ -2960,8 +3251,7 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
gradList = gradients->data;
for (uint32_t i = 0; i < gradients->count; ++i) {
if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) {
- if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops);
- //TODO: Properly inherit other property
+ _inheritGradient(loader, result, *gradList);
break;
}
++gradList;
@@ -2972,27 +3262,33 @@ static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const
}
-static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
+static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
{
if (node->child.count > 0) {
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- _updateGradient(*child, gradients);
+ _updateGradient(loader, *child, gradients);
}
} else {
if (node->style->fill.paint.url) {
- if (node->style->fill.paint.gradient) {
- node->style->fill.paint.gradient->clear();
- free(node->style->fill.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
+ if (newGrad) {
+ if (node->style->fill.paint.gradient) {
+ node->style->fill.paint.gradient->clear();
+ free(node->style->fill.paint.gradient);
+ }
+ node->style->fill.paint.gradient = newGrad;
}
- node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
}
if (node->style->stroke.paint.url) {
- if (node->style->stroke.paint.gradient) {
- node->style->stroke.paint.gradient->clear();
- free(node->style->stroke.paint.gradient);
+ auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
+ if (newGrad) {
+ if (node->style->stroke.paint.gradient) {
+ node->style->stroke.paint.gradient->clear();
+ free(node->style->stroke.paint.gradient);
+ }
+ node->style->stroke.paint.gradient = newGrad;
}
- node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
}
}
}
@@ -3181,7 +3477,8 @@ SvgLoader::~SvgLoader()
void SvgLoader::run(unsigned tid)
{
//According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
- if (renderingDisabled) {
+ if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) {
+ TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
root = Scene::gen();
return;
}
@@ -3199,12 +3496,12 @@ void SvgLoader::run(unsigned tid)
_updateComposite(loaderData.doc, loaderData.doc);
if (defs) _updateComposite(loaderData.doc, defs);
- if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
- if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
-
_updateStyle(loaderData.doc, nullptr);
+
+ if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
}
- root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath);
+ root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
}
@@ -3217,34 +3514,78 @@ bool SvgLoader::header()
if (!loaderData.svgParse) return false;
loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+ viewFlag = SvgViewFlag::None;
simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
- //Return the brief resource info such as viewbox:
- vx = loaderData.doc->node.doc.vx;
- vy = loaderData.doc->node.doc.vy;
- w = vw = loaderData.doc->node.doc.vw;
- h = vh = loaderData.doc->node.doc.vh;
-
- //Override size
- if (loaderData.doc->node.doc.w > 0) {
+ viewFlag = loaderData.doc->node.doc.viewFlag;
+ align = loaderData.doc->node.doc.align;
+ meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
+
+ if (viewFlag & SvgViewFlag::Viewbox) {
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
+
+ if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
+ else {
+ w = loaderData.doc->node.doc.vw;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w *= loaderData.doc->node.doc.w;
+ viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Width);
+ }
+ if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
+ else {
+ h = loaderData.doc->node.doc.vh;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h *= loaderData.doc->node.doc.h;
+ viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
+ }
+ viewFlag = (viewFlag | SvgViewFlag::Height);
+ }
+ //In case no viewbox and width/height data is provided the completion of loading
+ //has to be forced, in order to establish this data based on the whole picture.
+ } else {
+ //Before loading, set default viewbox & size if they are empty
+ vx = vy = 0.0f;
+ if (viewFlag & SvgViewFlag::Width) {
+ vw = w = loaderData.doc->node.doc.w;
+ } else {
+ vw = 1.0f;
+ if (viewFlag & SvgViewFlag::WidthInPercent) {
+ w = loaderData.doc->node.doc.w;
+ } else w = 1.0f;
+ }
+
+ if (viewFlag & SvgViewFlag::Height) {
+ vh = h = loaderData.doc->node.doc.h;
+ } else {
+ vh = 1.0f;
+ if (viewFlag & SvgViewFlag::HeightInPercent) {
+ h = loaderData.doc->node.doc.h;
+ } else h = 1.0f;
+ }
+
+ run(0);
+
+ //Override viewbox & size again after svg loading.
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ vw = loaderData.doc->node.doc.vw;
+ vh = loaderData.doc->node.doc.vh;
w = loaderData.doc->node.doc.w;
- if (vw < FLT_EPSILON) vw = w;
- }
- if (loaderData.doc->node.doc.h > 0) {
h = loaderData.doc->node.doc.h;
- if (vh < FLT_EPSILON) vh = h;
}
- align = loaderData.doc->node.doc.align;
- meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
- } else {
- TVGLOG("SVG", "No SVG File. There is no <svg/>");
- return false;
+ return true;
}
- return true;
+ TVGLOG("SVG", "No SVG File. There is no <svg/>");
+ return false;
}
@@ -3304,6 +3645,9 @@ bool SvgLoader::read()
{
if (!content || size == 0) return false;
+ //the loading has been already completed in header()
+ if (root) return true;
+
TaskScheduler::request(this);
return true;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
index c6fdde55af..5c74184ec8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SVG_LOADER_H_
#define _TVG_SVG_LOADER_H_
@@ -50,9 +51,9 @@ public:
unique_ptr<Paint> paint() override;
private:
+ SvgViewFlag viewFlag = SvgViewFlag::None;
AspectRatioAlign align = AspectRatioAlign::XMidYMid;
AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
- bool renderingDisabled = false;
bool header();
void clear();
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index 3588cabf0b..dec9fadebe 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_SVG_LOADER_COMMON_H_
#define _TVG_SVG_LOADER_COMMON_H_
@@ -79,6 +80,16 @@ enum class SvgFillFlags
ClipPath = 0x16
};
+constexpr bool operator &(SvgFillFlags a, SvgFillFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b)
+{
+ return SvgFillFlags(int(a) | int(b));
+}
+
enum class SvgStrokeFlags
{
Paint = 0x1,
@@ -91,6 +102,17 @@ enum class SvgStrokeFlags
Dash = 0x80,
};
+constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b)
+{
+ return SvgStrokeFlags(int(a) | int(b));
+}
+
+
enum class SvgGradientType
{
Linear,
@@ -114,9 +136,20 @@ enum class SvgStyleFlags
ClipPath = 0x1000,
Mask = 0x2000,
MaskType = 0x4000,
- Display = 0x8000
+ Display = 0x8000,
+ PaintOrder = 0x10000
};
+constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b)
+{
+ return SvgStyleFlags(int(a) | int(b));
+}
+
enum class SvgStopStyleFlags
{
StopDefault = 0x0,
@@ -124,6 +157,42 @@ enum class SvgStopStyleFlags
StopColor = 0x02
};
+constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b)
+{
+ return SvgStopStyleFlags(int(a) | int(b));
+}
+
+enum class SvgGradientFlags
+{
+ None = 0x0,
+ GradientUnits = 0x1,
+ SpreadMethod = 0x2,
+ X1 = 0x4,
+ X2 = 0x8,
+ Y1 = 0x10,
+ Y2 = 0x20,
+ Cx = 0x40,
+ Cy = 0x80,
+ R = 0x100,
+ Fx = 0x200,
+ Fy = 0x400
+};
+
+constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
+{
+ return int(a) & int(b);
+}
+
+constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b)
+{
+ return SvgGradientFlags(int(a) | int(b));
+}
+
enum class SvgFillRule
{
Winding = 0,
@@ -145,6 +214,31 @@ enum class SvgParserLengthType
Other
};
+enum class SvgViewFlag
+{
+ None = 0x0,
+ Width = 0x01, //viewPort width
+ Height = 0x02, //viewPort height
+ Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set
+ WidthInPercent = 0x08,
+ HeightInPercent = 0x10
+};
+
+constexpr bool operator &(SvgViewFlag a, SvgViewFlag b)
+{
+ return static_cast<int>(a) & static_cast<int>(b);
+}
+
+constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b)
+{
+ return SvgViewFlag(int(a) | int(b));
+}
+
+constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b)
+{
+ return SvgViewFlag(int(a) ^ int(b));
+}
+
enum class AspectRatioAlign
{
None,
@@ -167,12 +261,13 @@ enum class AspectRatioMeetOrSlice
struct SvgDocNode
{
- float w;
- float h;
+ float w; //unit: point or in percentage see: SvgViewFlag
+ float h; //unit: point or in percentage see: SvgViewFlag
float vx;
float vy;
float vw;
float vh;
+ SvgViewFlag viewFlag;
SvgNode* defs;
SvgNode* style;
AspectRatioAlign align;
@@ -339,6 +434,7 @@ struct SvgStyleGradient
SvgLinearGradient* linear;
Matrix* transform;
Array<Fill::ColorStop> stops;
+ SvgGradientFlags flags;
bool userSpace;
void clear()
@@ -384,6 +480,7 @@ struct SvgStyleProperty
SvgColor color;
bool curColorSet;
char* cssClass;
+ bool paintOrder; //true if default (fill, stroke), false otherwise
SvgStyleFlags flags;
};
@@ -456,6 +553,11 @@ struct SvgLoaderData
bool style = false;
};
+struct Box
+{
+ float x, y, w, h;
+};
+
/*
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
*
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index a09a2797d0..e044931b51 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -52,7 +52,6 @@
#include <cstring>
#include <math.h>
-#include <clocale>
#include <ctype.h>
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
@@ -545,11 +544,6 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point
char cmd = 0;
bool isQuadratic = false;
char* path = (char*)svgPath;
- char* curLocale;
-
- curLocale = setlocale(LC_NUMERIC, NULL);
- if (curLocale) curLocale = strdup(curLocale);
- setlocale(LC_NUMERIC, "POSIX");
while ((path[0] != '\0')) {
path = _nextCommand(path, &cmd, numberArray, &numberCount);
@@ -557,8 +551,5 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
}
- setlocale(LC_NUMERIC, curLocale);
- if (curLocale) free(curLocale);
-
return true;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
index 8f5f9035dc..4199088dc1 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index 254ee2d008..c5e5df893e 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -61,12 +61,6 @@
/* Internal Class Implementation */
/************************************************************************/
-struct Box
-{
- float x, y, w, h;
-};
-
-
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
@@ -255,7 +249,6 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->clipPath.applying = true;
auto comp = Shape::gen();
- if (node->transform) comp->transform(*node->transform);
auto child = compNode->child.data;
auto valid = false; //Composite only when valid shapes are existed
@@ -264,6 +257,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
}
+ if (node->transform) {
+ auto m = comp->transform();
+ m = mathMultiply(node->transform, &m);
+ comp->transform(m);
+ }
+
if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
node->style->clipPath.applying = false;
@@ -331,6 +330,8 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
//Apply the fill rule
vg->fill((tvg::FillRule)style->fill.fillRule);
+ //Rendering order
+ vg->order(!style->paintOrder);
//Apply node opacity
if (style->opacity < 255) vg->opacity(style->opacity);
@@ -444,9 +445,11 @@ enum class imageMimeTypeEncoding
base64 = 0x1,
utf8 = 0x2
};
+
constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b));
}
+
constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
return (static_cast<int>(a) & static_cast<int>(b));
}
@@ -755,24 +758,45 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
}
+static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag)
+{
+ bool validWidth = (viewFlag & SvgViewFlag::Width);
+ bool validHeight = (viewFlag & SvgViewFlag::Height);
+
+ float x, y;
+ scene->bounds(&x, &y, &vBox.w, &vBox.h, false);
+ if (!validWidth && !validHeight) {
+ vBox.x = x;
+ vBox.y = y;
+ } else {
+ if (validWidth) vBox.w = w;
+ if (validHeight) vBox.h = h;
+ }
+
+ //the size would have 1x1 or percentage values.
+ if (!validWidth) w *= vBox.w;
+ if (!validHeight) h *= vBox.h;
+}
+
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath)
+unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag)
{
//TODO: aspect ratio is valid only if viewBox was set
- if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
+ if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr;
- Box vBox = {vx, vy, vw, vh};
- auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0);
+ auto docNode = _sceneBuildHelper(loaderData.doc, vBox, svgPath, false, 0);
- if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
+ if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
+
+ if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
docNode->transform(m);
- } else if (!mathZero(vx) || !mathZero(vy)) {
- docNode->translate(-vx, -vy);
+ } else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
+ docNode->translate(-vBox.x, -vBox.y);
}
auto viewBoxClip = Shape::gen();
@@ -786,5 +810,12 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo
auto root = Scene::gen();
root->push(move(compositeLayer));
+ loaderData.doc->node.doc.vx = vBox.x;
+ loaderData.doc->node.doc.vy = vBox.y;
+ loaderData.doc->node.doc.vw = vBox.w;
+ loaderData.doc->node.doc.vh = vBox.h;
+ loaderData.doc->node.doc.w = w;
+ loaderData.doc->node.doc.h = h;
+
return root;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
index 311f3c80e6..f6a60f850d 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +25,6 @@
#include "tvgCommon.h"
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath);
+unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag);
#endif //_TVG_SVG_SCENE_BUILDER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 7fb108bc21..12d20c9731 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -94,8 +94,8 @@ float svgUtilStrtof(const char *nPtr, char **endPtr)
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
else goto error;
- if (tolower(*(iter + 3)) == 'i') {
- if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5;
+ if (tolower(*(iter)) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
else goto error;
}
if (endPtr) *endPtr = (char *)(iter);
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
index b5e6e1bdb2..6f94367aeb 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index 0e2c3fa141..77467e071b 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
index e2761ca8da..7333bb09fb 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
index 01a39b6e17..2b89d6bb59 100644
--- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <memory.h>
#ifdef _WIN32
@@ -357,27 +358,45 @@ static bool _parsePicture(TvgBinBlock block, Paint* paint)
{
auto picture = static_cast<Picture*>(paint);
- //Case1: Image Picture
- if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) {
- if (block.length < 2 * SIZE(uint32_t)) return false;
+ switch (block.type) {
+ case TVG_TAG_PICTURE_RAW_IMAGE: {
+ if (block.length < 2 * SIZE(uint32_t)) return false;
- auto ptr = block.data;
- uint32_t w, h;
+ auto ptr = block.data;
+ uint32_t w, h;
- READ_UI32(&w, ptr);
- ptr += SIZE(uint32_t);
- READ_UI32(&h, ptr);
- ptr += SIZE(uint32_t);
+ READ_UI32(&w, ptr);
+ ptr += SIZE(uint32_t);
+ READ_UI32(&h, ptr);
+ ptr += SIZE(uint32_t);
- auto size = w * h * SIZE(uint32_t);
- if (block.length != 2 * SIZE(uint32_t) + size) return false;
+ auto size = w * h * SIZE(uint32_t);
+ if (block.length != 2 * SIZE(uint32_t) + size) return false;
- picture->load((uint32_t*) ptr, w, h, true);
- return true;
- }
+ picture->load((uint32_t*) ptr, w, h, true);
- //Case2: Base Paint Properties
- if (_parsePaintProperty(block, picture)) return true;
+ return true;
+ }
+ case TVG_TAG_PICTURE_MESH: {
+ if (block.length < 1 * SIZE(uint32_t)) return false;
+
+ auto ptr = block.data;
+ uint32_t meshCnt;
+ READ_UI32(&meshCnt, ptr);
+ ptr += SIZE(uint32_t);
+
+ auto size = meshCnt * SIZE(Polygon);
+ if (block.length != SIZE(uint32_t) + size) return false;
+
+ picture->mesh((Polygon*) ptr, meshCnt);
+
+ return true;
+ }
+ //Base Paint Properties
+ default: {
+ if (_parsePaintProperty(block, picture)) return true;
+ }
+ }
//Vector Picture won't be requested since Saver replaces it with the Scene
return false;
@@ -414,7 +433,7 @@ static Paint* _parsePaint(TvgBinBlock baseBlock)
auto ptr = baseBlock.data;
- //2. Read Subsquent properties of the current paint.
+ //2. Read Subsequent properties of the current paint.
while (ptr < baseBlock.end) {
auto block = _readBlock(ptr);
if (block.end > baseBlock.end) return paint;
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
index a0762d0fcc..29fa1025b0 100644
--- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
index 95d629d1f6..82c578e6d4 100644
--- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include <memory.h>
#include <fstream>
#include "tvgLoader.h"
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
index 3ae841aa85..b98dff83b0 100644
--- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
index 57a21dcce1..1098570140 100644
--- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#include "tvgMath.h"
#include "tvgSaveModule.h"
#include "tvgTvgSaver.h"
@@ -169,7 +170,7 @@ bool TvgSaver::saveEncoding(const std::string& path)
memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
//Good optimization, flush to file.
- auto fp = _fopen(path.c_str(), "w+");
+ auto fp = _fopen(path.c_str(), "wb+");
if (!fp) goto fail;
//write header
@@ -192,7 +193,7 @@ fail:
bool TvgSaver::flushTo(const std::string& path)
{
- auto fp = _fopen(path.c_str(), "w+");
+ auto fp = _fopen(path.c_str(), "wb+");
if (!fp) return false;
if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
@@ -355,7 +356,7 @@ TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child,
TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
{
- auto it = this->iterator(scene);
+ auto it = IteratorAccessor::iterator(scene);
if (it->count() == 0) {
delete(it);
return 0;
@@ -567,7 +568,7 @@ TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransf
/* Picture has either a vector scene or a bitmap. */
TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
{
- auto it = this->iterator(picture);
+ auto it = IteratorAccessor::iterator(picture);
//Case - Vector Scene:
if (it->count() == 1) {
@@ -609,6 +610,20 @@ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* p
cnt += writeData(pixels, imgSize);
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+ //mesh: currently only available in bitmap image.
+ const Polygon* triangles = nullptr;
+ auto triangleCnt = picture->mesh(&triangles);
+ if (triangles && triangleCnt > 0) {
+ TvgBinCounter triangleCntSize = SIZE(triangleCnt);
+ TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]);
+
+ writeTag(TVG_TAG_PICTURE_MESH);
+ writeCount(triangleCntSize + trianglesSize);
+ cnt += writeData(&triangleCnt, triangleCntSize);
+ cnt += writeData(triangles, trianglesSize);
+ cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+ }
+
//Bitmap picture needs the transform info.
cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
index 4acb35e76a..0824475923 100644
--- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
#ifndef _TVG_TVGSAVER_H_
#define _TVG_TVGSAVER_H_
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index 34bb3b8e59..4eb7bf47ed 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,9 +1,9 @@
-VERSION=0.8.4
+VERSION=0.9.0
rm -rf AUTHORS inc LICENSE src *.zip
curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.zip
bsdtar --strip-components=1 -xvf *.zip
rm *.zip
-rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh
+rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh CODEOWNERS
find . -type f -name 'meson.build' -delete
rm -rf src/bin src/bindings src/examples src/wasm
rm -rf src/lib/gl_engine src/loaders/external_jpg src/loaders/png