summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig10
-rw-r--r--.github/workflows/static_checks.yml6
-rw-r--r--CHANGELOG.md5
-rw-r--r--CONTRIBUTING.md17
-rw-r--r--README.md2
-rw-r--r--SConstruct44
-rw-r--r--core/config/project_settings.cpp77
-rw-r--r--core/config/project_settings.h11
-rw-r--r--core/core_bind.cpp17
-rw-r--r--core/core_bind.h5
-rw-r--r--core/core_constants.cpp153
-rw-r--r--core/core_constants.h1
-rw-r--r--core/doc_data.h5
-rw-r--r--core/error/error_macros.h14
-rw-r--r--core/extension/extension_api_dump.cpp2
-rw-r--r--core/input/input.cpp25
-rw-r--r--core/input/input.h6
-rw-r--r--core/input/input_builders.py2
-rw-r--r--core/input/input_enums.h59
-rw-r--r--core/input/input_event.cpp67
-rw-r--r--core/input/input_event.h8
-rw-r--r--core/io/config_file.cpp4
-rw-r--r--core/io/file_access_compressed.cpp16
-rw-r--r--core/io/file_access_compressed.h3
-rw-r--r--core/io/resource_format_binary.cpp122
-rw-r--r--core/io/resource_format_binary.h2
-rw-r--r--core/io/resource_importer.cpp16
-rw-r--r--core/io/resource_importer.h8
-rw-r--r--core/io/resource_saver.cpp24
-rw-r--r--core/io/resource_saver.h4
-rw-r--r--core/math/a_star_grid_2d.cpp2
-rw-r--r--core/math/geometry_3d.cpp2
-rw-r--r--core/math/plane.h2
-rw-r--r--core/object/method_bind.h2
-rw-r--r--core/object/object.cpp19
-rw-r--r--core/object/object.h21
-rw-r--r--core/object/script_language.cpp42
-rw-r--r--core/object/script_language_extension.h3
-rw-r--r--core/os/keyboard.cpp3
-rw-r--r--core/os/keyboard.h5
-rw-r--r--core/os/os.h5
-rw-r--r--core/register_core_types.cpp7
-rw-r--r--core/templates/safe_refcount.h20
-rw-r--r--core/variant/array.cpp10
-rw-r--r--core/variant/binder_common.h39
-rw-r--r--core/variant/callable.cpp39
-rw-r--r--core/variant/callable.h5
-rw-r--r--core/variant/callable_bind.cpp60
-rw-r--r--core/variant/callable_bind.h5
-rw-r--r--core/variant/dictionary.cpp10
-rw-r--r--core/variant/type_info.h9
-rw-r--r--core/variant/variant.cpp92
-rw-r--r--core/variant/variant_call.cpp4
-rw-r--r--core/variant/variant_parser.cpp21
-rw-r--r--core/variant/variant_setget.cpp4
-rw-r--r--doc/classes/@GlobalScope.xml160
-rw-r--r--doc/classes/AnimatedSprite2D.xml1
-rw-r--r--doc/classes/AnimationPlayer.xml12
-rw-r--r--doc/classes/Array.xml2
-rw-r--r--doc/classes/ArrayMesh.xml4
-rw-r--r--doc/classes/BaseButton.xml4
-rw-r--r--doc/classes/BoneAttachment3D.xml31
-rw-r--r--doc/classes/Callable.xml14
-rw-r--r--doc/classes/CanvasItem.xml18
-rw-r--r--doc/classes/CollisionObject3D.xml6
-rw-r--r--doc/classes/Control.xml86
-rw-r--r--doc/classes/ConvexPolygonShape3D.xml2
-rw-r--r--doc/classes/DTLSServer.xml4
-rw-r--r--doc/classes/DisplayServer.xml9
-rw-r--r--doc/classes/EditorExportPlatform.xml3
-rw-r--r--doc/classes/EditorFileSystem.xml5
-rw-r--r--doc/classes/EditorPlugin.xml8
-rw-r--r--doc/classes/EditorSettings.xml6
-rw-r--r--doc/classes/EditorSpinSlider.xml2
-rw-r--r--doc/classes/EditorUndoRedoManager.xml2
-rw-r--r--doc/classes/GeometryInstance3D.xml5
-rw-r--r--doc/classes/GraphNode.xml20
-rw-r--r--doc/classes/Image.xml4
-rw-r--r--doc/classes/Input.xml4
-rw-r--r--doc/classes/InputEventAction.xml1
-rw-r--r--doc/classes/InputEventMouse.xml2
-rw-r--r--doc/classes/JavaScriptBridge.xml9
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/Mesh.xml50
-rw-r--r--doc/classes/MeshInstance2D.xml4
-rw-r--r--doc/classes/MultiMeshInstance2D.xml4
-rw-r--r--doc/classes/NavigationAgent2D.xml11
-rw-r--r--doc/classes/NavigationAgent3D.xml1
-rw-r--r--doc/classes/NavigationLink2D.xml1
-rw-r--r--doc/classes/NavigationLink3D.xml1
-rw-r--r--doc/classes/NavigationMesh.xml1
-rw-r--r--doc/classes/NavigationMeshGenerator.xml1
-rw-r--r--doc/classes/NavigationObstacle2D.xml1
-rw-r--r--doc/classes/NavigationObstacle3D.xml1
-rw-r--r--doc/classes/NavigationPathQueryParameters2D.xml1
-rw-r--r--doc/classes/NavigationPathQueryParameters3D.xml1
-rw-r--r--doc/classes/NavigationPathQueryResult2D.xml1
-rw-r--r--doc/classes/NavigationPathQueryResult3D.xml1
-rw-r--r--doc/classes/NavigationPolygon.xml1
-rw-r--r--doc/classes/NavigationRegion2D.xml1
-rw-r--r--doc/classes/NavigationRegion3D.xml1
-rw-r--r--doc/classes/NavigationServer2D.xml69
-rw-r--r--doc/classes/NavigationServer3D.xml111
-rw-r--r--doc/classes/Node.xml6
-rw-r--r--doc/classes/OS.xml17
-rw-r--r--doc/classes/Object.xml1
-rw-r--r--doc/classes/Performance.xml72
-rw-r--r--doc/classes/PhysicsDirectBodyState2D.xml7
-rw-r--r--doc/classes/PhysicsDirectBodyState2DExtension.xml6
-rw-r--r--doc/classes/PhysicsDirectBodyState3D.xml4
-rw-r--r--doc/classes/PhysicsDirectBodyState3DExtension.xml2
-rw-r--r--doc/classes/PhysicsDirectSpaceState2DExtension.xml6
-rw-r--r--doc/classes/PhysicsDirectSpaceState3DExtension.xml6
-rw-r--r--doc/classes/PhysicsServer2DExtension.xml12
-rw-r--r--doc/classes/PhysicsServer3DExtension.xml12
-rw-r--r--doc/classes/PhysicsServer3DManager.xml2
-rw-r--r--doc/classes/Plane.xml17
-rw-r--r--doc/classes/ProjectSettings.xml22
-rw-r--r--doc/classes/Range.xml2
-rw-r--r--doc/classes/RenderingServer.xml69
-rw-r--r--doc/classes/ResourceFormatSaver.xml8
-rw-r--r--doc/classes/ResourcePreloader.xml4
-rw-r--r--doc/classes/SceneTree.xml12
-rw-r--r--doc/classes/ShaderMaterial.xml3
-rw-r--r--doc/classes/Skeleton2D.xml2
-rw-r--r--doc/classes/Skeleton3D.xml96
-rw-r--r--doc/classes/SkeletonModification2D.xml2
-rw-r--r--doc/classes/SkeletonModification3D.xml66
-rw-r--r--doc/classes/SkeletonModification3DCCDIK.xml136
-rw-r--r--doc/classes/SkeletonModification3DFABRIK.xml161
-rw-r--r--doc/classes/SkeletonModification3DJiggle.xml199
-rw-r--r--doc/classes/SkeletonModification3DLookAt.xml64
-rw-r--r--doc/classes/SkeletonModification3DStackHolder.xml27
-rw-r--r--doc/classes/SkeletonModification3DTwoBoneIK.xml191
-rw-r--r--doc/classes/SkeletonModificationStack3D.xml88
-rw-r--r--doc/classes/SkeletonProfileHumanoid.xml1
-rw-r--r--doc/classes/SpinBox.xml2
-rw-r--r--doc/classes/SpriteFrames.xml21
-rw-r--r--doc/classes/TabBar.xml6
-rw-r--r--doc/classes/TextureProgressBar.xml2
-rw-r--r--doc/classes/TextureRect.xml24
-rw-r--r--doc/classes/TileMap.xml2
-rw-r--r--doc/classes/Timer.xml2
-rw-r--r--doc/classes/Transform3D.xml1
-rw-r--r--doc/classes/Tree.xml10
-rw-r--r--doc/classes/TreeItem.xml42
-rw-r--r--doc/classes/Tween.xml10
-rw-r--r--doc/classes/UndoRedo.xml2
-rw-r--r--doc/classes/VScrollBar.xml4
-rw-r--r--doc/classes/VSlider.xml4
-rw-r--r--doc/classes/Variant.xml4
-rw-r--r--doc/classes/VisualShader.xml24
-rw-r--r--doc/classes/VisualShaderNodeConstant.xml1
-rw-r--r--doc/classes/VisualShaderNodeDistanceFade.xml2
-rw-r--r--doc/classes/VisualShaderNodeIntParameter.xml12
-rw-r--r--doc/classes/VisualShaderNodeLinearSceneDepth.xml2
-rw-r--r--doc/classes/VisualShaderNodeParameter.xml4
-rw-r--r--doc/classes/VisualShaderNodeParticleAccelerator.xml6
-rw-r--r--doc/classes/VisualShaderNodeParticleBoxEmitter.xml2
-rw-r--r--doc/classes/VisualShaderNodeParticleConeVelocity.xml2
-rw-r--r--doc/classes/VisualShaderNodeParticleEmit.xml8
-rw-r--r--doc/classes/VisualShaderNodeParticleEmitter.xml1
-rw-r--r--doc/classes/VisualShaderNodeParticleMeshEmitter.xml5
-rw-r--r--doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml3
-rw-r--r--doc/classes/VisualShaderNodeParticleOutput.xml2
-rw-r--r--doc/classes/VisualShaderNodeParticleRandomness.xml7
-rw-r--r--doc/classes/VisualShaderNodeParticleRingEmitter.xml2
-rw-r--r--doc/classes/VisualShaderNodeParticleSphereEmitter.xml2
-rw-r--r--doc/classes/VisualShaderNodeProximityFade.xml2
-rw-r--r--doc/classes/VisualShaderNodeRandomRange.xml2
-rw-r--r--doc/classes/VisualShaderNodeRemap.xml2
-rw-r--r--doc/classes/VisualShaderNodeTexture2DArrayParameter.xml2
-rw-r--r--doc/classes/VisualShaderNodeTextureParameter.xml12
-rw-r--r--doc/classes/VisualShaderNodeUIntParameter.xml4
-rw-r--r--doc/classes/VisualShaderNodeUVFunc.xml1
-rw-r--r--doc/classes/VisualShaderNodeUVPolarCoord.xml2
-rw-r--r--doc/classes/VisualShaderNodeVarying.xml4
-rw-r--r--doc/classes/VisualShaderNodeVaryingGetter.xml2
-rw-r--r--doc/classes/VisualShaderNodeVaryingSetter.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorBase.xml5
-rw-r--r--doc/classes/Window.xml38
-rw-r--r--doc/classes/bool.xml2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h3
-rw-r--r--drivers/gles3/shaders/canvas.glsl6
-rw-r--r--drivers/gles3/shaders/canvas_uniforms_inc.glsl3
-rw-r--r--drivers/gles3/storage/material_storage.cpp8
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.cpp3
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.h2
-rw-r--r--drivers/gles3/storage/texture_storage.cpp64
-rw-r--r--drivers/unix/dir_access_unix.cpp4
-rw-r--r--drivers/unix/os_unix.cpp29
-rw-r--r--drivers/unix/os_unix.h6
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp3
-rw-r--r--editor/action_map_editor.cpp2
-rw-r--r--editor/animation_bezier_editor.cpp16
-rw-r--r--editor/animation_track_editor.cpp78
-rw-r--r--editor/animation_track_editor_plugins.cpp5
-rw-r--r--editor/array_property_edit.cpp7
-rw-r--r--editor/code_editor.cpp3
-rw-r--r--editor/connections_dialog.cpp6
-rw-r--r--editor/create_dialog.cpp7
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_parser.cpp2
-rw-r--r--editor/debugger/editor_debugger_node.cpp2
-rw-r--r--editor/debugger/editor_profiler.cpp4
-rw-r--r--editor/debugger/editor_visual_profiler.cpp4
-rw-r--r--editor/dictionary_property_edit.cpp5
-rw-r--r--editor/doc_tools.cpp1
-rw-r--r--editor/editor_about.cpp4
-rw-r--r--editor/editor_asset_installer.cpp1
-rw-r--r--editor/editor_audio_buses.cpp40
-rw-r--r--editor/editor_autoload_settings.cpp12
-rw-r--r--editor/editor_data.cpp51
-rw-r--r--editor/editor_data.h4
-rw-r--r--editor/editor_file_system.cpp10
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_inspector.cpp79
-rw-r--r--editor/editor_inspector.h1
-rw-r--r--editor/editor_locale_dialog.cpp9
-rw-r--r--editor/editor_node.cpp139
-rw-r--r--editor/editor_node.h7
-rw-r--r--editor/editor_plugin.cpp4
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/editor_properties.cpp293
-rw-r--r--editor/editor_properties.h34
-rw-r--r--editor/editor_properties_array_dict.cpp14
-rw-r--r--editor/editor_properties_array_dict.h2
-rw-r--r--editor/editor_property_name_processor.cpp1
-rw-r--r--editor/editor_resource_picker.cpp4
-rw-r--r--editor/editor_run_native.cpp98
-rw-r--r--editor/editor_run_native.h7
-rw-r--r--editor/editor_settings_dialog.cpp10
-rw-r--r--editor/editor_spin_slider.cpp17
-rw-r--r--editor/editor_spin_slider.h2
-rw-r--r--editor/editor_themes.cpp40
-rw-r--r--editor/editor_title_bar.cpp2
-rw-r--r--editor/editor_toaster.cpp49
-rw-r--r--editor/editor_toaster.h2
-rw-r--r--editor/editor_undo_redo_manager.cpp12
-rw-r--r--editor/editor_undo_redo_manager.h11
-rw-r--r--editor/event_listener_line_edit.cpp4
-rw-r--r--editor/event_listener_line_edit.h2
-rw-r--r--editor/export/editor_export.cpp6
-rw-r--r--editor/export/editor_export_platform.cpp236
-rw-r--r--editor/export/editor_export_platform.h10
-rw-r--r--editor/export/editor_export_platform_pc.h1
-rw-r--r--editor/export/editor_export_plugin.cpp5
-rw-r--r--editor/export/project_export.cpp4
-rw-r--r--editor/fbx_importer_manager.cpp8
-rw-r--r--editor/filesystem_dock.cpp19
-rw-r--r--editor/groups_editor.cpp14
-rw-r--r--editor/history_dock.cpp2
-rw-r--r--editor/history_dock.h2
-rw-r--r--editor/icons/PlayRemote.svg1
-rw-r--r--editor/icons/README.md2
-rw-r--r--editor/import/resource_importer_obj.cpp36
-rw-r--r--editor/import/resource_importer_shader_file.cpp1
-rw-r--r--editor/import/scene_import_settings.cpp22
-rw-r--r--editor/input_event_configuration_dialog.cpp1
-rw-r--r--editor/inspector_dock.cpp46
-rw-r--r--editor/inspector_dock.h2
-rw-r--r--editor/localization_editor.cpp19
-rw-r--r--editor/multi_node_edit.cpp2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp12
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp16
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp20
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp28
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h1
-rw-r--r--editor/plugins/animation_library_editor.cpp24
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp52
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp24
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp31
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/audio_stream_randomizer_editor_plugin.cpp4
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp63
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h1
-rw-r--r--editor/plugins/cast_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/control_editor_plugin.cpp7
-rw-r--r--editor/plugins/curve_editor_plugin.cpp14
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/gradient_editor.cpp2
-rw-r--r--editor/plugins/gradient_texture_2d_editor_plugin.cpp6
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/line_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/material_editor_plugin.cpp6
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp2
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp16
-rw-r--r--editor/plugins/navigation_link_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.cpp8
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp44
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp30
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp10
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp12
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp8
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp10
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp12
-rw-r--r--editor/plugins/script_editor_plugin.cpp61
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/plugins/shader_editor_plugin.cpp4
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp14
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp8
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp38
-rw-r--r--editor/plugins/text_editor.cpp2
-rw-r--r--editor/plugins/text_shader_editor.cpp4
-rw-r--r--editor/plugins/texture_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp17
-rw-r--r--editor/plugins/theme_editor_plugin.cpp48
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp81
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp30
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp120
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp66
-rw-r--r--editor/plugins/tiles/tile_proxies_manager_dialog.cpp13
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp143
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h2
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp20
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp8
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp27
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp168
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h7
-rw-r--r--editor/progress_dialog.cpp22
-rw-r--r--editor/project_converter_3_to_4.cpp7
-rw-r--r--editor/project_manager.cpp14
-rw-r--r--editor/project_settings_editor.cpp16
-rw-r--r--editor/rename_dialog.cpp2
-rw-r--r--editor/scene_create_dialog.cpp2
-rw-r--r--editor/scene_tree_dock.cpp48
-rw-r--r--editor/scene_tree_editor.cpp12
-rw-r--r--editor/shader_globals_editor.cpp6
-rw-r--r--main/main.cpp28
-rw-r--r--main/performance.cpp57
-rw-r--r--main/performance.h12
-rw-r--r--methods.py40
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj12
-rw-r--r--misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist10
-rw-r--r--misc/hooks/asmessage.applescript59
-rwxr-xr-xmisc/hooks/pre-commit-black19
-rwxr-xr-xmisc/hooks/pre-commit-clang-format19
-rwxr-xr-xmisc/scripts/file_format.sh18
-rw-r--r--modules/csg/editor/csg_gizmos.cpp8
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp5
-rw-r--r--modules/gdscript/gdscript.cpp81
-rw-r--r--modules/gdscript/gdscript.h14
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp776
-rw-r--r--modules/gdscript/gdscript_analyzer.h10
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp146
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h5
-rw-r--r--modules/gdscript/gdscript_codegen.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp50
-rw-r--r--modules/gdscript/gdscript_editor.cpp33
-rw-r--r--modules/gdscript/gdscript_parser.cpp116
-rw-r--r--modules/gdscript/gdscript_parser.h6
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp1
-rw-r--r--modules/gdscript/tests/README.md2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd20
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out (renamed from modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd (renamed from modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd)2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd112
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out55
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd86
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd50
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd41
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd62
-rw-r--r--modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd48
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_name.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_callable.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd76
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd40
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_assert.gd24
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_assert.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd17
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_on_void.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_on_void.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd51
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out14
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.gd20
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd24
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd45
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out3
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml8
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp10
-rw-r--r--modules/gridmap/grid_map.cpp10
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs44
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp40
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs217
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs58
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs72
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs110
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs107
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs36
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs22
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp4
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml16
-rw-r--r--modules/multiplayer/doc_classes/SceneMultiplayer.xml1
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp8
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp38
-rw-r--r--modules/multiplayer/multiplayer_spawner.h7
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp1
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp48
-rw-r--r--modules/multiplayer/scene_replication_interface.h7
-rw-r--r--modules/navigation/godot_navigation_server.cpp177
-rw-r--r--modules/navigation/godot_navigation_server.h38
-rw-r--r--modules/navigation/nav_map.cpp33
-rw-r--r--modules/navigation/nav_map.h20
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp6
-rw-r--r--modules/openxr/SCsub1
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp25
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.h2
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.h2
-rw-r--r--modules/openxr/extensions/openxr_android_extension.cpp12
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h2
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp1
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.cpp98
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.h (renamed from scene/resources/skeleton_modification_3d_stackholder.h)33
-rw-r--r--modules/openxr/openxr_api.cpp8
-rw-r--r--modules/openxr/openxr_api.h1
-rw-r--r--modules/openxr/register_types.cpp2
-rw-r--r--modules/openxr/util.h6
-rw-r--r--modules/raycast/SCsub2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml4
-rw-r--r--platform/android/README.md2
-rw-r--r--platform/android/android_input_handler.cpp40
-rw-r--r--platform/android/android_input_handler.h12
-rw-r--r--platform/android/display_server_android.cpp17
-rw-r--r--platform/android/display_server_android.h2
-rw-r--r--platform/android/export/export_plugin.cpp25
-rw-r--r--platform/android/java_godot_lib_jni.cpp10
-rw-r--r--platform/android/logo.pngbin968 -> 0 bytes
-rw-r--r--platform/android/logo.svg1
-rw-r--r--platform/android/run_icon.pngbin324 -> 0 bytes
-rw-r--r--platform/android/run_icon.svg1
-rw-r--r--platform/ios/README.md2
-rw-r--r--platform/ios/device_metrics.m242
-rw-r--r--platform/ios/display_server_ios.mm25
-rw-r--r--platform/ios/export/export_plugin.cpp39
-rw-r--r--platform/ios/export/export_plugin.h1
-rw-r--r--platform/ios/logo.pngbin1297 -> 0 bytes
-rw-r--r--platform/ios/logo.svg1
-rw-r--r--platform/linuxbsd/README.md2
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp40
-rw-r--r--platform/linuxbsd/detect.py11
-rw-r--r--platform/linuxbsd/export/export.cpp1
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp344
-rw-r--r--platform/linuxbsd/export/export_plugin.h45
-rw-r--r--platform/linuxbsd/joypad_linux.cpp24
-rw-r--r--platform/linuxbsd/joypad_linux.h2
-rw-r--r--platform/linuxbsd/logo.pngbin1679 -> 0 bytes
-rw-r--r--platform/linuxbsd/logo.svg1
-rw-r--r--platform/linuxbsd/run_icon.svg1
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp84
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h10
-rw-r--r--platform/macos/README.md2
-rw-r--r--platform/macos/display_server_macos.h7
-rw-r--r--platform/macos/display_server_macos.mm58
-rw-r--r--platform/macos/export/export_plugin.cpp388
-rw-r--r--platform/macos/export/export_plugin.h59
-rw-r--r--platform/macos/godot_content_view.h2
-rw-r--r--platform/macos/godot_content_view.mm41
-rw-r--r--platform/macos/joypad_macos.cpp27
-rw-r--r--platform/macos/logo.pngbin7195 -> 0 bytes
-rw-r--r--platform/macos/logo.svg1
-rw-r--r--platform/macos/run_icon.svg1
-rw-r--r--platform/uwp/README.md2
-rw-r--r--platform/uwp/export/export_plugin.cpp18
-rw-r--r--platform/uwp/logo.pngbin1519 -> 0 bytes
-rw-r--r--platform/uwp/logo.svg1
-rw-r--r--platform/uwp/os_uwp.cpp2
-rw-r--r--platform/uwp/os_uwp.h2
-rw-r--r--platform/web/README.md2
-rw-r--r--platform/web/api/api.cpp3
-rw-r--r--platform/web/api/javascript_bridge_singleton.h1
-rw-r--r--platform/web/display_server_web.cpp28
-rw-r--r--platform/web/export/export_plugin.cpp25
-rw-r--r--platform/web/export/export_plugin.h5
-rw-r--r--platform/web/javascript_bridge_singleton.cpp5
-rw-r--r--platform/web/logo.pngbin1234 -> 0 bytes
-rw-r--r--platform/web/logo.svg1
-rw-r--r--platform/web/os_web.cpp6
-rw-r--r--platform/web/os_web.h1
-rw-r--r--platform/web/package-lock.json12
-rw-r--r--platform/web/run_icon.pngbin290 -> 0 bytes
-rw-r--r--platform/web/run_icon.svg1
-rw-r--r--platform/windows/README.md2
-rw-r--r--platform/windows/display_server_windows.cpp80
-rw-r--r--platform/windows/display_server_windows.h7
-rw-r--r--platform/windows/export/export.cpp1
-rw-r--r--platform/windows/export/export_plugin.cpp316
-rw-r--r--platform/windows/export/export_plugin.h36
-rw-r--r--platform/windows/joypad_windows.cpp25
-rw-r--r--platform/windows/logo.pngbin1536 -> 0 bytes
-rw-r--r--platform/windows/logo.svg1
-rw-r--r--platform/windows/os_windows.cpp45
-rw-r--r--platform/windows/os_windows.h5
-rw-r--r--platform/windows/run_icon.svg1
-rw-r--r--scene/2d/cpu_particles_2d.cpp4
-rw-r--r--scene/2d/mesh_instance_2d.cpp13
-rw-r--r--scene/2d/mesh_instance_2d.h4
-rw-r--r--scene/2d/multimesh_instance_2d.cpp13
-rw-r--r--scene/2d/multimesh_instance_2d.h4
-rw-r--r--scene/2d/navigation_agent_2d.cpp20
-rw-r--r--scene/2d/navigation_agent_2d.h18
-rw-r--r--scene/2d/navigation_region_2d.cpp16
-rw-r--r--scene/2d/path_2d.cpp25
-rw-r--r--scene/2d/path_2d.h5
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp2
-rw-r--r--scene/3d/bone_attachment_3d.cpp52
-rw-r--r--scene/3d/bone_attachment_3d.h8
-rw-r--r--scene/3d/cpu_particles_3d.cpp4
-rw-r--r--scene/3d/lightmap_gi.cpp10
-rw-r--r--scene/3d/navigation_link_3d.cpp8
-rw-r--r--scene/3d/navigation_region_3d.cpp26
-rw-r--r--scene/3d/shape_cast_3d.cpp19
-rw-r--r--scene/3d/shape_cast_3d.h1
-rw-r--r--scene/3d/skeleton_3d.cpp263
-rw-r--r--scene/3d/skeleton_3d.h55
-rw-r--r--scene/3d/skeleton_ik_3d.cpp23
-rw-r--r--scene/3d/soft_body_3d.cpp2
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp2
-rw-r--r--scene/animation/animation_player.cpp60
-rw-r--r--scene/animation/animation_player.h7
-rw-r--r--scene/animation/tween.cpp44
-rw-r--r--scene/animation/tween.h5
-rw-r--r--scene/gui/base_button.cpp81
-rw-r--r--scene/gui/base_button.h19
-rw-r--r--scene/gui/box_container.cpp4
-rw-r--r--scene/gui/button.cpp2
-rw-r--r--scene/gui/code_edit.cpp13
-rw-r--r--scene/gui/color_picker.cpp2
-rw-r--r--scene/gui/container.cpp12
-rw-r--r--scene/gui/control.cpp165
-rw-r--r--scene/gui/control.h24
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/flow_container.cpp12
-rw-r--r--scene/gui/graph_edit.cpp9
-rw-r--r--scene/gui/graph_node.cpp8
-rw-r--r--scene/gui/grid_container.cpp4
-rw-r--r--scene/gui/label.cpp11
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/menu_bar.cpp21
-rw-r--r--scene/gui/menu_bar.h1
-rw-r--r--scene/gui/option_button.cpp4
-rw-r--r--scene/gui/option_button.h2
-rw-r--r--scene/gui/popup_menu.cpp12
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/rich_text_label.cpp1
-rw-r--r--scene/gui/scroll_container.cpp4
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/tab_bar.cpp1
-rw-r--r--scene/gui/tab_container.cpp18
-rw-r--r--scene/gui/tab_container.h2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/gui/texture_rect.cpp64
-rw-r--r--scene/gui/texture_rect.h19
-rw-r--r--scene/gui/tree.cpp105
-rw-r--r--scene/gui/tree.h22
-rw-r--r--scene/gui/view_panner.cpp2
-rw-r--r--scene/main/canvas_item.cpp60
-rw-r--r--scene/main/canvas_item.h12
-rw-r--r--scene/main/node.cpp10
-rw-r--r--scene/main/node.h3
-rw-r--r--scene/main/scene_tree.cpp18
-rw-r--r--scene/main/scene_tree.h1
-rw-r--r--scene/main/viewport.cpp77
-rw-r--r--scene/main/viewport.h7
-rw-r--r--scene/main/window.cpp20
-rw-r--r--scene/main/window.h6
-rw-r--r--scene/register_scene_types.cpp17
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp2
-rw-r--r--scene/resources/immediate_mesh.cpp19
-rw-r--r--scene/resources/immediate_mesh.h2
-rw-r--r--scene/resources/mesh.cpp66
-rw-r--r--scene/resources/mesh.h12
-rw-r--r--scene/resources/navigation_mesh.cpp4
-rw-r--r--scene/resources/particle_process_material.cpp4
-rw-r--r--scene/resources/primitive_meshes.cpp2
-rw-r--r--scene/resources/primitive_meshes.h2
-rw-r--r--scene/resources/resource_format_text.cpp91
-rw-r--r--scene/resources/resource_format_text.h3
-rw-r--r--scene/resources/skeleton_modification_3d.cpp151
-rw-r--r--scene/resources/skeleton_modification_3d.h79
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.cpp474
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h114
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp628
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h124
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp582
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h138
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp267
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h89
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.cpp104
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp617
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h118
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp224
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h91
-rw-r--r--scene/resources/tile_set.cpp1
-rw-r--r--scene/resources/visual_shader.cpp8
-rw-r--r--scene/resources/visual_shader_nodes.cpp2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp90
-rw-r--r--scene/resources/visual_shader_particle_nodes.h3
-rw-r--r--servers/display_server.cpp25
-rw-r--r--servers/display_server.h14
-rw-r--r--servers/extensions/physics_server_2d_extension.cpp6
-rw-r--r--servers/extensions/physics_server_2d_extension.h1
-rw-r--r--servers/extensions/physics_server_3d_extension.cpp5
-rw-r--r--servers/extensions/physics_server_3d_extension.h2
-rw-r--r--servers/navigation_server_2d.cpp120
-rw-r--r--servers/navigation_server_2d.h75
-rw-r--r--servers/navigation_server_3d.cpp16
-rw-r--r--servers/navigation_server_3d.h97
-rw-r--r--servers/physics_2d/godot_body_2d.h6
-rw-r--r--servers/physics_2d/godot_body_direct_state_2d.cpp5
-rw-r--r--servers/physics_2d/godot_body_direct_state_2d.h2
-rw-r--r--servers/physics_2d/godot_body_pair_2d.cpp37
-rw-r--r--servers/physics_2d/godot_body_pair_2d.h1
-rw-r--r--servers/physics_3d/godot_body_3d.h6
-rw-r--r--servers/physics_3d/godot_body_direct_state_3d.cpp5
-rw-r--r--servers/physics_3d/godot_body_direct_state_3d.h2
-rw-r--r--servers/physics_3d/godot_body_pair_3d.cpp68
-rw-r--r--servers/physics_3d/godot_body_pair_3d.h1
-rw-r--r--servers/physics_3d/godot_collision_solver_3d_sat.cpp415
-rw-r--r--servers/physics_server_2d.cpp1
-rw-r--r--servers/physics_server_2d.h1
-rw-r--r--servers/physics_server_3d.h2
-rw-r--r--servers/register_server_types.cpp4
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp250
-rw-r--r--servers/rendering/renderer_canvas_cull.h6
-rw-r--r--servers/rendering/renderer_rd/effects/luminance.cpp255
-rw-r--r--servers/rendering/renderer_rd/effects/luminance.h120
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp138
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h51
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp66
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp25
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp26
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp9
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h3
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp92
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h5
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp5
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl3
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl (renamed from servers/rendering/renderer_rd/shaders/luminance_reduce.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl (renamed from servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl (renamed from servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl12
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp13
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp32
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h13
-rw-r--r--servers/rendering/renderer_scene_render.cpp2
-rw-r--r--servers/rendering/renderer_viewport.cpp69
-rw-r--r--servers/rendering/rendering_device_binds.cpp2
-rw-r--r--servers/rendering/rendering_server_default.cpp4
-rw-r--r--servers/rendering/rendering_server_default.h3
-rw-r--r--servers/rendering/shader_compiler.cpp19
-rw-r--r--servers/rendering/storage/render_scene_buffers.cpp4
-rw-r--r--servers/rendering/storage/render_scene_buffers.h4
-rw-r--r--servers/rendering_server.cpp81
-rw-r--r--servers/rendering_server.h22
-rw-r--r--tests/core/math/test_plane.h4
-rw-r--r--tests/scene/test_code_edit.h14
-rw-r--r--tests/scene/test_curve_2d.h228
-rw-r--r--tests/scene/test_node.h106
-rw-r--r--tests/scene/test_text_edit.h58
-rw-r--r--tests/test_macros.h14
-rw-r--r--tests/test_main.cpp1
828 files changed, 10927 insertions, 10995 deletions
diff --git a/.editorconfig b/.editorconfig
index 92ee947a82..4bb7553b16 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,3 +21,13 @@ indent_size = 4
[*.{yml,yaml}]
indent_style = space
indent_size = 2
+
+# GDScript unit test files
+[*.gd]
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.out]
+insert_final_newline = true
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index d8951ddb78..d10156c04e 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -19,14 +19,14 @@ jobs:
sudo rm -f /etc/apt/sources.list.d/*
sudo cp -f misc/ci/sources.list /etc/apt/sources.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main"
+ sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main"
sudo apt-get update
- name: Install dependencies
run: |
- sudo apt-get install -qq dos2unix recode clang-format-13 libxml2-utils python3-pip moreutils
+ sudo apt-get install -qq dos2unix clang-format-15 libxml2-utils python3-pip moreutils
sudo update-alternatives --remove-all clang-format || true
- sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100
+ sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 100
sudo pip3 install black==22.3.0 pygments pytest==7.1.2 mypy==0.971
- name: File formatting checks (file_format.sh)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f59c0e645d..de0037d5e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,11 +37,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- [New and improved IK in Skeleton2D](https://github.com/godotengine/godot/pull/40347).
- New classes: SkeletonModifier2D, SkeletonModifierStack2D, SkeletonModification2DLookAt, SkeletonModification2DCCDIK, SkeletonModification2DFABRIK, SkeletonModification2DJiggle, SkeletonModification2DTwoBoneIK, PhysicalBone2D, SkeletonModification2DPhysicalBones, SkeletonModification2DStackHolder.
- New `Transform2D.looking_at()` function.
-- [New and improved IK in Skeleton3D](https://github.com/godotengine/godot/pull/39353).
- - New classes: SkeletonModifier3D, SkeletonModifierStack3D, SkeletonModifier3DLookAt, SkeletonModification3DCCDIK, SkeletonModification3DFABRIK, SkeletonModification3DJiggle, SkeletonModification3DTwoBoneIK, SkeletonModification3DStackHolder.
- - The Bone struct now includes a local_pose_override.
+- [Improvements to Skeleton3D](https://github.com/godotengine/godot/pull/39353).
- The Bone struct now keeps track of its children bones, if it has any.
- - Added functions to Skeleton3D for getting the forward vector using the information stored in the rest pose for the bones.
- New `Basis.rotate_to_align()` function.
- Refactored the BoneAttachment3D node.
- Removed the `process_list` functions.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 63e6a89e64..aea1c8c5ff 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -55,7 +55,7 @@ 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
+### Provide a simple example project
Sometimes, unexpected behavior can happen in your project. In such case,
understand that:
@@ -122,11 +122,12 @@ recommend that you have a look at it to know what's important to take into
account for a PR to be considered for merging.
In addition to the following tips, also take a look at the
-[Engine development guide](https://docs.godotengine.org/en/latest/development/cpp/)
+[Engine development guide](https://docs.godotengine.org/en/latest/contributing/development/index.html)
for an introduction to developing on Godot.
-The [Contributing docs](https://docs.godotengine.org/en/latest/community/contributing/index.html)
-also have important information on the PR workflow and the code style we use.
+The [Contributing docs](https://docs.godotengine.org/en/latest/contributing/ways_to_contribute.html)
+also have important information on the [PR workflow](https://docs.godotengine.org/en/latest/contributing/workflow/pr_workflow.html)
+and the [code style](https://docs.godotengine.org/en/latest/contributing/development/code_style_guidelines.html) we use.
### Document your changes
@@ -135,10 +136,10 @@ scripting APIs, you **must** update the class reference to document those.
This is to ensure the documentation coverage doesn't decrease as contributions
are merged.
-[Update the documentation template](https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html#updating-the-documentation-template)
+[Update documentation XML files](https://docs.godotengine.org/en/latest/contributing/documentation/updating_the_class_reference.html#updating-class-reference-when-working-on-the-engine)
using your compiled binary, then fill in the descriptions.
Follow the style guide described in the
-[Docs writing guidelines](https://docs.godotengine.org/en/latest/community/contributing/docs_writing_guidelines.html).
+[Writing guidelines](https://docs.godotengine.org/en/latest/contributing/documentation/docs_writing_guidelines.html).
If your pull request modifies parts of the code in a non-obvious way, make sure
to add comments in the code as well. This helps other people understand the
@@ -164,7 +165,7 @@ applicable.
Feel free to contribute standalone pull requests to add new tests or improve
existing tests as well.
-See [Unit testing](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html)
+See [Unit testing](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html)
for information on writing tests in Godot's C++ codebase.
### Be nice to the Git history
@@ -186,7 +187,7 @@ Internet).
This [Git style guide](https://github.com/agis-/git-style-guide) has some
good practices to have in mind.
-See our [PR workflow](https://docs.godotengine.org/en/latest/community/contributing/pr_workflow.html)
+See our [PR workflow](https://docs.godotengine.org/en/latest/contributing/workflow/pr_workflow.html)
documentation for tips on using Git, amending commits and rebasing branches.
### Format your commit messages with readability in mind
diff --git a/README.md b/README.md
index 6dfd4fc713..f921db4eba 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ Official binaries for the Godot editor and the export templates can be found
### Compiling from source
-[See the official docs](https://docs.godotengine.org/en/latest/development/compiling/)
+[See the official docs](https://docs.godotengine.org/en/latest/contributing/development/compiling)
for compilation instructions for every supported platform.
## Community and contributing
diff --git a/SConstruct b/SConstruct
index 1eb6c0adc8..1acbd83e63 100644
--- a/SConstruct
+++ b/SConstruct
@@ -202,7 +202,7 @@ opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all
opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
-opts.Add("build_feature_profile", "Path to a file containing a feature build profile", "")
+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)", "")
@@ -492,6 +492,25 @@ if selected_platform in platform_list:
env["LINKFLAGS"] = ""
env.Append(LINKFLAGS=str(LINKFLAGS).split())
+ # Feature build profile
+ disabled_classes = []
+ if env["build_profile"] != "":
+ print("Using feature build profile: " + env["build_profile"])
+ import json
+
+ try:
+ ft = json.load(open(env["build_profile"]))
+ if "disabled_classes" in ft:
+ disabled_classes = ft["disabled_classes"]
+ if "disabled_build_options" in ft:
+ dbo = ft["disabled_build_options"]
+ for c in dbo:
+ env[c] = dbo[c]
+ except:
+ print("Error opening feature build profile: " + env["build_profile"])
+ Exit(255)
+ methods.write_disabled_classes(disabled_classes)
+
# Platform specific flags.
# These can sometimes override default options.
flag_list = platform_flags[selected_platform]
@@ -540,6 +559,9 @@ if selected_platform in platform_list:
env.Append(CCFLAGS=["/Od"])
else:
if env["debug_symbols"]:
+ # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
+ # otherwise addr2line doesn't understand them
+ env.Append(CCFLAGS=["-gdwarf-4"])
if env.dev_build:
env.Append(CCFLAGS=["-g3"])
else:
@@ -815,26 +837,6 @@ if selected_platform in platform_list:
env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
- disabled_classes = []
-
- if env["build_feature_profile"] != "":
- print("Using build feature profile: " + env["build_feature_profile"])
- import json
-
- try:
- ft = json.load(open(env["build_feature_profile"]))
- if "disabled_classes" in ft:
- disabled_classes = ft["disabled_classes"]
- if "disabled_build_options" in ft:
- dbo = ft["disabled_build_options"]
- for c in dbo:
- env[c] = dbo[c]
- except:
- print("Error opening feature build profile: " + env["build_feature_profile"])
- Exit(255)
-
- methods.write_disabled_classes(disabled_classes)
-
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.")
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 0bf7430d84..de27b6d5ac 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -33,6 +33,7 @@
#include "core/core_bind.h" // For Compression enum.
#include "core/core_string_names.h"
#include "core/input/input_map.h"
+#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"
@@ -291,31 +292,26 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
- if (!disable_feature_overrides) {
+ { // Feature overrides.
int dot = p_name.operator String().find(".");
if (dot != -1) {
Vector<String> s = p_name.operator String().split(".");
- bool override_valid = false;
for (int i = 1; i < s.size(); i++) {
String feature = s[i].strip_edges();
- if (OS::get_singleton()->has_feature(feature) || custom_features.has(feature)) {
- override_valid = true;
- break;
+ Pair<StringName, StringName> fo(feature, p_name);
+
+ if (!feature_overrides.has(s[0])) {
+ feature_overrides[s[0]] = LocalVector<Pair<StringName, StringName>>();
}
- }
- if (override_valid) {
- feature_overrides[s[0]] = p_name;
+ feature_overrides[s[0]].push_back(fo);
}
}
}
if (props.has(p_name)) {
- if (!props[p_name].overridden) {
- props[p_name].variant = p_value;
- }
-
+ props[p_name].variant = p_value;
} else {
props[p_name] = VariantContainer(p_value, last_order++);
}
@@ -340,16 +336,35 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
_THREAD_SAFE_METHOD_
+ if (!props.has(p_name)) {
+ WARN_PRINT("Property not found: " + String(p_name));
+ return false;
+ }
+ r_ret = props[p_name].variant;
+ return true;
+}
+
+Variant ProjectSettings::get_setting_with_override(const StringName &p_name) const {
+ _THREAD_SAFE_METHOD_
+
StringName name = p_name;
- if (!disable_feature_overrides && feature_overrides.has(name)) {
- name = feature_overrides[name];
+ if (feature_overrides.has(name)) {
+ const LocalVector<Pair<StringName, StringName>> &overrides = feature_overrides[name];
+ for (uint32_t i = 0; i < overrides.size(); i++) {
+ if (OS::get_singleton()->has_feature(overrides[i].first)) { // Custom features are checked in OS.has_feature() already. No need to check twice.
+ if (props.has(overrides[i].second)) {
+ name = overrides[i].second;
+ break;
+ }
+ }
+ }
}
+
if (!props.has(name)) {
WARN_PRINT("Property not found: " + String(name));
- return false;
+ return Variant();
}
- r_ret = props[name].variant;
- return true;
+ return props[name].variant;
}
struct _VCSort {
@@ -1101,10 +1116,6 @@ const HashMap<StringName, PropertyInfo> &ProjectSettings::get_custom_property_in
return custom_prop_info;
}
-void ProjectSettings::set_disable_feature_overrides(bool p_disable) {
- disable_feature_overrides = p_disable;
-}
-
bool ProjectSettings::is_using_datapack() const {
return using_datapack;
}
@@ -1138,6 +1149,29 @@ Variant ProjectSettings::get_setting(const String &p_setting, const Variant &p_d
}
}
+Array ProjectSettings::get_global_class_list() {
+ Array script_classes;
+
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ if (cf->load(get_project_data_path().path_join("global_script_class_cache.cfg")) == OK) {
+ script_classes = cf->get_value("", "list");
+ } else {
+#ifndef TOOLS_ENABLED
+ // Script classes can't be recreated in exported project, so print an error.
+ ERR_PRINT("Could not load global script cache.");
+#endif
+ }
+ return script_classes;
+}
+
+void ProjectSettings::store_global_class_list(const Array &p_classes) {
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ cf->set_value("", "list", p_classes);
+ cf->save(get_project_data_path().path_join("global_script_class_cache.cfg"));
+}
+
bool ProjectSettings::has_custom_feature(const String &p_feature) const {
return custom_features.has(p_feature);
}
@@ -1169,6 +1203,7 @@ void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
ClassDB::bind_method(D_METHOD("get_setting", "name", "default_value"), &ProjectSettings::get_setting, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("get_setting_with_override", "name"), &ProjectSettings::get_setting_with_override);
ClassDB::bind_method(D_METHOD("set_order", "name", "position"), &ProjectSettings::set_order);
ClassDB::bind_method(D_METHOD("get_order", "name"), &ProjectSettings::get_order);
ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value"), &ProjectSettings::set_initial_value);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index 2aca1e7691..29c495c46b 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -34,6 +34,7 @@
#include "core/object/class_db.h"
#include "core/os/thread_safe.h"
#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
#include "core/templates/rb_set.h"
class ProjectSettings : public Object {
@@ -69,7 +70,6 @@ protected:
Variant variant;
Variant initial;
bool hide_from_editor = false;
- bool overridden = false;
bool restart_if_changed = false;
#ifdef DEBUG_METHODS_ENABLED
bool ignore_value_in_docs = false;
@@ -91,12 +91,11 @@ protected:
RBMap<StringName, VariantContainer> props; // NOTE: Key order is used e.g. in the save_custom method.
String resource_path;
HashMap<StringName, PropertyInfo> custom_prop_info;
- bool disable_feature_overrides = false;
bool using_datapack = false;
List<String> input_presets;
HashSet<String> custom_features;
- HashMap<StringName, StringName> feature_overrides;
+ HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides;
HashMap<StringName, AutoloadInfo> autoloads;
@@ -142,6 +141,8 @@ public:
void set_setting(const String &p_setting, const Variant &p_value);
Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const;
+ Array get_global_class_list();
+ void store_global_class_list(const Array &p_classes);
bool has_setting(String p_var) const;
String localize_path(const String &p_path) const;
@@ -181,7 +182,7 @@ public:
List<String> get_input_presets() const { return input_presets; }
- void set_disable_feature_overrides(bool p_disable);
+ Variant get_setting_with_override(const StringName &p_name) const;
bool is_using_datapack() const;
@@ -205,7 +206,7 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p
#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true)
#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true)
-#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
#define GLOBAL_DEF_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, true)
#define GLOBAL_DEF_RST_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, false, true)
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 0ed05a20a2..96e1da9dde 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -257,8 +257,8 @@ Error OS::shell_open(String p_uri) {
return ::OS::get_singleton()->shell_open(p_uri);
}
-String OS::read_string_from_stdin(bool p_block) {
- return ::OS::get_singleton()->get_stdin_string(true);
+String OS::read_string_from_stdin() {
+ return ::OS::get_singleton()->get_stdin_string();
}
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
@@ -322,8 +322,12 @@ String OS::get_environment(const String &p_var) const {
return ::OS::get_singleton()->get_environment(p_var);
}
-bool OS::set_environment(const String &p_var, const String &p_value) const {
- return ::OS::get_singleton()->set_environment(p_var, p_value);
+void OS::set_environment(const String &p_var, const String &p_value) const {
+ ::OS::get_singleton()->set_environment(p_var, p_value);
+}
+
+void OS::unset_environment(const String &p_var) const {
+ ::OS::get_singleton()->unset_environment(p_var);
}
String OS::get_name() const {
@@ -539,7 +543,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "weight", "stretch", "italic"), &OS::get_system_font_path, DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
- ClassDB::bind_method(D_METHOD("read_string_from_stdin", "block"), &OS::read_string_from_stdin, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
@@ -548,9 +552,10 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
+ ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
ClassDB::bind_method(D_METHOD("get_environment", "variable"), &OS::get_environment);
ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &OS::set_environment);
- ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
+ ClassDB::bind_method(D_METHOD("unset_environment", "variable"), &OS::unset_environment);
ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name);
ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name);
diff --git a/core/core_bind.h b/core/core_bind.h
index e8c59866e3..c0c87fd009 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -146,7 +146,7 @@ public:
String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
String get_executable_path() const;
- String read_string_from_stdin(bool p_block = true);
+ String read_string_from_stdin();
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
int create_instance(const Vector<String> &p_arguments);
@@ -162,7 +162,8 @@ public:
bool has_environment(const String &p_var) const;
String get_environment(const String &p_var) const;
- bool set_environment(const String &p_var, const String &p_value) const;
+ void set_environment(const String &p_var, const String &p_value) const;
+ void unset_environment(const String &p_var) const;
String get_name() const;
String get_distribution_name() const;
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 500a554a24..edb72f73cb 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -39,6 +39,7 @@ struct _CoreConstant {
#ifdef DEBUG_METHODS_ENABLED
StringName enum_name;
bool ignore_value_in_docs = false;
+ bool is_bitfield = false;
#endif
const char *name = nullptr;
int64_t value = 0;
@@ -46,9 +47,10 @@ struct _CoreConstant {
_CoreConstant() {}
#ifdef DEBUG_METHODS_ENABLED
- _CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value, bool p_ignore_value_in_docs = false) :
+ _CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value, bool p_ignore_value_in_docs = false, bool p_is_bitfield = false) :
enum_name(p_enum_name),
ignore_value_in_docs(p_ignore_value_in_docs),
+ is_bitfield(p_is_bitfield),
name(p_name),
value(p_value) {
}
@@ -70,13 +72,22 @@ static Vector<_CoreConstant> _global_constants;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant));
+#define BIND_CORE_BITFIELD_FLAG(m_constant) \
+ _global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, false, true));
+
// This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member));
+#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
+ _global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true));
+
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_name), #m_name, (int64_t)m_enum::m_member));
+#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
+ _global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_enum::m_member, #m_name), #m_name, (int64_t)m_enum::m_member, false, true));
+
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true));
@@ -100,13 +111,22 @@ static Vector<_CoreConstant> _global_constants;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
+#define BIND_CORE_BITFIELD_FLAG(m_constant) \
+ _global_constants.push_back(_CoreConstant(#m_constant, m_constant));
+
// This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
+#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
+ _global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
+
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(#m_name, (int64_t)m_enum::m_member));
+#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
+ _global_constants.push_back(_CoreConstant(#m_name, (int64_t)m_enum::m_member));
+
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
@@ -439,15 +459,15 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DIVISION);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YDIAERESIS);
- BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK);
- BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, CMD_OR_CTRL);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, SHIFT);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, ALT);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, META);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, CTRL);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, KPAD);
- BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, GROUP_SWITCH);
+ BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK);
+ BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, CMD_OR_CTRL);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, SHIFT);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, ALT);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, META);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, CTRL);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, KPAD);
+ BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, GROUP_SWITCH);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT);
@@ -459,11 +479,12 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_RIGHT);
BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON1, MB_XBUTTON1);
BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON2, MB_XBUTTON2);
- BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_LEFT);
- BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_RIGHT);
- BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_MIDDLE);
- BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_XBUTTON1);
- BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MASK_XBUTTON2);
+
+ BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, LEFT);
+ BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, RIGHT);
+ BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MIDDLE);
+ BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON1);
+ BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON2);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, INVALID);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, A);
@@ -600,14 +621,6 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_VARIANT_TYPE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_BASE_TYPE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_INSTANCE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_SCRIPT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_BASE_TYPE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_INSTANCE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_SCRIPT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE);
@@ -622,50 +635,48 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORAGE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR);
-
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CHECKABLE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CHECKED);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_GROUP);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SUBGROUP);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CLASS_IS_BITFIELD);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE);
-
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NULL);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CLASS_IS_ENUM);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NIL_IS_VARIANT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_INTERNAL);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_HIGH_END_GFX);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_KEYING_INCREMENTS);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFERRED_SET_RESOURCE);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_BASIC_SETTING);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_READ_ONLY);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ARRAY);
-
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL);
- BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NO_EDITOR);
-
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_NORMAL);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_EDITOR);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_CONST);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_VIRTUAL);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_VARARG);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_STATIC);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAG_OBJECT_CORE);
- BIND_CORE_ENUM_CONSTANT(METHOD_FLAGS_DEFAULT);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_STORAGE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_INTERNAL);
+
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKABLE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKED);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_GROUP);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CATEGORY);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SUBGROUP);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_BITFIELD);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_INSTANCE_STATE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_RESTART_IF_CHANGED);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SCRIPT_VARIABLE);
+
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_STORE_IF_NULL);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_ENUM);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NIL_IS_VARIANT);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ARRAY);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_HIGH_END_GFX);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_KEYING_INCREMENTS);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFERRED_SET_RESOURCE);
+ 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_DEFAULT);
+ BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR);
+
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_NORMAL);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_EDITOR);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_CONST);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VARARG);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_STATIC);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_OBJECT_CORE);
+ BIND_CORE_BITFIELD_FLAG(METHOD_FLAGS_DEFAULT);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL);
BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL);
@@ -753,6 +764,10 @@ StringName CoreConstants::get_global_constant_enum(int p_idx) {
return _global_constants[p_idx].enum_name;
}
+bool CoreConstants::is_global_constant_bitfield(int p_idx) {
+ return _global_constants[p_idx].is_bitfield;
+}
+
bool CoreConstants::get_ignore_value_in_docs(int p_idx) {
return _global_constants[p_idx].ignore_value_in_docs;
}
@@ -761,6 +776,10 @@ StringName CoreConstants::get_global_constant_enum(int p_idx) {
return StringName();
}
+bool CoreConstants::is_global_constant_bitfield(int p_idx) {
+ return false;
+}
+
bool CoreConstants::get_ignore_value_in_docs(int p_idx) {
return false;
}
diff --git a/core/core_constants.h b/core/core_constants.h
index 6bd725de88..5a5cd4394c 100644
--- a/core/core_constants.h
+++ b/core/core_constants.h
@@ -37,6 +37,7 @@ class CoreConstants {
public:
static int get_global_constant_count();
static StringName get_global_constant_enum(int p_idx);
+ static bool is_global_constant_bitfield(int p_idx);
static bool get_ignore_value_in_docs(int p_idx);
static const char *get_global_constant_name(int p_idx);
static int64_t get_global_constant_value(int p_idx);
diff --git a/core/doc_data.h b/core/doc_data.h
index c547138619..1cf4e4f206 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -378,7 +378,6 @@ public:
struct ClassDoc {
String name;
String inherits;
- String category; // FIXME: Wrongly used by VisualScriptPropertySelector, should be removed.
String brief_description;
String description;
Vector<TutorialDoc> tutorials;
@@ -409,10 +408,6 @@ public:
doc.inherits = p_dict["inherits"];
}
- if (p_dict.has("category")) {
- doc.category = p_dict["category"];
- }
-
if (p_dict.has("brief_description")) {
doc.brief_description = p_dict["brief_description"];
}
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index f651ef57a3..63a2d22416 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -33,7 +33,7 @@
#include "core/typedefs.h"
-#include "core/templates/safe_refcount.h"
+#include <atomic> // We'd normally use safe_refcount.h, but that would cause circular includes.
class String;
@@ -737,10 +737,10 @@ void _err_flush_stdout();
*/
#define WARN_DEPRECATED \
if (true) { \
- static SafeFlag warning_shown; \
- if (!warning_shown.is_set()) { \
+ static std::atomic<bool> warning_shown; \
+ if (!warning_shown.load()) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \
- warning_shown.set(); \
+ warning_shown.store(true); \
} \
} else \
((void)0)
@@ -750,10 +750,10 @@ void _err_flush_stdout();
*/
#define WARN_DEPRECATED_MSG(m_msg) \
if (true) { \
- static SafeFlag warning_shown; \
- if (!warning_shown.is_set()) { \
+ static std::atomic<bool> warning_shown; \
+ if (!warning_shown.load()) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \
- warning_shown.set(); \
+ warning_shown.store(true); \
} \
} else \
((void)0)
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 1957ad987a..312064a36c 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -463,12 +463,14 @@ Dictionary GDExtensionAPIDump::generate_extension_api() {
int64_t value = CoreConstants::get_global_constant_value(i);
String enum_name = CoreConstants::get_global_constant_enum(i);
String name = CoreConstants::get_global_constant_name(i);
+ bool bitfield = CoreConstants::is_global_constant_bitfield(i);
if (!enum_name.is_empty()) {
enum_list[enum_name].push_back(Pair<String, int64_t>(name, value));
} else {
Dictionary d;
d["name"] = name;
d["value"] = value;
+ d["is_bitfield"] = bitfield;
constants.push_back(d);
}
}
diff --git a/core/input/input.cpp b/core/input/input.cpp
index a1944c0920..0afa004515 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -237,7 +237,7 @@ bool Input::is_anything_pressed() const {
}
return !keys_pressed.is_empty() ||
!joy_buttons_pressed.is_empty() ||
- mouse_button_mask > MouseButton::NONE;
+ !mouse_button_mask.is_empty();
}
bool Input::is_key_pressed(Key p_keycode) const {
@@ -252,7 +252,7 @@ bool Input::is_physical_key_pressed(Key p_keycode) const {
bool Input::is_mouse_button_pressed(MouseButton p_button) const {
_THREAD_SAFE_METHOD_
- return (mouse_button_mask & mouse_button_to_mask(p_button)) != MouseButton::NONE;
+ return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
}
static JoyAxis _combine_device(JoyAxis p_value, int p_device) {
@@ -504,9 +504,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (mb.is_valid()) {
if (mb->is_pressed()) {
- mouse_button_mask |= mouse_button_to_mask(mb->get_button_index());
+ mouse_button_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- mouse_button_mask &= ~mouse_button_to_mask(mb->get_button_index());
+ mouse_button_mask.clear_flag(mouse_button_to_mask(mb->get_button_index()));
}
Point2 pos = mb->get_global_position();
@@ -534,7 +534,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Vector2 relative = mm->get_relative();
mouse_velocity_track.update(relative);
- if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
+ if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
Ref<InputEventScreenDrag> drag_event;
drag_event.instantiate();
@@ -585,11 +585,14 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
button_event->set_pressed(st->is_pressed());
button_event->set_button_index(MouseButton::LEFT);
button_event->set_double_click(st->is_double_tap());
+
+ BitField<MouseButtonMask> ev_bm = mouse_button_mask;
if (st->is_pressed()) {
- button_event->set_button_mask(MouseButton(mouse_button_mask | MouseButton::MASK_LEFT));
+ ev_bm.set_flag(MouseButtonMask::LEFT);
} else {
- button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));
+ ev_bm.clear_flag(MouseButtonMask::LEFT);
}
+ button_event->set_button_mask(ev_bm);
_parse_input_event_impl(button_event, true);
}
@@ -740,7 +743,7 @@ Point2 Input::get_last_mouse_velocity() {
return mouse_velocity_track.velocity;
}
-MouseButton Input::get_mouse_button_mask() const {
+BitField<MouseButtonMask> Input::get_mouse_button_mask() const {
return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state();
}
@@ -821,7 +824,9 @@ void Input::ensure_touch_mouse_raised() {
button_event->set_global_position(mouse_pos);
button_event->set_pressed(false);
button_event->set_button_index(MouseButton::LEFT);
- button_event->set_button_mask(MouseButton(mouse_button_mask & ~MouseButton::MASK_LEFT));
+ BitField<MouseButtonMask> ev_bm = mouse_button_mask;
+ ev_bm.clear_flag(MouseButtonMask::LEFT);
+ button_event->set_button_mask(ev_bm);
_parse_input_event_impl(button_event, true);
}
@@ -1022,7 +1027,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
}
}
-void Input::joy_hat(int p_device, HatMask p_val) {
+void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
_THREAD_SAFE_METHOD_;
const Joypad &joy = joy_names[p_device];
diff --git a/core/input/input.h b/core/input/input.h
index 5ef163e530..0915588700 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -82,7 +82,7 @@ public:
typedef void (*EventDispatchFunc)(const Ref<InputEvent> &p_event);
private:
- MouseButton mouse_button_mask = MouseButton::NONE;
+ BitField<MouseButtonMask> mouse_button_mask;
RBSet<Key> physical_keys_pressed;
RBSet<Key> keys_pressed;
@@ -273,7 +273,7 @@ public:
Point2 get_mouse_position() const;
Vector2 get_last_mouse_velocity();
- MouseButton get_mouse_button_mask() const;
+ BitField<MouseButtonMask> get_mouse_button_mask() const;
void warp_mouse(const Vector2 &p_position);
Point2i warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect);
@@ -310,7 +310,7 @@ public:
void parse_mapping(String p_mapping);
void joy_button(int p_device, JoyButton p_button, bool p_pressed);
void joy_axis(int p_device, JoyAxis p_axis, float p_value);
- void joy_hat(int p_device, HatMask p_val);
+ void joy_hat(int p_device, BitField<HatMask> p_val);
void add_joy_mapping(String p_mapping, bool p_update_existing = false);
void remove_joy_mapping(String p_guid);
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index a7729c9af2..76e199e0ff 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -45,7 +45,7 @@ def make_default_controller_mappings(target, source, env):
platform_mappings[current_platform][guid] = line
platform_variables = {
- "Linux": "#if X11_ENABLED",
+ "Linux": "#if LINUXBSD_ENABLED",
"Windows": "#ifdef WINDOWS_ENABLED",
"Mac OS X": "#ifdef MACOS_ENABLED",
"Android": "#if defined(__ANDROID__)",
diff --git a/core/input/input_enums.h b/core/input/input_enums.h
index c30516676f..02efde2d30 100644
--- a/core/input/input_enums.h
+++ b/core/input/input_enums.h
@@ -119,59 +119,18 @@ enum class MouseButton {
WHEEL_RIGHT = 7,
MB_XBUTTON1 = 8, // "XBUTTON1" is a reserved word on Windows.
MB_XBUTTON2 = 9, // "XBUTTON2" is a reserved word on Windows.
- MASK_LEFT = (1 << (LEFT - 1)),
- MASK_RIGHT = (1 << (RIGHT - 1)),
- MASK_MIDDLE = (1 << (MIDDLE - 1)),
- MASK_XBUTTON1 = (1 << (MB_XBUTTON1 - 1)),
- MASK_XBUTTON2 = (1 << (MB_XBUTTON2 - 1)),
};
-inline MouseButton mouse_button_to_mask(MouseButton button) {
- return MouseButton(1 << ((int)button - 1));
-}
-
-inline MouseButton operator&(MouseButton a, MouseButton b) {
- return (MouseButton)((int)a & (int)b);
-}
-
-inline MouseButton operator|(MouseButton a, MouseButton b) {
- return (MouseButton)((int)a | (int)b);
-}
-
-inline MouseButton operator^(MouseButton a, MouseButton b) {
- return (MouseButton)((int)a ^ (int)b);
-}
-
-inline MouseButton &operator|=(MouseButton &a, MouseButton b) {
- return (MouseButton &)((int &)a |= (int)b);
-}
-
-inline MouseButton &operator&=(MouseButton &a, MouseButton b) {
- return (MouseButton &)((int &)a &= (int)b);
-}
-
-inline MouseButton operator~(MouseButton a) {
- return (MouseButton)(~(int)a);
-}
-
-inline HatMask operator|(HatMask a, HatMask b) {
- return (HatMask)((int)a | (int)b);
-}
-
-inline HatMask operator&(HatMask a, HatMask b) {
- return (HatMask)((int)a & (int)b);
-}
-
-inline HatMask &operator&=(HatMask &a, HatMask b) {
- return (HatMask &)((int &)a &= (int)b);
-}
-
-inline HatMask &operator|=(HatMask &a, HatMask b) {
- return (HatMask &)((int &)a |= (int)b);
-}
+enum class MouseButtonMask {
+ LEFT = (1 << (int(MouseButton::LEFT) - 1)),
+ RIGHT = (1 << (int(MouseButton::RIGHT) - 1)),
+ MIDDLE = (1 << (int(MouseButton::MIDDLE) - 1)),
+ MB_XBUTTON1 = (1 << (int(MouseButton::MB_XBUTTON1) - 1)),
+ MB_XBUTTON2 = (1 << (int(MouseButton::MB_XBUTTON2) - 1)),
+};
-inline HatMask operator~(HatMask a) {
- return (HatMask)(~(int)a);
+inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
+ return MouseButtonMask(1 << ((int)button - 1));
}
#endif // INPUT_ENUMS_H
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index f421ecb22d..0dd7fdc19b 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -216,25 +216,25 @@ void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModif
set_meta_pressed(event->is_meta_pressed());
}
-Key InputEventWithModifiers::get_modifiers_mask() const {
- Key mask = Key::NONE;
+BitField<KeyModifierMask> InputEventWithModifiers::get_modifiers_mask() const {
+ BitField<KeyModifierMask> mask;
if (is_ctrl_pressed()) {
- mask |= KeyModifierMask::CTRL;
+ mask.set_flag(KeyModifierMask::CTRL);
}
if (is_shift_pressed()) {
- mask |= KeyModifierMask::SHIFT;
+ mask.set_flag(KeyModifierMask::SHIFT);
}
if (is_alt_pressed()) {
- mask |= KeyModifierMask::ALT;
+ mask.set_flag(KeyModifierMask::ALT);
}
if (is_meta_pressed()) {
- mask |= KeyModifierMask::META;
+ mask.set_flag(KeyModifierMask::META);
}
if (is_command_or_control_autoremap()) {
#ifdef MACOS_ENABLED
- mask |= KeyModifierMask::META;
+ mask.set_flag(KeyModifierMask::META);
#else
- mask |= KeyModifierMask::CTRL;
+ mask.set_flag(KeyModifierMask::CTRL);
#endif
}
return mask;
@@ -356,11 +356,11 @@ bool InputEventKey::is_echo() const {
}
Key InputEventKey::get_keycode_with_modifiers() const {
- return keycode | get_modifiers_mask();
+ return keycode | (int64_t)get_modifiers_mask();
}
Key InputEventKey::get_physical_keycode_with_modifiers() const {
- return physical_keycode | get_modifiers_mask();
+ return physical_keycode | (int64_t)get_modifiers_mask();
}
String InputEventKey::as_text() const {
@@ -440,8 +440,8 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_ma
} else {
match = get_physical_keycode() == key->get_physical_keycode();
}
- Key action_mask = get_modifiers_mask();
- Key key_mask = key->get_modifiers_mask();
+ Key action_mask = (Key)(int64_t)get_modifiers_mask();
+ Key key_mask = (Key)(int64_t)key->get_modifiers_mask();
if (key->is_pressed()) {
match &= (action_mask & key_mask) == action_mask;
}
@@ -505,12 +505,12 @@ void InputEventKey::_bind_methods() {
///////////////////////////////////
-void InputEventMouse::set_button_mask(MouseButton p_mask) {
+void InputEventMouse::set_button_mask(BitField<MouseButtonMask> p_mask) {
button_mask = p_mask;
emit_changed();
}
-MouseButton InputEventMouse::get_button_mask() const {
+BitField<MouseButtonMask> InputEventMouse::get_button_mask() const {
return button_mask;
}
@@ -610,8 +610,8 @@ bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool p_
}
bool match = button_index == mb->button_index;
- Key action_modifiers_mask = get_modifiers_mask();
- Key button_modifiers_mask = mb->get_modifiers_mask();
+ Key action_modifiers_mask = (Key)(int64_t)get_modifiers_mask();
+ Key button_modifiers_mask = (Key)(int64_t)mb->get_modifiers_mask();
if (mb->is_pressed()) {
match &= (action_modifiers_mask & button_modifiers_mask) == action_modifiers_mask;
}
@@ -808,26 +808,23 @@ String InputEventMouseMotion::as_text() const {
}
String InputEventMouseMotion::to_string() {
- MouseButton mouse_button_mask = get_button_mask();
+ BitField<MouseButtonMask> mouse_button_mask = get_button_mask();
String button_mask_string = itos((int64_t)mouse_button_mask);
- switch (mouse_button_mask) {
- case MouseButton::MASK_LEFT:
- button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::LEFT - 1]));
- break;
- case MouseButton::MASK_MIDDLE:
- button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MIDDLE - 1]));
- break;
- case MouseButton::MASK_RIGHT:
- button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::RIGHT - 1]));
- break;
- case MouseButton::MASK_XBUTTON1:
- button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON1 - 1]));
- break;
- case MouseButton::MASK_XBUTTON2:
- button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON2 - 1]));
- break;
- default:
- break;
+
+ if (mouse_button_mask.has_flag(MouseButtonMask::LEFT)) {
+ button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::LEFT - 1]));
+ }
+ if (mouse_button_mask.has_flag(MouseButtonMask::MIDDLE)) {
+ button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MIDDLE - 1]));
+ }
+ if (mouse_button_mask.has_flag(MouseButtonMask::RIGHT)) {
+ button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::RIGHT - 1]));
+ }
+ if (mouse_button_mask.has_flag(MouseButtonMask::MB_XBUTTON1)) {
+ button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON1 - 1]));
+ }
+ if (mouse_button_mask.has_flag(MouseButtonMask::MB_XBUTTON2)) {
+ button_mask_string += vformat(" (%s)", TTRGET(_mouse_button_descriptions[(size_t)MouseButton::MB_XBUTTON2 - 1]));
}
// Work around the fact vformat can only take 5 substitutions but 7 need to be passed.
diff --git a/core/input/input_event.h b/core/input/input_event.h
index e90f38b489..2d7a72e327 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -138,7 +138,7 @@ public:
void set_modifiers_from_event(const InputEventWithModifiers *event);
- Key get_modifiers_mask() const;
+ BitField<KeyModifierMask> get_modifiers_mask() const;
virtual String as_text() const override;
virtual String to_string() override;
@@ -195,7 +195,7 @@ public:
class InputEventMouse : public InputEventWithModifiers {
GDCLASS(InputEventMouse, InputEventWithModifiers);
- MouseButton button_mask = MouseButton::NONE;
+ BitField<MouseButtonMask> button_mask;
Vector2 pos;
Vector2 global_pos;
@@ -204,8 +204,8 @@ protected:
static void _bind_methods();
public:
- void set_button_mask(MouseButton p_mask);
- MouseButton get_button_mask() const;
+ void set_button_mask(BitField<MouseButtonMask> p_mask);
+ BitField<MouseButtonMask> get_button_mask() const;
void set_position(const Vector2 &p_pos);
Vector2 get_position() const;
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index f2cd3ba4d9..98f8c3de41 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -208,7 +208,7 @@ Error ConfigFile::_internal_save(Ref<FileAccess> file) {
file->store_string("\n");
}
if (!E.key.is_empty()) {
- file->store_string("[" + E.key + "]\n\n");
+ file->store_string("[" + E.key.replace("]", "\\]") + "]\n\n");
}
for (const KeyValue<String, Variant> &F : E.value) {
@@ -308,7 +308,7 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream)
if (!assign.is_empty()) {
set_value(section, assign, value);
} else if (!next_tag.name.is_empty()) {
- section = next_tag.name;
+ section = next_tag.name.replace("\\]", "]");
}
}
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index c9e0c2c638..c256668af0 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -184,6 +184,22 @@ bool FileAccessCompressed::is_open() const {
return f.is_valid();
}
+String FileAccessCompressed::get_path() const {
+ if (f.is_valid()) {
+ return f->get_path();
+ } else {
+ return "";
+ }
+}
+
+String FileAccessCompressed::get_path_absolute() const {
+ if (f.is_valid()) {
+ return f->get_path_absolute();
+ } else {
+ return "";
+ }
+}
+
void FileAccessCompressed::seek(uint64_t p_position) {
ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use.");
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index 53b4887b90..136fcede06 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -73,6 +73,9 @@ public:
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 String get_path() const override; /// returns the path for the current open file
+ virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
+
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
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index ba6ad16ca8..45e1301930 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1045,10 +1045,10 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
#ifdef TOOLS_ENABLED
// Silence a warning that can happen during the initial filesystem scan due to cache being regenerated.
if (ResourceLoader::get_resource_uid(res_path) != er.uid) {
- WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+ WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
}
#else
- WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+ WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
#endif
}
}
@@ -2209,12 +2209,130 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
return OK;
}
+Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_path + "'.");
+
+ Ref<FileAccess> fw;
+
+ local_path = p_path.get_base_dir();
+
+ uint8_t header[4];
+ f->get_buffer(header, 4);
+ if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') {
+ // Compressed.
+ Ref<FileAccessCompressed> fac;
+ fac.instantiate();
+ Error err = fac->open_after_magic(f);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
+ f = fac;
+
+ Ref<FileAccessCompressed> facw;
+ facw.instantiate();
+ facw->configure("RSCC");
+ err = facw->open_internal(p_path + ".uidren", FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".uidren'.");
+
+ fw = facw;
+
+ } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
+ // Not a binary resource.
+ return ERR_FILE_UNRECOGNIZED;
+ } else {
+ fw = FileAccess::open(p_path + ".uidren", FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".uidren'.");
+
+ uint8_t magich[4] = { 'R', 'S', 'R', 'C' };
+ fw->store_buffer(magich, 4);
+ }
+
+ big_endian = f->get_32();
+ bool use_real64 = f->get_32();
+ f->set_big_endian(big_endian != 0); //read big endian if saved as big endian
+#ifdef BIG_ENDIAN_ENABLED
+ fw->store_32(!big_endian);
+#else
+ fw->store_32(big_endian);
+#endif
+ fw->set_big_endian(big_endian != 0);
+ fw->store_32(use_real64); //use real64
+
+ uint32_t ver_major = f->get_32();
+ uint32_t ver_minor = f->get_32();
+ uint32_t ver_format = f->get_32();
+
+ if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) {
+ fw.unref();
+
+ {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ da->remove(p_path + ".uidren");
+ }
+
+ // Use the old approach.
+
+ WARN_PRINT("This file is old, so it does not support UIDs, opening and resaving '" + p_path + "'.");
+ return ERR_UNAVAILABLE;
+ }
+
+ if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
+ ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED,
+ vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).",
+ local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH));
+ }
+
+ // Since we're not actually converting the file contents, leave the version
+ // numbers in the file untouched.
+ fw->store_32(ver_major);
+ fw->store_32(ver_minor);
+ fw->store_32(ver_format);
+
+ save_ustring(fw, get_ustring(f)); //type
+
+ fw->store_64(f->get_64()); //metadata offset
+
+ uint32_t flags = f->get_32();
+ flags |= ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS;
+ f->get_64(); // Skip previous UID
+
+ fw->store_32(flags);
+ fw->store_64(p_uid);
+
+ //rest of file
+ uint8_t b = f->get_8();
+ while (!f->eof_reached()) {
+ fw->store_8(b);
+ b = f->get_8();
+ }
+
+ f.unref();
+
+ bool all_ok = fw->get_error() == OK;
+
+ if (!all_ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ fw.unref();
+
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(p_path);
+ da->rename(p_path + ".uidren", p_path);
+ return OK;
+}
+
Error ResourceFormatSaverBinary::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
ResourceFormatSaverBinaryInstance saver;
return saver.save(local_path, p_resource, p_flags);
}
+Error ResourceFormatSaverBinary::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ ResourceFormatSaverBinaryInstance saver;
+ return saver.set_uid(local_path, p_uid);
+}
+
bool ResourceFormatSaverBinary::recognize(const Ref<Resource> &p_resource) const {
return true; //all recognized
}
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 36613dbd58..2e8988005f 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -170,6 +170,7 @@ public:
RESERVED_FIELDS = 11
};
Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ Error set_uid(const String &p_path, ResourceUID::ID p_uid);
static void write_variant(Ref<FileAccess> f, const Variant &p_property, HashMap<Ref<Resource>, int> &resource_map, HashMap<Ref<Resource>, int> &external_resources, HashMap<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
};
@@ -177,6 +178,7 @@ class ResourceFormatSaverBinary : public ResourceFormatSaver {
public:
static ResourceFormatSaverBinary *singleton;
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid);
virtual bool recognize(const Ref<Resource> &p_resource) const;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index a4f4d705ee..dc1de6b9ce 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -31,6 +31,7 @@
#include "resource_importer.h"
#include "core/config/project_settings.h"
+#include "core/io/config_file.h"
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
@@ -484,3 +485,18 @@ void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importe
importers.push_back(p_importer);
}
}
+
+/////
+
+Error ResourceFormatImporterSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ Error err = cf->load(p_path + ".import");
+ if (err != OK) {
+ return err;
+ }
+ cf->set_value("remap", "uid", ResourceUID::get_singleton()->id_to_text(p_uid));
+ cf->save(p_path + ".import");
+
+ return OK;
+}
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index b104a9dffe..0089544caa 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -32,6 +32,7 @@
#define RESOURCE_IMPORTER_H
#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
class ResourceImporter;
@@ -149,4 +150,11 @@ public:
VARIANT_ENUM_CAST(ResourceImporter::ImportOrder);
+class ResourceFormatImporterSaver : public ResourceFormatSaver {
+ GDCLASS(ResourceFormatImporterSaver, ResourceFormatSaver)
+
+public:
+ virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override;
+};
+
#endif // RESOURCE_IMPORTER_H
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index de450a8420..9809b9a48f 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -47,6 +47,12 @@ Error ResourceFormatSaver::save(const Ref<Resource> &p_resource, const String &p
return (Error)res;
}
+Error ResourceFormatSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ Error err = ERR_FILE_UNRECOGNIZED;
+ GDVIRTUAL_CALL(_set_uid, p_path, p_uid, err);
+ return err;
+}
+
bool ResourceFormatSaver::recognize(const Ref<Resource> &p_resource) const {
bool success = false;
GDVIRTUAL_CALL(_recognize, p_resource, success);
@@ -85,6 +91,7 @@ bool ResourceFormatSaver::recognize_path(const Ref<Resource> &p_resource, const
void ResourceFormatSaver::_bind_methods() {
GDVIRTUAL_BIND(_save, "resource", "path", "flags");
+ GDVIRTUAL_BIND(_set_uid, "path", "uid");
GDVIRTUAL_BIND(_recognize, "resource");
GDVIRTUAL_BIND(_get_recognized_extensions, "resource");
GDVIRTUAL_BIND(_recognize_path, "resource", "path");
@@ -146,6 +153,23 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path,
return err;
}
+Error ResourceSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ String path = p_path;
+
+ ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Can't update UID to empty path. Provide non-empty path.");
+
+ Error err = ERR_FILE_UNRECOGNIZED;
+
+ for (int i = 0; i < saver_count; i++) {
+ err = saver[i]->set_uid(path, p_uid);
+ if (err == OK) {
+ break;
+ }
+ }
+
+ return err;
+}
+
void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) {
save_callback = p_callback;
}
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index f25463d71f..2043947963 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -42,12 +42,14 @@ protected:
static void _bind_methods();
GDVIRTUAL3R(int64_t, _save, Ref<Resource>, String, uint32_t)
+ GDVIRTUAL2R(Error, _set_uid, String, ResourceUID::ID)
GDVIRTUAL1RC(bool, _recognize, Ref<Resource>)
GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, Ref<Resource>)
GDVIRTUAL2RC(bool, _recognize_path, Ref<Resource>, String)
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid);
virtual bool recognize(const Ref<Resource> &p_resource) const;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
virtual bool recognize_path(const Ref<Resource> &p_resource, const String &p_path) const;
@@ -88,6 +90,8 @@ public:
static void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front = false);
static void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
+ static Error set_uid(const String &p_path, ResourceUID::ID p_uid);
+
static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; }
static bool get_timestamp_on_save() { return timestamp_on_save; }
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index cacdde6da5..30d50073d7 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -528,7 +528,7 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
if (a == b) {
TypedArray<Vector2i> ret;
- ret.push_back(a);
+ ret.push_back(a->id);
return ret;
}
diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp
index 3ac78e0709..c04fe7320d 100644
--- a/core/math/geometry_3d.cpp
+++ b/core/math/geometry_3d.cpp
@@ -748,7 +748,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
Vector3 right = p.normal.cross(ref).normalized();
Vector3 up = p.normal.cross(right).normalized();
- Vector3 center = p.center();
+ Vector3 center = p.get_center();
// make a quad clockwise
LocalVector<Vector3> vertices = {
diff --git a/core/math/plane.h b/core/math/plane.h
index 3bc7c54b9d..8159f25342 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -47,7 +47,7 @@ struct _NO_DISCARD_ Plane {
/* Plane-Point operations */
- _FORCE_INLINE_ Vector3 center() const { return normal * d; }
+ _FORCE_INLINE_ Vector3 get_center() const { return normal * d; }
Vector3 get_any_perpendicular_normal() const;
_FORCE_INLINE_ bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index a59129c3e2..8334a7eef6 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -33,7 +33,7 @@
#include "core/variant/binder_common.h"
-VARIANT_ENUM_CAST(MethodFlags)
+VARIANT_BITFIELD_CAST(MethodFlags)
// some helpers
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 8bc2f89c27..2cb56dfe6c 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1594,25 +1594,6 @@ bool Object::is_blocking_signals() const {
return _block_signals;
}
-void Object::get_translatable_strings(List<String> *p_strings) const {
- List<PropertyInfo> plist;
- get_property_list(&plist);
-
- for (const PropertyInfo &E : plist) {
- if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) {
- continue;
- }
-
- String text = get(E.name);
-
- if (text.is_empty()) {
- continue;
- }
-
- p_strings->push_back(text);
- }
-}
-
Variant::Type Object::get_static_property_type(const StringName &p_property, bool *r_valid) const {
bool valid;
Variant::Type t = ClassDB::get_property_type(get_class_name(), p_property, &valid);
diff --git a/core/object/object.h b/core/object/object.h
index 2ba86c9d31..f78c7c34fd 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -74,14 +74,6 @@ enum PropertyHint {
PROPERTY_HINT_OBJECT_ID,
PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose
PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts)
- PROPERTY_HINT_METHOD_OF_VARIANT_TYPE, ///< a method of a type
- PROPERTY_HINT_METHOD_OF_BASE_TYPE, ///< a method of a base type
- PROPERTY_HINT_METHOD_OF_INSTANCE, ///< a method of an instance
- PROPERTY_HINT_METHOD_OF_SCRIPT, ///< a method of a script & base
- PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type
- PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
- PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
- PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
@@ -95,16 +87,15 @@ enum PropertyHint {
PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor.
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_MAX,
- // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
enum PropertyUsageFlags {
PROPERTY_USAGE_NONE = 0,
PROPERTY_USAGE_STORAGE = 1 << 1,
PROPERTY_USAGE_EDITOR = 1 << 2,
- PROPERTY_USAGE_CHECKABLE = 1 << 3, // Used for editing global variables.
- PROPERTY_USAGE_CHECKED = 1 << 4, // Used for editing global variables.
- PROPERTY_USAGE_INTERNATIONALIZED = 1 << 5, // Hint for internationalized strings.
+ PROPERTY_USAGE_INTERNAL = 1 << 3,
+ PROPERTY_USAGE_CHECKABLE = 1 << 4, // Used for editing global variables.
+ PROPERTY_USAGE_CHECKED = 1 << 5, // Used for editing global variables.
PROPERTY_USAGE_GROUP = 1 << 6, // Used for grouping props in the editor.
PROPERTY_USAGE_CATEGORY = 1 << 7,
PROPERTY_USAGE_SUBGROUP = 1 << 8,
@@ -117,7 +108,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15,
PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16,
PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17,
- PROPERTY_USAGE_INTERNAL = 1 << 18,
+ PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array.
PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 19, // If the object is duplicated also this property will be duplicated.
PROPERTY_USAGE_HIGH_END_GFX = 1 << 20,
PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 21,
@@ -127,10 +118,8 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 25, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 26, //for project or editor settings, show when basic settings are selected.
PROPERTY_USAGE_READ_ONLY = 1 << 27, // Mark a property as read-only in the inspector.
- PROPERTY_USAGE_ARRAY = 1 << 28, // Used in the inspector to group properties as elements of an array.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
- PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNATIONALIZED,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
};
@@ -894,8 +883,6 @@ public:
Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = nullptr) const;
Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = nullptr) const;
- virtual void get_translatable_strings(List<String> *p_strings) const;
-
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
// Translate message (internationalization).
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 7be3a6c688..c9cfbdd4cb 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -186,6 +186,7 @@ void ScriptServer::unregister_language(const ScriptLanguage *p_language) {
void ScriptServer::init_languages() {
{ // Load global classes.
global_classes_clear();
+#ifndef DISABLE_DEPRECATED
if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
Array script_classes = GLOBAL_GET("_global_script_classes");
@@ -196,6 +197,17 @@ void ScriptServer::init_languages() {
}
add_global_class(c["class"], c["base"], c["language"], c["path"]);
}
+ ProjectSettings::get_singleton()->clear("_global_script_classes");
+ }
+#endif
+
+ Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary c = script_classes[i];
+ if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
+ continue;
+ }
+ add_global_class(c["class"], c["base"], c["language"], c["path"]);
}
}
@@ -291,6 +303,17 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
}
void ScriptServer::save_global_classes() {
+ Dictionary class_icons;
+
+ Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary d = script_classes[i];
+ if (!d.has("name") || !d.has("icon")) {
+ continue;
+ }
+ class_icons[d["name"]] = d["icon"];
+ }
+
List<StringName> gc;
get_global_class_list(&gc);
Array gcarr;
@@ -300,25 +323,10 @@ void ScriptServer::save_global_classes() {
d["language"] = global_classes[E].language;
d["path"] = global_classes[E].path;
d["base"] = global_classes[E].base;
+ d["icon"] = class_icons.get(E, "");
gcarr.push_back(d);
}
-
- Array old;
- if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
- old = GLOBAL_GET("_global_script_classes");
- }
- if ((!old.is_empty() || gcarr.is_empty()) && gcarr.hash() == old.hash()) {
- return;
- }
-
- if (gcarr.is_empty()) {
- if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
- ProjectSettings::get_singleton()->clear("_global_script_classes");
- }
- } else {
- ProjectSettings::get_singleton()->set("_global_script_classes", gcarr);
- }
- ProjectSettings::get_singleton()->save();
+ ProjectSettings::get_singleton()->store_global_class_list(gcarr);
}
////////////////////
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index b705d13cd6..912f2218c4 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -125,7 +125,8 @@ public:
}
Variant ret;
GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret);
- return ret;
+ r_value = ret;
+ return true;
}
EXBIND0(update_exports)
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index e0231b818f..5183b77cb1 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -63,12 +63,15 @@ static const _KeyCodeText _keycodes[] = {
{Key::CTRL ,"Ctrl"},
#if defined(MACOS_ENABLED)
{Key::META ,"Command"},
+ {Key::CMD_OR_CTRL ,"Command"},
{Key::ALT ,"Option"},
#elif defined(WINDOWS_ENABLED)
{Key::META ,"Windows"},
+ {Key::CMD_OR_CTRL ,"Ctrl"},
{Key::ALT ,"Alt"},
#else
{Key::META ,"Meta"},
+ {Key::CMD_OR_CTRL ,"Ctrl"},
{Key::ALT ,"Alt"},
#endif
{Key::CAPSLOCK ,"CapsLock"},
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 389baee915..c78fa2a631 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -65,6 +65,11 @@ enum class Key {
SHIFT = SPECIAL | 0x15,
CTRL = SPECIAL | 0x16,
META = SPECIAL | 0x17,
+#if defined(MACOS_ENABLED)
+ CMD_OR_CTRL = META,
+#else
+ CMD_OR_CTRL = CTRL,
+#endif
ALT = SPECIAL | 0x18,
CAPSLOCK = SPECIAL | 0x19,
NUMLOCK = SPECIAL | 0x1A,
diff --git a/core/os/os.h b/core/os/os.h
index 3c0c05f575..4818e9281a 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -131,7 +131,7 @@ public:
void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
- virtual String get_stdin_string(bool p_block = true) = 0;
+ virtual String get_stdin_string() = 0;
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
@@ -167,7 +167,8 @@ public:
virtual bool has_environment(const String &p_var) const = 0;
virtual String get_environment(const String &p_var) const = 0;
- virtual bool set_environment(const String &p_var, const String &p_value) const = 0;
+ virtual void set_environment(const String &p_var, const String &p_value) const = 0;
+ virtual void unset_environment(const String &p_var) const = 0;
virtual String get_name() const = 0;
virtual String get_distribution_name() const = 0;
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 81866ee7e4..700174bdae 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -84,6 +84,7 @@
static Ref<ResourceFormatSaverBinary> resource_saver_binary;
static Ref<ResourceFormatLoaderBinary> resource_loader_binary;
static Ref<ResourceFormatImporter> resource_format_importer;
+static Ref<ResourceFormatImporterSaver> resource_format_importer_saver;
static Ref<ResourceFormatLoaderImage> resource_format_image;
static Ref<TranslationLoaderPO> resource_format_po;
static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto;
@@ -144,6 +145,9 @@ void register_core_types() {
resource_format_importer.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_importer);
+ resource_format_importer_saver.instantiate();
+ ResourceSaver::add_resource_format_saver(resource_format_importer_saver);
+
resource_format_image.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_image);
@@ -389,6 +393,9 @@ void unregister_core_types() {
ResourceLoader::remove_resource_format_loader(resource_format_importer);
resource_format_importer.unref();
+ ResourceSaver::remove_resource_format_saver(resource_format_importer_saver);
+ resource_format_importer_saver.unref();
+
ResourceLoader::remove_resource_format_loader(resource_format_po);
resource_format_po.unref();
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 4d32e421cd..58ed019287 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -33,6 +33,10 @@
#include "core/typedefs.h"
+#ifdef DEV_ENABLED
+#include "core/error/error_macros.h"
+#endif
+
#include <atomic>
#include <type_traits>
@@ -163,6 +167,16 @@ public:
class SafeRefCount {
SafeNumeric<uint32_t> count;
+#ifdef DEV_ENABLED
+ _ALWAYS_INLINE_ void _check_unref_sanity() {
+ // This won't catch every misuse, but it's better than nothing.
+ CRASH_COND_MSG(count.get() == 0,
+ "Trying to unreference a SafeRefCount which is already zero is wrong and a symptom of it being misused.\n"
+ "Upon a SafeRefCount reaching zero any object whose lifetime is tied to it, as well as the ref count itself, must be destroyed.\n"
+ "Moreover, to guarantee that, no multiple threads should be racing to do the final unreferencing to zero.");
+ }
+#endif
+
public:
_ALWAYS_INLINE_ bool ref() { // true on success
return count.conditional_increment() != 0;
@@ -173,10 +187,16 @@ public:
}
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
+#ifdef DEV_ENABLED
+ _check_unref_sanity();
+#endif
return count.decrement() == 0;
}
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
+#ifdef DEV_ENABLED
+ _check_unref_sanity();
+#endif
return count.decrement();
}
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 0fecc2fe94..f8af78f3c1 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -54,16 +54,6 @@ void Array::_ref(const Array &p_from) const {
ERR_FAIL_COND(!_fp); // should NOT happen.
- if (unlikely(_fp->read_only != nullptr)) {
- // If p_from is a read-only array, just copy the contents to avoid further modification.
- _unref();
- _p = memnew(ArrayPrivate);
- _p->refcount.init();
- _p->array = _fp->array;
- _p->typed = _fp->typed;
- return;
- }
-
if (_fp == _p) {
return; // whatever it is, nothing to do here move along
}
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 358b3df0c5..81ac5adba7 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -146,22 +146,53 @@ VARIANT_ENUM_CAST(Side);
VARIANT_ENUM_CAST(ClockDirection);
VARIANT_ENUM_CAST(Corner);
VARIANT_ENUM_CAST(HatDir);
-VARIANT_ENUM_CAST(HatMask);
+VARIANT_BITFIELD_CAST(HatMask);
VARIANT_ENUM_CAST(JoyAxis);
VARIANT_ENUM_CAST(JoyButton);
-VARIANT_ENUM_CAST(Key);
-VARIANT_ENUM_CAST(KeyModifierMask);
+
VARIANT_ENUM_CAST(MIDIMessage);
VARIANT_ENUM_CAST(MouseButton);
+VARIANT_BITFIELD_CAST(MouseButtonMask);
VARIANT_ENUM_CAST(Orientation);
VARIANT_ENUM_CAST(HorizontalAlignment);
VARIANT_ENUM_CAST(VerticalAlignment);
VARIANT_ENUM_CAST(InlineAlignment);
VARIANT_ENUM_CAST(PropertyHint);
-VARIANT_ENUM_CAST(PropertyUsageFlags);
+VARIANT_BITFIELD_CAST(PropertyUsageFlags);
VARIANT_ENUM_CAST(Variant::Type);
VARIANT_ENUM_CAST(Variant::Operator);
+// Key
+
+VARIANT_ENUM_CAST(Key);
+VARIANT_BITFIELD_CAST(KeyModifierMask);
+
+static inline Key &operator|=(Key &a, BitField<KeyModifierMask> b) {
+ a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b.operator int64_t()));
+ return a;
+}
+
+static inline Key &operator&=(Key &a, BitField<KeyModifierMask> b) {
+ a = static_cast<Key>(static_cast<int>(a) & static_cast<int>(b.operator int64_t()));
+ return a;
+}
+
+static inline Key operator|(Key a, BitField<KeyModifierMask> b) {
+ return (Key)((int)a | (int)b.operator int64_t());
+}
+
+static inline Key operator&(Key a, BitField<KeyModifierMask> b) {
+ return (Key)((int)a & (int)b.operator int64_t());
+}
+
+static inline Key operator+(BitField<KeyModifierMask> a, Key b) {
+ return (Key)((int)a.operator int64_t() + (int)b);
+}
+
+static inline Key operator|(BitField<KeyModifierMask> a, Key b) {
+ return (Key)((int)a.operator int64_t() | (int)b);
+}
+
template <>
struct VariantCaster<char32_t> {
static _FORCE_INLINE_ char32_t cast(const Variant &p_variant) {
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 60549d567e..2f2acc55a6 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -117,6 +117,7 @@ Callable Callable::bindv(const Array &p_arguments) {
}
Callable Callable::unbind(int p_argcount) const {
+ ERR_FAIL_COND_V_MSG(p_argcount <= 0, Callable(*this), "Amount of unbind() arguments must be 1 or greater.");
return Callable(memnew(CallableCustomUnbind(*this, p_argcount)));
}
@@ -151,6 +152,35 @@ StringName Callable::get_method() const {
return method;
}
+int Callable::get_bound_arguments_count() const {
+ if (!is_null() && is_custom()) {
+ return custom->get_bound_arguments_count();
+ } else {
+ return 0;
+ }
+}
+
+void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const {
+ if (!is_null() && is_custom()) {
+ custom->get_bound_arguments(r_arguments, r_argcount);
+ } else {
+ r_arguments.clear();
+ r_argcount = 0;
+ }
+}
+
+Array Callable::get_bound_arguments() const {
+ Vector<Variant> arr;
+ int ac;
+ get_bound_arguments_ref(arr, ac);
+ Array ret;
+ ret.resize(arr.size());
+ for (int i = 0; i < arr.size(); i++) {
+ ret[i] = arr[i];
+ }
+ return ret;
+}
+
CallableCustom *Callable::get_custom() const {
ERR_FAIL_COND_V_MSG(!is_custom(), nullptr,
vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
@@ -358,6 +388,15 @@ const Callable *CallableCustom::get_base_comparator() const {
return nullptr;
}
+int CallableCustom::get_bound_arguments_count() const {
+ return 0;
+}
+
+void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
+ r_arguments = Vector<Variant>();
+ r_argcount = 0;
+}
+
CallableCustom::CallableCustom() {
ref_count.init();
}
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 43fef47ec0..0abbb64c0b 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -107,6 +107,9 @@ public:
ObjectID get_object_id() const;
StringName get_method() const;
CallableCustom *get_custom() const;
+ int get_bound_arguments_count() const;
+ void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below.
+ Array get_bound_arguments() const;
uint32_t hash() const;
@@ -147,6 +150,8 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;
+ virtual int get_bound_arguments_count() const;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const;
CallableCustom();
virtual ~CallableCustom() {}
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 4713a49303..5be91c6e11 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -87,6 +87,47 @@ const Callable *CallableCustomBind::get_base_comparator() const {
return &callable;
}
+int CallableCustomBind::get_bound_arguments_count() const {
+ return callable.get_bound_arguments_count() + binds.size();
+}
+
+void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
+ Vector<Variant> sub_args;
+ int sub_count;
+ callable.get_bound_arguments_ref(sub_args, sub_count);
+
+ if (sub_count == 0) {
+ r_arguments = binds;
+ r_argcount = binds.size();
+ return;
+ }
+
+ int new_count = sub_count + binds.size();
+ r_argcount = new_count;
+
+ if (new_count <= 0) {
+ // Removed more arguments than it adds.
+ r_arguments = Vector<Variant>();
+ return;
+ }
+
+ r_arguments.resize(new_count);
+
+ if (sub_count > 0) {
+ for (int i = 0; i < sub_count; i++) {
+ r_arguments.write[i] = sub_args[i];
+ }
+ for (int i = 0; i < binds.size(); i++) {
+ r_arguments.write[i + sub_count] = binds[i];
+ }
+ r_argcount = new_count;
+ } else {
+ for (int i = 0; i < binds.size() + sub_count; i++) {
+ r_arguments.write[i] = binds[i - sub_count];
+ }
+ }
+}
+
void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
for (int i = 0; i < p_argcount; i++) {
@@ -164,6 +205,25 @@ const Callable *CallableCustomUnbind::get_base_comparator() const {
return &callable;
}
+int CallableCustomUnbind::get_bound_arguments_count() const {
+ return callable.get_bound_arguments_count() - argcount;
+}
+
+void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
+ Vector<Variant> sub_args;
+ int sub_count;
+ callable.get_bound_arguments_ref(sub_args, sub_count);
+
+ r_argcount = sub_args.size() - argcount;
+
+ if (argcount >= sub_args.size()) {
+ r_arguments = Vector<Variant>();
+ } else {
+ sub_args.resize(sub_args.size() - argcount);
+ r_arguments = sub_args;
+ }
+}
+
void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
if (argcount > p_argcount) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index 2bdf7e4782..278ed335d0 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -51,7 +51,8 @@ public:
virtual ObjectID get_object() const override; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
-
+ virtual int get_bound_arguments_count() const override;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
Callable get_callable() { return callable; }
Vector<Variant> get_binds() { return binds; }
@@ -76,6 +77,8 @@ public:
virtual ObjectID get_object() const override; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
+ virtual int get_bound_arguments_count() const override;
+ virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
Callable get_callable() { return callable; }
int get_unbinds() { return argcount; }
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index c545109bd8..f87064a0d1 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -211,16 +211,6 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
}
void Dictionary::_ref(const Dictionary &p_from) const {
- if (unlikely(p_from._p->read_only != nullptr)) {
- // If p_from is a read-only dictionary, just copy the contents to avoid further modification.
- if (_p) {
- _unref();
- }
- _p = memnew(DictionaryPrivate);
- _p->refcount.init();
- _p->variant_map = p_from._p->variant_map;
- return;
- }
//make a copy first (thread safe)
if (!p_from._p->refcount.ref()) {
return; // couldn't copy
diff --git a/core/variant/type_info.h b/core/variant/type_info.h
index d3b507ba4d..e89658d25b 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -287,11 +287,14 @@ class BitField {
int64_t value = 0;
public:
- _FORCE_INLINE_ void set_flag(T p_flag) { value |= p_flag; }
- _FORCE_INLINE_ bool has_flag(T p_flag) const { return value & p_flag; }
- _FORCE_INLINE_ void clear_flag(T p_flag) { return value &= ~p_flag; }
+ _FORCE_INLINE_ void set_flag(T p_flag) { value |= (int64_t)p_flag; }
+ _FORCE_INLINE_ bool has_flag(T p_flag) const { return value & (int64_t)p_flag; }
+ _FORCE_INLINE_ bool is_empty() const { return value == 0; }
+ _FORCE_INLINE_ void clear_flag(T p_flag) { value &= ~(int64_t)p_flag; }
+ _FORCE_INLINE_ void clear() { value = 0; }
_FORCE_INLINE_ BitField() = default;
_FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; }
+ _FORCE_INLINE_ BitField(T p_value) { value = (int64_t)p_value; }
_FORCE_INLINE_ operator int64_t() const { return value; }
_FORCE_INLINE_ operator Variant() const { return value; }
};
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 7039a5d9c4..ca42738b05 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -3240,33 +3240,34 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
}
#define hash_compare_scalar(p_lhs, p_rhs) \
- ((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs))
-
-#define hash_compare_vector2(p_lhs, p_rhs) \
- (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \
- (hash_compare_scalar((p_lhs).y, (p_rhs).y))
-
-#define hash_compare_vector3(p_lhs, p_rhs) \
- (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \
- (hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
- (hash_compare_scalar((p_lhs).z, (p_rhs).z))
-#define hash_compare_vector4(p_lhs, p_rhs) \
- (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \
- (hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
- (hash_compare_scalar((p_lhs).z, (p_rhs).z)) && \
- (hash_compare_scalar((p_lhs).w, (p_rhs).w))
-
-#define hash_compare_quaternion(p_lhs, p_rhs) \
- (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \
- (hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
- (hash_compare_scalar((p_lhs).z, (p_rhs).z)) && \
- (hash_compare_scalar((p_lhs).w, (p_rhs).w))
-
-#define hash_compare_color(p_lhs, p_rhs) \
- (hash_compare_scalar((p_lhs).r, (p_rhs).r)) && \
- (hash_compare_scalar((p_lhs).g, (p_rhs).g)) && \
- (hash_compare_scalar((p_lhs).b, (p_rhs).b)) && \
- (hash_compare_scalar((p_lhs).a, (p_rhs).a))
+ (((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)))
+
+#define hash_compare_vector2(p_lhs, p_rhs) \
+ (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
+ hash_compare_scalar((p_lhs).y, (p_rhs).y))
+
+#define hash_compare_vector3(p_lhs, p_rhs) \
+ (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
+ hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
+ hash_compare_scalar((p_lhs).z, (p_rhs).z))
+
+#define hash_compare_vector4(p_lhs, p_rhs) \
+ (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
+ hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
+ hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
+ hash_compare_scalar((p_lhs).w, (p_rhs).w))
+
+#define hash_compare_quaternion(p_lhs, p_rhs) \
+ (hash_compare_scalar((p_lhs).x, (p_rhs).x) && \
+ hash_compare_scalar((p_lhs).y, (p_rhs).y) && \
+ hash_compare_scalar((p_lhs).z, (p_rhs).z) && \
+ hash_compare_scalar((p_lhs).w, (p_rhs).w))
+
+#define hash_compare_color(p_lhs, p_rhs) \
+ (hash_compare_scalar((p_lhs).r, (p_rhs).r) && \
+ hash_compare_scalar((p_lhs).g, (p_rhs).g) && \
+ hash_compare_scalar((p_lhs).b, (p_rhs).b) && \
+ hash_compare_scalar((p_lhs).a, (p_rhs).a))
#define hash_compare_packed_array(p_lhs, p_rhs, p_type, p_compare_func) \
const Vector<p_type> &l = PackedArrayRef<p_type>::get_array(p_lhs); \
@@ -3323,8 +3324,8 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
const Rect2 *l = reinterpret_cast<const Rect2 *>(_data._mem);
const Rect2 *r = reinterpret_cast<const Rect2 *>(p_variant._data._mem);
- return (hash_compare_vector2(l->position, r->position)) &&
- (hash_compare_vector2(l->size, r->size));
+ return hash_compare_vector2(l->position, r->position) &&
+ hash_compare_vector2(l->size, r->size);
} break;
case RECT2I: {
const Rect2i *l = reinterpret_cast<const Rect2i *>(_data._mem);
@@ -3338,7 +3339,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
Transform2D *r = p_variant._data._transform2d;
for (int i = 0; i < 3; i++) {
- if (!(hash_compare_vector2(l->columns[i], r->columns[i]))) {
+ if (!hash_compare_vector2(l->columns[i], r->columns[i])) {
return false;
}
}
@@ -3375,16 +3376,16 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
const Plane *l = reinterpret_cast<const Plane *>(_data._mem);
const Plane *r = reinterpret_cast<const Plane *>(p_variant._data._mem);
- return (hash_compare_vector3(l->normal, r->normal)) &&
- (hash_compare_scalar(l->d, r->d));
+ return hash_compare_vector3(l->normal, r->normal) &&
+ hash_compare_scalar(l->d, r->d);
} break;
case AABB: {
const ::AABB *l = _data._aabb;
const ::AABB *r = p_variant._data._aabb;
- return (hash_compare_vector3(l->position, r->position) &&
- (hash_compare_vector3(l->size, r->size)));
+ return hash_compare_vector3(l->position, r->position) &&
+ hash_compare_vector3(l->size, r->size);
} break;
@@ -3400,7 +3401,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
const Basis *r = p_variant._data._basis;
for (int i = 0; i < 3; i++) {
- if (!(hash_compare_vector3(l->rows[i], r->rows[i]))) {
+ if (!hash_compare_vector3(l->rows[i], r->rows[i])) {
return false;
}
}
@@ -3413,7 +3414,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
const Transform3D *r = p_variant._data._transform3d;
for (int i = 0; i < 3; i++) {
- if (!(hash_compare_vector3(l->basis.rows[i], r->basis.rows[i]))) {
+ if (!hash_compare_vector3(l->basis.rows[i], r->basis.rows[i])) {
return false;
}
}
@@ -3425,7 +3426,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
const Projection *r = p_variant._data._projection;
for (int i = 0; i < 4; i++) {
- if (!(hash_compare_vector4(l->columns[i], r->columns[i]))) {
+ if (!hash_compare_vector4(l->columns[i], r->columns[i])) {
return false;
}
}
@@ -3653,7 +3654,22 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
}
String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) {
- return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, p_argcount, ce);
+ Vector<Variant> binds;
+ int args_bound;
+ p_callable.get_bound_arguments_ref(binds, args_bound);
+ if (args_bound <= 0) {
+ return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, MAX(0, p_argcount + args_bound), ce);
+ } else {
+ Vector<const Variant *> argptrs;
+ argptrs.resize(p_argcount + binds.size());
+ for (int i = 0; i < p_argcount; i++) {
+ argptrs.write[i] = p_argptrs[i];
+ }
+ for (int i = 0; i < binds.size(); i++) {
+ argptrs.write[i + p_argcount] = &binds[i];
+ }
+ return get_call_error_text(p_callable.get_object(), p_callable.get_method(), (const Variant **)argptrs.ptr(), argptrs.size(), ce);
+ }
}
void Variant::register_types() {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 55bfa1f0f7..2cc0b3a8d7 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1923,7 +1923,7 @@ static void _register_variant_builtin_methods() {
/* Plane */
bind_method(Plane, normalized, sarray(), varray());
- bind_method(Plane, center, sarray(), varray());
+ bind_method(Plane, get_center, sarray(), varray());
bind_method(Plane, is_equal_approx, sarray("to_plane"), varray());
bind_method(Plane, is_finite, sarray(), varray());
bind_method(Plane, is_point_over, sarray("point"), varray());
@@ -2016,6 +2016,8 @@ static void _register_variant_builtin_methods() {
bind_method(Callable, get_object, sarray(), varray());
bind_method(Callable, get_object_id, sarray(), varray());
bind_method(Callable, get_method, sarray(), varray());
+ bind_method(Callable, get_bound_arguments_count, sarray(), varray());
+ bind_method(Callable, get_bound_arguments, sarray(), varray());
bind_method(Callable, hash, sarray(), varray());
bind_method(Callable, bindv, sarray("arguments"), varray());
bind_method(Callable, unbind, sarray("argcount"), varray());
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 39a039ffe8..87874deb8d 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -1358,6 +1358,7 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin
if (p_simple_tag) {
r_tag.name = "";
r_tag.fields.clear();
+ bool escaping = false;
if (p_stream->is_utf8()) {
CharString cs;
@@ -1368,7 +1369,15 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin
return ERR_PARSE_ERROR;
}
if (c == ']') {
- break;
+ if (escaping) {
+ escaping = false;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ escaping = true;
+ } else {
+ escaping = false;
}
cs += c;
}
@@ -1381,7 +1390,15 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin
return ERR_PARSE_ERROR;
}
if (c == ']') {
- break;
+ if (escaping) {
+ escaping = false;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ escaping = true;
+ } else {
+ escaping = false;
}
r_tag.name += String::chr(c);
}
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index b8d4495702..a74556d88f 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -1253,7 +1253,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
return _data._int > 0;
} break;
case FLOAT: {
- r_iter = 0;
+ r_iter = 0.0;
return _data._float > 0.0;
} break;
case VECTOR2: {
@@ -1457,7 +1457,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
return true;
} break;
case FLOAT: {
- int64_t idx = r_iter;
+ double idx = r_iter;
idx++;
if (idx >= _data._float) {
return false;
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 6b0dccd7fc..7e7cb07cef 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -494,7 +494,7 @@
<return type="bool" />
<param index="0" name="x" type="float" />
<description>
- Returns whether [code]x[/code] is a finite value, i.e. it is not [constant @GDScript.NAN], positive infinity, or negative infinity.
+ Returns whether [param x] is a finite value, i.e. it is not [constant @GDScript.NAN], positive infinity, or negative infinity.
</description>
</method>
<method name="is_inf">
@@ -529,7 +529,7 @@
<return type="bool" />
<param index="0" name="x" type="float" />
<description>
- Returns [code]true[/code] if [param x] is zero or almost zero.
+ Returns [code]true[/code] if [param x] is zero or almost zero. The comparison is done using a tolerance calculation with a small internal epsilon.
This function is faster than using [method is_equal_approx] with one value as zero.
</description>
</method>
@@ -2319,31 +2319,31 @@
<constant name="KEY_YDIAERESIS" value="255" enum="Key">
Ćæ key.
</constant>
- <constant name="KEY_CODE_MASK" value="8388607" enum="KeyModifierMask">
+ <constant name="KEY_CODE_MASK" value="8388607" enum="KeyModifierMask" is_bitfield="true">
Key Code mask.
</constant>
- <constant name="KEY_MODIFIER_MASK" value="532676608" enum="KeyModifierMask">
+ <constant name="KEY_MODIFIER_MASK" value="532676608" enum="KeyModifierMask" is_bitfield="true">
Modifier key mask.
</constant>
- <constant name="KEY_MASK_CMD_OR_CTRL" value="16777216" enum="KeyModifierMask">
+ <constant name="KEY_MASK_CMD_OR_CTRL" value="16777216" enum="KeyModifierMask" is_bitfield="true">
Automatically remapped to [constant KEY_META] on macOS and [constant KEY_CTRL] on other platforms, this mask is never set in the actual events, and should be used for key mapping only.
</constant>
- <constant name="KEY_MASK_SHIFT" value="33554432" enum="KeyModifierMask">
+ <constant name="KEY_MASK_SHIFT" value="33554432" enum="KeyModifierMask" is_bitfield="true">
Shift key mask.
</constant>
- <constant name="KEY_MASK_ALT" value="67108864" enum="KeyModifierMask">
+ <constant name="KEY_MASK_ALT" value="67108864" enum="KeyModifierMask" is_bitfield="true">
Alt or Option (on macOS) key mask.
</constant>
- <constant name="KEY_MASK_META" value="134217728" enum="KeyModifierMask">
+ <constant name="KEY_MASK_META" value="134217728" enum="KeyModifierMask" is_bitfield="true">
Command (on macOS) or Meta/Windows key mask.
</constant>
- <constant name="KEY_MASK_CTRL" value="268435456" enum="KeyModifierMask">
+ <constant name="KEY_MASK_CTRL" value="268435456" enum="KeyModifierMask" is_bitfield="true">
Ctrl key mask.
</constant>
- <constant name="KEY_MASK_KPAD" value="536870912" enum="KeyModifierMask">
+ <constant name="KEY_MASK_KPAD" value="536870912" enum="KeyModifierMask" is_bitfield="true">
Keypad key mask.
</constant>
- <constant name="KEY_MASK_GROUP_SWITCH" value="1073741824" enum="KeyModifierMask">
+ <constant name="KEY_MASK_GROUP_SWITCH" value="1073741824" enum="KeyModifierMask" is_bitfield="true">
Group Switch key mask.
</constant>
<constant name="MOUSE_BUTTON_NONE" value="0" enum="MouseButton">
@@ -2376,19 +2376,19 @@
<constant name="MOUSE_BUTTON_XBUTTON2" value="9" enum="MouseButton">
Extra mouse button 2. This is sometimes present, usually to the sides of the mouse.
</constant>
- <constant name="MOUSE_BUTTON_MASK_LEFT" value="1" enum="MouseButton">
+ <constant name="MOUSE_BUTTON_MASK_LEFT" value="1" enum="MouseButtonMask" is_bitfield="true">
Primary mouse button mask, usually for the left button.
</constant>
- <constant name="MOUSE_BUTTON_MASK_RIGHT" value="2" enum="MouseButton">
+ <constant name="MOUSE_BUTTON_MASK_RIGHT" value="2" enum="MouseButtonMask" is_bitfield="true">
Secondary mouse button mask, usually for the right button.
</constant>
- <constant name="MOUSE_BUTTON_MASK_MIDDLE" value="4" enum="MouseButton">
+ <constant name="MOUSE_BUTTON_MASK_MIDDLE" value="4" enum="MouseButtonMask" is_bitfield="true">
Middle mouse button mask.
</constant>
- <constant name="MOUSE_BUTTON_MASK_XBUTTON1" value="128" enum="MouseButton">
+ <constant name="MOUSE_BUTTON_MASK_MB_XBUTTON1" value="128" enum="MouseButtonMask" is_bitfield="true">
Extra mouse button 1 mask.
</constant>
- <constant name="MOUSE_BUTTON_MASK_XBUTTON2" value="256" enum="MouseButton">
+ <constant name="MOUSE_BUTTON_MASK_MB_XBUTTON2" value="256" enum="MouseButtonMask" is_bitfield="true">
Extra mouse button 2 mask.
</constant>
<constant name="JOY_BUTTON_INVALID" value="-1" enum="JoyButton">
@@ -2794,157 +2794,135 @@
</constant>
<constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="24" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="25" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="25" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="26" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="26" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="27" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_SAVE_FILE" value="27" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="28" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="28" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="29" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="29" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="30" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_POINTER" value="30" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="31" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_ARRAY_TYPE" value="31" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="32" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="33" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="34" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_SAVE_FILE" value="35" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="36" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="37" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_INT_IS_POINTER" value="38" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint">
- </constant>
- <constant name="PROPERTY_HINT_LOCALE_ID" value="40" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALE_ID" value="32" enum="PropertyHint">
Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country.
</constant>
- <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="41" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="33" enum="PropertyHint">
Hints that a dictionary property is string translation map. Dictionary keys are locale codes and, values are translated strings.
</constant>
- <constant name="PROPERTY_HINT_NODE_TYPE" value="42" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_NODE_TYPE" value="34" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_HIDE_QUATERNION_EDIT" value="43" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_HIDE_QUATERNION_EDIT" value="35" enum="PropertyHint">
Hints that a quaternion property should disable the temporary euler editor.
</constant>
- <constant name="PROPERTY_HINT_PASSWORD" value="44" enum="PropertyHint">
+ <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="45" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_MAX" value="37" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
The property is not stored, and does not display in the editor. This is the default for non-exported properties.
</constant>
- <constant name="PROPERTY_USAGE_STORAGE" value="2" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_STORAGE" value="2" enum="PropertyUsageFlags" is_bitfield="true">
The property is serialized and saved in the scene file (default).
</constant>
- <constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags" is_bitfield="true">
The property is shown in the [EditorInspector] (default).
</constant>
- <constant name="PROPERTY_USAGE_CHECKABLE" value="8" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_INTERNAL" value="8" enum="PropertyUsageFlags" is_bitfield="true">
+ </constant>
+ <constant name="PROPERTY_USAGE_CHECKABLE" value="16" enum="PropertyUsageFlags" is_bitfield="true">
The property can be checked in the [EditorInspector].
</constant>
- <constant name="PROPERTY_USAGE_CHECKED" value="16" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_CHECKED" value="32" enum="PropertyUsageFlags" is_bitfield="true">
The property is checked in the [EditorInspector].
</constant>
- <constant name="PROPERTY_USAGE_INTERNATIONALIZED" value="32" enum="PropertyUsageFlags">
- The property is a translatable string.
- </constant>
- <constant name="PROPERTY_USAGE_GROUP" value="64" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_GROUP" value="64" enum="PropertyUsageFlags" is_bitfield="true">
Used to group properties together in the editor. See [EditorInspector].
</constant>
- <constant name="PROPERTY_USAGE_CATEGORY" value="128" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_CATEGORY" value="128" enum="PropertyUsageFlags" is_bitfield="true">
Used to categorize properties together in the editor.
</constant>
- <constant name="PROPERTY_USAGE_SUBGROUP" value="256" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_SUBGROUP" value="256" enum="PropertyUsageFlags" is_bitfield="true">
Used to group properties together in the editor in a subgroup (under a group). See [EditorInspector].
</constant>
- <constant name="PROPERTY_USAGE_CLASS_IS_BITFIELD" value="512" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_CLASS_IS_BITFIELD" value="512" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_NO_INSTANCE_STATE" value="1024" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_NO_INSTANCE_STATE" value="1024" enum="PropertyUsageFlags" is_bitfield="true">
The property does not save its state in [PackedScene].
</constant>
- <constant name="PROPERTY_USAGE_RESTART_IF_CHANGED" value="2048" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_RESTART_IF_CHANGED" value="2048" enum="PropertyUsageFlags" is_bitfield="true">
Editing the property prompts the user for restarting the editor.
</constant>
- <constant name="PROPERTY_USAGE_SCRIPT_VARIABLE" value="4096" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_SCRIPT_VARIABLE" value="4096" enum="PropertyUsageFlags" is_bitfield="true">
The property is a script variable which should be serialized and saved in the scene file.
</constant>
- <constant name="PROPERTY_USAGE_STORE_IF_NULL" value="8192" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_STORE_IF_NULL" value="8192" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED" value="16384" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED" value="16384" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" value="32768" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" value="32768" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_CLASS_IS_ENUM" value="65536" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_CLASS_IS_ENUM" value="65536" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_NIL_IS_VARIANT" value="131072" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_NIL_IS_VARIANT" value="131072" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_INTERNAL" value="262144" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_ARRAY" value="262144" enum="PropertyUsageFlags" is_bitfield="true">
+ The property is an array.
</constant>
- <constant name="PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE" value="524288" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE" value="524288" enum="PropertyUsageFlags" is_bitfield="true">
If the property is a [Resource], a new copy of it is always created when calling [method Node.duplicate] or [method Resource.duplicate].
</constant>
- <constant name="PROPERTY_USAGE_HIGH_END_GFX" value="1048576" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_HIGH_END_GFX" value="1048576" enum="PropertyUsageFlags" is_bitfield="true">
The property is only shown in the editor if modern renderers are supported (GLES3 is excluded).
</constant>
- <constant name="PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" value="2097152" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" value="2097152" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" value="4194304" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT" value="4194304" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_KEYING_INCREMENTS" value="8388608" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_KEYING_INCREMENTS" value="8388608" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="16777216" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="16777216" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" value="33554432" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" value="33554432" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="67108864" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="67108864" enum="PropertyUsageFlags" is_bitfield="true">
</constant>
- <constant name="PROPERTY_USAGE_READ_ONLY" value="134217728" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_READ_ONLY" value="134217728" enum="PropertyUsageFlags" is_bitfield="true">
The property is read-only in the [EditorInspector].
</constant>
- <constant name="PROPERTY_USAGE_ARRAY" value="268435456" enum="PropertyUsageFlags">
- The property is an array.
- </constant>
- <constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true">
Default usage (storage, editor and network).
</constant>
- <constant name="PROPERTY_USAGE_DEFAULT_INTL" value="38" enum="PropertyUsageFlags">
- Default usage for translatable strings (storage, editor, network and internationalized).
- </constant>
- <constant name="PROPERTY_USAGE_NO_EDITOR" value="2" enum="PropertyUsageFlags">
+ <constant name="PROPERTY_USAGE_NO_EDITOR" value="2" enum="PropertyUsageFlags" is_bitfield="true">
Default usage but without showing the property in the editor (storage, network).
</constant>
- <constant name="METHOD_FLAG_NORMAL" value="1" enum="MethodFlags">
+ <constant name="METHOD_FLAG_NORMAL" value="1" enum="MethodFlags" is_bitfield="true">
Flag for a normal method.
</constant>
- <constant name="METHOD_FLAG_EDITOR" value="2" enum="MethodFlags">
+ <constant name="METHOD_FLAG_EDITOR" value="2" enum="MethodFlags" is_bitfield="true">
Flag for an editor method.
</constant>
- <constant name="METHOD_FLAG_CONST" value="4" enum="MethodFlags">
+ <constant name="METHOD_FLAG_CONST" value="4" enum="MethodFlags" is_bitfield="true">
Flag for a constant method.
</constant>
- <constant name="METHOD_FLAG_VIRTUAL" value="8" enum="MethodFlags">
+ <constant name="METHOD_FLAG_VIRTUAL" value="8" enum="MethodFlags" is_bitfield="true">
Flag for a virtual method.
</constant>
- <constant name="METHOD_FLAG_VARARG" value="16" enum="MethodFlags">
+ <constant name="METHOD_FLAG_VARARG" value="16" enum="MethodFlags" is_bitfield="true">
Flag for a method with a variable number of arguments.
</constant>
- <constant name="METHOD_FLAG_STATIC" value="32" enum="MethodFlags">
+ <constant name="METHOD_FLAG_STATIC" value="32" enum="MethodFlags" is_bitfield="true">
Flag for a static method.
</constant>
- <constant name="METHOD_FLAG_OBJECT_CORE" value="64" enum="MethodFlags">
+ <constant name="METHOD_FLAG_OBJECT_CORE" value="64" enum="MethodFlags" is_bitfield="true">
Used internally. Allows to not dump core virtual methods (such as [method Object._notification]) to the JSON API.
</constant>
- <constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags">
+ <constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags" is_bitfield="true">
Default method flags (normal).
</constant>
<constant name="TYPE_NIL" value="0" enum="Variant.Type">
diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml
index e20fb71c7e..cd9c0cee98 100644
--- a/doc/classes/AnimatedSprite2D.xml
+++ b/doc/classes/AnimatedSprite2D.xml
@@ -7,7 +7,6 @@
[AnimatedSprite2D] is similar to the [Sprite2D] node, except it carries multiple textures as animation frames. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel.
After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor.
To pause the current animation, set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time.
- [b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps.
</description>
<tutorials>
<link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 9efd3ac939..ca0cbf0ca1 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -137,6 +137,13 @@
Returns [code]true[/code] if playing an animation.
</description>
</method>
+ <method name="pause">
+ <return type="void" />
+ <description>
+ Pauses the currently playing animation. The [member current_animation_position] will be kept and calling [method play] or [method play_backwards] without arguments or with the same animation name as [member assigned_animation] will resume the animation.
+ See also [method stop].
+ </description>
+ </method>
<method name="play">
<return type="void" />
<param index="0" name="name" type="StringName" default="&quot;&quot;" />
@@ -201,10 +208,9 @@
</method>
<method name="stop">
<return type="void" />
- <param index="0" name="reset" type="bool" default="true" />
<description>
- Stops or pauses the currently playing animation. If [param reset] is [code]true[/code], the animation position is reset to [code]0[/code] and the playback speed is reset to [code]1.0[/code].
- If [param reset] is [code]false[/code], the [member current_animation_position] will be kept and calling [method play] or [method play_backwards] without arguments or with the same animation name as [member assigned_animation] will resume the animation.
+ Stops the currently playing animation. The animation position is reset to [code]0[/code] and the playback speed is reset to [code]1.0[/code].
+ See also [method pause].
</description>
</method>
</methods>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index ee21493434..21ccf79fe2 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -244,7 +244,7 @@
<param index="0" name="deep" type="bool" default="false" />
<description>
Returns a copy of the array.
- If [param deep] is [code]true[/code], a deep copy is performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array.
+ If [param deep] is [code]true[/code], a deep copy is performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array. Note that any [Object]-derived elements will be shallow copied regardless of the [param deep] setting.
</description>
</method>
<method name="erase">
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 70ce4ad516..f7764d5e32 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -65,7 +65,7 @@
<param index="1" name="arrays" type="Array" />
<param index="2" name="blend_shapes" type="Array[]" default="[]" />
<param index="3" name="lods" type="Dictionary" default="{}" />
- <param index="4" name="compress_flags" type="int" default="0" />
+ <param index="4" name="compress_flags" type="int" enum="Mesh.ArrayFormat" default="0" />
<description>
Creates a new surface.
Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
@@ -141,7 +141,7 @@
</description>
</method>
<method name="surface_get_format" qualifiers="const">
- <return type="int" />
+ <return type="int" enum="Mesh.ArrayFormat" />
<param index="0" name="surf_idx" type="int" />
<description>
Returns the format mask of the requested surface (see [method add_surface_from_arrays]).
diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml
index c92e39e08a..68f62d1c00 100644
--- a/doc/classes/BaseButton.xml
+++ b/doc/classes/BaseButton.xml
@@ -50,7 +50,7 @@
<member name="button_group" type="ButtonGroup" setter="set_button_group" getter="get_button_group">
The [ButtonGroup] associated with the button. Not to be confused with node groups.
</member>
- <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask" enum="MouseButton" default="1">
+ <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask" enum="MouseButtonMask" default="1">
Binary mask to choose which mouse buttons this button will respond to.
To allow both left-click and right-click, use [code]MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT[/code].
</member>
@@ -70,7 +70,7 @@
[Shortcut] associated to the button.
</member>
<member name="shortcut_feedback" type="bool" setter="set_shortcut_feedback" getter="is_shortcut_feedback" default="true">
- If [code]true[/code], the button will appear pressed when its shortcut is activated. If [code]false[/code] and [member toggle_mode] is [code]false[/code], the shortcut will activate the button without appearing to press the button.
+ If [code]true[/code], the button will highlight for a short amount of time when its shortcut is activated. If [code]false[/code] and [member toggle_mode] is [code]false[/code], the shortcut will activate without any visual feedback.
</member>
<member name="shortcut_in_tooltip" type="bool" setter="set_shortcut_in_tooltip" getter="is_shortcut_in_tooltip_enabled" default="true">
If [code]true[/code], the button will add information about its shortcut in the tooltip.
diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml
index f29525038e..dd01de3607 100644
--- a/doc/classes/BoneAttachment3D.xml
+++ b/doc/classes/BoneAttachment3D.xml
@@ -16,19 +16,6 @@
Returns the [NodePath] to the external [Skeleton3D] node, if one has been set.
</description>
</method>
- <method name="get_override_mode" qualifiers="const" is_deprecated="true">
- <return type="int" />
- <description>
- Deprecated. Local pose overrides will be removed.
- Returns the override mode for the BoneAttachment3D node (0=global / 1=local).
- </description>
- </method>
- <method name="get_override_pose" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the BoneAttachment3D node is overriding the bone pose of the bone it's attached to.
- </description>
- </method>
<method name="get_use_external_skeleton" qualifiers="const">
<return type="bool" />
<description>
@@ -49,21 +36,6 @@
Sets the [NodePath] to the external skeleton that the BoneAttachment3D node should use. The external [Skeleton3D] node is only used when [code]use_external_skeleton[/code] is set to [code]true[/code].
</description>
</method>
- <method name="set_override_mode" is_deprecated="true">
- <return type="void" />
- <param index="0" name="override_mode" type="int" />
- <description>
- Deprecated. Local pose overrides will be removed.
- Sets the override mode for the BoneAttachment3D node (0=global / 1=local). The override mode defines which of the bone poses the BoneAttachment3D node will override.
- </description>
- </method>
- <method name="set_override_pose">
- <return type="void" />
- <param index="0" name="override_pose" type="bool" />
- <description>
- Sets whether the BoneAttachment3D node will override the bone pose of the bone it is attached to. When set to [code]true[/code], the BoneAttachment3D node can change the pose of the bone.
- </description>
- </method>
<method name="set_use_external_skeleton">
<return type="void" />
<param index="0" name="use_external_skeleton" type="bool" />
@@ -79,5 +51,8 @@
<member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="&quot;&quot;">
The name of the attached bone.
</member>
+ <member name="override_pose" type="bool" setter="set_override_pose" getter="get_override_pose" default="false">
+ Whether the BoneAttachment3D node will override the bone pose of the bone it is attached to. When set to [code]true[/code], the BoneAttachment3D node can change the pose of the bone. When set to [code]false[/code], the BoneAttachment3D will always be set to the bone's transform.
+ </member>
</members>
</class>
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index 5c845ba57e..79e65f3472 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -14,7 +14,7 @@
func test():
var callable = Callable(self, "print_args")
callable.call("hello", "world") # Prints "hello world ".
- callable.call(Vector2.UP, 42, callable) # Prints "(0, -1) 42 Node(Node.gd)::print_args".
+ callable.call(Vector2.UP, 42, callable) # Prints "(0, -1) 42 Node(node.gd)::print_args".
callable.call("invalid") # Invalid call, should have at least 2 arguments.
[/gdscript]
[csharp]
@@ -107,6 +107,18 @@
Calls the method represented by this [Callable]. Unlike [method call], this method expects all arguments to be contained inside the [param arguments] [Array].
</description>
</method>
+ <method name="get_bound_arguments" qualifiers="const">
+ <return type="Array" />
+ <description>
+ Return the bound arguments (as long as [method get_bound_arguments_count] is greater than zero), or empty (if [method get_bound_arguments_count] is less than or equal to zero).
+ </description>
+ </method>
+ <method name="get_bound_arguments_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the total amount of arguments bound (or unbound) via successive [method bind] or [method unbind] calls. If the amount of arguments unbound is greater than the ones bound, this function returns a value less than zero.
+ </description>
+ </method>
<method name="get_method" qualifiers="const">
<return type="StringName" />
<description>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 5171e0acbe..e79bb97a92 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -95,10 +95,12 @@
<param index="0" name="from" type="Vector2" />
<param index="1" name="to" type="Vector2" />
<param index="2" name="color" type="Color" />
- <param index="3" name="width" type="float" default="1.0" />
+ <param index="3" name="width" type="float" default="-1.0" />
<param index="4" name="dash" type="float" default="2.0" />
+ <param index="5" name="aligned" type="bool" default="true" />
<description>
Draws a dashed line from a 2D point to another, with a given color and width. See also [method draw_multiline] and [method draw_polyline].
+ If [param width] is negative, then a two-point primitives will be drawn instead of a four-point ones. This means that when the CanvasItem is scaled, the line parts will remain thin. If this behavior is not desired, then pass a positive [param width] like [code]1.0[/code].
</description>
</method>
<method name="draw_end_animation">
@@ -129,10 +131,11 @@
<param index="0" name="from" type="Vector2" />
<param index="1" name="to" type="Vector2" />
<param index="2" name="color" type="Color" />
- <param index="3" name="width" type="float" default="1.0" />
+ <param index="3" name="width" type="float" default="-1.0" />
<param index="4" name="antialiased" type="bool" default="false" />
<description>
Draws a line from a 2D point to another, with a given color and width. It can be optionally antialiased. See also [method draw_multiline] and [method draw_polyline].
+ If [param width] is negative, then a two-point primitive will be drawn instead of a four-point one. This means that when the CanvasItem is scaled, the line will remain thin. If this behavior is not desired, then pass a positive [param width] like [code]1.0[/code].
</description>
</method>
<method name="draw_mesh">
@@ -164,18 +167,20 @@
<return type="void" />
<param index="0" name="points" type="PackedVector2Array" />
<param index="1" name="color" type="Color" />
- <param index="2" name="width" type="float" default="1.0" />
+ <param index="2" name="width" type="float" default="-1.0" />
<description>
Draws multiple disconnected lines with a uniform [param color]. When drawing large amounts of lines, this is faster than using individual [method draw_line] calls. To draw interconnected lines, use [method draw_polyline] instead.
+ 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].
</description>
</method>
<method name="draw_multiline_colors">
<return type="void" />
<param index="0" name="points" type="PackedVector2Array" />
<param index="1" name="colors" type="PackedColorArray" />
- <param index="2" name="width" type="float" default="1.0" />
+ <param index="2" name="width" type="float" default="-1.0" />
<description>
Draws multiple disconnected lines with a uniform [param width] and segment-by-segment coloring. Colors assigned to line segments match by index between [param points] and [param colors]. When drawing large amounts of lines, this is faster than using individual [method draw_line] calls. To draw interconnected lines, use [method draw_polyline_colors] instead.
+ 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].
</description>
</method>
<method name="draw_multiline_string" qualifiers="const">
@@ -259,7 +264,6 @@
<param index="1" name="colors" type="PackedColorArray" />
<param index="2" name="uvs" type="PackedVector2Array" />
<param index="3" name="texture" type="Texture2D" default="null" />
- <param index="4" name="width" type="float" default="1.0" />
<description>
Draws a custom primitive. 1 point for a point, 2 points for a line, 3 points for a triangle, and 4 points for a quad. If 0 points or more than 4 points are specified, nothing will be drawn and an error message will be printed. See also [method draw_line], [method draw_polyline], [method draw_polygon], and [method draw_rect].
</description>
@@ -269,10 +273,12 @@
<param index="0" name="rect" type="Rect2" />
<param index="1" name="color" type="Color" />
<param index="2" name="filled" type="bool" default="true" />
- <param index="3" name="width" type="float" default="1.0" />
+ <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.
+ 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]).
</description>
</method>
<method name="draw_set_transform">
diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml
index c302963b92..31b5842930 100644
--- a/doc/classes/CollisionObject3D.xml
+++ b/doc/classes/CollisionObject3D.xml
@@ -216,13 +216,13 @@
<signal name="mouse_entered">
<description>
Emitted when the mouse pointer enters any of this object's shapes. Requires [member input_ray_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set.
- [b]Note:[/b] Due to the lack of continuous collision detection, this signal may not be emitted in the expected order if the mouse moves fast enough and the [CollisionObject2D]'s area is small. This signal may also not be emitted if another [CollisionObject2D] is overlapping the [CollisionObject2D] in question.
+ [b]Note:[/b] Due to the lack of continuous collision detection, this signal may not be emitted in the expected order if the mouse moves fast enough and the [CollisionObject3D]'s area is small. This signal may also not be emitted if another [CollisionObject3D] is overlapping the [CollisionObject3D] in question.
</description>
</signal>
<signal name="mouse_exited">
<description>
Emitted when the mouse pointer exits all this object's shapes. Requires [member input_ray_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set.
- [b]Note:[/b] Due to the lack of continuous collision detection, this signal may not be emitted in the expected order if the mouse moves fast enough and the [CollisionObject2D]'s area is small. This signal may also not be emitted if another [CollisionObject2D] is overlapping the [CollisionObject2D] in question.
+ [b]Note:[/b] Due to the lack of continuous collision detection, this signal may not be emitted in the expected order if the mouse moves fast enough and the [CollisionObject3D]'s area is small. This signal may also not be emitted if another [CollisionObject3D] is overlapping the [CollisionObject3D] in question.
</description>
</signal>
</signals>
@@ -232,7 +232,7 @@
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_MAKE_STATIC" value="1" enum="DisableMode">
- When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area2D]. [PhysicsBody3D] can't be affected by forces or other bodies while static.
+ When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area3D]. [PhysicsBody3D] can't be affected by forces or other bodies while static.
Automatically set [PhysicsBody3D] back to its original mode when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="2" enum="DisableMode">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index b0351559ee..75afb0cdbf 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -180,14 +180,14 @@
[codeblocks]
[gdscript]
func _make_custom_tooltip(for_text):
- var tooltip = preload("res://SomeTooltipScene.tscn").instantiate()
+ var tooltip = preload("res://some_tooltip_scene.tscn").instantiate()
tooltip.get_node("Label").text = for_text
return tooltip
[/gdscript]
[csharp]
public override Godot.Control _MakeCustomTooltip(String forText)
{
- Node tooltip = ResourceLoader.Load&lt;PackedScene&gt;("res://SomeTooltipScene.tscn").Instantiate();
+ Node tooltip = ResourceLoader.Load&lt;PackedScene&gt;("res://some_tooltip_scene.tscn").Instantiate();
tooltip.GetNode&lt;Label&gt;("Label").Text = forText;
return tooltip;
}
@@ -780,67 +780,13 @@
</method>
<method name="set_drag_forwarding">
<return type="void" />
- <param index="0" name="target" type="Object" />
+ <param index="0" name="drag_func" type="Callable" />
+ <param index="1" name="can_drop_func" type="Callable" />
+ <param index="2" name="drop_func" type="Callable" />
<description>
- Forwards the handling of this control's drag and drop to [param target] object.
- Forwarding can be implemented in the target object similar to the methods [method _get_drag_data], [method _can_drop_data], and [method _drop_data] but with two differences:
- 1. The function name must be suffixed with [b]_fw[/b]
- 2. The function must take an extra argument that is the control doing the forwarding
- [codeblocks]
- [gdscript]
- # ThisControl.gd
- extends Control
- export(Control) var target_control
-
- func _ready():
- set_drag_forwarding(target_control)
-
- # TargetControl.gd
- extends Control
-
- func _can_drop_data_fw(position, data, from_control):
- return true
-
- func _drop_data_fw(position, data, from_control):
- my_handle_data(data) # Your handler method.
-
- func _get_drag_data_fw(position, from_control):
- set_drag_preview(my_preview)
- return my_data()
- [/gdscript]
- [csharp]
- // ThisControl.cs
- public class ThisControl : Control
- {
- [Export]
- public Control TargetControl { get; set; }
- public override void _Ready()
- {
- SetDragForwarding(TargetControl);
- }
- }
-
- // TargetControl.cs
- public class TargetControl : Control
- {
- public void CanDropDataFw(Vector2 position, object data, Control fromControl)
- {
- return true;
- }
-
- public void DropDataFw(Vector2 position, object data, Control fromControl)
- {
- MyHandleData(data); // Your handler method.
- }
-
- public void GetDragDataFw(Vector2 position, Control fromControl)
- {
- SetDragPreview(MyPreview);
- return MyData();
- }
- }
- [/csharp]
- [/codeblocks]
+ Forwards the handling of this control's [method _get_drag_data], [method _can_drop_data] and [method _drop_data] virtual functions to delegate callables.
+ For each argument, if not empty, the delegate callable is used, otherwise the local (virtual) function is used.
+ The function format for each callable should be exactly the same as the virtual functions described above.
</description>
</method>
<method name="set_drag_preview">
@@ -1064,13 +1010,13 @@
<member name="size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)">
The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically.
</member>
- <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" default="1">
+ <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" enum="Control.SizeFlags" default="1">
Tells the parent [Container] nodes how they should resize and place the node on the X axis. Use one of the [enum SizeFlags] constants to change the flags. See the constants to learn what each does.
</member>
<member name="size_flags_stretch_ratio" type="float" setter="set_stretch_ratio" getter="get_stretch_ratio" default="1.0">
If the node and at least one of its neighbors uses the [constant SIZE_EXPAND] size flag, the parent [Container] will let it take more or less space depending on this property. If this node has a stretch ratio of 2 and its neighbor a ratio of 1, this node will take two thirds of the available space.
</member>
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" default="1">
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" enum="Control.SizeFlags" default="1">
Tells the parent [Container] nodes how they should resize and place the node on the Y axis. Use one of the [enum SizeFlags] constants to change the flags. See the constants to learn what each does.
</member>
<member name="theme" type="Theme" setter="set_theme" getter="get_theme">
@@ -1315,23 +1261,23 @@
<constant name="PRESET_MODE_KEEP_SIZE" value="3" enum="LayoutPresetMode">
The control's size will not change.
</constant>
- <constant name="SIZE_SHRINK_BEGIN" value="0" enum="SizeFlags">
+ <constant name="SIZE_SHRINK_BEGIN" value="0" enum="SizeFlags" is_bitfield="true">
Tells the parent [Container] to align the node with its start, either the top or the left edge. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
[b]Note:[/b] Setting this flag is equal to not having any size flags.
</constant>
- <constant name="SIZE_FILL" value="1" enum="SizeFlags">
+ <constant name="SIZE_FILL" value="1" enum="SizeFlags" is_bitfield="true">
Tells the parent [Container] to expand the bounds of this node to fill all the available space without pushing any other node. It is mutually exclusive with shrink size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
- <constant name="SIZE_EXPAND" value="2" enum="SizeFlags">
+ <constant name="SIZE_EXPAND" value="2" enum="SizeFlags" is_bitfield="true">
Tells the parent [Container] to let this node take all the available space on the axis you flag. If multiple neighboring nodes are set to expand, they'll share the space based on their stretch ratio. See [member size_flags_stretch_ratio]. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
- <constant name="SIZE_EXPAND_FILL" value="3" enum="SizeFlags">
+ <constant name="SIZE_EXPAND_FILL" value="3" enum="SizeFlags" is_bitfield="true">
Sets the node's size flags to both fill and expand. See [constant SIZE_FILL] and [constant SIZE_EXPAND] for more information.
</constant>
- <constant name="SIZE_SHRINK_CENTER" value="4" enum="SizeFlags">
+ <constant name="SIZE_SHRINK_CENTER" value="4" enum="SizeFlags" is_bitfield="true">
Tells the parent [Container] to center the node in the available space. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
- <constant name="SIZE_SHRINK_END" value="8" enum="SizeFlags">
+ <constant name="SIZE_SHRINK_END" value="8" enum="SizeFlags" is_bitfield="true">
Tells the parent [Container] to align the node with its end, either the bottom or the right edge. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="MOUSE_FILTER_STOP" value="0" enum="MouseFilter">
diff --git a/doc/classes/ConvexPolygonShape3D.xml b/doc/classes/ConvexPolygonShape3D.xml
index 32dc8f673b..66d2280280 100644
--- a/doc/classes/ConvexPolygonShape3D.xml
+++ b/doc/classes/ConvexPolygonShape3D.xml
@@ -4,7 +4,7 @@
Convex polygon shape resource for 3D physics.
</brief_description>
<description>
- 3D convex polygon shape resource to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node. Unlike [ConcavePolygonShape3D], [ConvexPolygonShape3D] cannot store concave polygon shapes. [ConvexPolygonShape2D]s can be manually drawn in the editor using the [CollisionPolygon3D] node.
+ 3D convex polygon shape resource to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node. Unlike [ConcavePolygonShape3D], [ConvexPolygonShape3D] cannot store concave polygon shapes. [ConvexPolygonShape3D]s can be manually drawn in the editor using the [CollisionPolygon3D] node.
[b]Convex decomposition:[/b] Concave objects' collisions can be represented accurately using [i]several[/i] [ConvexPolygonShape3D]s. This allows dynamic physics bodies to have complex concave collisions (at a performance cost). This is available in the editor by selecting the [MeshInstance3D], going to the [b]Mesh[/b] menu and choosing [b]Create Multiple Convex Collision Siblings[/b]. Alternatively, [method MeshInstance3D.create_multiple_convex_collisions] can be called in a script to perform this decomposition at run-time.
[b]Performance:[/b] [ConvexPolygonShape3D] is faster to check collisions against compared to [ConcavePolygonShape3D], but it is slower than primitive collision shapes such as [SphereShape3D] or [BoxShape3D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by a primitive shape.
</description>
diff --git a/doc/classes/DTLSServer.xml b/doc/classes/DTLSServer.xml
index 9af8be99ef..457513b8aa 100644
--- a/doc/classes/DTLSServer.xml
+++ b/doc/classes/DTLSServer.xml
@@ -8,7 +8,7 @@
Below a small example of how to use it:
[codeblocks]
[gdscript]
- # ServerNode.gd
+ # server_node.gd
extends Node
var dtls := DTLSServer.new()
@@ -86,7 +86,7 @@
[/codeblocks]
[codeblocks]
[gdscript]
- # ClientNode.gd
+ # client_node.gd
extends Node
var dtls := PacketPeerDTLS.new()
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 98e53b86f9..832adb6e98 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -785,7 +785,7 @@
</description>
</method>
<method name="mouse_get_button_state" qualifiers="const">
- <return type="int" enum="MouseButton" />
+ <return type="int" enum="MouseButtonMask" />
<description>
Returns the current state of mouse buttons (whether each button is pressed) as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method Input.get_mouse_button_mask].
</description>
@@ -1525,7 +1525,7 @@
Makes the mouse cursor hidden if it is visible.
</constant>
<constant name="MOUSE_MODE_CAPTURED" value="2" enum="MouseMode">
- Captures the mouse. The mouse will be hidden and its position locked at the center of the screen.
+ Captures the mouse. The mouse will be hidden and its position locked at the center of the window manager's window.
[b]Note:[/b] If you want to process the mouse's movement in this mode, you need to use [member InputEventMouseMotion.relative].
</constant>
<constant name="MOUSE_MODE_CONFINED" value="3" enum="MouseMode">
@@ -1694,7 +1694,10 @@
Use [method window_get_safe_title_margins] to determine area under the title bar that is not covered by decorations.
[b]Note:[/b] This flag is implemented on macOS.
</constant>
- <constant name="WINDOW_FLAG_MAX" value="7" enum="WindowFlags">
+ <constant name="WINDOW_FLAG_MOUSE_PASSTHROUGH" value="7" enum="WindowFlags">
+ All mouse events are passed to the underlying window of the same application.
+ </constant>
+ <constant name="WINDOW_FLAG_MAX" value="8" enum="WindowFlags">
Max value of the [enum WindowFlags].
</constant>
<constant name="WINDOW_EVENT_MOUSE_ENTER" value="0" enum="WindowEvent">
diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml
index 1d63af9233..f2b39ab134 100644
--- a/doc/classes/EditorExportPlatform.xml
+++ b/doc/classes/EditorExportPlatform.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorExportPlatform" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Identifies a supported export platform, and internally provides the functionality of exporting to that platform.
</brief_description>
<description>
+ Base resource that provides the functionality of exporting a release build of a project to a platform, from the editor. Stores platform-specific metadata such as the name and supported features of the platform, and performs the exporting of projects, PCK files, and ZIP files. Uses an export template for the platform provided at the time of project exporting.
+ Used in scripting by [EditorExportPlugin] to configure platform-specific customization of scenes and resources. See [method EditorExportPlugin._begin_customize_scenes] and [method EditorExportPlugin._begin_customize_resources] for more details.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml
index e8df6ae7fe..cddf3662aa 100644
--- a/doc/classes/EditorFileSystem.xml
+++ b/doc/classes/EditorFileSystem.xml
@@ -96,6 +96,11 @@
Emitted if at least one resource is reloaded when the filesystem is scanned.
</description>
</signal>
+ <signal name="script_classes_updated">
+ <description>
+ Emitted when the list of global script classes gets updated.
+ </description>
+ </signal>
<signal name="sources_changed">
<param index="0" name="exist" type="bool" />
<description>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index adcc87d062..2124a97751 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -143,7 +143,7 @@
[gdscript]
func _forward_canvas_draw_over_viewport(overlay):
# Draw a circle at cursor position.
- overlay.draw_circle(overlay.get_local_mouse_position(), 64, Color.white)
+ overlay.draw_circle(overlay.get_local_mouse_position(), 64, Color.WHITE)
func _forward_canvas_gui_input(event):
if event is InputEventMouseMotion:
@@ -347,7 +347,7 @@
[codeblock]
func _set_state(data):
zoom = data.get("zoom", 1.0)
- preferred_color = data.get("my_color", Color.white)
+ preferred_color = data.get("my_color", Color.WHITE)
[/codeblock]
</description>
</method>
@@ -359,7 +359,7 @@
[codeblock]
func _set_window_layout(configuration):
$Window.position = configuration.get_value("MyPlugin", "window_position", Vector2())
- $Icon.modulate = configuration.get_value("MyPlugin", "icon_color", Color.white)
+ $Icon.modulate = configuration.get_value("MyPlugin", "icon_color", Color.WHITE)
[/codeblock]
</description>
</method>
@@ -664,7 +664,7 @@
<return type="void" />
<param index="0" name="callable" type="Callable" />
<description>
- Removes a callback previsously added by [method add_undo_redo_inspector_hook_callback].
+ Removes a callback previously added by [method add_undo_redo_inspector_hook_callback].
</description>
</method>
<method name="set_force_draw_over_forwarding_enabled">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 98f4789163..fca87f6a56 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -499,7 +499,7 @@
</member>
<member name="interface/editor/editor_language" type="String" setter="" getter="">
The language to use for the editor interface.
- Translations are provided by the community. If you spot a mistake, [url=https://docs.godotengine.org/en/latest/community/contributing/editor_and_docs_localization.html]contribute to editor translations on Weblate![/url]
+ Translations are provided by the community. If you spot a mistake, [url=$DOCS_URL/contributing/documentation/editor_and_docs_localization.html]contribute to editor translations on Weblate![/url]
</member>
<member name="interface/editor/expand_to_title" type="bool" setter="" getter="">
Expanding main editor window content to the title, if supported by [DisplayServer]. See [constant DisplayServer.WINDOW_FLAG_EXTEND_TO_TITLE].
@@ -741,7 +741,7 @@
</member>
<member name="text_editor/behavior/indent/type" type="int" setter="" getter="">
The indentation style to use (tabs or spaces).
- [b]Note:[/b] The [url=https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url] recommends using tabs for indentation. It is advised to change this setting only if you need to work on a project that currently uses spaces for indentation.
+ [b]Note:[/b] The [url=$DOCS_URL/getting_started/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url] recommends using tabs for indentation. It is advised to change this setting only if you need to work on a project that currently uses spaces for indentation.
</member>
<member name="text_editor/behavior/navigation/drag_and_drop_selection" type="bool" setter="" getter="">
If [code]true[/code], allows drag-and-dropping text in the script editor to move text. Disable this if you find yourself accidentally drag-and-dropping text in the script editor.
@@ -782,7 +782,7 @@
If [code]true[/code], the code completion tooltip will appear below the current line unless there is no space on screen below the current line. If [code]false[/code], the code completion tooltip will appear above the current line.
</member>
<member name="text_editor/completion/use_single_quotes" type="bool" setter="" getter="">
- If [code]true[/code], performs string autocompletion with single quotes. If [code]false[/code], performs string autocompletion with double quotes (which matches the [url=https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url]).
+ If [code]true[/code], performs string autocompletion with single quotes. If [code]false[/code], performs string autocompletion with double quotes (which matches the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url]).
</member>
<member name="text_editor/help/class_reference_examples" type="int" setter="" getter="">
Controls which multi-line code blocks should be displayed in the editor help. This setting does not affect single-line code literals in the editor help.
diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml
index d270d32df7..1aef79f6e5 100644
--- a/doc/classes/EditorSpinSlider.xml
+++ b/doc/classes/EditorSpinSlider.xml
@@ -22,7 +22,7 @@
<member name="read_only" type="bool" setter="set_read_only" getter="is_read_only" default="false">
If [code]true[/code], the slider can't be interacted with.
</member>
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="1" />
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="1" />
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
<member name="suffix" type="String" setter="set_suffix" getter="get_suffix" default="&quot;&quot;">
The suffix to display after the value (in a faded color). This should generally be a plural word. You may have to use an abbreviation if the suffix is too long to be displayed.
diff --git a/doc/classes/EditorUndoRedoManager.xml b/doc/classes/EditorUndoRedoManager.xml
index cd96e740e8..4d6938e6aa 100644
--- a/doc/classes/EditorUndoRedoManager.xml
+++ b/doc/classes/EditorUndoRedoManager.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="EditorUndoRedoManager" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="EditorUndoRedoManager" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Manages undo history of scenes opened in the editor.
</brief_description>
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index 1ae4718536..127c5daf95 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -21,7 +21,10 @@
<param index="0" name="name" type="StringName" />
<param index="1" name="value" type="Variant" />
<description>
- Set the value of a shader parameter for this instance only.
+ Set the value of a shader uniform for this instance only ([url=$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html#per-instance-uniforms]per-instance uniform[/url]). See also [method ShaderMaterial.set_shader_parameter] to assign a uniform on all instances using the same [ShaderMaterial].
+ [b]Note:[/b] For a shader uniform to be assignable on a per-instance basis, it [i]must[/i] be defined with [code]instance uniform ...[/code] rather than [code]uniform ...[/code] in the shader code.
+ [b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
+ [b]Note:[/b] Per-instance shader uniforms are currently only available in 3D, so there is no 2D equivalent of this method.
</description>
</method>
</methods>
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index 16386ff81b..3f0080ac15 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -280,11 +280,6 @@
Emitted when the GraphNode is requested to be closed. Happens on clicking the close button (see [member show_close]).
</description>
</signal>
- <signal name="deselected">
- <description>
- Emitted when the GraphNode is deselected.
- </description>
- </signal>
<signal name="dragged">
<param index="0" name="from" type="Vector2" />
<param index="1" name="to" type="Vector2" />
@@ -292,6 +287,16 @@
Emitted when the GraphNode is dragged.
</description>
</signal>
+ <signal name="node_deselected">
+ <description>
+ Emitted when the GraphNode is deselected.
+ </description>
+ </signal>
+ <signal name="node_selected">
+ <description>
+ Emitted when the GraphNode is selected.
+ </description>
+ </signal>
<signal name="position_offset_changed">
<description>
Emitted when the GraphNode is moved.
@@ -308,11 +313,6 @@
Emitted when the GraphNode is requested to be resized. Happens on dragging the resizer handle (see [member resizable]).
</description>
</signal>
- <signal name="selected">
- <description>
- Emitted when the GraphNode is selected.
- </description>
- </signal>
<signal name="slot_updated">
<param index="0" name="idx" type="int" />
<description>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index ed1b40488e..051e087611 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -489,7 +489,7 @@
var img = Image.new()
img.create(img_width, img_height, false, Image.FORMAT_RGBA8)
- img.set_pixel(1, 2, Color.red) # Sets the color at (1, 2) to red.
+ img.set_pixel(1, 2, Color.RED) # Sets the color at (1, 2) to red.
[/gdscript]
[csharp]
int imgWidth = 10;
@@ -517,7 +517,7 @@
var img = Image.new()
img.create(img_width, img_height, false, Image.FORMAT_RGBA8)
- img.set_pixelv(Vector2i(1, 2), Color.red) # Sets the color at (1, 2) to red.
+ img.set_pixelv(Vector2i(1, 2), Color.RED) # Sets the color at (1, 2) to red.
[/gdscript]
[csharp]
int imgWidth = 10;
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index be8c8ff83f..a0d2d93a7d 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -155,7 +155,7 @@
</description>
</method>
<method name="get_mouse_button_mask" qualifiers="const">
- <return type="int" enum="MouseButton" />
+ <return type="int" enum="MouseButtonMask" />
<description>
Returns mouse buttons as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method DisplayServer.mouse_get_button_state].
</description>
@@ -397,7 +397,7 @@
Makes the mouse cursor hidden if it is visible.
</constant>
<constant name="MOUSE_MODE_CAPTURED" value="2" enum="MouseMode">
- Captures the mouse. The mouse will be hidden and its position locked at the center of the screen.
+ Captures the mouse. The mouse will be hidden and its position locked at the center of the window manager's window.
[b]Note:[/b] If you want to process the mouse's movement in this mode, you need to use [member InputEventMouseMotion.relative].
</constant>
<constant name="MOUSE_MODE_CONFINED" value="3" enum="MouseMode">
diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml
index 87e2b8ea20..3816847804 100644
--- a/doc/classes/InputEventAction.xml
+++ b/doc/classes/InputEventAction.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
Contains a generic action which can be targeted from several types of inputs. Actions can be created from the [b]Input Map[/b] tab in the [b]Project &gt; Project Settings[/b] menu. See [method Node._input].
+ [b]Note:[/b] Unlike the other [InputEvent] subclasses which map to unique physical events, this virtual one is not emitted by the engine. This class is useful to emit actions manually with [method Input.parse_input_event], which are then received in [method Node._input]. To check if a physical event matches an action from the Input Map, use [method InputEvent.is_action] and [method InputEvent.is_action_pressed].
</description>
<tutorials>
<link title="InputEvent: Actions">$DOCS_URL/tutorials/inputs/inputevent.html#actions</link>
diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml
index 889329d97e..0a1f3d2c1a 100644
--- a/doc/classes/InputEventMouse.xml
+++ b/doc/classes/InputEventMouse.xml
@@ -10,7 +10,7 @@
<link title="InputEvent">$DOCS_URL/tutorials/inputs/inputevent.html</link>
</tutorials>
<members>
- <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask" enum="MouseButton" default="0">
+ <member name="button_mask" type="int" setter="set_button_mask" getter="get_button_mask" enum="MouseButtonMask" default="0">
The mouse button mask identifier, one of or a bitwise combination of the [enum MouseButton] button masks.
</member>
<member name="global_position" type="Vector2" setter="set_global_position" getter="get_global_position" default="Vector2(0, 0)">
diff --git a/doc/classes/JavaScriptBridge.xml b/doc/classes/JavaScriptBridge.xml
index 5e36b5cc80..f79c11b334 100644
--- a/doc/classes/JavaScriptBridge.xml
+++ b/doc/classes/JavaScriptBridge.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
The JavaScriptBridge singleton is implemented only in the Web export. It's used to access the browser's JavaScript context. This allows interaction with embedding pages or calling third-party JavaScript APIs.
- [b]Note:[/b] This singleton can be disabled at build-time to improve security. By default, the JavaScriptBridge singleton is enabled. Official export templates also have the JavaScriptBridge singleton enabled. See [url=$DOCS_URL/development/compiling/compiling_for_web.html]Compiling for the Web[/url] in the documentation for more information.
+ [b]Note:[/b] This singleton can be disabled at build-time to improve security. By default, the JavaScriptBridge singleton is enabled. Official export templates also have the JavaScriptBridge singleton enabled. See [url=$DOCS_URL/contributing/development/compiling/compiling_for_web.html]Compiling for the Web[/url] in the documentation for more information.
</description>
<tutorials>
<link title="Exporting for the Web: Calling JavaScript from script">$DOCS_URL/tutorials/export/exporting_for_web.html#calling-javascript-from-script</link>
@@ -46,6 +46,13 @@
If [param use_global_execution_context] is [code]true[/code], the code will be evaluated in the global execution context. Otherwise, it is evaluated in the execution context of a function within the engine's runtime environment.
</description>
</method>
+ <method name="force_fs_sync">
+ <return type="void" />
+ <description>
+ Force synchronization of the persistent file system (when enabled).
+ [b]Note:[/b] This is only useful for modules or extensions that can't use [FileAccess] to write files.
+ </description>
+ </method>
<method name="get_interface">
<return type="JavaScriptObject" />
<param index="0" name="interface" type="String" />
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 1a8cbf0584..a14856b9b2 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -62,7 +62,7 @@
Limits the lines of text the node shows on screen.
</member>
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="2" />
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="4" />
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="4" />
<member name="structured_text_bidi_override" type="int" setter="set_structured_text_bidi_override" getter="get_structured_text_bidi_override" enum="TextServer.StructuredTextParser" default="0">
Set BiDi algorithm override for the structured text.
</member>
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 4d3fb7ed5c..94e80ffb2b 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -266,79 +266,79 @@
<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">
+ <constant name="ARRAY_FORMAT_VERTEX" value="1" enum="ArrayFormat" is_bitfield="true">
Mesh array contains vertices. All meshes require a vertex array so this should always be present.
</constant>
- <constant name="ARRAY_FORMAT_NORMAL" value="2" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_NORMAL" value="2" enum="ArrayFormat" is_bitfield="true">
Mesh array contains normals.
</constant>
- <constant name="ARRAY_FORMAT_TANGENT" value="4" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TANGENT" value="4" enum="ArrayFormat" is_bitfield="true">
Mesh array contains tangents.
</constant>
- <constant name="ARRAY_FORMAT_COLOR" value="8" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_COLOR" value="8" enum="ArrayFormat" is_bitfield="true">
Mesh array contains colors.
</constant>
- <constant name="ARRAY_FORMAT_TEX_UV" value="16" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TEX_UV" value="16" enum="ArrayFormat" is_bitfield="true">
Mesh array contains UVs.
</constant>
- <constant name="ARRAY_FORMAT_TEX_UV2" value="32" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TEX_UV2" value="32" enum="ArrayFormat" is_bitfield="true">
Mesh array contains second UV.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM0" value="64" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM0" value="64" enum="ArrayFormat" is_bitfield="true">
Mesh array contains custom channel index 0.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM1" value="128" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM1" value="128" enum="ArrayFormat" is_bitfield="true">
Mesh array contains custom channel index 1.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM2" value="256" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM2" value="256" enum="ArrayFormat" is_bitfield="true">
Mesh array contains custom channel index 2.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM3" value="512" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM3" value="512" enum="ArrayFormat" is_bitfield="true">
Mesh array contains custom channel index 3.
</constant>
- <constant name="ARRAY_FORMAT_BONES" value="1024" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BONES" value="1024" enum="ArrayFormat" is_bitfield="true">
Mesh array contains bones.
</constant>
- <constant name="ARRAY_FORMAT_WEIGHTS" value="2048" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_WEIGHTS" value="2048" enum="ArrayFormat" is_bitfield="true">
Mesh array contains bone weights.
</constant>
- <constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat" is_bitfield="true">
Mesh array uses indices.
</constant>
- <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat" is_bitfield="true">
Mask of mesh channels permitted in blend shapes.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat" is_bitfield="true">
Shift of first custom channel.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_BITS" value="3" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_BITS" value="3" enum="ArrayFormat" is_bitfield="true">
Number of format bits per custom channel. See [enum ArrayCustomFormat].
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM0_SHIFT" value="13" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM0_SHIFT" value="13" enum="ArrayFormat" is_bitfield="true">
Amount to shift [enum ArrayCustomFormat] for custom channel index 0.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM1_SHIFT" value="16" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM1_SHIFT" value="16" enum="ArrayFormat" is_bitfield="true">
Amount to shift [enum ArrayCustomFormat] for custom channel index 1.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM2_SHIFT" value="19" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM2_SHIFT" value="19" enum="ArrayFormat" is_bitfield="true">
Amount to shift [enum ArrayCustomFormat] for custom channel index 2.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM3_SHIFT" value="22" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM3_SHIFT" value="22" enum="ArrayFormat" is_bitfield="true">
Amount to shift [enum ArrayCustomFormat] for custom channel index 3.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_MASK" value="7" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_MASK" value="7" enum="ArrayFormat" is_bitfield="true">
Mask of custom format bits per custom channel. Must be shifted by one of the SHIFT constants. See [enum ArrayCustomFormat].
</constant>
- <constant name="ARRAY_COMPRESS_FLAGS_BASE" value="25" enum="ArrayFormat">
+ <constant name="ARRAY_COMPRESS_FLAGS_BASE" value="25" enum="ArrayFormat" is_bitfield="true">
Shift of first compress flag. Compress flags should be passed to [method ArrayMesh.add_surface_from_arrays] and [method SurfaceTool.commit].
</constant>
- <constant name="ARRAY_FLAG_USE_2D_VERTICES" value="33554432" enum="ArrayFormat">
+ <constant name="ARRAY_FLAG_USE_2D_VERTICES" value="33554432" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the array contains 2D vertices.
</constant>
- <constant name="ARRAY_FLAG_USE_DYNAMIC_UPDATE" value="67108864" enum="ArrayFormat">
+ <constant name="ARRAY_FLAG_USE_DYNAMIC_UPDATE" value="67108864" enum="ArrayFormat" is_bitfield="true">
Flag indices that the mesh data will use [code]GL_DYNAMIC_DRAW[/code] on GLES. Unused on Vulkan.
</constant>
- <constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat">
+ <constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the mesh contains up to 8 bone influences per vertex. This flag indicates that [constant ARRAY_BONES] and [constant ARRAY_WEIGHTS] elements will have double length.
</constant>
<constant name="BLEND_SHAPE_MODE_NORMALIZED" value="0" enum="BlendShapeMode">
diff --git a/doc/classes/MeshInstance2D.xml b/doc/classes/MeshInstance2D.xml
index e9666337a1..7bb33fc46d 100644
--- a/doc/classes/MeshInstance2D.xml
+++ b/doc/classes/MeshInstance2D.xml
@@ -13,10 +13,6 @@
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
The [Mesh] that will be drawn by the [MeshInstance2D].
</member>
- <member name="normal_map" type="Texture2D" setter="set_normal_map" getter="get_normal_map">
- The normal map that will be used if using the default [CanvasItemMaterial].
- [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines.
- </member>
<member name="texture" type="Texture2D" setter="set_texture" getter="get_texture">
The [Texture2D] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader.
</member>
diff --git a/doc/classes/MultiMeshInstance2D.xml b/doc/classes/MultiMeshInstance2D.xml
index daf5ef6287..16c0f6070e 100644
--- a/doc/classes/MultiMeshInstance2D.xml
+++ b/doc/classes/MultiMeshInstance2D.xml
@@ -13,10 +13,6 @@
<member name="multimesh" type="MultiMesh" setter="set_multimesh" getter="get_multimesh">
The [MultiMesh] that will be drawn by the [MultiMeshInstance2D].
</member>
- <member name="normal_map" type="Texture2D" setter="set_normal_map" getter="get_normal_map">
- The normal map that will be used if using the default [CanvasItemMaterial].
- [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines.
- </member>
<member name="texture" type="Texture2D" setter="set_texture" getter="get_texture">
The [Texture2D] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader.
</member>
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 31d347d76c..b561748b30 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -8,6 +8,7 @@
[b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] 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.
</description>
<tutorials>
+ <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link>
</tutorials>
<methods>
<method name="distance_to_target" qualifiers="const">
@@ -113,7 +114,7 @@
<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.
</member>
- <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="200.0">
+ <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="100.0">
The maximum speed that an agent can move.
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
@@ -122,10 +123,10 @@
<member name="neighbor_distance" type="float" setter="set_neighbor_distance" getter="get_neighbor_distance" default="500.0">
The distance to search for other agents.
</member>
- <member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="1.0">
+ <member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="20.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_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
+ <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="100.0">
The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
</member>
<member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters2D.PathMetadataFlags" default="7">
@@ -135,13 +136,13 @@
The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_distance]).
Does not affect normal pathfinding. To change an actor's pathfinding radius bake [NavigationMesh] resources with a different [member NavigationMesh.agent_radius] property and use different navigation maps for each actor size.
</member>
- <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
+ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="10.0">
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_location" type="Vector2" setter="set_target_location" getter="get_target_location" default="Vector2(0, 0)">
The user-defined target location. Setting this property will clear the current navigation path.
</member>
- <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="20.0">
+ <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>
</members>
diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index c3f4809b5e..a1b007ee56 100644
--- a/doc/classes/NavigationAgent3D.xml
+++ b/doc/classes/NavigationAgent3D.xml
@@ -8,6 +8,7 @@
[b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] 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.
</description>
<tutorials>
+ <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link>
</tutorials>
<methods>
<method name="distance_to_target" qualifiers="const">
diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml
index 9d75694360..44d2110a7c 100644
--- a/doc/classes/NavigationLink2D.xml
+++ b/doc/classes/NavigationLink2D.xml
@@ -7,6 +7,7 @@
Creates a link between two locations that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps.
</description>
<tutorials>
+ <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link>
</tutorials>
<methods>
<method name="get_navigation_layer_value" qualifiers="const">
diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml
index 730c43c5a8..4aa5801afb 100644
--- a/doc/classes/NavigationLink3D.xml
+++ b/doc/classes/NavigationLink3D.xml
@@ -7,6 +7,7 @@
Creates a link between two locations that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps.
</description>
<tutorials>
+ <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link>
</tutorials>
<methods>
<method name="get_navigation_layer_value" qualifiers="const">
diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml
index ff898551d4..85af44a13e 100644
--- a/doc/classes/NavigationMesh.xml
+++ b/doc/classes/NavigationMesh.xml
@@ -8,6 +8,7 @@
</description>
<tutorials>
<link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link>
+ <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link>
</tutorials>
<methods>
<method name="add_polygon">
diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml
index 15d149a229..0d4c083eb0 100644
--- a/doc/classes/NavigationMeshGenerator.xml
+++ b/doc/classes/NavigationMeshGenerator.xml
@@ -11,6 +11,7 @@
[b]Note:[/b] Using meshes to not only define walkable surfaces but also obstruct navigation baking does not always work. The navigation baking has no concept of what is a geometry "inside" when dealing with mesh source geometry and this is intentional. Depending on current baking parameters, as soon as the obstructing mesh is large enough to fit a navigation mesh area inside, the baking will generate navigation mesh areas that are inside the obstructing source geometry mesh.
</description>
<tutorials>
+ <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link>
</tutorials>
<methods>
<method name="bake">
diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml
index ce0f05ad28..7d391bfa99 100644
--- a/doc/classes/NavigationObstacle2D.xml
+++ b/doc/classes/NavigationObstacle2D.xml
@@ -8,6 +8,7 @@
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.
</description>
<tutorials>
+ <link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
</tutorials>
<methods>
<method name="get_navigation_map" qualifiers="const">
diff --git a/doc/classes/NavigationObstacle3D.xml b/doc/classes/NavigationObstacle3D.xml
index 78bbb788d9..ce24d2bb0e 100644
--- a/doc/classes/NavigationObstacle3D.xml
+++ b/doc/classes/NavigationObstacle3D.xml
@@ -8,6 +8,7 @@
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.
</description>
<tutorials>
+ <link title="Using NavigationObstacles">$DOCS_URL/tutorials/navigation/navigation_using_navigationobstacles.html</link>
</tutorials>
<methods>
<method name="get_navigation_map" qualifiers="const">
diff --git a/doc/classes/NavigationPathQueryParameters2D.xml b/doc/classes/NavigationPathQueryParameters2D.xml
index 511b2e7a8c..05d7e9f918 100644
--- a/doc/classes/NavigationPathQueryParameters2D.xml
+++ b/doc/classes/NavigationPathQueryParameters2D.xml
@@ -7,6 +7,7 @@
This class contains the start and target position and other parameters to be used with [method NavigationServer2D.query_path].
</description>
<tutorials>
+ <link title="Using NavigationPathQueryObjects">$DOCS_URL/tutorials/navigation/navigation_using_navigationpathqueryobjects.html</link>
</tutorials>
<members>
<member name="map" type="RID" setter="set_map" getter="get_map">
diff --git a/doc/classes/NavigationPathQueryParameters3D.xml b/doc/classes/NavigationPathQueryParameters3D.xml
index b5031f60f2..690d74ab70 100644
--- a/doc/classes/NavigationPathQueryParameters3D.xml
+++ b/doc/classes/NavigationPathQueryParameters3D.xml
@@ -7,6 +7,7 @@
This class contains the start and target position and other parameters to be used with [method NavigationServer3D.query_path].
</description>
<tutorials>
+ <link title="Using NavigationPathQueryObjects">$DOCS_URL/tutorials/navigation/navigation_using_navigationpathqueryobjects.html</link>
</tutorials>
<members>
<member name="map" type="RID" setter="set_map" getter="get_map">
diff --git a/doc/classes/NavigationPathQueryResult2D.xml b/doc/classes/NavigationPathQueryResult2D.xml
index 75f7cc47aa..7bc588619d 100644
--- a/doc/classes/NavigationPathQueryResult2D.xml
+++ b/doc/classes/NavigationPathQueryResult2D.xml
@@ -7,6 +7,7 @@
This class contains the result of a navigation path query from [method NavigationServer2D.query_path].
</description>
<tutorials>
+ <link title="Using NavigationPathQueryObjects">$DOCS_URL/tutorials/navigation/navigation_using_navigationpathqueryobjects.html</link>
</tutorials>
<methods>
<method name="reset">
diff --git a/doc/classes/NavigationPathQueryResult3D.xml b/doc/classes/NavigationPathQueryResult3D.xml
index 03d41cb230..118a597b6c 100644
--- a/doc/classes/NavigationPathQueryResult3D.xml
+++ b/doc/classes/NavigationPathQueryResult3D.xml
@@ -7,6 +7,7 @@
This class contains the result of a navigation path query from [method NavigationServer3D.query_path].
</description>
<tutorials>
+ <link title="Using NavigationPathQueryObjects">$DOCS_URL/tutorials/navigation/navigation_using_navigationpathqueryobjects.html</link>
</tutorials>
<methods>
<method name="reset">
diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml
index b30dd2703a..c223f88d6d 100644
--- a/doc/classes/NavigationPolygon.xml
+++ b/doc/classes/NavigationPolygon.xml
@@ -44,6 +44,7 @@
</description>
<tutorials>
<link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link>
+ <link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link>
</tutorials>
<methods>
<method name="add_outline">
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index 8b8793b3b4..5d0fe9dae4 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -13,6 +13,7 @@
[b]Note:[/b] This node caches changes to its properties, so if you make changes to the underlying region [RID] in [NavigationServer2D], they will not be reflected in this node's properties.
</description>
<tutorials>
+ <link title="Using NavigationRegions">$DOCS_URL/tutorials/navigation/navigation_using_navigationregions.html</link>
</tutorials>
<methods>
<method name="get_navigation_layer_value" qualifiers="const">
diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml
index 10662db869..c3e554e7d6 100644
--- a/doc/classes/NavigationRegion3D.xml
+++ b/doc/classes/NavigationRegion3D.xml
@@ -13,6 +13,7 @@
[b]Note:[/b] This node caches changes to its properties, so if you make changes to the underlying region [RID] in [NavigationServer3D], they will not be reflected in this node's properties.
</description>
<tutorials>
+ <link title="Using NavigationRegions">$DOCS_URL/tutorials/navigation/navigation_using_navigationregions.html</link>
</tutorials>
<methods>
<method name="bake_navigation_mesh">
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 32e48cde54..1ba949b294 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -15,9 +15,10 @@
</description>
<tutorials>
<link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link>
+ <link title="Using NavigationServer">$DOCS_URL/tutorials/navigation/navigation_using_navigationservers.html</link>
</tutorials>
<methods>
- <method name="agent_create" qualifiers="const">
+ <method name="agent_create">
<return type="RID" />
<description>
Creates the agent.
@@ -37,7 +38,7 @@
Returns true if the map got changed the previous frame.
</description>
</method>
- <method name="agent_set_callback" qualifiers="const">
+ <method name="agent_set_callback">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="object_id" type="int" />
@@ -48,7 +49,7 @@
[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 a [code]0[/code] ObjectID as the [param object_id].
</description>
</method>
- <method name="agent_set_map" qualifiers="const">
+ <method name="agent_set_map">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="map" type="RID" />
@@ -56,7 +57,7 @@
Puts the agent in the map.
</description>
</method>
- <method name="agent_set_max_neighbors" qualifiers="const">
+ <method name="agent_set_max_neighbors">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="count" type="int" />
@@ -64,7 +65,7 @@
Sets the maximum number of other agents the 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.
</description>
</method>
- <method name="agent_set_max_speed" qualifiers="const">
+ <method name="agent_set_max_speed">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="max_speed" type="float" />
@@ -72,7 +73,7 @@
Sets the maximum speed of the agent. Must be positive.
</description>
</method>
- <method name="agent_set_neighbor_distance" qualifiers="const">
+ <method name="agent_set_neighbor_distance">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="distance" type="float" />
@@ -80,7 +81,7 @@
Sets the maximum distance 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.
</description>
</method>
- <method name="agent_set_position" qualifiers="const">
+ <method name="agent_set_position">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="position" type="Vector2" />
@@ -88,7 +89,7 @@
Sets the position of the agent in world space.
</description>
</method>
- <method name="agent_set_radius" qualifiers="const">
+ <method name="agent_set_radius">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="radius" type="float" />
@@ -96,7 +97,7 @@
Sets the radius of the agent.
</description>
</method>
- <method name="agent_set_target_velocity" qualifiers="const">
+ <method name="agent_set_target_velocity">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="target_velocity" type="Vector2" />
@@ -104,7 +105,7 @@
Sets the new target velocity.
</description>
</method>
- <method name="agent_set_time_horizon" qualifiers="const">
+ <method name="agent_set_time_horizon">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="time" type="float" />
@@ -112,7 +113,7 @@
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.
</description>
</method>
- <method name="agent_set_velocity" qualifiers="const">
+ <method name="agent_set_velocity">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector2" />
@@ -120,7 +121,7 @@
Sets the current velocity of the agent.
</description>
</method>
- <method name="free_rid" qualifiers="const">
+ <method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
@@ -133,7 +134,7 @@
Returns all created navigation map [RID]s on the NavigationServer. This returns both 2D and 3D created navigation maps as there is technically no distinction between them.
</description>
</method>
- <method name="link_create" qualifiers="const">
+ <method name="link_create">
<return type="RID" />
<description>
Create a new link between two locations on a map.
@@ -195,7 +196,7 @@
Returns whether this [code]link[/code] can be travelled in both directions.
</description>
</method>
- <method name="link_set_bidirectional" qualifiers="const">
+ <method name="link_set_bidirectional">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="bidirectional" type="bool" />
@@ -203,7 +204,7 @@
Sets whether this [code]link[/code] can be travelled in both directions.
</description>
</method>
- <method name="link_set_end_location" qualifiers="const">
+ <method name="link_set_end_location">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="location" type="Vector2" />
@@ -211,7 +212,7 @@
Sets the exit location for the [code]link[/code].
</description>
</method>
- <method name="link_set_enter_cost" qualifiers="const">
+ <method name="link_set_enter_cost">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="enter_cost" type="float" />
@@ -219,7 +220,7 @@
Sets the [code]enter_cost[/code] for this [code]link[/code].
</description>
</method>
- <method name="link_set_map" qualifiers="const">
+ <method name="link_set_map">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="map" type="RID" />
@@ -227,7 +228,7 @@
Sets the navigation map [RID] for the link.
</description>
</method>
- <method name="link_set_navigation_layers" qualifiers="const">
+ <method name="link_set_navigation_layers">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="navigation_layers" type="int" />
@@ -235,7 +236,7 @@
Set the links's navigation layers. This allows selecting links from a path request (when using [method NavigationServer2D.map_get_path]).
</description>
</method>
- <method name="link_set_owner_id" qualifiers="const">
+ <method name="link_set_owner_id">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="owner_id" type="int" />
@@ -243,7 +244,7 @@
Set the [code]ObjectID[/code] of the object which manages this link.
</description>
</method>
- <method name="link_set_start_location" qualifiers="const">
+ <method name="link_set_start_location">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="location" type="Vector2" />
@@ -251,7 +252,7 @@
Sets the entry location for this [code]link[/code].
</description>
</method>
- <method name="link_set_travel_cost" qualifiers="const">
+ <method name="link_set_travel_cost">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="travel_cost" type="float" />
@@ -259,7 +260,7 @@
Sets the [code]travel_cost[/code] for this [code]link[/code].
</description>
</method>
- <method name="map_create" qualifiers="const">
+ <method name="map_create">
<return type="RID" />
<description>
Create a new map.
@@ -351,7 +352,7 @@
Returns true if the map is active.
</description>
</method>
- <method name="map_set_active" qualifiers="const">
+ <method name="map_set_active">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="active" type="bool" />
@@ -359,7 +360,7 @@
Sets the map active.
</description>
</method>
- <method name="map_set_cell_size" qualifiers="const">
+ <method name="map_set_cell_size">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="cell_size" type="float" />
@@ -367,7 +368,7 @@
Set the map cell size used to weld the navigation mesh polygons.
</description>
</method>
- <method name="map_set_edge_connection_margin" qualifiers="const">
+ <method name="map_set_edge_connection_margin">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="margin" type="float" />
@@ -375,7 +376,7 @@
Set the map edge connection margin used to weld the compatible region edges.
</description>
</method>
- <method name="map_set_link_connection_radius" qualifiers="const">
+ <method name="map_set_link_connection_radius">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="radius" type="float" />
@@ -391,7 +392,7 @@
Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters2D]. Updates the provided [NavigationPathQueryResult2D] result object with the path among other results requested by the query.
</description>
</method>
- <method name="region_create" qualifiers="const">
+ <method name="region_create">
<return type="RID" />
<description>
Creates a new region.
@@ -465,7 +466,7 @@
[b]Note:[/b] If navigation meshes from different navigation regions overlap (which should be avoided in general) the result might not be what is expected.
</description>
</method>
- <method name="region_set_enter_cost" qualifiers="const">
+ <method name="region_set_enter_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="enter_cost" type="float" />
@@ -473,7 +474,7 @@
Sets the [param enter_cost] for this [param region].
</description>
</method>
- <method name="region_set_map" qualifiers="const">
+ <method name="region_set_map">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="map" type="RID" />
@@ -481,7 +482,7 @@
Sets the map for the region.
</description>
</method>
- <method name="region_set_navigation_layers" qualifiers="const">
+ <method name="region_set_navigation_layers">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="navigation_layers" type="int" />
@@ -489,7 +490,7 @@
Set the region's navigation layers. This allows selecting regions from a path request (when using [method NavigationServer2D.map_get_path]).
</description>
</method>
- <method name="region_set_navigation_polygon" qualifiers="const">
+ <method name="region_set_navigation_polygon">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="navigation_polygon" type="NavigationPolygon" />
@@ -497,7 +498,7 @@
Sets the [param navigation_polygon] for the region.
</description>
</method>
- <method name="region_set_owner_id" qualifiers="const">
+ <method name="region_set_owner_id">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="owner_id" type="int" />
@@ -505,7 +506,7 @@
Set the [code]ObjectID[/code] of the object which manages this region.
</description>
</method>
- <method name="region_set_transform" qualifiers="const">
+ <method name="region_set_transform">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="transform" type="Transform2D" />
@@ -513,7 +514,7 @@
Sets the global transformation for the region.
</description>
</method>
- <method name="region_set_travel_cost" qualifiers="const">
+ <method name="region_set_travel_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="travel_cost" type="float" />
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index c156dfac16..e007c71342 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -15,9 +15,10 @@
</description>
<tutorials>
<link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link>
+ <link title="Using NavigationServer">$DOCS_URL/tutorials/navigation/navigation_using_navigationservers.html</link>
</tutorials>
<methods>
- <method name="agent_create" qualifiers="const">
+ <method name="agent_create">
<return type="RID" />
<description>
Creates the agent.
@@ -37,7 +38,7 @@
Returns true if the map got changed the previous frame.
</description>
</method>
- <method name="agent_set_callback" qualifiers="const">
+ <method name="agent_set_callback">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="object_id" type="int" />
@@ -48,7 +49,7 @@
[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 a [code]0[/code] ObjectID as the [param object_id].
</description>
</method>
- <method name="agent_set_map" qualifiers="const">
+ <method name="agent_set_map">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="map" type="RID" />
@@ -56,7 +57,7 @@
Puts the agent in the map.
</description>
</method>
- <method name="agent_set_max_neighbors" qualifiers="const">
+ <method name="agent_set_max_neighbors">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="count" type="int" />
@@ -64,7 +65,7 @@
Sets the maximum number of other agents the 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.
</description>
</method>
- <method name="agent_set_max_speed" qualifiers="const">
+ <method name="agent_set_max_speed">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="max_speed" type="float" />
@@ -72,7 +73,7 @@
Sets the maximum speed of the agent. Must be positive.
</description>
</method>
- <method name="agent_set_neighbor_distance" qualifiers="const">
+ <method name="agent_set_neighbor_distance">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="distance" type="float" />
@@ -80,7 +81,7 @@
Sets the maximum distance 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.
</description>
</method>
- <method name="agent_set_position" qualifiers="const">
+ <method name="agent_set_position">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="position" type="Vector3" />
@@ -88,7 +89,7 @@
Sets the position of the agent in world space.
</description>
</method>
- <method name="agent_set_radius" qualifiers="const">
+ <method name="agent_set_radius">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="radius" type="float" />
@@ -96,7 +97,7 @@
Sets the radius of the agent.
</description>
</method>
- <method name="agent_set_target_velocity" qualifiers="const">
+ <method name="agent_set_target_velocity">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="target_velocity" type="Vector3" />
@@ -104,7 +105,7 @@
Sets the new target velocity.
</description>
</method>
- <method name="agent_set_time_horizon" qualifiers="const">
+ <method name="agent_set_time_horizon">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="time" type="float" />
@@ -112,7 +113,7 @@
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.
</description>
</method>
- <method name="agent_set_velocity" qualifiers="const">
+ <method name="agent_set_velocity">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector3" />
@@ -120,7 +121,7 @@
Sets the current velocity of the agent.
</description>
</method>
- <method name="free_rid" qualifiers="const">
+ <method name="free_rid">
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
@@ -133,7 +134,14 @@
Returns all created navigation map [RID]s on the NavigationServer. This returns both 2D and 3D created navigation maps as there is technically no distinction between them.
</description>
</method>
- <method name="link_create" qualifiers="const">
+ <method name="get_process_info" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="process_info" type="int" enum="NavigationServer3D.ProcessInfo" />
+ <description>
+ Returns information about the current state of the NavigationServer. See [enum ProcessInfo] for a list of available states.
+ </description>
+ </method>
+ <method name="link_create">
<return type="RID" />
<description>
Create a new link between two locations on a map.
@@ -195,7 +203,7 @@
Returns whether this [code]link[/code] can be travelled in both directions.
</description>
</method>
- <method name="link_set_bidirectional" qualifiers="const">
+ <method name="link_set_bidirectional">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="bidirectional" type="bool" />
@@ -203,7 +211,7 @@
Sets whether this [code]link[/code] can be travelled in both directions.
</description>
</method>
- <method name="link_set_end_location" qualifiers="const">
+ <method name="link_set_end_location">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="location" type="Vector3" />
@@ -211,7 +219,7 @@
Sets the exit location for the [code]link[/code].
</description>
</method>
- <method name="link_set_enter_cost" qualifiers="const">
+ <method name="link_set_enter_cost">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="enter_cost" type="float" />
@@ -219,7 +227,7 @@
Sets the [code]enter_cost[/code] for this [code]link[/code].
</description>
</method>
- <method name="link_set_map" qualifiers="const">
+ <method name="link_set_map">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="map" type="RID" />
@@ -227,7 +235,7 @@
Sets the navigation map [RID] for the link.
</description>
</method>
- <method name="link_set_navigation_layers" qualifiers="const">
+ <method name="link_set_navigation_layers">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="navigation_layers" type="int" />
@@ -235,7 +243,7 @@
Set the links's navigation layers. This allows selecting links from a path request (when using [method NavigationServer3D.map_get_path]).
</description>
</method>
- <method name="link_set_owner_id" qualifiers="const">
+ <method name="link_set_owner_id">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="owner_id" type="int" />
@@ -243,7 +251,7 @@
Set the [code]ObjectID[/code] of the object which manages this link.
</description>
</method>
- <method name="link_set_start_location" qualifiers="const">
+ <method name="link_set_start_location">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="location" type="Vector3" />
@@ -251,7 +259,7 @@
Sets the entry location for this [code]link[/code].
</description>
</method>
- <method name="link_set_travel_cost" qualifiers="const">
+ <method name="link_set_travel_cost">
<return type="void" />
<param index="0" name="link" type="RID" />
<param index="1" name="travel_cost" type="float" />
@@ -259,7 +267,7 @@
Sets the [code]travel_cost[/code] for this [code]link[/code].
</description>
</method>
- <method name="map_create" qualifiers="const">
+ <method name="map_create">
<return type="RID" />
<description>
Create a new map.
@@ -376,7 +384,7 @@
Returns true if the map is active.
</description>
</method>
- <method name="map_set_active" qualifiers="const">
+ <method name="map_set_active">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="active" type="bool" />
@@ -384,7 +392,7 @@
Sets the map active.
</description>
</method>
- <method name="map_set_cell_size" qualifiers="const">
+ <method name="map_set_cell_size">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="cell_size" type="float" />
@@ -392,7 +400,7 @@
Set the map cell size used to weld the navigation mesh polygons.
</description>
</method>
- <method name="map_set_edge_connection_margin" qualifiers="const">
+ <method name="map_set_edge_connection_margin">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="margin" type="float" />
@@ -400,7 +408,7 @@
Set the map edge connection margin used to weld the compatible region edges.
</description>
</method>
- <method name="map_set_link_connection_radius" qualifiers="const">
+ <method name="map_set_link_connection_radius">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="radius" type="float" />
@@ -408,7 +416,7 @@
Set the map's link connection radius used to connect links to navigation polygons.
</description>
</method>
- <method name="map_set_up" qualifiers="const">
+ <method name="map_set_up">
<return type="void" />
<param index="0" name="map" type="RID" />
<param index="1" name="up" type="Vector3" />
@@ -433,7 +441,7 @@
Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query.
</description>
</method>
- <method name="region_bake_navigation_mesh" qualifiers="const">
+ <method name="region_bake_navigation_mesh">
<return type="void" />
<param index="0" name="navigation_mesh" type="NavigationMesh" />
<param index="1" name="root_node" type="Node" />
@@ -441,7 +449,7 @@
Bakes the [param navigation_mesh] with bake source geometry collected starting from the [param root_node].
</description>
</method>
- <method name="region_create" qualifiers="const">
+ <method name="region_create">
<return type="RID" />
<description>
Creates a new region.
@@ -515,7 +523,7 @@
[b]Note:[/b] If navigation meshes from different navigation regions overlap (which should be avoided in general) the result might not be what is expected.
</description>
</method>
- <method name="region_set_enter_cost" qualifiers="const">
+ <method name="region_set_enter_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="enter_cost" type="float" />
@@ -523,7 +531,7 @@
Sets the [param enter_cost] for this [param region].
</description>
</method>
- <method name="region_set_map" qualifiers="const">
+ <method name="region_set_map">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="map" type="RID" />
@@ -531,7 +539,7 @@
Sets the map for the region.
</description>
</method>
- <method name="region_set_navigation_layers" qualifiers="const">
+ <method name="region_set_navigation_layers">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="navigation_layers" type="int" />
@@ -539,7 +547,7 @@
Set the region's navigation layers. This allows selecting regions from a path request (when using [method NavigationServer3D.map_get_path]).
</description>
</method>
- <method name="region_set_navigation_mesh" qualifiers="const">
+ <method name="region_set_navigation_mesh">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="navigation_mesh" type="NavigationMesh" />
@@ -547,7 +555,7 @@
Sets the navigation mesh for the region.
</description>
</method>
- <method name="region_set_owner_id" qualifiers="const">
+ <method name="region_set_owner_id">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="owner_id" type="int" />
@@ -555,7 +563,7 @@
Set the [code]ObjectID[/code] of the object which manages this region.
</description>
</method>
- <method name="region_set_transform" qualifiers="const">
+ <method name="region_set_transform">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="transform" type="Transform3D" />
@@ -563,7 +571,7 @@
Sets the global transformation for the region.
</description>
</method>
- <method name="region_set_travel_cost" qualifiers="const">
+ <method name="region_set_travel_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
<param index="1" name="travel_cost" type="float" />
@@ -571,7 +579,7 @@
Sets the [param travel_cost] for this [param region].
</description>
</method>
- <method name="set_active" qualifiers="const">
+ <method name="set_active">
<return type="void" />
<param index="0" name="active" type="bool" />
<description>
@@ -592,4 +600,33 @@
</description>
</signal>
</signals>
+ <constants>
+ <constant name="INFO_ACTIVE_MAPS" value="0" enum="ProcessInfo">
+ Constant to get the number of active navigation maps.
+ </constant>
+ <constant name="INFO_REGION_COUNT" value="1" enum="ProcessInfo">
+ Constant to get the number of active navigation regions.
+ </constant>
+ <constant name="INFO_AGENT_COUNT" value="2" enum="ProcessInfo">
+ Constant to get the number of active navigation agents processing avoidance.
+ </constant>
+ <constant name="INFO_LINK_COUNT" value="3" enum="ProcessInfo">
+ Constant to get the number of active navigation links.
+ </constant>
+ <constant name="INFO_POLYGON_COUNT" value="4" enum="ProcessInfo">
+ Constant to get the number of navigation mesh polygons.
+ </constant>
+ <constant name="INFO_EDGE_COUNT" value="5" enum="ProcessInfo">
+ Constant to get the number of navigation mesh polygon edges.
+ </constant>
+ <constant name="INFO_EDGE_MERGE_COUNT" value="6" enum="ProcessInfo">
+ Constant to get the number of navigation mesh polygon edges that were merged due to edge key overlap.
+ </constant>
+ <constant name="INFO_EDGE_CONNECTION_COUNT" value="7" enum="ProcessInfo">
+ Constant to get the number of navigation mesh polygon edges that are considered connected by edge proximity.
+ </constant>
+ <constant name="INFO_EDGE_FREE_COUNT" value="8" enum="ProcessInfo">
+ Constant to get the number of navigation mesh polygon edges that could not be merged but may be still connected by edge proximity or with links.
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 0ccc4155b4..02fd6dae30 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -421,6 +421,12 @@
Returns the node's [Viewport].
</description>
</method>
+ <method name="get_window" qualifiers="const">
+ <return type="Window" />
+ <description>
+ Returns the [Window] that contains this node. If the node is in the main window, this is equivalent to getting the root node ([code]get_tree().get_root()[/code]).
+ </description>
+ </method>
<method name="has_node" qualifiers="const">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index ff4982e2fb..1bc81ffb42 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -572,10 +572,9 @@
</method>
<method name="read_string_from_stdin">
<return type="String" />
- <param index="0" name="block" type="bool" default="true" />
<description>
- Reads a user input string from the standard input (usually the terminal).
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows. Non-blocking reads are not currently supported on any platform.
+ Reads a user input string from the standard input (usually the terminal). This operation is [i]blocking[/i], which causes the window to freeze if [method read_string_from_stdin] is called on the main thread. The thread calling [method read_string_from_stdin] will block until the program receives a line break in standard input (usually by the user pressing [kbd]Enter[/kbd]).
+ [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>
</method>
<method name="request_permission">
@@ -593,12 +592,12 @@
</description>
</method>
<method name="set_environment" qualifiers="const">
- <return type="bool" />
+ <return type="void" />
<param index="0" name="variable" type="String" />
<param index="1" name="value" type="String" />
<description>
Sets the value of the environment variable [param variable] to [param value]. The environment variable will be set for the Godot process and any process executed with [method execute] after running [method set_environment]. The environment variable will [i]not[/i] persist to processes run after the Godot process was terminated.
- [b]Note:[/b] Double-check the casing of [param variable]. Environment variable names are case-sensitive on all platforms except Windows.
+ [b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character. On Windows, there is a 32767 characters limit for the combined length of [param variable], [param value], and the [code]=[/code] and null terminator characters that will be registered in the environment block.
</description>
</method>
<method name="set_restart_on_exit">
@@ -638,6 +637,14 @@
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows.
</description>
</method>
+ <method name="unset_environment" qualifiers="const">
+ <return type="void" />
+ <param index="0" name="variable" type="String" />
+ <description>
+ Removes the environment [param variable] from the current environment, if it exists. The environment variable will be removed for the Godot process and any process executed with [method execute] after running [method unset_environment]. The removal of the environment variable will [i]not[/i] persist to processes run after the Godot process was terminated.
+ [b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character.
+ </description>
+ </method>
</methods>
<members>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false">
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index e015bec134..a9e17f4666 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -24,6 +24,7 @@
[b]Note:[/b] The [code]script[/code] is not exposed like most properties. To set or get an object's [Script] in code, use [method set_script] and [method get_script], respectively.
</description>
<tutorials>
+ <link title="Object class introduction">$DOCS_URL/contributing/development/core_and_modules/object_class.html</link>
<link title="When and how to avoid using nodes for everything">$DOCS_URL/tutorials/best_practices/node_alternatives.html</link>
<link title="Object notifications">$DOCS_URL/tutorials/best_practices/godot_notifications.html</link>
</tutorials>
diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml
index 381fa3e9ef..6b7daa534e 100644
--- a/doc/classes/Performance.xml
+++ b/doc/classes/Performance.xml
@@ -131,67 +131,97 @@
<constant name="TIME_PHYSICS_PROCESS" value="2" enum="Monitor">
Time it took to complete one physics frame, in seconds. [i]Lower is better.[/i]
</constant>
- <constant name="MEMORY_STATIC" value="3" enum="Monitor">
+ <constant name="TIME_NAVIGATION_PROCESS" value="3" enum="Monitor">
+ Time it took to complete one navigation step, in seconds. This includes navigation map updates as well as agent avoidance calculations. [i]Lower is better.[/i]
+ </constant>
+ <constant name="MEMORY_STATIC" value="4" enum="Monitor">
Static memory currently used, in bytes. Not available in release builds. [i]Lower is better.[/i]
</constant>
- <constant name="MEMORY_STATIC_MAX" value="4" enum="Monitor">
+ <constant name="MEMORY_STATIC_MAX" value="5" enum="Monitor">
Available static memory. Not available in release builds. [i]Lower is better.[/i]
</constant>
- <constant name="MEMORY_MESSAGE_BUFFER_MAX" value="5" enum="Monitor">
+ <constant name="MEMORY_MESSAGE_BUFFER_MAX" value="6" enum="Monitor">
Largest amount of memory the message queue buffer has used, in bytes. The message queue is used for deferred functions calls and notifications. [i]Lower is better.[/i]
</constant>
- <constant name="OBJECT_COUNT" value="6" enum="Monitor">
+ <constant name="OBJECT_COUNT" value="7" enum="Monitor">
Number of objects currently instantiated (including nodes). [i]Lower is better.[/i]
</constant>
- <constant name="OBJECT_RESOURCE_COUNT" value="7" enum="Monitor">
+ <constant name="OBJECT_RESOURCE_COUNT" value="8" enum="Monitor">
Number of resources currently used. [i]Lower is better.[/i]
</constant>
- <constant name="OBJECT_NODE_COUNT" value="8" enum="Monitor">
+ <constant name="OBJECT_NODE_COUNT" value="9" enum="Monitor">
Number of nodes currently instantiated in the scene tree. This also includes the root node. [i]Lower is better.[/i]
</constant>
- <constant name="OBJECT_ORPHAN_NODE_COUNT" value="9" enum="Monitor">
+ <constant name="OBJECT_ORPHAN_NODE_COUNT" value="10" enum="Monitor">
Number of orphan nodes, i.e. nodes which are not parented to a node of the scene tree. [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_TOTAL_OBJECTS_IN_FRAME" value="10" enum="Monitor">
+ <constant name="RENDER_TOTAL_OBJECTS_IN_FRAME" value="11" enum="Monitor">
The total number of objects in the last rendered frame. This metric doesn't include culled objects (either via hiding nodes, frustum culling or occlusion culling). [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_TOTAL_PRIMITIVES_IN_FRAME" value="11" enum="Monitor">
+ <constant name="RENDER_TOTAL_PRIMITIVES_IN_FRAME" value="12" enum="Monitor">
The total number of vertices or indices rendered in the last rendered frame. This metric doesn't include primitives from culled objects (either via hiding nodes, frustum culling or occlusion culling). Due to the depth prepass and shadow passes, the number of primitives is always higher than the actual number of vertices in the scene (typically double or triple the original vertex count). [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_TOTAL_DRAW_CALLS_IN_FRAME" value="12" enum="Monitor">
+ <constant name="RENDER_TOTAL_DRAW_CALLS_IN_FRAME" value="13" enum="Monitor">
The total number of draw calls performed in the last rendered frame. This metric doesn't include culled objects (either via hiding nodes, frustum culling or occlusion culling), since they do not result in draw calls. [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_VIDEO_MEM_USED" value="13" enum="Monitor">
+ <constant name="RENDER_VIDEO_MEM_USED" value="14" enum="Monitor">
The amount of video memory used (texture and vertex memory combined, in bytes). Since this metric also includes miscellaneous allocations, this value is always greater than the sum of [constant RENDER_TEXTURE_MEM_USED] and [constant RENDER_BUFFER_MEM_USED]. [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_TEXTURE_MEM_USED" value="14" enum="Monitor">
+ <constant name="RENDER_TEXTURE_MEM_USED" value="15" enum="Monitor">
The amount of texture memory used (in bytes). [i]Lower is better.[/i]
</constant>
- <constant name="RENDER_BUFFER_MEM_USED" value="15" enum="Monitor">
+ <constant name="RENDER_BUFFER_MEM_USED" value="16" enum="Monitor">
The amount of render buffer memory used (in bytes). [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_2D_ACTIVE_OBJECTS" value="16" enum="Monitor">
+ <constant name="PHYSICS_2D_ACTIVE_OBJECTS" value="17" enum="Monitor">
Number of active [RigidBody2D] nodes in the game. [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_2D_COLLISION_PAIRS" value="17" enum="Monitor">
+ <constant name="PHYSICS_2D_COLLISION_PAIRS" value="18" enum="Monitor">
Number of collision pairs in the 2D physics engine. [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_2D_ISLAND_COUNT" value="18" enum="Monitor">
+ <constant name="PHYSICS_2D_ISLAND_COUNT" value="19" enum="Monitor">
Number of islands in the 2D physics engine. [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_3D_ACTIVE_OBJECTS" value="19" enum="Monitor">
+ <constant name="PHYSICS_3D_ACTIVE_OBJECTS" value="20" enum="Monitor">
Number of active [RigidBody3D] and [VehicleBody3D] nodes in the game. [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_3D_COLLISION_PAIRS" value="20" enum="Monitor">
+ <constant name="PHYSICS_3D_COLLISION_PAIRS" value="21" enum="Monitor">
Number of collision pairs in the 3D physics engine. [i]Lower is better.[/i]
</constant>
- <constant name="PHYSICS_3D_ISLAND_COUNT" value="21" enum="Monitor">
+ <constant name="PHYSICS_3D_ISLAND_COUNT" value="22" enum="Monitor">
Number of islands in the 3D physics engine. [i]Lower is better.[/i]
</constant>
- <constant name="AUDIO_OUTPUT_LATENCY" value="22" enum="Monitor">
+ <constant name="AUDIO_OUTPUT_LATENCY" value="23" enum="Monitor">
Output latency of the [AudioServer]. [i]Lower is better.[/i]
</constant>
- <constant name="MONITOR_MAX" value="23" enum="Monitor">
+ <constant name="NAVIGATION_ACTIVE_MAPS" value="24" enum="Monitor">
+ Number of active navigation maps in the [NavigationServer3D]. This also includes the two empty default navigation maps created by World2D and World3D.
+ </constant>
+ <constant name="NAVIGATION_REGION_COUNT" value="25" enum="Monitor">
+ Number of active navigation regions in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_AGENT_COUNT" value="26" enum="Monitor">
+ Number of active navigation agents processing avoidance in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_LINK_COUNT" value="27" enum="Monitor">
+ Number of active navigation links in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_POLYGON_COUNT" value="28" enum="Monitor">
+ Number of navigation mesh polygons in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_EDGE_COUNT" value="29" enum="Monitor">
+ Number of navigation mesh polygon edges in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_EDGE_MERGE_COUNT" value="30" enum="Monitor">
+ Number of navigation mesh polygon edges that were merged due to edge key overlap in the [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_EDGE_CONNECTION_COUNT" value="31" enum="Monitor">
+ Number of polygon edges that are considered connected by edge proximity [NavigationServer3D].
+ </constant>
+ <constant name="NAVIGATION_EDGE_FREE_COUNT" value="32" enum="Monitor">
+ Number of navigation mesh polygon edges that could not be merged in the [NavigationServer3D]. The edges still may be connected by edge proximity or with links.
+ </constant>
+ <constant name="MONITOR_MAX" value="33" enum="Monitor">
Represents the size of the [enum Monitor] enum.
</constant>
</constants>
diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml
index eca6a1cbc7..a46de4c189 100644
--- a/doc/classes/PhysicsDirectBodyState2D.xml
+++ b/doc/classes/PhysicsDirectBodyState2D.xml
@@ -151,6 +151,13 @@
[b]Note:[/b] By default, this returns 0 unless bodies are configured to monitor contacts. See [member RigidBody2D.contact_monitor].
</description>
</method>
+ <method name="get_contact_impulse" qualifiers="const">
+ <return type="Vector2" />
+ <param index="0" name="contact_idx" type="int" />
+ <description>
+ Returns the impulse created by the contact.
+ </description>
+ </method>
<method name="get_contact_local_normal" qualifiers="const">
<return type="Vector2" />
<param index="0" name="contact_idx" type="int" />
diff --git a/doc/classes/PhysicsDirectBodyState2DExtension.xml b/doc/classes/PhysicsDirectBodyState2DExtension.xml
index 8fd34c1243..496cbf9136 100644
--- a/doc/classes/PhysicsDirectBodyState2DExtension.xml
+++ b/doc/classes/PhysicsDirectBodyState2DExtension.xml
@@ -130,6 +130,12 @@
<description>
</description>
</method>
+ <method name="_get_contact_impulse" qualifiers="virtual const">
+ <return type="Vector2" />
+ <param index="0" name="contact_idx" type="int" />
+ <description>
+ </description>
+ </method>
<method name="_get_contact_local_normal" qualifiers="virtual const">
<return type="Vector2" />
<param index="0" name="contact_idx" type="int" />
diff --git a/doc/classes/PhysicsDirectBodyState3D.xml b/doc/classes/PhysicsDirectBodyState3D.xml
index a809384642..edd219e66e 100644
--- a/doc/classes/PhysicsDirectBodyState3D.xml
+++ b/doc/classes/PhysicsDirectBodyState3D.xml
@@ -152,10 +152,10 @@
</description>
</method>
<method name="get_contact_impulse" qualifiers="const">
- <return type="float" />
+ <return type="Vector3" />
<param index="0" name="contact_idx" type="int" />
<description>
- Impulse created by the contact. Only implemented for Bullet physics.
+ Impulse created by the contact.
</description>
</method>
<method name="get_contact_local_normal" qualifiers="const">
diff --git a/doc/classes/PhysicsDirectBodyState3DExtension.xml b/doc/classes/PhysicsDirectBodyState3DExtension.xml
index 4432f89b9d..35cf4d4dd5 100644
--- a/doc/classes/PhysicsDirectBodyState3DExtension.xml
+++ b/doc/classes/PhysicsDirectBodyState3DExtension.xml
@@ -131,7 +131,7 @@
</description>
</method>
<method name="_get_contact_impulse" qualifiers="virtual const">
- <return type="float" />
+ <return type="Vector3" />
<param index="0" name="contact_idx" type="int" />
<description>
</description>
diff --git a/doc/classes/PhysicsDirectSpaceState2DExtension.xml b/doc/classes/PhysicsDirectSpaceState2DExtension.xml
index 3235793853..fbbb98a959 100644
--- a/doc/classes/PhysicsDirectSpaceState2DExtension.xml
+++ b/doc/classes/PhysicsDirectSpaceState2DExtension.xml
@@ -87,5 +87,11 @@
<description>
</description>
</method>
+ <method name="is_body_excluded_from_query" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="body" type="RID" />
+ <description>
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/PhysicsDirectSpaceState3DExtension.xml b/doc/classes/PhysicsDirectSpaceState3DExtension.xml
index 98593012db..4297846e53 100644
--- a/doc/classes/PhysicsDirectSpaceState3DExtension.xml
+++ b/doc/classes/PhysicsDirectSpaceState3DExtension.xml
@@ -95,5 +95,11 @@
<description>
</description>
</method>
+ <method name="is_body_excluded_from_query" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="body" type="RID" />
+ <description>
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml
index 9bb11e0d89..7ae27c3f32 100644
--- a/doc/classes/PhysicsServer2DExtension.xml
+++ b/doc/classes/PhysicsServer2DExtension.xml
@@ -933,5 +933,17 @@
<description>
</description>
</method>
+ <method name="body_test_motion_is_excluding_body" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="body" type="RID" />
+ <description>
+ </description>
+ </method>
+ <method name="body_test_motion_is_excluding_object" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="object" type="int" />
+ <description>
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/PhysicsServer3DExtension.xml b/doc/classes/PhysicsServer3DExtension.xml
index d45cb17510..5fe616b948 100644
--- a/doc/classes/PhysicsServer3DExtension.xml
+++ b/doc/classes/PhysicsServer3DExtension.xml
@@ -1284,5 +1284,17 @@
<description>
</description>
</method>
+ <method name="body_test_motion_is_excluding_body" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="body" type="RID" />
+ <description>
+ </description>
+ </method>
+ <method name="body_test_motion_is_excluding_object" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="object" type="int" />
+ <description>
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/PhysicsServer3DManager.xml b/doc/classes/PhysicsServer3DManager.xml
index 3ec03fede4..4d789ceb3f 100644
--- a/doc/classes/PhysicsServer3DManager.xml
+++ b/doc/classes/PhysicsServer3DManager.xml
@@ -15,7 +15,7 @@
<param index="0" name="name" type="String" />
<param index="1" name="create_callback" type="Callable" />
<description>
- Register a [PhysicsServer3D] implementation by passing a [param name] and a [Callable] that returns a [PhysicsServer2D] object.
+ Register a [PhysicsServer3D] implementation by passing a [param name] and a [Callable] that returns a [PhysicsServer3D] object.
</description>
</method>
<method name="set_default_server">
diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml
index fbe8afa8d1..aa09081e3e 100644
--- a/doc/classes/Plane.xml
+++ b/doc/classes/Plane.xml
@@ -38,6 +38,7 @@
<param index="0" name="normal" type="Vector3" />
<description>
Creates a plane from the normal vector. The plane will intersect the origin.
+ The [param normal] of the plane must be a unit vector.
</description>
</constructor>
<constructor name="Plane">
@@ -46,6 +47,7 @@
<param index="1" name="d" type="float" />
<description>
Creates a plane from the normal vector and the plane's distance from the origin.
+ The [param normal] of the plane must be a unit vector.
</description>
</constructor>
<constructor name="Plane">
@@ -54,6 +56,7 @@
<param index="1" name="point" type="Vector3" />
<description>
Creates a plane from the normal vector and a point on the plane.
+ The [param normal] of the plane must be a unit vector.
</description>
</constructor>
<constructor name="Plane">
@@ -67,12 +70,6 @@
</constructor>
</constructors>
<methods>
- <method name="center" qualifiers="const">
- <return type="Vector3" />
- <description>
- Returns the center of the plane.
- </description>
- </method>
<method name="distance_to" qualifiers="const">
<return type="float" />
<param index="0" name="point" type="Vector3" />
@@ -80,6 +77,12 @@
Returns the shortest distance from the plane to the position [param point]. If the point is above the plane, the distance will be positive. If below, the distance will be negative.
</description>
</method>
+ <method name="get_center" qualifiers="const">
+ <return type="Vector3" />
+ <description>
+ Returns the center of the plane.
+ </description>
+ </method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector3" />
@@ -152,7 +155,7 @@
In the scalar equation of the plane [code]ax + by + cz = d[/code], this is [code]d[/code], while the [code](a, b, c)[/code] coordinates are represented by the [member normal] property.
</member>
<member name="normal" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
- The normal of the plane, which must be normalized.
+ The normal of the plane, which must be a unit vector.
In the scalar equation of the plane [code]ax + by + cz = d[/code], this is the vector [code](a, b, c)[/code], where [code]d[/code] is the [member d] property.
</member>
<member name="x" type="float" setter="" getter="" default="0.0">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index cfcfca9880..9e1c751662 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -84,6 +84,25 @@
GD.Print(ProjectSettings.GetSetting("application/config/custom_description", "No description specified."));
[/csharp]
[/codeblocks]
+ [b]Note:[/b] This method doesn't take potential feature overrides into account automatically. Use [method get_setting_with_override] to handle seamlessly.
+ </description>
+ </method>
+ <method name="get_setting_with_override" qualifiers="const">
+ <return type="Variant" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Similar to [method get_setting], but applies feature tag overrides if any exists and is valid.
+ [b]Example:[/b]
+ If the following setting override exists "application/config/name.windows", and the following code is executed:
+ [codeblocks]
+ [gdscript]
+ print(ProjectSettings.get_setting_with_override("application/config/name"))
+ [/gdscript]
+ [csharp]
+ GD.Print(ProjectSettings.GetSettingWithOverride("application/config/name"));
+ [/csharp]
+ [/codeblocks]
+ Then the overridden setting will be returned instead if the project is running on the [i]Windows[/i] operating system.
</description>
</method>
<method name="globalize_path" qualifiers="const">
@@ -741,6 +760,9 @@
<member name="gui/theme/lcd_subpixel_layout" type="int" setter="" getter="" default="1">
LCD subpixel layout used for font anti-aliasing. See [enum TextServer.FontLCDSubpixelLayout].
</member>
+ <member name="gui/timers/button_shortcut_feedback_highlight_time" type="float" setter="" getter="" default="0.2">
+ When [member BaseButton.shortcut_feedback] is enabled, this is the time the [BaseButton] will remain highlighted after a shortcut.
+ </member>
<member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="" default="2000">
Timer setting for incremental search in [Tree], [ItemList], etc. controls (in milliseconds).
</member>
diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml
index 2dcfc90955..4803ea9bfb 100644
--- a/doc/classes/Range.xml
+++ b/doc/classes/Range.xml
@@ -62,7 +62,7 @@
<member name="rounded" type="bool" setter="set_use_rounded_values" getter="is_using_rounded_values" default="false">
If [code]true[/code], [code]value[/code] will always be rounded to the nearest integer.
</member>
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="0" />
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="0" />
<member name="step" type="float" setter="set_step" getter="get_step" default="0.01">
If greater than 0, [code]value[/code] will always be rounded to a multiple of [code]step[/code]. If [code]rounded[/code] is also [code]true[/code], [code]value[/code] will first be rounded to a multiple of [code]step[/code] then rounded to the nearest integer.
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 87e569ba20..32281953b2 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -217,7 +217,7 @@
<param index="1" name="from" type="Vector2" />
<param index="2" name="to" type="Vector2" />
<param index="3" name="color" type="Color" />
- <param index="4" name="width" type="float" default="1.0" />
+ <param index="4" name="width" type="float" default="-1.0" />
<param index="5" name="antialiased" type="bool" default="false" />
<description>
</description>
@@ -303,7 +303,6 @@
<param index="2" name="colors" type="PackedColorArray" />
<param index="3" name="uvs" type="PackedVector2Array" />
<param index="4" name="texture" type="RID" />
- <param index="5" name="width" type="float" default="1.0" />
<description>
</description>
</method>
@@ -1262,6 +1261,12 @@
Tries to free an object in the RenderingServer.
</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.
+ </description>
+ </method>
<method name="get_frame_setup_time_cpu" qualifiers="const">
<return type="float" />
<description>
@@ -1930,7 +1935,7 @@
<param index="2" name="arrays" type="Array" />
<param index="3" name="blend_shapes" type="Array" default="[]" />
<param index="4" name="lods" type="Dictionary" default="{}" />
- <param index="5" name="compress_format" type="int" default="0" />
+ <param index="5" name="compress_format" type="int" enum="RenderingServer.ArrayFormat" default="0" />
<description>
</description>
</method>
@@ -2032,14 +2037,14 @@
</method>
<method name="mesh_surface_get_format_attribute_stride" qualifiers="const">
<return type="int" />
- <param index="0" name="format" type="int" />
+ <param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" />
<param index="1" name="vertex_count" type="int" />
<description>
</description>
</method>
<method name="mesh_surface_get_format_offset" qualifiers="const">
<return type="int" />
- <param index="0" name="format" type="int" />
+ <param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" />
<param index="1" name="vertex_count" type="int" />
<param index="2" name="array_index" type="int" />
<description>
@@ -2047,14 +2052,14 @@
</method>
<method name="mesh_surface_get_format_skin_stride" qualifiers="const">
<return type="int" />
- <param index="0" name="format" type="int" />
+ <param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" />
<param index="1" name="vertex_count" type="int" />
<description>
</description>
</method>
<method name="mesh_surface_get_format_vertex_stride" qualifiers="const">
<return type="int" />
- <param index="0" name="format" type="int" />
+ <param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" />
<param index="1" name="vertex_count" type="int" />
<description>
</description>
@@ -3745,65 +3750,65 @@
</constant>
<constant name="ARRAY_CUSTOM_MAX" value="8" enum="ArrayCustomFormat">
</constant>
- <constant name="ARRAY_FORMAT_VERTEX" value="1" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_VERTEX" value="1" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a vertex array.
</constant>
- <constant name="ARRAY_FORMAT_NORMAL" value="2" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_NORMAL" value="2" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a normal array.
</constant>
- <constant name="ARRAY_FORMAT_TANGENT" value="4" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TANGENT" value="4" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a tangent array.
</constant>
- <constant name="ARRAY_FORMAT_COLOR" value="8" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_COLOR" value="8" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a color array.
</constant>
- <constant name="ARRAY_FORMAT_TEX_UV" value="16" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TEX_UV" value="16" enum="ArrayFormat" is_bitfield="true">
Flag used to mark an UV coordinates array.
</constant>
- <constant name="ARRAY_FORMAT_TEX_UV2" value="32" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_TEX_UV2" value="32" enum="ArrayFormat" is_bitfield="true">
Flag used to mark an UV coordinates array for the second UV coordinates.
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM0" value="64" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM0" value="64" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM1" value="128" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM1" value="128" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM2" value="256" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM2" value="256" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM3" value="512" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM3" value="512" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_BONES" value="1024" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BONES" value="1024" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a bone information array.
</constant>
- <constant name="ARRAY_FORMAT_WEIGHTS" value="2048" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_WEIGHTS" value="2048" enum="ArrayFormat" is_bitfield="true">
Flag used to mark a weights array.
</constant>
- <constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat" is_bitfield="true">
Flag used to mark an index array.
</constant>
- <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_BITS" value="3" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_BITS" value="3" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM0_SHIFT" value="13" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM0_SHIFT" value="13" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM1_SHIFT" value="16" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM1_SHIFT" value="16" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM2_SHIFT" value="19" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM2_SHIFT" value="19" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM3_SHIFT" value="22" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM3_SHIFT" value="22" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FORMAT_CUSTOM_MASK" value="7" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_CUSTOM_MASK" value="7" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_COMPRESS_FLAGS_BASE" value="25" enum="ArrayFormat">
+ <constant name="ARRAY_COMPRESS_FLAGS_BASE" value="25" enum="ArrayFormat" is_bitfield="true">
</constant>
- <constant name="ARRAY_FLAG_USE_2D_VERTICES" value="33554432" enum="ArrayFormat">
+ <constant name="ARRAY_FLAG_USE_2D_VERTICES" value="33554432" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the array contains 2D vertices.
</constant>
- <constant name="ARRAY_FLAG_USE_DYNAMIC_UPDATE" value="67108864" enum="ArrayFormat">
+ <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">
+ <constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
</constant>
<constant name="PRIMITIVE_POINTS" value="0" enum="PrimitiveType">
Primitive to draw consists of points.
diff --git a/doc/classes/ResourceFormatSaver.xml b/doc/classes/ResourceFormatSaver.xml
index 1f2af6d157..b0c57bc7cb 100644
--- a/doc/classes/ResourceFormatSaver.xml
+++ b/doc/classes/ResourceFormatSaver.xml
@@ -43,5 +43,13 @@
Returns [constant OK] on success, or an [enum Error] constant in case of failure.
</description>
</method>
+ <method name="_set_uid" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="uid" type="int" />
+ <description>
+ Sets a new UID for the resource at the given [param path]. Returns [constant OK] on success, or an [enum Error] constant in case of failure.
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/ResourcePreloader.xml b/doc/classes/ResourcePreloader.xml
index 17904697e6..5c0079f408 100644
--- a/doc/classes/ResourcePreloader.xml
+++ b/doc/classes/ResourcePreloader.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ResourcePreloader" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Resource Preloader Node.
+ Preloads a list of resources inside a scene.
</brief_description>
<description>
- This node is used to preload sub-resources inside a scene, so when the scene is loaded, all the resources are ready to use and can be retrieved from the preloader.
+ This node is used to preload sub-resources inside a scene, so when the scene is loaded, all the resources are ready to use and can be retrieved from the preloader. You can add the resources using the ResourcePreloader tab when the node is selected.
GDScript has a simplified [method @GDScript.preload] built-in method which can be used in most situations, leaving the use of [ResourcePreloader] for more advanced scenarios.
</description>
<tutorials>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index bd5b656e1a..bf19ebc23a 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -42,15 +42,15 @@
<description>
Changes the running scene to the one at the given [param path], after loading it into a [PackedScene] and creating a new instance.
Returns [constant OK] on success, [constant ERR_CANT_OPEN] if the [param path] cannot be loaded into a [PackedScene], or [constant ERR_CANT_CREATE] if that scene cannot be instantiated.
- [b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. You won't be able to access it immediately after the [method change_scene_to_file] call.
+ [b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. This ensures that both scenes are never loaded at the same time, which can exhaust system resources if the scenes are too large or if running in a memory constrained environment. As such, you won't be able to access the loaded scene immediately after the [method change_scene_to_file] call.
</description>
</method>
<method name="change_scene_to_packed">
<return type="int" enum="Error" />
<param index="0" name="packed_scene" type="PackedScene" />
<description>
- Changes the running scene to a new instance of the given [PackedScene].
- Returns [constant OK] on success or [constant ERR_CANT_CREATE] if the scene cannot be instantiated.
+ Changes the running scene to a new instance of the given [PackedScene] (which must be valid).
+ Returns [constant OK] on success, [constant ERR_CANT_CREATE] if the scene cannot be instantiated, or [constant ERR_INVALID_PARAMETER] if the scene is invalid.
[b]Note:[/b] The scene change is deferred, which means that the new scene node is added on the next idle frame. You won't be able to access it immediately after the [method change_scene_to_packed] call.
</description>
</method>
@@ -209,6 +209,12 @@
Sets a custom [MultiplayerAPI] with the given [param root_path] (controlling also the relative subpaths), or override the default one if [param root_path] is empty.
</description>
</method>
+ <method name="unload_current_scene">
+ <return type="void" />
+ <description>
+ If a current scene is loaded, calling this method will unload it.
+ </description>
+ </method>
</methods>
<members>
<member name="auto_accept_quit" type="bool" setter="set_auto_accept_quit" getter="is_auto_accept_quit" default="true">
diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml
index 1af7ac4fc5..a2346822e5 100644
--- a/doc/classes/ShaderMaterial.xml
+++ b/doc/classes/ShaderMaterial.xml
@@ -23,7 +23,8 @@
<param index="1" name="value" type="Variant" />
<description>
Changes the value set for this material of a uniform in the shader.
- [b]Note:[/b] [param param] must match the name of the uniform in the code exactly.
+ [b]Note:[/b] [param param] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
+ [b]Note:[/b] Changes to the shader uniform will be effective on all instances using this [ShaderMaterial]. To prevent this, use per-instance uniforms with [method GeometryInstance3D.set_instance_shader_parameter] or duplicate the [ShaderMaterial] resource using [method Resource.duplicate]. Per-instance uniforms allow for better shader reuse and are therefore faster, so they should be preferred over duplicating the [ShaderMaterial] when possible.
</description>
</method>
</methods>
diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml
index 808f93b491..39bdc5c796 100644
--- a/doc/classes/Skeleton2D.xml
+++ b/doc/classes/Skeleton2D.xml
@@ -16,7 +16,7 @@
<param index="0" name="delta" type="float" />
<param index="1" name="execution_mode" type="int" />
<description>
- Executes all the modifications on the [SkeletonModificationStack2D], if the Skeleton3D has one assigned.
+ Executes all the modifications on the [SkeletonModificationStack2D], if the Skeleton2D has one assigned.
</description>
</method>
<method name="get_bone">
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 3bd0e04b92..70986ba06a 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -7,6 +7,7 @@
Skeleton3D provides a hierarchical interface for managing 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.
</description>
<tutorials>
<link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link>
@@ -32,26 +33,11 @@
Removes the global pose override on all bones in the skeleton.
</description>
</method>
- <method name="clear_bones_local_pose_override" is_deprecated="true">
- <return type="void" />
- <description>
- Deprecated. Local pose overrides will be removed.
- Removes the local pose override on all bones in the skeleton.
- </description>
- </method>
<method name="create_skin_from_rest_transforms">
<return type="Skin" />
<description>
</description>
</method>
- <method name="execute_modifications" is_deprecated="true">
- <return type="void" />
- <param index="0" name="delta" type="float" />
- <param index="1" name="execution_mode" type="int" />
- <description>
- Executes all the modifications on the [SkeletonModificationStack3D], if the Skeleton3D has one assigned.
- </description>
- </method>
<method name="find_bone" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="String" />
@@ -113,13 +99,6 @@
Returns the global rest transform for [param bone_idx].
</description>
</method>
- <method name="get_bone_local_pose_override" qualifiers="const">
- <return type="Transform3D" />
- <param index="0" name="bone_idx" type="int" />
- <description>
- Returns the local pose override transform for [param bone_idx].
- </description>
- </method>
<method name="get_bone_name" qualifiers="const">
<return type="String" />
<param index="0" name="bone_idx" type="int" />
@@ -167,43 +146,18 @@
Returns the rest transform for a bone [param bone_idx].
</description>
</method>
- <method name="get_modification_stack" is_deprecated="true">
- <return type="SkeletonModificationStack3D" />
- <description>
- Returns the modification stack attached to this skeleton, if one exists.
- </description>
- </method>
<method name="get_parentless_bones" qualifiers="const">
<return type="PackedInt32Array" />
<description>
Returns an array with all of the bones that are parentless. Another way to look at this is that it returns the indexes of all the bones that are not dependent or modified by other bones in the Skeleton.
</description>
</method>
- <method name="global_pose_to_local_pose" is_deprecated="true">
- <return type="Transform3D" />
- <param index="0" name="bone_idx" type="int" />
- <param index="1" name="global_pose" type="Transform3D" />
- <description>
- Takes the passed-in global pose and converts it to local pose transform.
- This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform in [method set_bone_local_pose_override].
- </description>
- </method>
- <method name="global_pose_to_world_transform" is_deprecated="true">
- <return type="Transform3D" />
- <param index="0" name="global_pose" type="Transform3D" />
- <description>
- Deprecated. Use [Node3D] apis instead.
- Takes the passed-in global pose and converts it to a world transform.
- This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform usable with a node's transform, like [member Node3D.global_transform] for example.
- </description>
- </method>
- <method name="global_pose_z_forward_to_bone_forward" is_deprecated="true">
- <return type="Basis" />
- <param index="0" name="bone_idx" type="int" />
- <param index="1" name="basis" type="Basis" />
+ <method name="get_version" qualifiers="const">
+ <return type="int" />
<description>
- Rotates the given [Basis] so that the forward axis of the Basis is facing in the forward direction of the bone at [param bone_idx].
- This is helper function to make using [method Transform3D.looking_at] easier with bone poses.
+ Returns the number of times the bone hierarchy has changed within this skeleton, including renames.
+ The Skeleton version is not serialized: only use within a single instance of Skeleton3D.
+ Use for invalidating caches in IK solvers and other nodes which process bones.
</description>
</method>
<method name="is_bone_enabled" qualifiers="const">
@@ -213,15 +167,6 @@
Returns whether the bone pose for the bone at [param bone_idx] is enabled.
</description>
</method>
- <method name="local_pose_to_global_pose" is_deprecated="true">
- <return type="Transform3D" />
- <param index="0" name="bone_idx" type="int" />
- <param index="1" name="local_pose" type="Transform3D" />
- <description>
- Converts the passed-in local pose to a global pose relative to the inputted bone, [param bone_idx].
- This could be used to convert [method get_bone_pose] for use with the [method set_bone_global_pose_override] function.
- </description>
- </method>
<method name="localize_rests">
<return type="void" />
<description>
@@ -298,19 +243,6 @@
[b]Note:[/b] The pose transform needs to be a global pose! To convert a world transform from a [Node3D] to a global bone pose, multiply the [method Transform3D.affine_inverse] of the node's [member Node3D.global_transform] by the desired world transform
</description>
</method>
- <method name="set_bone_local_pose_override" is_deprecated="true">
- <return type="void" />
- <param index="0" name="bone_idx" type="int" />
- <param index="1" name="pose" type="Transform3D" />
- <param index="2" name="amount" type="float" />
- <param index="3" name="persistent" type="bool" default="false" />
- <description>
- Deprecated. Local pose overrides will be removed.
- Sets the local pose transform, [param pose], for the bone at [param bone_idx].
- [param amount] is the interpolation strength that will be used when applying the pose, and [param persistent] determines if the applied pose will remain.
- [b]Note:[/b] The pose transform needs to be a local pose! Use [method global_pose_to_local_pose] to convert a global pose to a local pose.
- </description>
- </method>
<method name="set_bone_name">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
@@ -356,13 +288,6 @@
Sets the rest transform for bone [param bone_idx].
</description>
</method>
- <method name="set_modification_stack" is_deprecated="true">
- <return type="void" />
- <param index="0" name="modification_stack" type="SkeletonModificationStack3D" />
- <description>
- Sets the modification stack for this skeleton to the passed-in modification stack, [param modification_stack].
- </description>
- </method>
<method name="unparent_bone_and_rest">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
@@ -370,15 +295,6 @@
Unparents the bone at [param bone_idx] and sets its rest position to that of its parent prior to being reset.
</description>
</method>
- <method name="world_transform_to_global_pose" is_deprecated="true">
- <return type="Transform3D" />
- <param index="0" name="world_transform" type="Transform3D" />
- <description>
- Deprecated. Use [Node3D] apis instead.
- Takes the passed-in global transform and converts it to a global pose.
- This can be used to easily convert a global transform from [member Node3D.global_transform] to a global pose usable with [method set_bone_global_pose_override], for example.
- </description>
- </method>
</methods>
<members>
<member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true">
diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml
index 77aaf0213b..3a78f13bff 100644
--- a/doc/classes/SkeletonModification2D.xml
+++ b/doc/classes/SkeletonModification2D.xml
@@ -56,7 +56,7 @@
<method name="get_modification_stack">
<return type="SkeletonModificationStack2D" />
<description>
- Returns the [SkeletonModificationStack2D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on.
+ Returns the [SkeletonModificationStack2D] that this modification is bound to. Through the modification stack, you can access the Skeleton2D the modification is operating on.
</description>
</method>
<method name="set_editor_draw_gizmo">
diff --git a/doc/classes/SkeletonModification3D.xml b/doc/classes/SkeletonModification3D.xml
deleted file mode 100644
index 25431ea96f..0000000000
--- a/doc/classes/SkeletonModification3D.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3D" inherits="Resource" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A resource that operates on bones in a [Skeleton3D].
- </brief_description>
- <description>
- This resource provides an interface that can be expanded so code that operates on bones in a [Skeleton3D] can be mixed and matched together to create complex interactions.
- This is used to provide Godot with a flexible and powerful Inverse Kinematics solution that can be adapted for many different uses.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="_execute" qualifiers="virtual">
- <return type="void" />
- <param index="0" name="delta" type="float" />
- <description>
- Executes the given modification. This is where the modification performs whatever function it is designed to do.
- </description>
- </method>
- <method name="_setup_modification" qualifiers="virtual">
- <return type="void" />
- <param index="0" name="modification_stack" type="SkeletonModificationStack3D" />
- <description>
- Sets up the modification so it can be executed. This function should be called automatically by the [SkeletonModificationStack3D] containing this modification.
- If you need to initialize a modification before use, this is the place to do it!
- </description>
- </method>
- <method name="clamp_angle">
- <return type="float" />
- <param index="0" name="angle" type="float" />
- <param index="1" name="min" type="float" />
- <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.
- </description>
- </method>
- <method name="get_is_setup" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether this modification has been successfully setup or not.
- </description>
- </method>
- <method name="get_modification_stack">
- <return type="SkeletonModificationStack3D" />
- <description>
- Returns the [SkeletonModificationStack3D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on.
- </description>
- </method>
- <method name="set_is_setup">
- <return type="void" />
- <param index="0" name="is_setup" type="bool" />
- <description>
- Manually allows you to set the setup state of the modification. This function should only rarely be used, as the [SkeletonModificationStack3D] the modification is bound to should handle setting the modification up.
- </description>
- </method>
- </methods>
- <members>
- <member name="enabled" type="bool" setter="set_enabled" getter="get_enabled" default="true">
- When true, the modification's [method _execute] function will be called by the [SkeletonModificationStack3D].
- </member>
- <member name="execution_mode" type="int" setter="set_execution_mode" getter="get_execution_mode" default="0">
- The execution mode for the modification. This tells the modification stack when to execute the modification. Some modifications have settings that are only available in certain execution modes.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModification3DCCDIK.xml b/doc/classes/SkeletonModification3DCCDIK.xml
deleted file mode 100644
index 90b2e78449..0000000000
--- a/doc/classes/SkeletonModification3DCCDIK.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DCCDIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that uses CCDIK to manipulate a series of bones to reach a target.
- </brief_description>
- <description>
- This [SkeletonModification3D] uses an algorithm called Cyclic Coordinate Descent Inverse Kinematics, or CCDIK, to manipulate a chain of bones in a Skeleton so it reaches a defined target.
- CCDIK works by rotating a set of bones, typically called a "bone chain", on a single axis. Each bone is rotated to face the target from the tip (by default), which over a chain of bones allow it to rotate properly to reach the target. Because the bones only rotate on a single axis, CCDIK [i]can[/i] look more robotic than other IK solvers.
- [b]Note:[/b] The CCDIK modifier has [code]ccdik_joints[/code], which are the data objects that hold the data for each joint in the CCDIK chain. This is different from a bone! CCDIK joints hold the data needed for each bone in the bone chain used by CCDIK.
- CCDIK also fully supports angle constraints, allowing for more control over how a solution is met.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_ccdik_joint_bone_index" qualifiers="const">
- <return type="int" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the bone index of the bone assigned to the CCDIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_ccdik_joint_bone_name" qualifiers="const">
- <return type="String" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the name of the bone that is assigned to the CCDIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_ccdik_joint_ccdik_axis" qualifiers="const">
- <return type="int" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the integer representing the joint axis of the CCDIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_ccdik_joint_constraint_angle_max" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the maximum angle constraint for the joint at [param joint_idx]. [b]Note:[/b] This angle is in degrees!
- </description>
- </method>
- <method name="get_ccdik_joint_constraint_angle_min" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the minimum angle constraint for the joint at [param joint_idx]. [b]Note:[/b] This angle is in degrees!
- </description>
- </method>
- <method name="get_ccdik_joint_constraint_invert" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns whether the CCDIK joint at [param joint_idx] uses an inverted joint constraint. See [method set_ccdik_joint_constraint_invert] for details.
- </description>
- </method>
- <method name="get_ccdik_joint_enable_joint_constraint" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Enables angle constraints to the CCDIK joint at [param joint_idx].
- </description>
- </method>
- <method name="set_ccdik_joint_bone_index">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="bone_index" type="int" />
- <description>
- Sets the bone index, [param bone_index], of the CCDIK joint at [param joint_idx]. When possible, this will also update the [code]bone_name[/code] of the CCDIK joint based on data provided by the linked skeleton.
- </description>
- </method>
- <method name="set_ccdik_joint_bone_name">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="bone_name" type="String" />
- <description>
- Sets the bone name, [param bone_name], of the CCDIK joint at [param joint_idx]. When possible, this will also update the [code]bone_index[/code] of the CCDIK joint based on data provided by the linked skeleton.
- </description>
- </method>
- <method name="set_ccdik_joint_ccdik_axis">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="axis" type="int" />
- <description>
- Sets the joint axis of the CCDIK joint at [param joint_idx] to the passed-in joint axis, [param axis].
- </description>
- </method>
- <method name="set_ccdik_joint_constraint_angle_max">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="max_angle" type="float" />
- <description>
- Sets the maximum angle constraint for the joint at [param joint_idx]. [b]Note:[/b] This angle must be in radians!
- </description>
- </method>
- <method name="set_ccdik_joint_constraint_angle_min">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="min_angle" type="float" />
- <description>
- Sets the minimum angle constraint for the joint at [param joint_idx]. [b]Note:[/b] This angle must be in radians!
- </description>
- </method>
- <method name="set_ccdik_joint_constraint_invert">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="invert" type="bool" />
- <description>
- Sets whether the CCDIK joint at [param joint_idx] uses an inverted joint constraint.
- An inverted joint constraint only constraints the CCDIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values.
- </description>
- </method>
- <method name="set_ccdik_joint_enable_joint_constraint">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="enable" type="bool" />
- <description>
- Sets whether joint constraints are enabled for the CCDIK joint at [param joint_idx].
- </description>
- </method>
- </methods>
- <members>
- <member name="ccdik_data_chain_length" type="int" setter="set_ccdik_data_chain_length" getter="get_ccdik_data_chain_length" default="0">
- The number of CCDIK joints in the CCDIK modification.
- </member>
- <member name="high_quality_solve" type="bool" setter="set_use_high_quality_solve" getter="get_use_high_quality_solve" default="true">
- When true, the CCDIK algorithm will perform a higher quality solve that returns more natural results. A high quality solve requires more computation power to solve though, and therefore can be disabled to save performance.
- </member>
- <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
- The NodePath to the node that is the target for the CCDIK modification. This node is what the CCDIK chain will attempt to rotate the bone chain to.
- </member>
- <member name="tip_nodepath" type="NodePath" setter="set_tip_node" getter="get_tip_node" default="NodePath(&quot;&quot;)">
- The end position of the CCDIK chain. Typically, this should be a child of a [BoneAttachment3D] node attached to the final bone in the CCDIK chain, where the child node is offset so it is at the end of the final bone.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModification3DFABRIK.xml b/doc/classes/SkeletonModification3DFABRIK.xml
deleted file mode 100644
index a2bec2b559..0000000000
--- a/doc/classes/SkeletonModification3DFABRIK.xml
+++ /dev/null
@@ -1,161 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DFABRIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that uses FABRIK to manipulate a series of bones to reach a target.
- </brief_description>
- <description>
- This [SkeletonModification3D] uses an algorithm called Forward And Backward Reaching Inverse Kinematics, or FABRIK, to rotate a bone chain so that it reaches a target.
- FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other.
- Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification3DCCDIK], though FABRIK currently does not support joint constraints.
- [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from a bone! FABRIK joints hold the data needed for each bone in the bone chain used by FABRIK.
- To help control how the FABRIK joints move, a magnet vector can be passed, which can nudge the bones in a certain direction prior to solving, giving a level of control over the final result.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="fabrik_joint_auto_calculate_length">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Will attempt to automatically calculate the length of the bone assigned to the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_auto_calculate_length" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns a boolean that indicates whether this modification will attempt to autocalculate the length of the bone assigned to the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_bone_index" qualifiers="const">
- <return type="int" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the bone index of the bone assigned to the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_bone_name" qualifiers="const">
- <return type="String" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the name of the bone that is assigned to the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_length" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the length of the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_magnet" qualifiers="const">
- <return type="Vector3" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the magnet vector of the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="get_fabrik_joint_tip_node" qualifiers="const">
- <return type="NodePath" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the [Node3D]-based node placed at the tip of the FABRIK joint at [param joint_idx], if one has been set.
- </description>
- </method>
- <method name="get_fabrik_joint_use_target_basis" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns a boolean indicating whether the FABRIK joint uses the target's [Basis] for its rotation.
- [b]Note:[/b] This option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones.
- </description>
- </method>
- <method name="get_fabrik_joint_use_tip_node" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Sets the [Node3D]-based node that will be used as the tip of the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="set_fabrik_joint_auto_calculate_length">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="auto_calculate_length" type="bool" />
- <description>
- When [code]true[/code], this modification will attempt to automatically calculate the length of the bone for the FABRIK joint at [param joint_idx]. It does this by either using the tip node assigned, if there is one assigned, or the distance the of the bone's children, if the bone has any. If the bone has no children and no tip node is assigned, then the modification [b]cannot[/b] autocalculate the joint's length. In this case, the joint length should be entered manually or a tip node assigned.
- </description>
- </method>
- <method name="set_fabrik_joint_bone_index">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="bone_index" type="int" />
- <description>
- Sets the bone index, [param bone_index], of the FABRIK joint at [param joint_idx]. When possible, this will also update the [code]bone_name[/code] of the FABRIK joint based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_fabrik_joint_bone_name">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="bone_name" type="String" />
- <description>
- Sets the bone name, [param bone_name], of the FABRIK joint at [param joint_idx]. When possible, this will also update the [code]bone_index[/code] of the FABRIK joint based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_fabrik_joint_length">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="length" type="float" />
- <description>
- Sets the joint length, [param length], of the FABRIK joint at [param joint_idx].
- </description>
- </method>
- <method name="set_fabrik_joint_magnet">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="magnet_position" type="Vector3" />
- <description>
- Sets the magenet position to [param magnet_position] for the joint at [param joint_idx]. The magnet position is used to nudge the joint in that direction when solving, which gives some control over how that joint will bend when being solved.
- </description>
- </method>
- <method name="set_fabrik_joint_tip_node">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="tip_node" type="NodePath" />
- <description>
- Sets the nodepath of the FARIK joint at [param joint_idx] to [param tip_node]. The tip node is used to calculate the length of the FABRIK joint when set to automatically calculate joint length.
- [b]Note:[/b] The tip node should generally be a child node of a [BoneAttachment3D] node attached to the bone that this FABRIK joint operates on, with the child node being offset so it is at the end of the bone.
- </description>
- </method>
- <method name="set_fabrik_joint_use_target_basis">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="use_target_basis" type="bool" />
- <description>
- Sets whether the FABRIK joint at [param joint_idx] uses the target's [Basis] for its rotation.
- [b]Note:[/b] This option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones.
- </description>
- </method>
- <method name="set_fabrik_joint_use_tip_node">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="use_tip_node" type="bool" />
- <description>
- Sets whether the tip node should be used when autocalculating the joint length for the FABRIK joint at [param joint_idx]. This will only work if there is a node assigned to the tip nodepath for this joint.
- </description>
- </method>
- </methods>
- <members>
- <member name="chain_max_iterations" type="int" setter="set_chain_max_iterations" getter="get_chain_max_iterations" default="10">
- The number of times FABRIK will try to solve each time the [code]execute[/code] function is called. Setting this value to a lower number will be result in better performance, but this can also result in harsher movements and slower solves.
- </member>
- <member name="chain_tolerance" type="float" setter="set_chain_tolerance" getter="get_chain_tolerance" default="0.01">
- The minimum distance the target has to be from the tip of the final bone in the bone chain. Setting this value to a higher number allows for greater performance, but less accurate solves.
- </member>
- <member name="fabrik_data_chain_length" type="int" setter="set_fabrik_data_chain_length" getter="get_fabrik_data_chain_length" default="0">
- The amount of FABRIK joints in the FABRIK modification.
- </member>
- <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
- The NodePath to the node that is the target for the FABRIK modification. This node is what the FABRIK chain will attempt to rotate the bone chain to.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModification3DJiggle.xml b/doc/classes/SkeletonModification3DJiggle.xml
deleted file mode 100644
index 304f08bb20..0000000000
--- a/doc/classes/SkeletonModification3DJiggle.xml
+++ /dev/null
@@ -1,199 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DJiggle" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that jiggles bones as they move towards a target.
- </brief_description>
- <description>
- This modification moves a series of bones, typically called a bone chain, towards a target. What makes this modification special is that it calculates the velocity and acceleration for each bone in the bone chain, and runs a very light physics-like calculation using the inputted values. This allows the bones to overshoot the target and "jiggle" around. It can be configured to act more like a spring, or sway around like cloth might.
- This modification is useful for adding additional motion to things like hair, the edges of clothing, and more. It has several settings to that allow control over how the joint moves when the target moves.
- [b]Note:[/b] The Jiggle modifier has [code]jiggle_joints[/code], which are the data objects that hold the data for each joint in the Jiggle chain. This is different from a bone! Jiggle joints hold the data needed for each bone in the bone chain used by the Jiggle modification.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_collision_mask" qualifiers="const">
- <return type="int" />
- <description>
- Returns the collision mask that the Jiggle modifier will take into account when performing physics calculations.
- </description>
- </method>
- <method name="get_jiggle_joint_bone_index" qualifiers="const">
- <return type="int" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the bone index of the bone assigned to the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="get_jiggle_joint_bone_name" qualifiers="const">
- <return type="String" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the name of the bone that is assigned to the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="get_jiggle_joint_damping" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the amount of dampening of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="get_jiggle_joint_gravity" qualifiers="const">
- <return type="Vector3" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns a [Vector3] representign the amount of gravity the Jiggle joint at [param joint_idx] is influenced by.
- </description>
- </method>
- <method name="get_jiggle_joint_mass" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the amount of mass of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="get_jiggle_joint_override" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns a boolean that indicates whether the joint at [param joint_idx] is overriding the default jiggle joint data defined in the modification.
- </description>
- </method>
- <method name="get_jiggle_joint_roll" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the amount of roll/twist applied to the bone that the Jiggle joint is applied to.
- </description>
- </method>
- <method name="get_jiggle_joint_stiffness" qualifiers="const">
- <return type="float" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns the stiffness of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="get_jiggle_joint_use_gravity" qualifiers="const">
- <return type="bool" />
- <param index="0" name="joint_idx" type="int" />
- <description>
- Returns a boolean that indicates whether the joint at [param joint_idx] is using gravity or not.
- </description>
- </method>
- <method name="get_use_colliders" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the Jiggle modifier is taking physics colliders into account when solving.
- </description>
- </method>
- <method name="set_collision_mask">
- <return type="void" />
- <param index="0" name="mask" type="int" />
- <description>
- Sets the collision mask that the Jiggle modifier takes into account when performing physics calculations.
- </description>
- </method>
- <method name="set_jiggle_joint_bone_index">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="bone_idx" type="int" />
- <description>
- Sets the bone index, [param bone_idx], of the Jiggle joint at [param joint_idx]. When possible, this will also update the [code]bone_name[/code] of the Jiggle joint based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_jiggle_joint_bone_name">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="name" type="String" />
- <description>
- Sets the bone name, [param name], of the Jiggle joint at [param joint_idx]. When possible, this will also update the [code]bone_index[/code] of the Jiggle joint based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_jiggle_joint_damping">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="damping" type="float" />
- <description>
- Sets the amount of dampening of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="set_jiggle_joint_gravity">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="gravity" type="Vector3" />
- <description>
- Sets the gravity vector of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="set_jiggle_joint_mass">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="mass" type="float" />
- <description>
- Sets the of mass of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="set_jiggle_joint_override">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="override" type="bool" />
- <description>
- Sets whether the Jiggle joint at [param joint_idx] should override the default Jiggle joint settings. Setting this to true will make the joint use its own settings rather than the default ones attached to the modification.
- </description>
- </method>
- <method name="set_jiggle_joint_roll">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="roll" type="float" />
- <description>
- Sets the amount of roll/twist on the bone the Jiggle joint is attached to.
- </description>
- </method>
- <method name="set_jiggle_joint_stiffness">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="stiffness" type="float" />
- <description>
- Sets the of stiffness of the Jiggle joint at [param joint_idx].
- </description>
- </method>
- <method name="set_jiggle_joint_use_gravity">
- <return type="void" />
- <param index="0" name="joint_idx" type="int" />
- <param index="1" name="use_gravity" type="bool" />
- <description>
- Sets whether the Jiggle joint at [param joint_idx] should use gravity.
- </description>
- </method>
- <method name="set_use_colliders">
- <return type="void" />
- <param index="0" name="use_colliders" type="bool" />
- <description>
- When [code]true[/code], the Jiggle modifier will use raycasting to prevent the Jiggle joints from rotating themselves into collision objects when solving.
- </description>
- </method>
- </methods>
- <members>
- <member name="damping" type="float" setter="set_damping" getter="get_damping" default="0.75">
- The default amount of dampening applied to the Jiggle joints, if they are not overridden. Higher values lead to more of the calculated velocity being applied.
- </member>
- <member name="gravity" type="Vector3" setter="set_gravity" getter="get_gravity" default="Vector3(0, -6, 0)">
- The default amount of gravity applied to the Jiggle joints, if they are not overridden.
- </member>
- <member name="jiggle_data_chain_length" type="int" setter="set_jiggle_data_chain_length" getter="get_jiggle_data_chain_length" default="0">
- The amount of Jiggle joints in the Jiggle modification.
- </member>
- <member name="mass" type="float" setter="set_mass" getter="get_mass" default="0.75">
- The default amount of mass assigned to the Jiggle joints, if they are not overridden. Higher values lead to faster movements and more overshooting.
- </member>
- <member name="stiffness" type="float" setter="set_stiffness" getter="get_stiffness" default="3.0">
- The default amount of stiffness assigned to the Jiggle joints, if they are not overridden. Higher values act more like springs, quickly moving into the correct position.
- </member>
- <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
- The NodePath to the node that is the target for the Jiggle modification. This node is what the Jiggle chain will attempt to rotate the bone chain to.
- </member>
- <member name="use_gravity" type="bool" setter="set_use_gravity" getter="get_use_gravity" default="false">
- Whether the gravity vector, [member gravity], should be applied to the Jiggle joints, assuming they are not overriding the default settings.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModification3DLookAt.xml b/doc/classes/SkeletonModification3DLookAt.xml
deleted file mode 100644
index aeed953ca9..0000000000
--- a/doc/classes/SkeletonModification3DLookAt.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DLookAt" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that rotates a bone to look at a target.
- </brief_description>
- <description>
- This [SkeletonModification3D] rotates a bone to look a target. This is extremely helpful for moving character's heads to look at the player, rotating a turret to look at a target, or any other case where you want to make a bone rotate towards something quickly and easily.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_additional_rotation" qualifiers="const">
- <return type="Vector3" />
- <description>
- Returns the amount of extra rotation that is applied to the bone after the LookAt modification executes.
- </description>
- </method>
- <method name="get_lock_rotation_plane" qualifiers="const">
- <return type="int" />
- <description>
- Returns the plane that the LookAt modification is limiting rotation to.
- </description>
- </method>
- <method name="get_lock_rotation_to_plane" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the LookAt modification is limiting rotation to a single plane in 3D space.
- </description>
- </method>
- <method name="set_additional_rotation">
- <return type="void" />
- <param index="0" name="additional_rotation" type="Vector3" />
- <description>
- Sets the amount of extra rotation to be applied after the LookAt modification executes. This allows you to adjust the finished result.
- </description>
- </method>
- <method name="set_lock_rotation_plane">
- <return type="void" />
- <param index="0" name="plane" type="int" />
- <description>
- </description>
- </method>
- <method name="set_lock_rotation_to_plane">
- <return type="void" />
- <param index="0" name="lock_to_plane" type="bool" />
- <description>
- When [code]true[/code], the LookAt modification will limit its rotation to a single plane in 3D space. The plane used can be configured using the [code]set_lock_rotation_plane[/code] function.
- </description>
- </method>
- </methods>
- <members>
- <member name="bone_index" type="int" setter="set_bone_index" getter="get_bone_index" default="-2">
- The bone index of the bone that should be operated on by this modification.
- When possible, this will also update the [member bone_name] based on data provided by the [Skeleton3D].
- </member>
- <member name="bone_name" type="String" setter="set_bone_name" getter="get_bone_name" default="&quot;&quot;">
- The name of the bone that should be operated on by this modification.
- When possible, this will also update the [member bone_index] based on data provided by the [Skeleton3D].
- </member>
- <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
- The NodePath to the node that is the target for the modification.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModification3DStackHolder.xml b/doc/classes/SkeletonModification3DStackHolder.xml
deleted file mode 100644
index 9448e2c783..0000000000
--- a/doc/classes/SkeletonModification3DStackHolder.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DStackHolder" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that holds and executes a [SkeletonModificationStack3D].
- </brief_description>
- <description>
- This [SkeletonModification3D] holds a reference to a [SkeletonModificationStack3D], allowing you to use multiple modification stacks on a single [Skeleton3D].
- [b]Note:[/b] The modifications in the held [SkeletonModificationStack3D] will only be executed if their execution mode matches the execution mode of the SkeletonModification3DStackHolder.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_held_modification_stack" qualifiers="const">
- <return type="SkeletonModificationStack3D" />
- <description>
- Returns the [SkeletonModificationStack3D] that this modification is holding.
- </description>
- </method>
- <method name="set_held_modification_stack">
- <return type="void" />
- <param index="0" name="held_modification_stack" type="SkeletonModificationStack3D" />
- <description>
- Sets the [SkeletonModificationStack3D] that this modification is holding. This modification stack will then be executed when this modification is executed.
- </description>
- </method>
- </methods>
-</class>
diff --git a/doc/classes/SkeletonModification3DTwoBoneIK.xml b/doc/classes/SkeletonModification3DTwoBoneIK.xml
deleted file mode 100644
index 0e7ffd5c80..0000000000
--- a/doc/classes/SkeletonModification3DTwoBoneIK.xml
+++ /dev/null
@@ -1,191 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModification3DTwoBoneIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A modification that moves two bones to reach the target.
- </brief_description>
- <description>
- This [SkeletonModification3D] 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.
- 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 [SkeletonModification3DFABRIK], but gives similar, natural looking results.
- A [Node3D]-based node can be used to define the pole, or bend direction, allowing control over which direction the joint takes when bending to reach the target when the target is within reach.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_auto_calculate_joint_length" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the TwoBoneIK modification will attempt to autocalculate the lengths of the two bones.
- </description>
- </method>
- <method name="get_joint_one_bone_idx" qualifiers="const">
- <return type="int" />
- <description>
- Returns the bone index of the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_one_bone_name" qualifiers="const">
- <return type="String" />
- <description>
- Returns the name of the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_one_length" qualifiers="const">
- <return type="float" />
- <description>
- Returns the length of the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_one_roll" qualifiers="const">
- <return type="float" />
- <description>
- Returns the amount of roll/twist applied to the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_two_bone_idx" qualifiers="const">
- <return type="int" />
- <description>
- Returns the bone index of the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_two_bone_name" qualifiers="const">
- <return type="String" />
- <description>
- Returns the name of the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_two_length" qualifiers="const">
- <return type="float" />
- <description>
- Returns the length of the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_joint_two_roll" qualifiers="const">
- <return type="float" />
- <description>
- Returns the amount of roll/twist applied to the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="get_pole_node" qualifiers="const">
- <return type="NodePath" />
- <description>
- Returns the node that is being used as the pole node for the TwoBoneIK modification, if a pole node has been set.
- </description>
- </method>
- <method name="get_tip_node" qualifiers="const">
- <return type="NodePath" />
- <description>
- Returns the node that is being used to calculate the tip position of the second bone in the TwoBoneIK modification, if a tip node has been set.
- </description>
- </method>
- <method name="get_use_pole_node" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the TwoBoneIK modification will attempt to use the pole node to figure out which direction to bend, if a pole node has been set.
- </description>
- </method>
- <method name="get_use_tip_node" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the TwoBoneIK modification will attempt to use the tip node to figure out the length and position of the tip of the second bone.
- </description>
- </method>
- <method name="set_auto_calculate_joint_length">
- <return type="void" />
- <param index="0" name="auto_calculate_joint_length" type="bool" />
- <description>
- If true, the TwoBoneIK modification will attempt to autocalculate the lengths of the bones being used. The first bone will be calculated by using the distance from the origin of the first bone to the origin of the second bone.
- The second bone will be calculated either using the tip node if that setting is enabled, or by using the distances of the second bone's children. If the tip node is not enabled and the bone has no children, then the length cannot be autocalculated. In this case, the length will either have to be manually inputted or a tip node used to calculate the length.
- </description>
- </method>
- <method name="set_joint_one_bone_idx">
- <return type="void" />
- <param index="0" name="bone_idx" type="int" />
- <description>
- Sets the bone index, [param bone_idx], of the first bone. When possible, this will also update the [code]bone_name[/code] of the first bone based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_joint_one_bone_name">
- <return type="void" />
- <param index="0" name="bone_name" type="String" />
- <description>
- Sets the bone name, [param bone_name], of the first bone. When possible, this will also update the [code]bone_index[/code] of the first bone based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_joint_one_length">
- <return type="void" />
- <param index="0" name="bone_length" type="float" />
- <description>
- Sets the length of the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="set_joint_one_roll">
- <return type="void" />
- <param index="0" name="roll" type="float" />
- <description>
- Sets the amount of roll/twist applied to the first bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="set_joint_two_bone_idx">
- <return type="void" />
- <param index="0" name="bone_idx" type="int" />
- <description>
- Sets the bone index, [param bone_idx], of the second bone. When possible, this will also update the [code]bone_name[/code] of the second bone based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_joint_two_bone_name">
- <return type="void" />
- <param index="0" name="bone_name" type="String" />
- <description>
- Sets the bone name, [param bone_name], of the second bone. When possible, this will also update the [code]bone_index[/code] of the second bone based on data provided by the [Skeleton3D].
- </description>
- </method>
- <method name="set_joint_two_length">
- <return type="void" />
- <param index="0" name="bone_length" type="float" />
- <description>
- Sets the length of the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="set_joint_two_roll">
- <return type="void" />
- <param index="0" name="roll" type="float" />
- <description>
- Sets the amount of roll/twist applied to the second bone in the TwoBoneIK modification.
- </description>
- </method>
- <method name="set_pole_node">
- <return type="void" />
- <param index="0" name="pole_nodepath" type="NodePath" />
- <description>
- Sets the node to be used as the for the pole of the TwoBoneIK. When a node is set and the modification is set to use the pole node, the TwoBoneIK modification will bend the nodes in the direction towards this node when the bones need to bend.
- </description>
- </method>
- <method name="set_tip_node">
- <return type="void" />
- <param index="0" name="tip_nodepath" type="NodePath" />
- <description>
- Sets the node to be used as the tip for the second bone. This is used to calculate the length and position of the end of the second bone in the TwoBoneIK modification.
- [b]Note:[/b] The tip node should generally be a child node of a [BoneAttachment3D] node attached to the second bone, with the child node being offset so it is at the end of the bone.
- </description>
- </method>
- <method name="set_use_pole_node">
- <return type="void" />
- <param index="0" name="use_pole_node" type="bool" />
- <description>
- When [code]true[/code], the TwoBoneIK modification will bend the bones towards the pole node, if one has been set. This gives control over the direction the TwoBoneIK solver will bend, which is helpful for joints like elbows that only bend in certain directions.
- </description>
- </method>
- <method name="set_use_tip_node">
- <return type="void" />
- <param index="0" name="use_tip_node" type="bool" />
- <description>
- When [code]true[/code], the TwoBoneIK modification will use the tip node to calculate the distance and position of the end/tip of the second bone. This is the most stable solution for knowing the tip position and length of the second bone.
- </description>
- </method>
- </methods>
- <members>
- <member name="target_nodepath" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath(&quot;&quot;)">
- The NodePath to the node that is the target for the TwoBoneIK modification. This node is what the modification will attempt to rotate the bones to reach.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonModificationStack3D.xml b/doc/classes/SkeletonModificationStack3D.xml
deleted file mode 100644
index 9eaeeefd8e..0000000000
--- a/doc/classes/SkeletonModificationStack3D.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="SkeletonModificationStack3D" inherits="Resource" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
- <brief_description>
- A resource that holds a stack of [SkeletonModification3D]s.
- </brief_description>
- <description>
- This resource is used by the Skeleton and holds a stack of [SkeletonModification3D]s. The SkeletonModificationStack3D controls the order of the modifications, which controls how they are applied. Modification order is especially important for full-body IK setups, as you need to execute the modifications in the correct order to get the desired results. For example, you want to execute a modification on the spine [i]before[/i] the arms on a humanoid skeleton.
- Additionally, the SkeletonModificationStack3D also controls how strongly the modifications are applied to the [Skeleton3D] node.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="add_modification">
- <return type="void" />
- <param index="0" name="modification" type="SkeletonModification3D" />
- <description>
- Adds the passed-in [SkeletonModification3D] to the stack.
- </description>
- </method>
- <method name="delete_modification">
- <return type="void" />
- <param index="0" name="mod_idx" type="int" />
- <description>
- Deletes the [SkeletonModification3D] at the index position [param mod_idx], if it exists.
- </description>
- </method>
- <method name="enable_all_modifications">
- <return type="void" />
- <param index="0" name="enabled" type="bool" />
- <description>
- Enables all [SkeletonModification3D]s in the stack.
- </description>
- </method>
- <method name="execute">
- <return type="void" />
- <param index="0" name="delta" type="float" />
- <param index="1" name="execution_mode" type="int" />
- <description>
- Executes all of the [SkeletonModification3D]s in the stack that use the same execution mode as the passed-in [param execution_mode], starting from index [code]0[/code] to [member modification_count].
- [b]Note:[/b] The order of the modifications can matter depending on the modifications. For example, modifications on a spine should operate before modifications on the arms in order to get proper results.
- </description>
- </method>
- <method name="get_is_setup" qualifiers="const">
- <return type="bool" />
- <description>
- Returns a boolean that indicates whether the modification stack is setup and can execute.
- </description>
- </method>
- <method name="get_modification" qualifiers="const">
- <return type="SkeletonModification3D" />
- <param index="0" name="mod_idx" type="int" />
- <description>
- Returns the [SkeletonModification3D] at the passed-in index, [param mod_idx].
- </description>
- </method>
- <method name="get_skeleton" qualifiers="const">
- <return type="Skeleton3D" />
- <description>
- Returns the [Skeleton3D] node that the SkeletonModificationStack3D is bound to.
- </description>
- </method>
- <method name="set_modification">
- <return type="void" />
- <param index="0" name="mod_idx" type="int" />
- <param index="1" name="modification" type="SkeletonModification3D" />
- <description>
- Sets the modification at [param mod_idx] to the passed-in modification, [param modification].
- </description>
- </method>
- <method name="setup">
- <return type="void" />
- <description>
- Sets up the modification stack so it can execute. This function should be called by [Skeleton3D] and shouldn't be called unless you know what you are doing.
- </description>
- </method>
- </methods>
- <members>
- <member name="enabled" type="bool" setter="set_enabled" getter="get_enabled" default="false">
- When true, the modification's in the stack will be called. This is handled automatically through the [Skeleton3D] node.
- </member>
- <member name="modification_count" type="int" setter="set_modification_count" getter="get_modification_count" default="0">
- The number of modifications in the stack.
- </member>
- <member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0">
- The interpolation strength of the modifications in stack. A value of [code]0[/code] will make it where the modifications are not applied, a strength of [code]0.5[/code] will be half applied, and a strength of [code]1[/code] will allow the modifications to be fully applied and override the skeleton bone poses.
- </member>
- </members>
-</class>
diff --git a/doc/classes/SkeletonProfileHumanoid.xml b/doc/classes/SkeletonProfileHumanoid.xml
index 0dbd66d8d6..7445272ccc 100644
--- a/doc/classes/SkeletonProfileHumanoid.xml
+++ b/doc/classes/SkeletonProfileHumanoid.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonProfileHumanoid" inherits="SkeletonProfile" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A humanoid [SkeletonProfile] preset.
</brief_description>
<description>
A [SkeletonProfile] as a preset that is optimized for the human form. This exists for standardization, so all parameters are read-only.
diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml
index 7d63747bd2..e2db2166ed 100644
--- a/doc/classes/SpinBox.xml
+++ b/doc/classes/SpinBox.xml
@@ -60,7 +60,7 @@
<member name="select_all_on_focus" type="bool" setter="set_select_all_on_focus" getter="is_select_all_on_focus" default="false">
If [code]true[/code], the [SpinBox] will select the whole text when the [LineEdit] gains focus. Clicking the up and down arrows won't trigger this behavior.
</member>
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="1" />
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="1" />
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
<member name="suffix" type="String" setter="set_suffix" getter="get_suffix" default="&quot;&quot;">
Adds the specified [code]suffix[/code] string after the numerical value of the [SpinBox].
diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml
index 87b823bd2a..195f3598d5 100644
--- a/doc/classes/SpriteFrames.xml
+++ b/doc/classes/SpriteFrames.xml
@@ -5,7 +5,6 @@
</brief_description>
<description>
Sprite frame library for an [AnimatedSprite2D] or [AnimatedSprite3D] node. Contains frames and animation data for playback.
- [b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps.
</description>
<tutorials>
</tutorials>
@@ -14,7 +13,7 @@
<return type="void" />
<param index="0" name="anim" type="StringName" />
<description>
- Adds a new animation to the library.
+ Adds a new [param anim] animation to the library.
</description>
</method>
<method name="add_frame">
@@ -24,20 +23,20 @@
<param index="2" name="duration" type="float" default="1.0" />
<param index="3" name="at_position" type="int" default="-1" />
<description>
- Adds a frame to the given animation.
+ Adds a frame to the [param anim] animation. If [param at_position] is [code]-1[/code], the frame will be added to the end of the animation.
</description>
</method>
<method name="clear">
<return type="void" />
<param index="0" name="anim" type="StringName" />
<description>
- Removes all frames from the given animation.
+ Removes all frames from the [param anim] animation.
</description>
</method>
<method name="clear_all">
<return type="void" />
<description>
- Removes all animations. A "default" animation will be created.
+ Removes all animations. An empty [code]default[/code] animation will be created.
</description>
</method>
<method name="get_animation_loop" qualifiers="const">
@@ -91,14 +90,14 @@
<return type="bool" />
<param index="0" name="anim" type="StringName" />
<description>
- If [code]true[/code], the named animation exists.
+ Returns [code]true[/code] if the [param anim] animation exists.
</description>
</method>
<method name="remove_animation">
<return type="void" />
<param index="0" name="anim" type="StringName" />
<description>
- Removes the given animation.
+ Removes the [param anim] animation.
</description>
</method>
<method name="remove_frame">
@@ -106,7 +105,7 @@
<param index="0" name="anim" type="StringName" />
<param index="1" name="idx" type="int" />
<description>
- Removes the animation's selected frame.
+ Removes the [param anim] animation's frame [param idx].
</description>
</method>
<method name="rename_animation">
@@ -114,7 +113,7 @@
<param index="0" name="anim" type="StringName" />
<param index="1" name="newname" type="StringName" />
<description>
- Changes the animation's name to [param newname].
+ Changes the [param anim] animation's name to [param newname].
</description>
</method>
<method name="set_animation_loop">
@@ -122,7 +121,7 @@
<param index="0" name="anim" type="StringName" />
<param index="1" name="loop" type="bool" />
<description>
- If [code]true[/code], the animation will loop.
+ If [param loop] is [code]true[/code], the [param anim] animation will loop when it reaches the end, or the start if it is played in reverse.
</description>
</method>
<method name="set_animation_speed">
@@ -140,7 +139,7 @@
<param index="2" name="texture" type="Texture2D" />
<param index="3" name="duration" type="float" default="1.0" />
<description>
- Sets the texture and the duration of the frame [param idx] in the [param anim] animation.
+ Sets the [param texture] and the [param duration] of the frame [param idx] in the [param anim] animation.
</description>
</method>
</methods>
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml
index 8d56cbda13..3c7191392a 100644
--- a/doc/classes/TabBar.xml
+++ b/doc/classes/TabBar.xml
@@ -17,6 +17,12 @@
Adds a new tab.
</description>
</method>
+ <method name="clear_tabs">
+ <return type="void" />
+ <description>
+ Clears all tabs.
+ </description>
+ </method>
<method name="ensure_tab_visible">
<return type="void" />
<param index="0" name="idx" type="int" />
diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml
index d7a9d06a15..f381ee771e 100644
--- a/doc/classes/TextureProgressBar.xml
+++ b/doc/classes/TextureProgressBar.xml
@@ -43,7 +43,7 @@
<member name="radial_initial_angle" type="float" setter="set_radial_initial_angle" getter="get_radial_initial_angle" default="0.0">
Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees].
</member>
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="1" />
+ <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" default="1" />
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
<member name="stretch_margin_bottom" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0">
The height of the 9-patch's bottom row. A margin of 16 means the 9-slice's bottom corners and side will have a height of 16 pixels. You can set all 4 margin values individually to create panels with non-uniform borders.
diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml
index 348b4a5837..460ffbbb80 100644
--- a/doc/classes/TextureRect.xml
+++ b/doc/classes/TextureRect.xml
@@ -10,15 +10,15 @@
<link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link>
</tutorials>
<members>
+ <member name="expand_mode" type="int" setter="set_expand_mode" getter="get_expand_mode" enum="TextureRect.ExpandMode" default="0">
+ Defines how minimum size is determined based on the texture's size. See [enum ExpandMode] for options.
+ </member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
If [code]true[/code], texture is flipped horizontally.
</member>
<member name="flip_v" type="bool" setter="set_flip_v" getter="is_flipped_v" default="false">
If [code]true[/code], texture is flipped vertically.
</member>
- <member name="ignore_texture_size" type="bool" setter="set_ignore_texture_size" getter="get_ignore_texture_size" default="false">
- If [code]true[/code], the size of the texture won't be considered for minimum size calculation, so the [TextureRect] can be shrunk down past the texture size. Useful for preventing [TextureRect]s from breaking GUI layout regardless of their texture size.
- </member>
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" overrides="Control" enum="Control.MouseFilter" default="1" />
<member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureRect.StretchMode" default="0">
Controls the texture's behavior when resizing the node's bounding rectangle. See [enum StretchMode].
@@ -28,6 +28,24 @@
</member>
</members>
<constants>
+ <constant name="EXPAND_KEEP_SIZE" value="0" enum="ExpandMode">
+ The minimum size will be equal to texture size, i.e. [TextureRect] can't be smaller than the texture.
+ </constant>
+ <constant name="EXPAND_IGNORE_SIZE" value="1" enum="ExpandMode">
+ The size of the texture won't be considered for minimum size calculation, so the [TextureRect] can be shrunk down past the texture size.
+ </constant>
+ <constant name="EXPAND_FIT_WIDTH" value="2" enum="ExpandMode">
+ The height of the texture will be ignored. Minimum width will be equal to the current height. Useful for horizontal layouts, e.g. inside [HBoxContainer].
+ </constant>
+ <constant name="EXPAND_FIT_WIDTH_PROPORTIONAL" value="3" enum="ExpandMode">
+ Same as [constant EXPAND_FIT_WIDTH], but keeps texture's aspect ratio.
+ </constant>
+ <constant name="EXPAND_FIT_HEIGHT" value="4" enum="ExpandMode">
+ The width of the texture will be ignored. Minimum height will be equal to the current width. Useful for vertical layouts, e.g. inside [VBoxContainer].
+ </constant>
+ <constant name="EXPAND_FIT_HEIGHT_PROPORTIONAL" value="5" enum="ExpandMode">
+ Same as [constant EXPAND_FIT_HEIGHT], but keeps texture's aspect ratio.
+ </constant>
<constant name="STRETCH_SCALE" value="0" enum="StretchMode">
Scale to fit the node's bounding rectangle.
</constant>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 8b537545bc..8176901ff7 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -321,7 +321,7 @@
<param index="1" name="y_sort_enabled" type="bool" />
<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 behvaior.
+ 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.
</description>
</method>
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index d171797e80..1b6c05284e 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -48,7 +48,7 @@
</member>
<member name="time_left" type="float" setter="" getter="get_time_left">
The timer's remaining time in seconds. Returns 0 if the timer is inactive.
- [b]Note:[/b] You cannot set this value. To change the timer's remaining time, use [method start].
+ [b]Note:[/b] This value is read-only and cannot be set. It is based on [member wait_time], which can be set using [method start].
</member>
<member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time" default="1.0">
The wait time in seconds.
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index b3145ea022..90c10e3664 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -41,6 +41,7 @@
<return type="Transform3D" />
<param index="0" name="from" type="Projection" />
<description>
+ Constructs a Transform3D from a [Projection] by trimming the last row of the projection matrix ([code]from.x.w[/code], [code]from.y.w[/code], [code]from.z.w[/code], and [code]from.w.w[/code] are not copied over).
</description>
</constructor>
<constructor name="Transform3D">
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 584a2a2a7b..ff5a665bfd 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -45,11 +45,17 @@
<method name="create_item">
<return type="TreeItem" />
<param index="0" name="parent" type="TreeItem" default="null" />
- <param index="1" name="idx" type="int" default="-1" />
+ <param index="1" name="index" type="int" default="-1" />
<description>
Creates an item in the tree and adds it as a child of [param parent], which can be either a valid [TreeItem] or [code]null[/code].
If [param parent] is [code]null[/code], the root item will be the parent, or the new item will be the root itself if the tree is empty.
- The new item will be the [param idx]th child of parent, or it will be the last child if there are not enough siblings.
+ The new item will be the [param index]-th child of parent, or it will be the last child if there are not enough siblings.
+ </description>
+ </method>
+ <method name="deselect_all">
+ <return type="void" />
+ <description>
+ Deselects all tree items (rows and columns). In [constant SELECT_MULTI] mode also removes selection cursor.
</description>
</method>
<method name="edit_selected">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index ec6b166e57..91248092d9 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -18,7 +18,7 @@
<param index="3" name="disabled" type="bool" default="false" />
<param index="4" name="tooltip_text" type="String" default="&quot;&quot;" />
<description>
- Adds a button with [Texture2D] [param button] at column [param column]. The [param id] is used to identify the button. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text].
+ Adds a button with [Texture2D] [param button] at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text].
</description>
</method>
<method name="call_recursive" qualifiers="vararg">
@@ -44,10 +44,10 @@
</method>
<method name="create_child">
<return type="TreeItem" />
- <param index="0" name="idx" type="int" default="-1" />
+ <param index="0" name="index" type="int" default="-1" />
<description>
Creates an item and adds it as a child.
- The new item will be inserted as position [param idx] (the default value [code]-1[/code] means the last position), or it will be the last child if [param idx] is higher than the child count.
+ The new item will be inserted as position [param index] (the default value [code]-1[/code] means the last position), or it will be the last child if [param index] is higher than the child count.
</description>
</method>
<method name="deselect">
@@ -60,17 +60,17 @@
<method name="erase_button">
<return type="void" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<description>
- Removes the button at index [param button_idx] in column [param column].
+ Removes the button at index [param button_index] in column [param column].
</description>
</method>
<method name="get_button" qualifiers="const">
<return type="Texture2D" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<description>
- Returns the [Texture2D] of the button at index [param button_idx] in column [param column].
+ Returns the [Texture2D] of the button at index [param button_index] in column [param column].
</description>
</method>
<method name="get_button_by_id" qualifiers="const">
@@ -91,17 +91,17 @@
<method name="get_button_id" qualifiers="const">
<return type="int" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<description>
- Returns the ID for the button at index [param button_idx] in column [param column].
+ Returns the ID for the button at index [param button_index] in column [param column].
</description>
</method>
<method name="get_button_tooltip_text" qualifiers="const">
<return type="String" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<description>
- Returns the tooltip text for the button at index [param button_idx] in column [param column].
+ Returns the tooltip text for the button at index [param button_index] in column [param column].
</description>
</method>
<method name="get_cell_mode" qualifiers="const">
@@ -113,9 +113,9 @@
</method>
<method name="get_child">
<return type="TreeItem" />
- <param index="0" name="idx" type="int" />
+ <param index="0" name="index" type="int" />
<description>
- Returns a child item by its index (see [method get_child_count]). This method is often used for iterating all children of an item.
+ Returns a child item by its [param index] (see [method get_child_count]). This method is often used for iterating all children of an item.
Negative indices access the children from the last one.
</description>
</method>
@@ -332,9 +332,9 @@
<method name="is_button_disabled" qualifiers="const">
<return type="bool" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<description>
- Returns [code]true[/code] if the button at index [param button_idx] for the given [param column] is disabled.
+ Returns [code]true[/code] if the button at index [param button_index] for the given [param column] is disabled.
</description>
</method>
<method name="is_checked" qualifiers="const">
@@ -419,28 +419,28 @@
<method name="set_button">
<return type="void" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<param index="2" name="button" type="Texture2D" />
<description>
- Sets the given column's button [Texture2D] at index [param button_idx] to [param button].
+ Sets the given column's button [Texture2D] at index [param button_index] to [param button].
</description>
</method>
<method name="set_button_color">
<return type="void" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<param index="2" name="color" type="Color" />
<description>
- Sets the given column's button color at index [param button_idx] to [param color].
+ Sets the given column's button color at index [param button_index] to [param color].
</description>
</method>
<method name="set_button_disabled">
<return type="void" />
<param index="0" name="column" type="int" />
- <param index="1" name="button_idx" type="int" />
+ <param index="1" name="button_index" type="int" />
<param index="2" name="disabled" type="bool" />
<description>
- If [code]true[/code], disables the button at index [param button_idx] in the given [param column].
+ If [code]true[/code], disables the button at index [param button_index] in the given [param column].
</description>
</method>
<method name="set_cell_mode">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index eef35049e5..fc0dd9f05d 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -11,7 +11,7 @@
[codeblocks]
[gdscript]
var tween = get_tree().create_tween()
- tween.tween_property($Sprite, "modulate", Color.red, 1)
+ tween.tween_property($Sprite, "modulate", Color.RED, 1)
tween.tween_property($Sprite, "scale", Vector2(), 1)
tween.tween_callback($Sprite.queue_free)
[/gdscript]
@@ -27,7 +27,7 @@
[codeblocks]
[gdscript]
var tween = get_tree().create_tween()
- tween.tween_property($Sprite, "modulate", Color.red, 1).set_trans(Tween.TRANS_SINE)
+ tween.tween_property($Sprite, "modulate", Color.RED, 1).set_trans(Tween.TRANS_SINE)
tween.tween_property($Sprite, "scale", Vector2(), 1).set_trans(Tween.TRANS_BOUNCE)
tween.tween_callback($Sprite.queue_free)
[/gdscript]
@@ -42,7 +42,7 @@
[codeblocks]
[gdscript]
var tween = get_tree().create_tween().bind_node(self).set_trans(Tween.TRANS_ELASTIC)
- tween.tween_property($Sprite, "modulate", Color.red, 1)
+ tween.tween_property($Sprite, "modulate", Color.RED, 1)
tween.tween_property($Sprite, "scale", Vector2(), 1)
tween.tween_callback($Sprite.queue_free)
[/gdscript]
@@ -288,8 +288,8 @@
[codeblocks]
[gdscript]
var tween = get_tree().create_tween()
- tween.tween_callback($Sprite.set_modulate.bind(Color.red)).set_delay(2)
- tween.tween_callback($Sprite.set_modulate.bind(Color.blue)).set_delay(2)
+ tween.tween_callback($Sprite.set_modulate.bind(Color.RED)).set_delay(2)
+ tween.tween_callback($Sprite.set_modulate.bind(Color.BLUE)).set_delay(2)
[/gdscript]
[csharp]
Tween tween = GetTree().CreateTween();
diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml
index 7258efbdda..42baf7728d 100644
--- a/doc/classes/UndoRedo.xml
+++ b/doc/classes/UndoRedo.xml
@@ -17,7 +17,7 @@
func undo_something():
pass # Put here the code that reverts what's done by "do_something()".
- func _on_MyButton_pressed():
+ func _on_my_button_pressed():
var node = get_node("MyNode2D")
undo_redo.create_action("Move the node")
undo_redo.add_do_method(self, "do_something")
diff --git a/doc/classes/VScrollBar.xml b/doc/classes/VScrollBar.xml
index f9baf194b7..41a87ba886 100644
--- a/doc/classes/VScrollBar.xml
+++ b/doc/classes/VScrollBar.xml
@@ -9,8 +9,8 @@
<tutorials>
</tutorials>
<members>
- <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" default="0" />
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="1" />
+ <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" default="0" />
+ <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="decrement" data_type="icon" type="Texture2D">
diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml
index 4bc98dea6f..2a5b5c1e0a 100644
--- a/doc/classes/VSlider.xml
+++ b/doc/classes/VSlider.xml
@@ -10,8 +10,8 @@
<tutorials>
</tutorials>
<members>
- <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" default="0" />
- <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" default="1" />
+ <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" overrides="Control" enum="Control.SizeFlags" default="0" />
+ <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="grabber_offset" data_type="constant" type="int" default="0">
diff --git a/doc/classes/Variant.xml b/doc/classes/Variant.xml
index 6b384d6a77..5416468ab6 100644
--- a/doc/classes/Variant.xml
+++ b/doc/classes/Variant.xml
@@ -38,7 +38,7 @@
# To get the name of the underlying Object type, you need the `get_class()` method.
print("foo is a(n) %s" % foo.get_class()) # inject the class name into a formatted string.
# Note also that there is not yet any way to get a script's `class_name` string easily.
- # To fetch that value, you need to dig deeply into a hidden ProjectSettings setting: an Array of Dictionaries called "_global_script_classes".
+ # To fetch that value, you can parse the [code]res://.godot/global_script_class_cache.cfg[/code] file with the [ConfigFile] API.
# Open your project.godot file to see it up close.
[/gdscript]
[csharp]
@@ -70,6 +70,6 @@
Modifications to a container will modify all references to it. A [Mutex] should be created to lock it if multi-threaded access is desired.
</description>
<tutorials>
- <link title="Variant class">$DOCS_URL/development/cpp/variant_class.html</link>
+ <link title="Variant class introduction">$DOCS_URL/contributing/development/core_and_modules/variant_class.html</link>
</tutorials>
</class>
diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml
index 2d59810a5f..6bffcdef36 100644
--- a/doc/classes/VisualShader.xml
+++ b/doc/classes/VisualShader.xml
@@ -26,6 +26,7 @@
<param index="1" name="mode" type="int" enum="VisualShader.VaryingMode" />
<param index="2" name="type" type="int" enum="VisualShader.VaryingType" />
<description>
+ Adds a new varying value node to the shader.
</description>
</method>
<method name="can_connect_nodes" qualifiers="const">
@@ -106,12 +107,14 @@
<return type="int" />
<param index="0" name="type" type="int" enum="VisualShader.Type" />
<description>
+ Returns next valid node ID that can be added to the shader graph.
</description>
</method>
<method name="has_varying" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="String" />
<description>
+ Returns [code]true[/code] if the shader has a varying with the given [param name].
</description>
</method>
<method name="is_node_connection" qualifiers="const">
@@ -137,6 +140,7 @@
<return type="void" />
<param index="0" name="name" type="String" />
<description>
+ Removes a varying value node with the given [param name]. Prints an error if a node with this name is not found.
</description>
</method>
<method name="replace_node">
@@ -181,16 +185,22 @@
A shader for light calculations.
</constant>
<constant name="TYPE_START" value="3" enum="Type">
+ A function for the "start" stage of particle shader.
</constant>
<constant name="TYPE_PROCESS" value="4" enum="Type">
+ A function for the "process" stage of particle shader.
</constant>
<constant name="TYPE_COLLIDE" value="5" enum="Type">
+ A function for the "collide" stage (particle collision handler) of particle shader.
</constant>
<constant name="TYPE_START_CUSTOM" value="6" enum="Type">
+ A function for the "start" stage of particle shader, with customized output.
</constant>
<constant name="TYPE_PROCESS_CUSTOM" value="7" enum="Type">
+ A function for the "process" stage of particle shader, with customized output.
</constant>
<constant name="TYPE_SKY" value="8" enum="Type">
+ A shader for 3D environment's sky.
</constant>
<constant name="TYPE_FOG" value="9" enum="Type">
A compute shader that runs for each froxel of the volumetric fog map.
@@ -199,32 +209,46 @@
Represents the size of the [enum Type] enum.
</constant>
<constant name="VARYING_MODE_VERTEX_TO_FRAG_LIGHT" value="0" enum="VaryingMode">
+ Varying is passed from [code]Vertex[/code] function to [code]Fragment[/code] and [code]Light[/code] functions.
</constant>
<constant name="VARYING_MODE_FRAG_TO_LIGHT" value="1" enum="VaryingMode">
+ Varying is passed from [code]Fragment[/code] function to [code]Light[/code] function.
</constant>
<constant name="VARYING_MODE_MAX" value="2" enum="VaryingMode">
+ Represents the size of the [enum VaryingMode] enum.
</constant>
<constant name="VARYING_TYPE_FLOAT" value="0" enum="VaryingType">
+ Varying is of type [float].
</constant>
<constant name="VARYING_TYPE_INT" value="1" enum="VaryingType">
+ Varying is of type [int].
</constant>
<constant name="VARYING_TYPE_UINT" value="2" enum="VaryingType">
+ Varying is of type unsigned [int].
</constant>
<constant name="VARYING_TYPE_VECTOR_2D" value="3" enum="VaryingType">
+ Varying is of type [Vector2].
</constant>
<constant name="VARYING_TYPE_VECTOR_3D" value="4" enum="VaryingType">
+ Varying is of type [Vector3].
</constant>
<constant name="VARYING_TYPE_VECTOR_4D" value="5" enum="VaryingType">
+ Varying is of type [Vector4].
</constant>
<constant name="VARYING_TYPE_BOOLEAN" value="6" enum="VaryingType">
+ Varying is of type [bool].
</constant>
<constant name="VARYING_TYPE_TRANSFORM" value="7" enum="VaryingType">
+ Varying is of type [Transform3D].
</constant>
<constant name="VARYING_TYPE_MAX" value="8" enum="VaryingType">
+ Represents the size of the [enum VaryingType] enum.
</constant>
<constant name="NODE_ID_INVALID" value="-1">
+ Denotes invalid [VisualShader] node.
</constant>
<constant name="NODE_ID_OUTPUT" value="0">
+ Denotes output node of [VisualShader].
</constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeConstant.xml b/doc/classes/VisualShaderNodeConstant.xml
index 213ab664ad..23b97a78c7 100644
--- a/doc/classes/VisualShaderNodeConstant.xml
+++ b/doc/classes/VisualShaderNodeConstant.xml
@@ -4,6 +4,7 @@
A base type for the constants within the visual shader graph.
</brief_description>
<description>
+ This is an abstract class. See the derived types for descriptions of the possible values.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeDistanceFade.xml b/doc/classes/VisualShaderNodeDistanceFade.xml
index 8ea0857776..c707035253 100644
--- a/doc/classes/VisualShaderNodeDistanceFade.xml
+++ b/doc/classes/VisualShaderNodeDistanceFade.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeDistanceFade" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node representing distance fade effect.
</brief_description>
<description>
+ The distance fade effect fades out each pixel based on its distance to another object.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeIntParameter.xml b/doc/classes/VisualShaderNodeIntParameter.xml
index 70335b0c77..1ee7e3f217 100644
--- a/doc/classes/VisualShaderNodeIntParameter.xml
+++ b/doc/classes/VisualShaderNodeIntParameter.xml
@@ -1,33 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeIntParameter" inherits="VisualShaderNodeParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node for shader parameter (uniform) of type [int].
</brief_description>
<description>
+ A [VisualShaderNodeParameter] of type [int]. Offers additional customization for range of accepted values.
</description>
<tutorials>
</tutorials>
<members>
<member name="default_value" type="int" setter="set_default_value" getter="get_default_value" default="0">
+ Default value of this parameter, which will be used if not set externally. [member default_value_enabled] must be enabled; defaults to [code]0[/code] otherwise.
</member>
<member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false">
+ If [code]true[/code], the node will have a custom default value.
</member>
<member name="hint" type="int" setter="set_hint" getter="get_hint" enum="VisualShaderNodeIntParameter.Hint" default="0">
+ Range hint of this node. Use it to customize valid parameter range.
</member>
<member name="max" type="int" setter="set_max" getter="get_max" default="100">
+ The maximum value this parameter can take. [member hint] must be either [constant HINT_RANGE] or [constant HINT_RANGE_STEP] for this to take effect.
</member>
<member name="min" type="int" setter="set_min" getter="get_min" default="0">
+ The minimum value this parameter can take. [member hint] must be either [constant HINT_RANGE] or [constant HINT_RANGE_STEP] for this to take effect.
</member>
<member name="step" type="int" setter="set_step" getter="get_step" default="1">
+ The step between parameter's values. Forces the parameter to be a multiple of the given value. [member hint] must be [constant HINT_RANGE_STEP] for this to take effect.
</member>
</members>
<constants>
<constant name="HINT_NONE" value="0" enum="Hint">
+ The parameter will not constrain its value.
</constant>
<constant name="HINT_RANGE" value="1" enum="Hint">
+ The parameter's value must be within the specified [member min]/[member max] range.
</constant>
<constant name="HINT_RANGE_STEP" value="2" enum="Hint">
+ The parameter's value must be within the specified range, with the given [member step] between values.
</constant>
<constant name="HINT_MAX" value="3" enum="Hint">
+ Represents the size of the [enum Hint] enum.
</constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeLinearSceneDepth.xml b/doc/classes/VisualShaderNodeLinearSceneDepth.xml
index fa8c01ac0a..0dff2d780d 100644
--- a/doc/classes/VisualShaderNodeLinearSceneDepth.xml
+++ b/doc/classes/VisualShaderNodeLinearSceneDepth.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeLinearSceneDepth" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that returns the depth value of the DEPTH_TEXTURE node in a linear space.
</brief_description>
<description>
+ This node can be used in fragment shaders.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParameter.xml b/doc/classes/VisualShaderNodeParameter.xml
index 55b10ac810..acc180a7ca 100644
--- a/doc/classes/VisualShaderNodeParameter.xml
+++ b/doc/classes/VisualShaderNodeParameter.xml
@@ -13,14 +13,18 @@
Name of the parameter, by which it can be accessed through the [ShaderMaterial] properties.
</member>
<member name="qualifier" type="int" setter="set_qualifier" getter="get_qualifier" enum="VisualShaderNodeParameter.Qualifier" default="0">
+ Defines the scope of the parameter.
</member>
</members>
<constants>
<constant name="QUAL_NONE" value="0" enum="Qualifier">
+ The parameter will be tied to the [ShaderMaterial] using this shader.
</constant>
<constant name="QUAL_GLOBAL" value="1" enum="Qualifier">
+ The parameter will use a global value, defined in Project Settings.
</constant>
<constant name="QUAL_INSTANCE" value="2" enum="Qualifier">
+ The parameter will be tied to the node with attached [ShaderMaterial] using this shader.
</constant>
<constant name="QUAL_MAX" value="3" enum="Qualifier">
Represents the size of the [enum Qualifier] enum.
diff --git a/doc/classes/VisualShaderNodeParticleAccelerator.xml b/doc/classes/VisualShaderNodeParticleAccelerator.xml
index f26362b336..7a197dd8cb 100644
--- a/doc/classes/VisualShaderNodeParticleAccelerator.xml
+++ b/doc/classes/VisualShaderNodeParticleAccelerator.xml
@@ -1,21 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleAccelerator" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that accelerates particles.
</brief_description>
<description>
+ Particle accelerator can be used in "process" step of particle shader. It will accelerate the particles. Connect it to the Velocity output port.
</description>
<tutorials>
</tutorials>
<members>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="VisualShaderNodeParticleAccelerator.Mode" default="0">
+ Defines in what manner the particles will be accelerated.
</member>
</members>
<constants>
<constant name="MODE_LINEAR" value="0" enum="Mode">
+ The particles will be accelerated based on their velocity.
</constant>
<constant name="MODE_RADIAL" value="1" enum="Mode">
+ The particles will be accelerated towards or away from the center.
</constant>
<constant name="MODE_TANGENTIAL" value="2" enum="Mode">
+ The particles will be accelerated tangentially to the radius vector from center to their position.
</constant>
<constant name="MODE_MAX" value="3" enum="Mode">
Represents the size of the [enum Mode] enum.
diff --git a/doc/classes/VisualShaderNodeParticleBoxEmitter.xml b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml
index dbef4b806d..1bdb9d15bc 100644
--- a/doc/classes/VisualShaderNodeParticleBoxEmitter.xml
+++ b/doc/classes/VisualShaderNodeParticleBoxEmitter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleBoxEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that makes particles emitted in a box shape.
</brief_description>
<description>
+ [VisualShaderNodeParticleEmitter] that makes the particles emitted in box shape with the specified extents.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParticleConeVelocity.xml b/doc/classes/VisualShaderNodeParticleConeVelocity.xml
index c3b3621fbc..48da5b874b 100644
--- a/doc/classes/VisualShaderNodeParticleConeVelocity.xml
+++ b/doc/classes/VisualShaderNodeParticleConeVelocity.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleConeVelocity" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that makes particles move in a cone shape.
</brief_description>
<description>
+ This node can be used in "start" step of particle shader. It defines the initial velocity of the particles, making them move in cone shape starting from the center, with a given spread.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParticleEmit.xml b/doc/classes/VisualShaderNodeParticleEmit.xml
index c5e9d25ca1..3e52a74aed 100644
--- a/doc/classes/VisualShaderNodeParticleEmit.xml
+++ b/doc/classes/VisualShaderNodeParticleEmit.xml
@@ -1,25 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleEmit" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that forces to emit a particle from a sub-emitter.
</brief_description>
<description>
+ This node internally calls [code]emit_subparticle[/code] shader method. It will emit a particle from the configured sub-emitter and also allows to customize how its emitted. Requires a sub-emitter assigned to the particles node with this shader.
</description>
<tutorials>
</tutorials>
<members>
<member name="flags" type="int" setter="set_flags" getter="get_flags" enum="VisualShaderNodeParticleEmit.EmitFlags" default="31">
+ Flags used to override the properties defined in the sub-emitter's process material.
</member>
</members>
<constants>
<constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags">
+ If enabled, the particle starts with the position defined by this node.
</constant>
<constant name="EMIT_FLAG_ROT_SCALE" value="2" enum="EmitFlags">
+ If enabled, the particle starts with the rotation and scale defined by this node.
</constant>
<constant name="EMIT_FLAG_VELOCITY" value="4" enum="EmitFlags">
+ If enabled,the particle starts with the velocity defined by this node.
</constant>
<constant name="EMIT_FLAG_COLOR" value="8" enum="EmitFlags">
+ If enabled, the particle starts with the color defined by this node.
</constant>
<constant name="EMIT_FLAG_CUSTOM" value="16" enum="EmitFlags">
+ If enabled, the particle starts with the [code]CUSTOM[/code] data defined by this node.
</constant>
</constants>
</class>
diff --git a/doc/classes/VisualShaderNodeParticleEmitter.xml b/doc/classes/VisualShaderNodeParticleEmitter.xml
index ddfd410708..abb5528d5f 100644
--- a/doc/classes/VisualShaderNodeParticleEmitter.xml
+++ b/doc/classes/VisualShaderNodeParticleEmitter.xml
@@ -4,6 +4,7 @@
A base class for particle emitters.
</brief_description>
<description>
+ Particle emitter nodes can be used in "start" step of particle shaders and they define the starting position of the particles. Connect them to the Position output port.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParticleMeshEmitter.xml b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml
index 25dd925112..59dc74690e 100644
--- a/doc/classes/VisualShaderNodeParticleMeshEmitter.xml
+++ b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml
@@ -1,17 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleMeshEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that makes particles emitted in a shape defined by a [Mesh].
</brief_description>
<description>
+ [VisualShaderNodeParticleEmitter] that makes the particles emitted in a shape of the assigned [member mesh]. It will emit from the mesh's surfaces, either all or only the specified one.
</description>
<tutorials>
</tutorials>
<members>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ The [Mesh] that defines emission shape.
</member>
<member name="surface_index" type="int" setter="set_surface_index" getter="get_surface_index" default="0">
+ Index of the surface that emits particles. [member use_all_surfaces] must be [code]false[/code] for this to take effect.
</member>
<member name="use_all_surfaces" type="bool" setter="set_use_all_surfaces" getter="is_use_all_surfaces" default="true">
+ If [code]true[/code], the particles will emit from all surfaces of the mesh.
</member>
</members>
</class>
diff --git a/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml
index a911dbf3b4..95d031807b 100644
--- a/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml
+++ b/doc/classes/VisualShaderNodeParticleMultiplyByAxisAngle.xml
@@ -1,13 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleMultiplyByAxisAngle" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader helper node for multiplying position and rotation of particles.
</brief_description>
<description>
+ This node helps to multiply a position input vector by rotation using specific axis. Intended to work with emitters.
</description>
<tutorials>
</tutorials>
<members>
<member name="degrees_mode" type="bool" setter="set_degrees_mode" getter="is_degrees_mode" default="true">
+ If [code]true[/code], the angle will be interpreted in degrees instead of radians.
</member>
</members>
</class>
diff --git a/doc/classes/VisualShaderNodeParticleOutput.xml b/doc/classes/VisualShaderNodeParticleOutput.xml
index 7542272e61..4a1e61b879 100644
--- a/doc/classes/VisualShaderNodeParticleOutput.xml
+++ b/doc/classes/VisualShaderNodeParticleOutput.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleOutput" inherits="VisualShaderNodeOutput" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Visual shader node that defines output values for particle emitting.
</brief_description>
<description>
+ This node defines how particles are emitted. It allows to customize e.g. position and velocity. Available ports are different depending on which function this node is inside (start, process, collision) and whether custom data is enabled.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParticleRandomness.xml b/doc/classes/VisualShaderNodeParticleRandomness.xml
index a1f9ce040a..233e072246 100644
--- a/doc/classes/VisualShaderNodeParticleRandomness.xml
+++ b/doc/classes/VisualShaderNodeParticleRandomness.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleRandomness" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Visual shader node for randomizing particle values.
</brief_description>
<description>
+ Randomness node will output pseudo-random values of the given type based on the specified minimum and maximum values.
</description>
<tutorials>
</tutorials>
@@ -21,7 +23,10 @@
<constant name="OP_TYPE_VECTOR_3D" value="2" enum="OpType">
A 3D vector type.
</constant>
- <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_4D" value="3" enum="OpType">
+ A 4D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="4" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeParticleRingEmitter.xml b/doc/classes/VisualShaderNodeParticleRingEmitter.xml
index bebce51d6b..e2ff44ed55 100644
--- a/doc/classes/VisualShaderNodeParticleRingEmitter.xml
+++ b/doc/classes/VisualShaderNodeParticleRingEmitter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleRingEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that makes particles emitted in a ring shape.
</brief_description>
<description>
+ [VisualShaderNodeParticleEmitter] that makes the particles emitted in ring shape with the specified inner and outer radii and height.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeParticleSphereEmitter.xml b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml
index ffbd384f1e..3d1e332c97 100644
--- a/doc/classes/VisualShaderNodeParticleSphereEmitter.xml
+++ b/doc/classes/VisualShaderNodeParticleSphereEmitter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleSphereEmitter" inherits="VisualShaderNodeParticleEmitter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that makes particles emitted in a sphere shape.
</brief_description>
<description>
+ [VisualShaderNodeParticleEmitter] that makes the particles emitted in sphere shape with the specified inner and outer radii.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeProximityFade.xml b/doc/classes/VisualShaderNodeProximityFade.xml
index 25051eed71..8405fcef36 100644
--- a/doc/classes/VisualShaderNodeProximityFade.xml
+++ b/doc/classes/VisualShaderNodeProximityFade.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeProximityFade" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node representing proximity fade effect.
</brief_description>
<description>
+ The proximity fade effect fades out each pixel based on its distance to another object.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeRandomRange.xml b/doc/classes/VisualShaderNodeRandomRange.xml
index adc83d808c..e1a5e0b266 100644
--- a/doc/classes/VisualShaderNodeRandomRange.xml
+++ b/doc/classes/VisualShaderNodeRandomRange.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeRandomRange" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that generates a pseudo-random scalar.
</brief_description>
<description>
+ Random range node will output a pseudo-random scalar value in the specified range, based on the seed. The value is always the same for the given seed and range, so you should provide a changing input, e.g. by using time.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeRemap.xml b/doc/classes/VisualShaderNodeRemap.xml
index 5a73a76e7f..73512bcb99 100644
--- a/doc/classes/VisualShaderNodeRemap.xml
+++ b/doc/classes/VisualShaderNodeRemap.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeRemap" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node for remap function.
</brief_description>
<description>
+ Remap will transform the input range into output range, e.g. you can change a [code]0..1[/code] value to [code]-2..2[/code] etc. See [method @GlobalScope.remap] for more details.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml b/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml
index 2afaa8e219..885325bc6c 100644
--- a/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml
+++ b/doc/classes/VisualShaderNodeTexture2DArrayParameter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeTexture2DArrayParameter" inherits="VisualShaderNodeTextureParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node for shader parameter (uniform) of type [Texture2DArray].
</brief_description>
<description>
+ This parameter allows to provide a collection of textures for the shader. You can use [VisualShaderNodeTexture2DArray] to extract the textures from array.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeTextureParameter.xml b/doc/classes/VisualShaderNodeTextureParameter.xml
index ad21c4e990..333226dc58 100644
--- a/doc/classes/VisualShaderNodeTextureParameter.xml
+++ b/doc/classes/VisualShaderNodeTextureParameter.xml
@@ -51,27 +51,39 @@
Represents the size of the [enum ColorDefault] enum.
</constant>
<constant name="FILTER_DEFAULT" value="0" enum="TextureFilter">
+ Sample the texture using the filter determined by the node this shader is attached to.
</constant>
<constant name="FILTER_NEAREST" value="1" enum="TextureFilter">
+ The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
</constant>
<constant name="FILTER_LINEAR" value="2" enum="TextureFilter">
+ The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style.
</constant>
<constant name="FILTER_NEAREST_MIPMAP" value="3" enum="TextureFilter">
+ The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps.
</constant>
<constant name="FILTER_LINEAR_MIPMAP" value="4" enum="TextureFilter">
+ The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="FILTER_NEAREST_MIPMAP_ANISOTROPIC" value="5" enum="TextureFilter">
+ The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
+ [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate.
</constant>
<constant name="FILTER_LINEAR_MIPMAP_ANISOTROPIC" value="6" enum="TextureFilter">
+ The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
+ [b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate.
</constant>
<constant name="FILTER_MAX" value="7" enum="TextureFilter">
Represents the size of the [enum TextureFilter] enum.
</constant>
<constant name="REPEAT_DEFAULT" value="0" enum="TextureRepeat">
+ Sample the texture using the repeat mode determined by the node this shader is attached to.
</constant>
<constant name="REPEAT_ENABLED" value="1" enum="TextureRepeat">
+ Texture will repeat normally.
</constant>
<constant name="REPEAT_DISABLED" value="2" enum="TextureRepeat">
+ Texture will not repeat.
</constant>
<constant name="REPEAT_MAX" value="3" enum="TextureRepeat">
Represents the size of the [enum TextureRepeat] enum.
diff --git a/doc/classes/VisualShaderNodeUIntParameter.xml b/doc/classes/VisualShaderNodeUIntParameter.xml
index 3b549c84f7..4c95e58962 100644
--- a/doc/classes/VisualShaderNodeUIntParameter.xml
+++ b/doc/classes/VisualShaderNodeUIntParameter.xml
@@ -1,15 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeUIntParameter" inherits="VisualShaderNodeParameter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node for shader parameter (uniform) of type unsigned [int].
</brief_description>
<description>
+ A [VisualShaderNodeParameter] of type unsigned [int]. Offers additional customization for range of accepted values.
</description>
<tutorials>
</tutorials>
<members>
<member name="default_value" type="int" setter="set_default_value" getter="get_default_value" default="0">
+ Default value of this parameter, which will be used if not set externally. [member default_value_enabled] must be enabled; defaults to [code]0[/code] otherwise.
</member>
<member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false">
+ If [code]true[/code], the node will have a custom default value.
</member>
</members>
</class>
diff --git a/doc/classes/VisualShaderNodeUVFunc.xml b/doc/classes/VisualShaderNodeUVFunc.xml
index 541991b790..b5143b647c 100644
--- a/doc/classes/VisualShaderNodeUVFunc.xml
+++ b/doc/classes/VisualShaderNodeUVFunc.xml
@@ -4,6 +4,7 @@
Contains functions to modify texture coordinates ([code]uv[/code]) to be used within the visual shader graph.
</brief_description>
<description>
+ UV functions are similar to [Vector2] functions, but the input port of this node uses the shader's UV value by default.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeUVPolarCoord.xml b/doc/classes/VisualShaderNodeUVPolarCoord.xml
index 8582939db3..49f7f52bc1 100644
--- a/doc/classes/VisualShaderNodeUVPolarCoord.xml
+++ b/doc/classes/VisualShaderNodeUVPolarCoord.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeUVPolarCoord" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that modifies the texture UV using polar coordinates.
</brief_description>
<description>
+ UV polar coord node will transform UV values into polar coordinates, with specified scale, zoom strength and repeat parameters. It can be used to create various swirl distortions.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeVarying.xml b/doc/classes/VisualShaderNodeVarying.xml
index 0dbbd61f3a..0a5c5a70af 100644
--- a/doc/classes/VisualShaderNodeVarying.xml
+++ b/doc/classes/VisualShaderNodeVarying.xml
@@ -1,15 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeVarying" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that represents a "varying" shader value.
</brief_description>
<description>
+ Varying values are shader variables that can be passed between shader functions, e.g. from Vertex shader to Fragment shader.
</description>
<tutorials>
</tutorials>
<members>
<member name="varying_name" type="String" setter="set_varying_name" getter="get_varying_name" default="&quot;[None]&quot;">
+ Name of the variable. Must be unique.
</member>
<member name="varying_type" type="int" setter="set_varying_type" getter="get_varying_type" enum="VisualShader.VaryingType" default="0">
+ Type of the variable. Determines where the variable can be accessed.
</member>
</members>
</class>
diff --git a/doc/classes/VisualShaderNodeVaryingGetter.xml b/doc/classes/VisualShaderNodeVaryingGetter.xml
index de30b18d67..dea47ed3c1 100644
--- a/doc/classes/VisualShaderNodeVaryingGetter.xml
+++ b/doc/classes/VisualShaderNodeVaryingGetter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeVaryingGetter" inherits="VisualShaderNodeVarying" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that gets a value of a varying.
</brief_description>
<description>
+ Outputs a value of a varying defined in the shader. You need to first create a varying that can be used in the given function, e.g. varying getter in Fragment shader requires a varying with mode set to [constant VisualShader.VARYING_MODE_VERTEX_TO_FRAG_LIGHT].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeVaryingSetter.xml b/doc/classes/VisualShaderNodeVaryingSetter.xml
index 57ead3d82b..b305fdd3ef 100644
--- a/doc/classes/VisualShaderNodeVaryingSetter.xml
+++ b/doc/classes/VisualShaderNodeVaryingSetter.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeVaryingSetter" inherits="VisualShaderNodeVarying" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ A visual shader node that sets a value of a varying.
</brief_description>
<description>
+ Inputs a value to a varying defined in the shader. You need to first create a varying that can be used in the given function, e.g. varying setter in Fragment shader requires a varying with mode set to [constant VisualShader.VARYING_MODE_FRAG_TO_LIGHT].
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VisualShaderNodeVectorBase.xml b/doc/classes/VisualShaderNodeVectorBase.xml
index d9c9f2d79c..68cf00e819 100644
--- a/doc/classes/VisualShaderNodeVectorBase.xml
+++ b/doc/classes/VisualShaderNodeVectorBase.xml
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeVectorBase" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- A base type for the nodes using different vector types within the visual shader graph.
+ A base type for the nodes that perform vector operations within the visual shader graph.
</brief_description>
<description>
+ This is an abstract class. See the derived types for descriptions of the possible operations.
</description>
<tutorials>
</tutorials>
<members>
<member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeVectorBase.OpType" default="1">
- A base type.
+ A vector type that this operation is performed on.
</member>
</members>
<constants>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 4cd6cf41da..da31e6761e 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -527,6 +527,39 @@
Set's the window's current mode.
[b]Note:[/b] Fullscreen mode is not exclusive full screen on Windows and Linux.
</member>
+ <member name="mouse_passthrough" type="bool" setter="set_flag" getter="get_flag" default="false">
+ If [code]true[/code], all mouse event as passed to the underlying window of the same application. See also [member mouse_passthrough_polygon].
+ [b]Note:[/b] This property is implemented on Linux (X11), macOS and Windows.
+ </member>
+ <member name="mouse_passthrough_polygon" type="PackedVector2Array" setter="set_mouse_passthrough_polygon" getter="get_mouse_passthrough_polygon" default="PackedVector2Array()">
+ Sets a polygonal region of the window which accepts mouse events. Mouse events outside the region will be passed through.
+ Passing an empty array will disable passthrough support (all mouse events will be intercepted by the window, which is the default behavior).
+ [codeblocks]
+ [gdscript]
+ # Set region, using Path2D node.
+ $Window.mouse_passthrough_polygon = $Path2D.curve.get_baked_points()
+
+ # Set region, using Polygon2D node.
+ $Window.mouse_passthrough_polygon = $Polygon2D.polygon
+
+ # Reset region to default.
+ $Window.mouse_passthrough_polygon = []
+ [/gdscript]
+ [csharp]
+ // Set region, using Path2D node.
+ GetNode&lt;Window&gt;("Window").MousePassthrough = GetNode&lt;Path2D&gt;("Path2D").Curve.GetBakedPoints();
+
+ // Set region, using Polygon2D node.
+ GetNode&lt;Window&gt;("Window").MousePassthrough = GetNode&lt;Polygon2D&gt;("Polygon2D").Polygon;
+
+ // Reset region to default.
+ GetNode&lt;Window&gt;("Window").MousePassthrough = new Vector2[] {};
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] This property is ignored if [member mouse_passthrough] is set to [code]true[/code].
+ [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux (X11) and macOS it is.
+ [b]Note:[/b] This property is implemented on Linux (X11), macOS and Windows.
+ </member>
<member name="popup_window" type="bool" setter="set_flag" getter="get_flag" default="false">
If [code]true[/code], the [Window] will be considered a popup. Popups are sub-windows that don't show as separate windows in system's window manager's window list and will send close request when anything is clicked outside of them (unless [member exclusive] is enabled).
</member>
@@ -695,7 +728,10 @@
Window content is expanded to the full size of the window. Unlike borderless window, the frame is left intact and can be used to resize the window, title bar is transparent, but have minimize/maximize/close buttons. Set with [member extend_to_title].
[b]Note:[/b] This flag is implemented on macOS.
</constant>
- <constant name="FLAG_MAX" value="7" enum="Flags">
+ <constant name="FLAG_MOUSE_PASSTHROUGH" value="7" enum="Flags">
+ All mouse events are passed to the underlying window of the same application.
+ </constant>
+ <constant name="FLAG_MAX" value="8" enum="Flags">
Max value of the [enum Flags].
</constant>
<constant name="CONTENT_SCALE_MODE_DISABLED" value="0" enum="ContentScaleMode">
diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml
index d0ef664281..e1a98f0ea4 100644
--- a/doc/classes/bool.xml
+++ b/doc/classes/bool.xml
@@ -60,7 +60,7 @@
_can_shoot = false
_cool_down.start()
- func _on_CoolDownTimer_timeout():
+ func _on_cool_down_timer_timeout():
_can_shoot = true
[/gdscript]
[csharp]
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 2887b6b44f..a946a2ceb5 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -905,10 +905,12 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
if (rect->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
+ state.instance_data_array[r_index].flags |= FLAGS_FLIP_H;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
+ state.instance_data_array[r_index].flags |= FLAGS_FLIP_V;
}
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index db9203b249..916e12057c 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -75,6 +75,9 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
FLAGS_USE_MSDF = (1 << 28),
FLAGS_USE_LCD = (1 << 29),
+
+ FLAGS_FLIP_H = (1 << 30),
+ FLAGS_FLIP_V = (1 << 31),
};
enum {
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 9a90f33674..8c53ab6529 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -579,6 +579,12 @@ void main() {
if (normal_used || (using_light && bool(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
+ if (bool(read_draw_data_flags & FLAGS_FLIP_H)) {
+ normal.x = -normal.x;
+ }
+ if (bool(read_draw_data_flags & FLAGS_FLIP_V)) {
+ normal.y = -normal.y;
+ }
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
} else {
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
index f65558f042..d53c0fcb26 100644
--- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -27,6 +27,9 @@
#define FLAGS_USE_MSDF uint(1 << 28)
#define FLAGS_USE_LCD uint(1 << 29)
+#define FLAGS_FLIP_H uint(1 << 30)
+#define FLAGS_FLIP_V uint(1 << 31)
+
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index e54fad1551..2d81771898 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -2765,6 +2765,14 @@ void MaterialStorage::material_free(RID p_rid) {
Material *material = material_owner.get_or_null(p_rid);
ERR_FAIL_COND(!material);
+ // Need to clear texture arrays to prevent spin locking of their RID's.
+ // This happens when the app is being closed.
+ for (KeyValue<StringName, Variant> &E : material->params) {
+ if (E.value.get_type() == Variant::ARRAY) {
+ Array(E.value).clear();
+ }
+ }
+
material_set_shader(p_rid, RID()); //clean up shader
material->dependency.deleted_notify(p_rid);
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
index 65c4ca6131..19bf57df94 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
@@ -37,13 +37,14 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
free_render_buffer_data();
}
-void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
+void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
//internal_size.x = p_internal_size.x; // ignore for now
//internal_size.y = p_internal_size.y;
width = p_target_size.x;
height = p_target_size.y;
+ //scaling_3d_mode = p_scaling_3d_mode
//fsr_sharpness = p_fsr_sharpness;
//texture_mipmap_bias = p_texture_mipmap_bias;
render_target = p_render_target;
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h
index 52a7737910..d07a0812f6 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.h
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.h
@@ -81,7 +81,7 @@ public:
private:
public:
virtual ~RenderSceneBuffersGLES3();
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
+ virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
virtual void set_fsr_sharpness(float p_fsr_sharpness) override{};
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{};
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 2f0a253e46..8818ab2118 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -300,6 +300,7 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_real_format = p_format;
bool need_decompress = false;
+ bool decompress_ra_to_rg = false;
switch (p_format) {
case Image::FORMAT_L8: {
@@ -565,6 +566,34 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
need_decompress = true;
}
} break;
+ case Image::FORMAT_ETC2_RA_AS_RG: {
+#ifndef WEB_ENABLED
+ if (config->etc2_supported) {
+ r_gl_internal_format = _EXT_COMPRESSED_RGBA8_ETC2_EAC;
+ r_gl_format = GL_RGBA;
+ r_gl_type = GL_UNSIGNED_BYTE;
+ r_compressed = true;
+ } else
+#endif
+ {
+ need_decompress = true;
+ }
+ decompress_ra_to_rg = true;
+ } break;
+ case Image::FORMAT_DXT5_RA_AS_RG: {
+#ifndef WEB_ENABLED
+ if (config->s3tc_supported) {
+ r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ r_gl_format = GL_RGBA;
+ r_gl_type = GL_UNSIGNED_BYTE;
+ r_compressed = true;
+ } else
+#endif
+ {
+ need_decompress = true;
+ }
+ decompress_ra_to_rg = true;
+ } break;
default: {
ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer");
}
@@ -575,7 +604,18 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
image = image->duplicate();
image->decompress();
ERR_FAIL_COND_V(image->is_compressed(), image);
+ if (decompress_ra_to_rg) {
+ image->convert_ra_rgba8_to_rg();
+ image->convert(Image::FORMAT_RG8);
+ }
switch (image->get_format()) {
+ case Image::FORMAT_RG8: {
+ r_gl_format = GL_RG;
+ r_gl_internal_format = GL_RG8;
+ r_gl_type = GL_UNSIGNED_BYTE;
+ r_real_format = Image::FORMAT_RG8;
+ r_compressed = false;
+ } break;
case Image::FORMAT_RGB8: {
r_gl_format = GL_RGB;
r_gl_internal_format = GL_RGB;
@@ -597,7 +637,6 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
r_gl_type = GL_UNSIGNED_BYTE;
r_real_format = Image::FORMAT_RGBA8;
r_compressed = false;
-
} break;
}
}
@@ -1104,15 +1143,14 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
texture->gl_set_repeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- //set swizle for older format compatibility
-#ifdef GLES_OVER_GL
+#ifndef WEB_ENABLED
switch (texture->format) {
+#ifdef GLES_OVER_GL
case Image::FORMAT_L8: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE);
-
} break;
case Image::FORMAT_LA8: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
@@ -1120,15 +1158,29 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN);
} break;
+#endif // GLES3_OVER_GL
+
+ case Image::FORMAT_ETC2_RA_AS_RG:
+ case Image::FORMAT_DXT5_RA_AS_RG: {
+ glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
+ if (texture->format == real_format) {
+ // Swizzle RA from compressed texture into RG
+ glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_ALPHA);
+ } else {
+ // Converted textures are already in RG, leave as-is
+ glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
+ }
+ glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_ZERO);
+ glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE);
+ } break;
default: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
-
} break;
}
-#endif
+#endif // WEB_ENABLED
int mipmaps = img->has_mipmaps() ? img->get_mipmap_count() + 1 : 1;
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 557060c907..a162f46103 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -189,7 +189,7 @@ void DirAccessUnix::list_dir_end() {
_cisdir = false;
}
-#if defined(HAVE_MNTENT) && defined(X11_ENABLED)
+#if defined(HAVE_MNTENT) && defined(LINUXBSD_ENABLED)
static bool _filter_drive(struct mntent *mnt) {
// Ignore devices that don't point to /dev
if (strncmp(mnt->mnt_fsname, "/dev", 4) != 0) {
@@ -213,7 +213,7 @@ static void _get_drives(List<String> *list) {
// Add root.
list->push_back("/");
-#if defined(HAVE_MNTENT) && defined(X11_ENABLED)
+#if defined(HAVE_MNTENT) && defined(LINUXBSD_ENABLED)
// Check /etc/mtab for the list of mounted partitions.
FILE *mtab = setmntent("/etc/mtab", "r");
if (mtab) {
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 2f3a78a689..178f01b185 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -149,13 +149,9 @@ Vector<String> OS_Unix::get_video_adapter_driver_info() const {
return Vector<String>();
}
-String OS_Unix::get_stdin_string(bool p_block) {
- if (p_block) {
- char buff[1024];
- return String::utf8(fgets(buff, 1024, stdin));
- }
-
- return String();
+String OS_Unix::get_stdin_string() {
+ char buff[1024];
+ return String::utf8(fgets(buff, 1024, stdin));
}
Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) {
@@ -415,10 +411,6 @@ bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
return true;
}
-bool OS_Unix::has_environment(const String &p_var) const {
- return getenv(p_var.utf8().get_data()) != nullptr;
-}
-
String OS_Unix::get_locale() const {
if (!has_environment("LANG")) {
return "en";
@@ -491,6 +483,10 @@ Error OS_Unix::set_cwd(const String &p_cwd) {
return OK;
}
+bool OS_Unix::has_environment(const String &p_var) const {
+ return getenv(p_var.utf8().get_data()) != nullptr;
+}
+
String OS_Unix::get_environment(const String &p_var) const {
if (getenv(p_var.utf8().get_data())) {
return getenv(p_var.utf8().get_data());
@@ -498,8 +494,15 @@ String OS_Unix::get_environment(const String &p_var) const {
return "";
}
-bool OS_Unix::set_environment(const String &p_var, const String &p_value) const {
- return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0;
+void OS_Unix::set_environment(const String &p_var, const String &p_value) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1);
+ ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var));
+}
+
+void OS_Unix::unset_environment(const String &p_var) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ unsetenv(p_var.utf8().get_data());
}
String OS_Unix::get_user_data_dir() const {
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 56c4d1747f..03429622ae 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -51,7 +51,7 @@ public:
virtual Vector<String> get_video_adapter_driver_info() const override;
- virtual String get_stdin_string(bool p_block) override;
+ virtual String get_stdin_string() override;
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
@@ -81,7 +81,9 @@ public:
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
- virtual bool set_environment(const String &p_var, const String &p_value) const override;
+ virtual void set_environment(const String &p_var, const String &p_value) const override;
+ virtual void unset_environment(const String &p_var) const override;
+
virtual String get_locale() const override;
virtual void initialize_debugging() override;
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 425ca1c516..6c833c8a26 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -36,7 +36,6 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/templates/hashfuncs.h"
-#include "core/version.h"
#include "drivers/vulkan/vulkan_context.h"
#include "thirdparty/misc/smolv.h"
@@ -4574,7 +4573,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) {
#define SHADER_BINARY_VERSION 3
String RenderingDeviceVulkan::shader_get_binary_cache_key() const {
- return "Vulkan-SV" + itos(SHADER_BINARY_VERSION) + "-" + String(VERSION_NUMBER) + "-" + String(VERSION_HASH);
+ return "Vulkan-SV" + itos(SHADER_BINARY_VERSION);
}
struct RenderingDeviceVulkanShaderBinaryDataBinding {
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 8e55f3a1c3..712b11d7d7 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -578,7 +578,7 @@ ActionMapEditor::ActionMapEditor() {
action_tree->connect("button_clicked", callable_mp(this, &ActionMapEditor::_tree_button_pressed));
main_vbox->add_child(action_tree);
- action_tree->set_drag_forwarding(this);
+ action_tree->set_drag_forwarding_compat(this);
// Adding event dialog
event_config_dialog = memnew(InputEventConfigurationDialog);
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index d4ab907e78..639f5e6de5 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -788,7 +788,7 @@ void AnimationBezierTrackEdit::_clear_selection() {
}
void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode, bool p_auto) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Update Selected Key Handles"));
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
const IntPair track_key_pair = E->get();
@@ -985,7 +985,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (I.value.has_point(mb->get_position())) {
if (I.key == REMOVE_ICON) {
if (!read_only) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Remove Bezier Track");
undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
@@ -1172,7 +1172,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
time += 0.0001;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "bezier_track_insert_key", selected_track, time, new_point);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
@@ -1270,7 +1270,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_selection) {
//combit it
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Bezier Points"));
List<AnimMoveRestore> to_restore;
@@ -1471,7 +1471,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if ((moving_handle == -1 || moving_handle == 1) && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (!read_only) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Bezier Points"));
if (moving_handle == -1) {
real_t ratio = timeline->get_zoom_scale() * v_zoom;
@@ -1543,7 +1543,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
time += 0.001;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
@@ -1591,7 +1591,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Duplicate Keys"));
List<Pair<int, real_t>> new_selection_values;
@@ -1637,7 +1637,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
void AnimationBezierTrackEdit::delete_selection() {
if (selection.size()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Delete Keys"));
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 65bc7b2414..857a9a664a 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -107,7 +107,7 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
float val = p_value;
float prev_val = animation->track_get_key_transition(track, key);
setting = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Change Transition"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(animation.ptr(), "track_set_key_transition", track, key, val);
undo_redo->add_undo_method(animation.ptr(), "track_set_key_transition", track, key, prev_val);
@@ -119,7 +119,7 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
return true;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (animation->track_get_type(track)) {
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
@@ -704,7 +704,7 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p
float val = p_value;
float prev_val = animation->track_get_key_transition(track, key);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (!setting) {
setting = true;
undo_redo->create_action(TTR("Animation Multi Change Transition"), UndoRedo::MERGE_ENDS);
@@ -714,7 +714,7 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p
update_obj = true;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (animation->track_get_type(track)) {
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
@@ -925,7 +925,7 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (setting) {
if (update_obj) {
undo_redo->add_do_method(this, "_update_obj", animation);
@@ -1252,8 +1252,8 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
}
editing = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
- undo_redo->create_action(TTR("Change Animation Length"));
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Change Animation Length"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(animation.ptr(), "set_length", p_new_len);
undo_redo->add_undo_method(animation.ptr(), "set_length", animation->get_length());
undo_redo->commit_action();
@@ -1265,7 +1265,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
void AnimationTimelineEdit::_anim_loop_pressed() {
if (!read_only) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Loop"));
switch (animation->get_loop_mode()) {
case Animation::LOOP_NONE: {
@@ -2392,7 +2392,7 @@ void AnimationTrackEdit::_zoom_changed() {
}
void AnimationTrackEdit::_path_submitted(const String &p_text) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Track Path"));
undo_redo->add_do_method(animation.ptr(), "track_set_path", track, p_text);
undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
@@ -2630,7 +2630,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (!read_only) {
if (check_rect.has_point(pos)) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Toggle Track Enabled"));
undo_redo->add_do_method(animation.ptr(), "track_set_enabled", track, !animation->track_is_enabled(track));
undo_redo->add_undo_method(animation.ptr(), "track_set_enabled", track, animation->track_is_enabled(track));
@@ -2919,7 +2919,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && moving_selection_attempt) {
+ if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::LEFT) && moving_selection_attempt) {
if (!moving_selection) {
moving_selection = true;
emit_signal(SNAME("move_selection_begin"));
@@ -3020,7 +3020,7 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
case MENU_CALL_MODE_DISCRETE:
case MENU_CALL_MODE_CAPTURE: {
Animation::UpdateMode update_mode = Animation::UpdateMode(p_index);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Update Mode"));
undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", track, update_mode);
undo_redo->add_undo_method(animation.ptr(), "value_track_set_update_mode", track, animation->value_track_get_update_mode(track));
@@ -3034,7 +3034,7 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
case MENU_INTERPOLATION_LINEAR_ANGLE:
case MENU_INTERPOLATION_CUBIC_ANGLE: {
Animation::InterpolationType interp_mode = Animation::InterpolationType(p_index - MENU_INTERPOLATION_NEAREST);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Interpolation Mode"));
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", track, interp_mode);
undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", track, animation->track_get_interpolation_type(track));
@@ -3044,7 +3044,7 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
case MENU_LOOP_WRAP:
case MENU_LOOP_CLAMP: {
bool loop_wrap = p_index == MENU_LOOP_WRAP;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Loop Mode"));
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_loop_wrap", track, loop_wrap);
undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_loop_wrap", track, animation->track_get_interpolation_loop_wrap(track));
@@ -3438,7 +3438,7 @@ void AnimationTrackEditor::_animation_track_remove_request(int p_track, Ref<Anim
}
int idx = p_track;
if (idx >= 0 && idx < p_from_animation->get_track_count()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Anim Track"), UndoRedo::MERGE_DISABLE, p_from_animation.ptr());
// Remove corresponding reset tracks if they are no longer needed.
@@ -3639,7 +3639,7 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
}
void AnimationTrackEditor::_insert_track(bool p_reset_wanted, bool p_create_beziers) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Insert Key"));
Ref<Animation> reset_anim;
@@ -3968,7 +3968,7 @@ Ref<Animation> AnimationTrackEditor::_create_and_get_reset_animation() {
Ref<Animation> reset_anim;
reset_anim.instantiate();
reset_anim->set_length(ANIM_MIN_LENGTH);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(al.ptr(), "add_animation", SceneStringNames::get_singleton()->RESET, reset_anim);
undo_redo->add_do_method(AnimationPlayerEditor::get_singleton(), "_animation_player_changed", player);
undo_redo->add_undo_method(al.ptr(), "remove_animation", SceneStringNames::get_singleton()->RESET);
@@ -3978,7 +3978,7 @@ Ref<Animation> AnimationTrackEditor::_create_and_get_reset_animation() {
}
void AnimationTrackEditor::_confirm_insert_list() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Insert Key"));
bool create_reset = insert_confirm_reset->is_visible() && insert_confirm_reset->is_pressed();
@@ -4148,7 +4148,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (create_normal_track) {
if (p_create_beziers) {
bool valid;
@@ -4600,7 +4600,7 @@ void AnimationTrackEditor::_update_scroll(double) {
}
void AnimationTrackEditor::_update_step(double p_new_step) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Step"));
float step_value = p_new_step;
if (timeline->is_using_fps()) {
@@ -4627,7 +4627,7 @@ void AnimationTrackEditor::_dropped_track(int p_from_track, int p_to_track) {
}
_clear_selection(true);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rearrange Tracks"));
undo_redo->add_do_method(animation.ptr(), "track_move_to", p_from_track, p_to_track);
// Take into account that the position of the tracks that come after the one removed will change.
@@ -4671,7 +4671,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D:
case Animation::TYPE_METHOD: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
@@ -4700,7 +4700,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
@@ -4719,7 +4719,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
undo_redo->add_do_method(animation.ptr(), "track_set_path", animation->get_track_count(), path_to);
@@ -4744,7 +4744,7 @@ void AnimationTrackEditor::_add_track(int p_type) {
void AnimationTrackEditor::_new_track_property_selected(String p_name) {
String full_path = String(adding_track_path) + ":" + p_name;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (adding_track_type == Animation::TYPE_VALUE) {
Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
{
@@ -4835,7 +4835,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
p_ofs += 0.0001;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (animation->track_get_type(p_track)) {
case Animation::TYPE_POSITION_3D: {
if (!root->has_node(animation->track_get_path(p_track))) {
@@ -4991,7 +4991,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
}
d["args"] = params;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Method Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
@@ -5178,7 +5178,7 @@ void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_t
}
void AnimationTrackEditor::_move_selection_commit() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Move Keys"));
List<_AnimMoveRestore> to_restore;
@@ -5309,7 +5309,7 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && box_selecting) {
- if ((mm->get_button_mask() & MouseButton::MASK_LEFT) == MouseButton::NONE) {
+ if (!mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
// No longer.
box_selection->hide();
box_selecting = false;
@@ -5430,7 +5430,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
int start_track = transpose ? _get_track_selected() : top_track;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Duplicate Keys"));
List<Pair<int, float>> new_selection_values;
@@ -5660,7 +5660,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
int base_track = animation->get_track_count();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Paste Tracks"));
for (int i = 0; i < track_clipboard.size(); i++) {
undo_redo->add_do_method(animation.ptr(), "add_track", track_clipboard[i].track_type);
@@ -5730,7 +5730,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
float s = scale->get_value();
ERR_FAIL_COND_MSG(s == 0, "Can't scale to 0.");
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Scale Keys"));
List<_AnimMoveRestore> to_restore;
@@ -5810,7 +5810,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
ease_dialog->popup_centered(Size2(200, 100) * EDSCALE);
} break;
case EDIT_EASE_CONFIRM: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Make Easing Keys"));
Tween::TransitionType transition_type = static_cast<Tween::TransitionType>(transition_selection->get_selected_id());
@@ -5917,7 +5917,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
_anim_duplicate_keys(true);
} break;
case EDIT_ADD_RESET_KEY: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Add RESET Keys"));
Ref<Animation> reset = _create_and_get_reset_animation();
@@ -5978,7 +5978,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
if (selection.size()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Delete Keys"));
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
@@ -6009,7 +6009,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
bake_dialog->popup_centered(Size2(200, 100) * EDSCALE);
} break;
case EDIT_BAKE_ANIMATION_CONFIRM: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Bake Animation as Linear keys."));
int track_len = animation->get_track_count();
@@ -6130,7 +6130,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
animation->optimize(optimize_velocity_error->get_value(), optimize_angular_error->get_value(), optimize_precision_error->get_value());
_redraw_tracks();
_update_key_edit();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->clear_history(true, undo_redo->get_history_id_for_object(animation.ptr()));
undo_redo->clear_history(true, undo_redo->get_history_id_for_object(this));
@@ -6200,7 +6200,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->clear_history(true, undo_redo->get_history_id_for_object(animation.ptr()));
undo_redo->clear_history(true, undo_redo->get_history_id_for_object(this));
_update_tracks();
@@ -6801,7 +6801,7 @@ void AnimationTrackKeyEditEditor::_time_edit_exited() {
}
int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS);
if (existing != -1) {
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 0926a63f88..ba73a63245 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -31,7 +31,6 @@
#include "animation_track_editor_plugins.h"
#include "editor/audio_stream_preview.h"
-#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
@@ -1018,7 +1017,7 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant
ofs += 0.0001;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Audio Track Clip"));
undo_redo->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);
undo_redo->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs);
@@ -1125,7 +1124,7 @@ void AnimationTrackEditTypeAudio::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (len_resizing && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (len_resizing_rel == 0 || len_resizing_index < 0) {
len_resizing = false;
diff --git a/editor/array_property_edit.cpp b/editor/array_property_edit.cpp
index b2e12e2409..dd27b61df9 100644
--- a/editor/array_property_edit.cpp
+++ b/editor/array_property_edit.cpp
@@ -31,7 +31,6 @@
#include "array_property_edit.h"
#include "core/io/marshalls.h"
-#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#define ITEMS_PER_PAGE 100
@@ -88,7 +87,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Resize Array"));
ur->add_do_method(this, "_set_size", newsize);
ur->add_undo_method(this, "_set_size", size);
@@ -135,7 +134,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
Callable::CallError ce;
Variant new_value;
Variant::construct(Variant::Type(type), new_value, nullptr, 0, ce);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Array Value Type"));
ur->add_do_method(this, "_set_value", idx, new_value);
@@ -151,7 +150,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
Variant arr = get_array();
Variant value = arr.get(idx);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Array Value"));
ur->add_do_method(this, "_set_value", idx, p_value);
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index ba7e7f2877..df8adf01e4 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -143,6 +143,7 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
}
bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col) {
+ text_editor->remove_secondary_carets();
String text = get_search_text();
Point2i pos = text_editor->search(text, p_flags, p_from_line, p_from_col);
@@ -178,6 +179,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
}
void FindReplaceBar::_replace() {
+ text_editor->remove_secondary_carets();
bool selection_enabled = text_editor->has_selection(0);
Point2i selection_begin, selection_end;
if (selection_enabled) {
@@ -225,6 +227,7 @@ void FindReplaceBar::_replace() {
}
void FindReplaceBar::_replace_all() {
+ text_editor->remove_secondary_carets();
text_editor->disconnect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
// Line as x so it gets priority in comparison, column as y.
Point2i orig_cursor(text_editor->get_caret_line(0), text_editor->get_caret_column(0));
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 9b2e6fbf81..6f4736dca7 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -685,7 +685,7 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) {
}
Callable callable = p_cd.get_callable();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Connect '%s' to '%s'"), String(p_cd.signal), String(p_cd.method)));
undo_redo->add_do_method(source, "connect", p_cd.signal, callable, p_cd.flags);
undo_redo->add_undo_method(source, "disconnect", p_cd.signal, callable);
@@ -706,7 +706,7 @@ void ConnectionsDock::_disconnect(TreeItem &p_item) {
ERR_FAIL_COND(cd.source != selected_node); // Shouldn't happen but... Bugcheck.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), cd.signal, cd.method));
Callable callable = cd.get_callable();
@@ -733,7 +733,7 @@ void ConnectionsDock::_disconnect_all() {
TreeItem *child = item->get_first_child();
String signal_name = item->get_metadata(0).operator Dictionary()["name"];
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Disconnect all from signal: '%s'"), signal_name));
while (child) {
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 76d70b09e6..98fcde17c4 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -148,6 +148,11 @@ bool CreateDialog::_should_hide_type(const String &p_type) const {
return true;
}
+ StringName native_type = ScriptServer::get_global_class_native_base(p_type);
+ if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) {
+ return true;
+ }
+
String script_path = ScriptServer::get_global_class_path(p_type);
if (script_path.begins_with("res://addons/")) {
if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {
@@ -754,7 +759,7 @@ CreateDialog::CreateDialog() {
favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected));
favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated));
favorites->add_theme_constant_override("draw_guides", 1);
- favorites->set_drag_forwarding(this);
+ favorites->set_drag_forwarding_compat(this);
fav_vb->add_margin_child(TTR("Favorites:"), favorites, true);
VBoxContainer *rec_vb = memnew(VBoxContainer);
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
index d685db40d9..fc806ded5e 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
@@ -213,7 +213,7 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const {
}
EditorNode *editor = EditorNode::get_singleton();
- Error err = platform_string == "android" ? editor->run_play_native(device, idx) : editor->run_play_native(-1, idx);
+ Error err = platform_string == "android" ? editor->run_play_native(device * 10000 + idx) : editor->run_play_native(idx);
if (err) {
if (err == ERR_INVALID_PARAMETER && platform_string == "android") {
return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index e4afbde89f..a368cacf56 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -275,7 +275,7 @@ void EditorDebuggerNode::stop(bool p_force) {
});
_break_state_changed();
breakpoints.clear();
- EditorNode::get_undo_redo()->clear_history(false, EditorUndoRedoManager::REMOTE_HISTORY);
+ EditorUndoRedoManager::get_singleton()->clear_history(false, EditorUndoRedoManager::REMOTE_HISTORY);
set_process(false);
}
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index 2e68e91c8b..e4730faf38 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -468,7 +468,7 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
x = frame_metrics.size() - 1;
}
- if (mb.is_valid() || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mb.is_valid() || (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
updating_frame = true;
if (x < total_metrics) {
@@ -670,7 +670,7 @@ EditorProfiler::EditorProfiler() {
variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited));
graph = memnew(TextureRect);
- graph->set_ignore_texture_size(true);
+ graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
graph->set_mouse_filter(MOUSE_FILTER_STOP);
graph->connect("draw", callable_mp(this, &EditorProfiler::_graph_tex_draw));
graph->connect("gui_input", callable_mp(this, &EditorProfiler::_graph_tex_input));
diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp
index 8fa77fd028..1a06e85f90 100644
--- a/editor/debugger/editor_visual_profiler.cpp
+++ b/editor/debugger/editor_visual_profiler.cpp
@@ -544,7 +544,7 @@ void EditorVisualProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
hover_metric = -1;
}
- if (mb.is_valid() || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mb.is_valid() || mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
//cursor_metric=x;
updating_frame = true;
@@ -798,7 +798,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected));
graph = memnew(TextureRect);
- graph->set_ignore_texture_size(true);
+ graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
graph->set_mouse_filter(MOUSE_FILTER_STOP);
graph->connect("draw", callable_mp(this, &EditorVisualProfiler::_graph_tex_draw));
graph->connect("gui_input", callable_mp(this, &EditorVisualProfiler::_graph_tex_input));
diff --git a/editor/dictionary_property_edit.cpp b/editor/dictionary_property_edit.cpp
index 0d191cabe8..ad59a1c169 100644
--- a/editor/dictionary_property_edit.cpp
+++ b/editor/dictionary_property_edit.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "dictionary_property_edit.h"
-#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
void DictionaryPropertyEdit::_notif_change() {
@@ -119,7 +118,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val
int index = pn.substr(0, slash).to_int();
if (type == "key" && index < keys.size()) {
const Variant &key = keys[index];
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Dictionary Key"));
ur->add_do_method(this, "_set_key", key, p_value);
@@ -131,7 +130,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val
const Variant &key = keys[index];
if (dict.has(key)) {
Variant value = dict[key];
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Dictionary Value"));
ur->add_do_method(this, "_set_value", key, p_value);
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 002599acdb..85adc312f2 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -845,6 +845,7 @@ void DocTools::generate(bool p_basic_types) {
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
DocData::ConstantDoc cd;
cd.name = CoreConstants::get_global_constant_name(i);
+ cd.is_bitfield = CoreConstants::is_global_constant_bitfield(i);
if (!CoreConstants::get_ignore_value_in_docs(i)) {
cd.value = itos(CoreConstants::get_global_constant_value(i));
cd.is_value_valid = true;
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index ae9abc638f..25bca2a099 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -153,8 +153,8 @@ EditorAbout::EditorAbout() {
Label *about_text = memnew(Label);
about_text->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- about_text->set_text(String::utf8("\xc2\xa9 2014-present ") + TTR("Godot Engine contributors") +
- "\n\xc2\xa9 2007-2014 Juan Linietsky, Ariel Manzur.\n");
+ about_text->set_text(String::utf8("\xc2\xa9 2014-present ") + TTR("Godot Engine contributors") + "." +
+ String::utf8("\n\xc2\xa9 2007-2014 Juan Linietsky, Ariel Manzur.\n"));
version_info_vbc->add_child(about_text);
hbc->add_child(version_info_vbc);
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 0258ac532f..a5bc90159a 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -120,7 +120,6 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
// Mark C# support as unavailable.
extension_guess["cs"] = tree->get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"));
}
- extension_guess["vs"] = tree->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
extension_guess["res"] = tree->get_theme_icon(SNAME("Resource"), SNAME("EditorIcons"));
extension_guess["tres"] = tree->get_theme_icon(SNAME("Resource"), SNAME("EditorIcons"));
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 5296749c18..5c97dc2d03 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -282,7 +282,7 @@ void EditorAudioBus::_name_changed(const String &p_new_name) {
}
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
StringName current = AudioServer::get_singleton()->get_bus_name(get_index());
ur->create_action(TTR("Rename Audio Bus"));
@@ -323,7 +323,7 @@ void EditorAudioBus::_volume_changed(float p_normalized) {
slider->set_value(_scaled_db_to_normalized_volume(Math::round(p_db)));
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Audio Bus Volume"), UndoRedo::MERGE_ENDS);
ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), p_db);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), AudioServer::get_singleton()->get_bus_volume_db(get_index()));
@@ -417,7 +417,7 @@ void EditorAudioBus::_hide_value_preview() {
void EditorAudioBus::_solo_toggled() {
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Toggle Audio Bus Solo"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), solo->is_pressed());
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), AudioServer::get_singleton()->is_bus_solo(get_index()));
@@ -431,7 +431,7 @@ void EditorAudioBus::_solo_toggled() {
void EditorAudioBus::_mute_toggled() {
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Toggle Audio Bus Mute"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), mute->is_pressed());
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), AudioServer::get_singleton()->is_bus_mute(get_index()));
@@ -445,7 +445,7 @@ void EditorAudioBus::_mute_toggled() {
void EditorAudioBus::_bypass_toggled() {
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Toggle Audio Bus Bypass Effects"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), bypass->is_pressed());
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), AudioServer::get_singleton()->is_bus_bypassing_effects(get_index()));
@@ -459,7 +459,7 @@ void EditorAudioBus::_bypass_toggled() {
void EditorAudioBus::_send_selected(int p_which) {
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Select Audio Bus Send"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_send", get_index(), send->get_item_text(p_which));
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send", get_index(), AudioServer::get_singleton()->get_bus_send(get_index()));
@@ -509,7 +509,7 @@ void EditorAudioBus::_effect_edited() {
int index = effect->get_metadata(0);
updating_bus = true;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Select Audio Bus Send"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, effect->is_checked(0));
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index));
@@ -536,7 +536,7 @@ void EditorAudioBus::_effect_add(int p_which) {
afxr->set_name(effect_options->get_item_text(p_which));
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Add Audio Bus Effect"));
ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), afxr, -1);
ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect_count(get_index()));
@@ -690,7 +690,7 @@ void EditorAudioBus::drop_data_fw(const Point2 &p_point, const Variant &p_data,
bool enabled = AudioServer::get_singleton()->is_bus_effect_enabled(bus, effect);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Move Bus Effect"));
ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", bus, effect);
ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(bus, effect), paste_at);
@@ -732,7 +732,7 @@ void EditorAudioBus::_delete_effect_pressed(int p_option) {
int index = item->get_metadata(0);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Delete Bus Effect"));
ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), index);
ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(get_index(), index), index);
@@ -903,7 +903,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
effects->connect("item_edited", callable_mp(this, &EditorAudioBus::_effect_edited));
effects->connect("cell_selected", callable_mp(this, &EditorAudioBus::_effect_selected));
effects->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
- effects->set_drag_forwarding(this);
+ effects->set_drag_forwarding_compat(this);
effects->connect("item_mouse_selected", callable_mp(this, &EditorAudioBus::_effect_rmb));
effects->set_allow_rmb_select(true);
effects->set_focus_mode(FOCUS_CLICK);
@@ -1065,7 +1065,7 @@ void EditorAudioBuses::_notification(int p_what) {
}
void EditorAudioBuses::_add_bus() {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Add Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1);
@@ -1097,7 +1097,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) {
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Delete Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "remove_bus", index);
@@ -1119,7 +1119,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) {
void EditorAudioBuses::_duplicate_bus(int p_which) {
int add_at_pos = p_which + 1;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Duplicate Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "add_bus", add_at_pos);
ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", add_at_pos, AudioServer::get_singleton()->get_bus_name(p_which) + " Copy");
@@ -1142,7 +1142,7 @@ void EditorAudioBuses::_reset_bus_volume(Object *p_which) {
EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which);
int index = bus->get_index();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Reset Bus Volume"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", index, 0.f);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", index, AudioServer::get_singleton()->get_bus_volume_db(index));
@@ -1162,7 +1162,7 @@ void EditorAudioBuses::_request_drop_end() {
}
void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Move Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "move_bus", p_bus, p_index);
@@ -1221,7 +1221,7 @@ void EditorAudioBuses::_load_default_layout() {
file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
_update_buses();
- EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
+ EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
@@ -1237,7 +1237,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
_update_buses();
- EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
+ EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
} else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
@@ -1257,7 +1257,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
edited_path = p_string;
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
_update_buses();
- EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
+ EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
}
@@ -1356,7 +1356,7 @@ void EditorAudioBuses::open_layout(const String &p_path) {
file->set_text(p_path.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
_update_buses();
- EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
+ EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index db251c857c..65e3e5c0d8 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -194,7 +194,7 @@ void EditorAutoloadSettings::_autoload_edited() {
TreeItem *ti = tree->get_edited();
int column = tree->get_edited_column();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (column == 0) {
String name = ti->get_text(0);
@@ -289,7 +289,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu
String name = "autoload/" + ti->get_text(0);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_button) {
case BUTTON_OPEN: {
@@ -717,7 +717,7 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
orders.sort();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rearrange Autoloads"));
@@ -760,7 +760,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
name = "autoload/" + name;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Autoload"));
// Singleton autoloads are represented with a leading "*" in their path.
@@ -786,7 +786,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
void EditorAutoloadSettings::autoload_remove(const String &p_name) {
String name = "autoload/" + p_name;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
int order = ProjectSettings::get_singleton()->get_order(name);
@@ -935,7 +935,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
tree->set_select_mode(Tree::SELECT_MULTI);
tree->set_allow_reselect(true);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
tree->set_columns(4);
tree->set_column_titles_visible(true);
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index c5fe89e35d..6e66962605 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -390,7 +390,7 @@ void EditorData::set_scene_as_saved(int p_idx) {
}
ERR_FAIL_INDEX(p_idx, edited_scene.size());
- get_undo_redo()->set_history_as_saved(edited_scene[p_idx].history_id);
+ undo_redo_manager->set_history_as_saved(edited_scene[p_idx].history_id);
}
bool EditorData::is_scene_changed(int p_idx) {
@@ -399,7 +399,7 @@ bool EditorData::is_scene_changed(int p_idx) {
}
ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), false);
- uint64_t current_scene_version = get_undo_redo()->get_or_create_history(edited_scene[p_idx].history_id).undo_redo->get_version();
+ uint64_t current_scene_version = undo_redo_manager->get_or_create_history(edited_scene[p_idx].history_id).undo_redo->get_version();
bool is_changed = edited_scene[p_idx].last_checked_version != current_scene_version;
edited_scene.write[p_idx].last_checked_version = current_scene_version;
return is_changed;
@@ -425,10 +425,6 @@ int EditorData::get_scene_history_id(int p_idx) const {
return edited_scene[p_idx].history_id;
}
-Ref<EditorUndoRedoManager> &EditorData::get_undo_redo() {
- return undo_redo_manager;
-}
-
void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) {
undo_redo_callbacks.push_back(p_callable);
}
@@ -998,6 +994,8 @@ void EditorData::script_class_set_name(const String &p_path, const StringName &p
}
void EditorData::script_class_save_icon_paths() {
+ Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
+
Dictionary d;
for (const KeyValue<StringName, String> &E : _script_class_icon_paths) {
if (ScriptServer::is_global_class(E.key)) {
@@ -1005,27 +1003,20 @@ void EditorData::script_class_save_icon_paths() {
}
}
- Dictionary old;
- if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) {
- old = GLOBAL_GET("_global_script_class_icons");
- }
- if ((!old.is_empty() || d.is_empty()) && d.hash() == old.hash()) {
- return;
- }
-
- if (d.is_empty()) {
- if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) {
- ProjectSettings::get_singleton()->clear("_global_script_class_icons");
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary d2 = script_classes[i];
+ if (!d2.has("class")) {
+ continue;
}
- } else {
- ProjectSettings::get_singleton()->set("_global_script_class_icons", d);
+ d2["icon"] = d.get(d2["class"], "");
}
- ProjectSettings::get_singleton()->save();
+ ProjectSettings::get_singleton()->store_global_class_list(script_classes);
}
void EditorData::script_class_load_icon_paths() {
script_class_clear_icon_paths();
+#ifndef DISABLE_DEPRECATED
if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) {
Dictionary d = GLOBAL_GET("_global_script_class_icons");
List<Variant> keys;
@@ -1038,15 +1029,33 @@ void EditorData::script_class_load_icon_paths() {
String path = ScriptServer::get_global_class_path(name);
script_class_set_name(path, name);
}
+ ProjectSettings::get_singleton()->clear("_global_script_class_icons");
+ }
+#endif
+
+ Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
+ for (int i = 0; i < script_classes.size(); i++) {
+ Dictionary d = script_classes[i];
+ if (!d.has("class") || !d.has("path") || !d.has("icon")) {
+ continue;
+ }
+
+ String name = d["class"];
+ _script_class_icon_paths[name] = d["icon"];
+ script_class_set_name(d["path"], name);
}
}
EditorData::EditorData() {
current_edited_scene = -1;
- undo_redo_manager.instantiate();
+ undo_redo_manager = memnew(EditorUndoRedoManager);
script_class_load_icon_paths();
}
+EditorData::~EditorData() {
+ memdelete(undo_redo_manager);
+}
+
///////////////////////////////////////////////////////////////////////////////
void EditorSelection::_node_removed(Node *p_node) {
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 385bcad1f9..bce9dd345d 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -133,7 +133,7 @@ private:
HashMap<String, Vector<CustomType>> custom_types;
List<PropertyData> clipboard;
- Ref<EditorUndoRedoManager> undo_redo_manager;
+ EditorUndoRedoManager *undo_redo_manager;
Vector<Callable> undo_redo_callbacks;
HashMap<StringName, Callable> move_element_functions;
@@ -168,7 +168,6 @@ public:
int get_editor_plugin_count() const;
EditorPlugin *get_editor_plugin(int p_idx);
- Ref<EditorUndoRedoManager> &get_undo_redo();
void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value)
void remove_undo_redo_inspector_hook_callback(Callable p_callable);
const Vector<Callable> get_undo_redo_inspector_hook_callback();
@@ -245,6 +244,7 @@ public:
void script_class_load_icon_paths();
EditorData();
+ ~EditorData();
};
/**
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 28424d53b1..9c056ffdd9 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -433,7 +433,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
}
if (!found_uid) {
- return true; //UUID not found, old format, reimport.
+ return true; //UID not found, old format, reimport.
}
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
@@ -868,7 +868,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc
}
if (fc->uid == ResourceUID::INVALID_ID) {
- // imported files should always have a UUID, so attempt to fetch it.
+ // imported files should always have a UID, so attempt to fetch it.
fi->uid = ResourceLoader::get_resource_uid(path);
}
@@ -1527,6 +1527,7 @@ void EditorFileSystem::update_script_classes() {
ScriptServer::save_global_classes();
EditorNode::get_editor_data().script_class_save_icon_paths();
+ emit_signal("script_classes_updated");
// Rescan custom loaders and savers.
// Doing the following here because the `filesystem_changed` signal fires multiple times and isn't always followed by script classes update.
@@ -2319,14 +2320,14 @@ ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const
}
if (p_generate) {
- return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple.
+ return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
} else {
return ResourceUID::INVALID_ID;
}
} else if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
return fs->files[cpos]->uid;
} else if (p_generate) {
- return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UUID at that time, to keep things simple.
+ return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
} else {
return ResourceUID::INVALID_ID;
}
@@ -2417,6 +2418,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("reimport_files", "files"), &EditorFileSystem::reimport_files);
ADD_SIGNAL(MethodInfo("filesystem_changed"));
+ ADD_SIGNAL(MethodInfo("script_classes_updated"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
ADD_SIGNAL(MethodInfo("resources_reimported", PropertyInfo(Variant::PACKED_STRING_ARRAY, "resources")));
ADD_SIGNAL(MethodInfo("resources_reload", PropertyInfo(Variant::PACKED_STRING_ARRAY, "resources")));
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 5baa58873e..9b1a5e028b 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -41,7 +41,7 @@
#include "editor/plugins/script_editor_plugin.h"
#include "scene/gui/line_edit.h"
-#define CONTRIBUTE_URL vformat("%s/community/contributing/updating_the_class_reference.html", VERSION_DOCS_URL)
+#define CONTRIBUTE_URL vformat("%s/contributing/documentation/updating_the_class_reference.html", VERSION_DOCS_URL)
DocTools *EditorHelp::doc = nullptr;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index c24bab054f..5c977de8ff 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -618,7 +618,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
if (is_layout_rtl()) {
mpos.x = get_size().x - mpos.x;
}
- bool button_left = (me->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE;
+ bool button_left = me->get_button_mask().has_flag(MouseButtonMask::LEFT);
bool new_keying_hover = keying_rect.has_point(mpos) && !button_left;
if (new_keying_hover != keying_hover) {
@@ -1361,38 +1361,22 @@ void EditorInspectorSection::_notification(int p_what) {
} break;
case NOTIFICATION_DRAG_BEGIN: {
- Dictionary dd = get_viewport()->gui_get_drag_data();
-
- // Only allow dropping if the section contains properties which can take the dragged data.
- bool children_can_drop = false;
- for (int child_idx = 0; child_idx < vbox->get_child_count(); child_idx++) {
- Control *editor_property = Object::cast_to<Control>(vbox->get_child(child_idx));
-
- // Test can_drop_data and can_drop_data_fw, since can_drop_data only works if set up with forwarding or if script attached.
- if (editor_property && (editor_property->can_drop_data(Point2(), dd) || editor_property->call("_can_drop_data_fw", Point2(), dd, this))) {
- children_can_drop = true;
- break;
- }
- }
-
- dropping = children_can_drop;
- queue_redraw();
+ dropping_for_unfold = true;
} break;
case NOTIFICATION_DRAG_END: {
- dropping = false;
- queue_redraw();
+ dropping_for_unfold = false;
} break;
case NOTIFICATION_MOUSE_ENTER: {
- if (dropping) {
+ if (dropping || dropping_for_unfold) {
dropping_unfold_timer->start();
}
queue_redraw();
} break;
case NOTIFICATION_MOUSE_EXIT: {
- if (dropping) {
+ if (dropping || dropping_for_unfold) {
dropping_unfold_timer->stop();
}
queue_redraw();
@@ -1696,13 +1680,13 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
} else {
action_name = vformat("Move element %d to position %d in property array with prefix %s.", p_element_index, p_to_pos, array_element_prefix);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(action_name);
if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) {
// Call the function.
Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
if (move_function.is_valid()) {
- Variant args[] = { undo_redo.ptr(), object, array_element_prefix, p_element_index, p_to_pos };
+ Variant args[] = { undo_redo, object, array_element_prefix, p_element_index, p_to_pos };
const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
Variant return_value;
Callable::CallError call_error;
@@ -1840,14 +1824,14 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
}
void EditorInspectorArray::_clear_array() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat("Clear property array with prefix %s.", array_element_prefix));
if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) {
for (int i = count - 1; i >= 0; i--) {
// Call the function.
Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
if (move_function.is_valid()) {
- Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 };
+ Variant args[] = { undo_redo, object, array_element_prefix, i, -1 };
const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
Variant return_value;
Callable::CallError call_error;
@@ -1893,7 +1877,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat("Resize property array with prefix %s.", array_element_prefix));
if (p_size > count) {
if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) {
@@ -1901,7 +1885,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
// Call the function.
Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
if (move_function.is_valid()) {
- Variant args[] = { undo_redo.ptr(), object, array_element_prefix, -1, -1 };
+ Variant args[] = { undo_redo, object, array_element_prefix, -1, -1 };
const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
Variant return_value;
Callable::CallError call_error;
@@ -1920,7 +1904,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
// Call the function.
Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
if (move_function.is_valid()) {
- Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 };
+ Variant args[] = { undo_redo, object, array_element_prefix, i, -1 };
const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
Variant return_value;
Callable::CallError call_error;
@@ -2065,7 +2049,7 @@ void EditorInspectorArray::_setup() {
ae.panel = memnew(PanelContainer);
ae.panel->set_focus_mode(FOCUS_ALL);
ae.panel->set_mouse_filter(MOUSE_FILTER_PASS);
- ae.panel->set_drag_forwarding(this);
+ ae.panel->set_drag_forwarding_compat(this);
ae.panel->set_meta("index", begin_array_index + i);
ae.panel->set_tooltip_text(vformat(TTR("Element %d: %s%d*"), i, array_element_prefix, i));
ae.panel->connect("focus_entered", callable_mp((CanvasItem *)ae.panel, &PanelContainer::queue_redraw));
@@ -3568,8 +3552,8 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
- if (!undo_redo.is_valid() || bool(object->call("_dont_undo_redo"))) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ if (bool(object->call("_dont_undo_redo"))) {
object->set(p_name, p_value);
if (p_refresh_all) {
_edit_request_change(object, "");
@@ -3689,7 +3673,7 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array
}
names += p_paths[i];
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS);
for (int i = 0; i < p_paths.size(); i++) {
_edit_set(p_paths[i], p_values[i], false, "");
@@ -3724,7 +3708,7 @@ void EditorInspector::_property_deleted(const String &p_path) {
if (p_path.begins_with("metadata/")) {
String name = p_path.replace_first("metadata/", "");
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Remove metadata %s"), name));
undo_redo->add_do_method(object, "remove_meta", name);
undo_redo->add_undo_method(object, "set_meta", name, object->get_meta(name));
@@ -3790,26 +3774,17 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) {
Node *node = Object::cast_to<Node>(object);
ERR_FAIL_COND(!node);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
- if (undo_redo.is_valid()) {
- undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
- undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
- undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
- if (editor_property_map.has(p_path)) {
- for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
- undo_redo->add_do_method(E->get(), "_update_editor_property_status");
- undo_redo->add_undo_method(E->get(), "_update_editor_property_status");
- }
- }
- undo_redo->commit_action();
- } else {
- node->set_property_pinned(p_path, p_pinned);
- if (editor_property_map.has(p_path)) {
- for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
- E->get()->update_editor_property_status();
- }
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
+ undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
+ undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
+ if (editor_property_map.has(p_path)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
+ undo_redo->add_do_method(E->get(), "_update_editor_property_status");
+ undo_redo->add_undo_method(E->get(), "_update_editor_property_status");
}
}
+ undo_redo->commit_action();
}
void EditorInspector::_property_selected(const String &p_path, int p_focusable) {
@@ -3988,7 +3963,7 @@ void EditorInspector::_add_meta_confirm() {
Variant defval;
Callable::CallError ce;
Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add metadata %s"), name));
undo_redo->add_do_method(object, "set_meta", name, defval);
undo_redo->add_undo_method(object, "remove_meta", name);
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index af8d1e6806..699a88e657 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -276,6 +276,7 @@ class EditorInspectorSection : public Container {
Timer *dropping_unfold_timer = nullptr;
bool dropping = false;
+ bool dropping_for_unfold = false;
HashSet<StringName> revertable_properties;
diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp
index d57b3b66cc..5a372412fa 100644
--- a/editor/editor_locale_dialog.cpp
+++ b/editor/editor_locale_dialog.cpp
@@ -31,7 +31,6 @@
#include "editor_locale_dialog.h"
#include "core/config/project_settings.h"
-#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/check_button.h"
@@ -141,7 +140,7 @@ void EditorLocaleDialog::_filter_lang_option_changed() {
f_lang_all.sort();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Changed Locale Language Filter"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", f_lang_all);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", prev);
@@ -175,7 +174,7 @@ void EditorLocaleDialog::_filter_script_option_changed() {
f_script_all.sort();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Changed Locale Script Filter"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", f_script_all);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", prev);
@@ -209,7 +208,7 @@ void EditorLocaleDialog::_filter_cnt_option_changed() {
f_cnt_all.sort();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Changed Locale Country Filter"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", f_cnt_all);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", prev);
@@ -224,7 +223,7 @@ void EditorLocaleDialog::_filter_mode_changed(int p_mode) {
prev = GLOBAL_GET("internationalization/locale/locale_filter_mode");
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Changed Locale Filter Mode"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", f_mode);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", prev);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 13dd03068f..62223b2445 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -288,7 +288,7 @@ void EditorNode::_update_scene_tabs() {
icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node");
}
- bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i));
+ bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon);
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) {
@@ -493,16 +493,16 @@ void EditorNode::_update_from_settings() {
tree->set_debug_collision_contact_color(GLOBAL_GET("debug/shapes/collision/contact_color"));
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/edge_connection_color"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_color"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_face_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_color"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_edge_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_disabled_color"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_face_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_disabled_color"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_connections(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_connections_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections_xray"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_lines(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_lines_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines_xray"));
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_geometry_face_random_color(GLOBAL_GET("debug/shapes/navigation/enable_geometry_face_random_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/edge_connection_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_disabled_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_disabled_color(GLOBAL_GET("debug/shapes/navigation/geometry_face_disabled_color"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_connections_xray"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_lines(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_lines_xray(GLOBAL_GET("debug/shapes/navigation/enable_edge_lines_xray"));
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_geometry_face_random_color(GLOBAL_GET("debug/shapes/navigation/enable_geometry_face_random_color"));
#endif // DEBUG_ENABLED
}
@@ -533,8 +533,8 @@ void EditorNode::_notification(int p_what) {
opening_prev = false;
}
- bool global_unsaved = get_undo_redo()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY);
- bool scene_or_global_unsaved = global_unsaved || get_undo_redo()->is_history_unsaved(editor_data.get_current_edited_scene_history_id());
+ bool global_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY);
+ bool scene_or_global_unsaved = global_unsaved || EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_current_edited_scene_history_id());
if (unsaved_cache != scene_or_global_unsaved) {
unsaved_cache = scene_or_global_unsaved;
_update_title();
@@ -584,10 +584,11 @@ void EditorNode::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
Engine::get_singleton()->set_editor_hint(true);
- Window *window = static_cast<Window *>(get_tree()->get_root());
+ Window *window = get_window();
if (window) {
// Handle macOS fullscreen and extend-to-title changes.
window->connect("titlebar_changed", callable_mp(this, &EditorNode::_titlebar_resized));
+ window->set_theme(theme);
}
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));
@@ -608,6 +609,9 @@ void EditorNode::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
+ if (progress_dialog) {
+ progress_dialog->queue_free();
+ }
editor_data.save_editor_external_data();
FileAccess::set_file_close_fail_notify_callback(nullptr);
log->deinit(); // Do not get messages anymore.
@@ -630,6 +634,10 @@ void EditorNode::_notification(int p_what) {
set_addon_plugin_enabled(addons[i], true);
}
_initializing_plugins = false;
+
+ if (!pending_addons.is_empty()) {
+ EditorFileSystem::get_singleton()->connect("script_classes_updated", callable_mp(this, &EditorNode::_enable_pending_addons));
+ }
}
RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true);
@@ -688,9 +696,11 @@ void EditorNode::_notification(int p_what) {
if (theme_changed) {
theme = create_custom_theme(theme_base->get_theme());
+ DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), SNAME("Editor")));
theme_base->set_theme(theme);
gui_base->set_theme(theme);
+ get_window()->set_theme(theme);
gui_base->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Background"), SNAME("EditorStyles")));
scene_root_parent->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Content"), SNAME("EditorStyles")));
@@ -1123,6 +1133,12 @@ void EditorNode::_version_button_pressed() {
DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
}
+void EditorNode::_update_undo_redo_allowed() {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo());
+ file_menu->set_item_disabled(file_menu->get_item_index(EDIT_REDO), !undo_redo->has_redo());
+}
+
void EditorNode::_node_renamed() {
if (InspectorDock::get_inspector_singleton()) {
InspectorDock::get_inspector_singleton()->update_tree();
@@ -1670,7 +1686,7 @@ int EditorNode::_save_external_resources() {
saved++;
}
- get_undo_redo()->set_history_as_saved(EditorUndoRedoManager::GLOBAL_HISTORY);
+ EditorUndoRedoManager::get_singleton()->set_history_as_saved(EditorUndoRedoManager::GLOBAL_HISTORY);
return saved;
}
@@ -1847,7 +1863,7 @@ void EditorNode::_mark_unsaved_scenes() {
String path = node->get_scene_file_path();
if (!path.is_empty() && !FileAccess::exists(path)) {
// Mark scene tab as unsaved if the file is gone.
- get_undo_redo()->set_history_as_unsaved(editor_data.get_scene_history_id(i));
+ EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(editor_data.get_scene_history_id(i));
}
}
@@ -2330,7 +2346,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
if (main_plugin && !skip_main_plugin) {
// Special case if use of external editor is true.
Resource *current_res = Object::cast_to<Resource>(current_obj);
- if (main_plugin->get_name() == "Script" && !current_obj->is_class("VisualScript") && current_res && !current_res->is_built_in() && (bool(EDITOR_GET("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
+ if (main_plugin->get_name() == "Script" && current_res && !current_res->is_built_in() && (bool(EDITOR_GET("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
if (!changing_scene) {
main_plugin->edit(current_obj);
}
@@ -2734,9 +2750,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
} else {
- String action = editor_data.get_undo_redo()->get_current_action_name();
- int id = editor_data.get_undo_redo()->get_current_action_history_id();
- if (!editor_data.get_undo_redo()->undo()) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ String action = undo_redo->get_current_action_name();
+ int id = undo_redo->get_current_action_history_id();
+ if (!undo_redo->undo()) {
log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR);
} else if (!action.is_empty()) {
switch (id) {
@@ -2753,18 +2770,19 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
}
} break;
case EDIT_REDO: {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
log->add_message(TTR("Can't redo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
} else {
- if (!editor_data.get_undo_redo()->redo()) {
+ if (!undo_redo->redo()) {
log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR);
} else {
- String action = editor_data.get_undo_redo()->get_current_action_name();
+ String action = undo_redo->get_current_action_name();
if (action.is_empty()) {
break;
}
- switch (editor_data.get_undo_redo()->get_current_action_history_id()) {
+ switch (undo_redo->get_current_action_history_id()) {
case EditorUndoRedoManager::GLOBAL_HISTORY:
log->add_message(vformat(TTR("Global Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
break;
@@ -2807,7 +2825,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
ERR_PRINT("Failed to load scene");
}
editor_data.move_edited_scene_to_index(cur_idx);
- get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id());
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id());
scene_tabs->set_current_tab(cur_idx);
} break;
@@ -2899,7 +2917,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case RELOAD_CURRENT_PROJECT: {
if (!p_confirmed) {
bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit");
- if (_next_unsaved_scene(!save_each) == -1 && !get_undo_redo()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY)) {
+ if (_next_unsaved_scene(!save_each) == -1 && !EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY)) {
_discard_changes();
break;
} else {
@@ -3138,7 +3156,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
if (!editor_data.get_edited_scene_root(i)) {
continue;
}
- bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i));
+ bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
if (unsaved) {
String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
if (p_valid_filename && scene_filename.length() == 0) {
@@ -3245,16 +3263,8 @@ void EditorNode::_discard_changes(const String &p_str) {
}
void EditorNode::_update_file_menu_opened() {
- Ref<Shortcut> close_scene_sc = ED_GET_SHORTCUT("editor/close_scene");
- close_scene_sc->set_name(TTR("Close Scene"));
- Ref<Shortcut> reopen_closed_scene_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene");
- reopen_closed_scene_sc->set_name(TTR("Reopen Closed Scene"));
-
file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty());
-
- Ref<EditorUndoRedoManager> undo_redo = editor_data.get_undo_redo();
- file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo());
- file_menu->set_item_disabled(file_menu->get_item_index(EDIT_REDO), !undo_redo->has_redo());
+ _update_undo_redo_allowed();
}
void EditorNode::_update_file_menu_closed() {
@@ -3467,6 +3477,12 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
// Errors in the script cause the base_type to be an empty StringName.
if (scr->get_instance_base_type() == StringName()) {
+ if (_initializing_plugins) {
+ // However, if it happens during initialization, waiting for file scan might help.
+ pending_addons.push_back(p_addon);
+ return;
+ }
+
show_warning(vformat(TTR("Unable to load addon script from path: '%s'. This might be due to a code error in that script.\nDisabling the addon at '%s' to prevent further errors."), script_path, addon_path));
_remove_plugin_from_enabled(addon_path);
return;
@@ -3655,7 +3671,7 @@ void EditorNode::set_current_scene(int p_idx) {
editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
}
- get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(p_idx));
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_scene_history_id(p_idx));
}
changing_scene = true;
@@ -3722,7 +3738,7 @@ int EditorNode::new_scene() {
// Remove placeholder empty scene.
if (editor_data.get_edited_scene_count() > 1) {
for (int i = 0; i < editor_data.get_edited_scene_count() - 1; i++) {
- bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i));
+ bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
if (!unsaved && editor_data.get_scene_path(i).is_empty() && editor_data.get_edited_scene_root(i) == nullptr) {
editor_data.remove_scene(i);
idx--;
@@ -3947,10 +3963,6 @@ void EditorNode::request_instantiate_scenes(const Vector<String> &p_files) {
SceneTreeDock::get_singleton()->instantiate_scenes(p_files);
}
-Ref<EditorUndoRedoManager> &EditorNode::get_undo_redo() {
- return singleton->editor_data.get_undo_redo();
-}
-
void EditorNode::_inherit_request(String p_file) {
current_menu_option = FILE_NEW_INHERITED_SCENE;
_dialog_action(p_file);
@@ -4309,7 +4321,7 @@ bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringNa
void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
if (singleton->cmdline_export_mode) {
print_line(p_task + ": begin: " + p_label + " steps: " + itos(p_steps));
- } else {
+ } else if (singleton->progress_dialog) {
singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
}
}
@@ -4318,15 +4330,17 @@ bool EditorNode::progress_task_step(const String &p_task, const String &p_state,
if (singleton->cmdline_export_mode) {
print_line("\t" + p_task + ": step " + itos(p_step) + ": " + p_state);
return false;
- } else {
+ } else if (singleton->progress_dialog) {
return singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
+ } else {
+ return false;
}
}
void EditorNode::progress_end_task(const String &p_task) {
if (singleton->cmdline_export_mode) {
print_line(p_task + ": end");
- } else {
+ } else if (singleton->progress_dialog) {
singleton->progress_dialog->end_task(p_task);
}
}
@@ -4374,6 +4388,13 @@ void EditorNode::_build_icon_type_cache() {
}
}
+void EditorNode::_enable_pending_addons() {
+ for (uint32_t i = 0; i < pending_addons.size(); i++) {
+ set_addon_plugin_enabled(pending_addons[i], true);
+ }
+ pending_addons.clear();
+}
+
void EditorNode::_file_dialog_register(FileDialog *p_dialog) {
singleton->file_dialogs.insert(p_dialog);
}
@@ -5061,8 +5082,8 @@ bool EditorNode::ensure_main_scene(bool p_from_native) {
return true;
}
-Error EditorNode::run_play_native(int p_idx, int p_platform) {
- return run_native->run_native(p_idx, p_platform);
+Error EditorNode::run_play_native(int p_id) {
+ return run_native->run_native(p_id);
}
void EditorNode::run_play() {
@@ -5218,7 +5239,7 @@ void EditorNode::_scene_tab_closed(int p_tab, int option) {
return;
}
- bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(p_tab));
+ bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab));
if (unsaved) {
save_confirmation->set_ok_button_text(TTR("Save & Close"));
save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene->get_scene_file_path().is_empty() ? scene->get_scene_file_path() : "unsaved scene"));
@@ -5281,12 +5302,10 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE);
scene_tabs_context_menu->add_separator();
- Ref<Shortcut> close_tab_sc = ED_GET_SHORTCUT("editor/close_scene");
- close_tab_sc->set_name(TTR("Close Tab"));
- scene_tabs_context_menu->add_shortcut(close_tab_sc, FILE_CLOSE);
- Ref<Shortcut> undo_close_tab_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene");
- undo_close_tab_sc->set_name(TTR("Undo Close Tab"));
- scene_tabs_context_menu->add_shortcut(undo_close_tab_sc, FILE_OPEN_PREV);
+ scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), FILE_CLOSE);
+ scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(FILE_CLOSE), TTR("Close Tab"));
+ scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene"), FILE_OPEN_PREV);
+ scene_tabs_context_menu->set_item_text(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), TTR("Undo Close Tab"));
if (previous_scenes.is_empty()) {
scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true);
}
@@ -5719,7 +5738,7 @@ void EditorNode::reload_scene(const String &p_path) {
if (scene_idx == -1) {
if (get_edited_scene()) {
// Scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
- editor_data.get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id());
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id());
}
return;
}
@@ -5735,7 +5754,7 @@ void EditorNode::reload_scene(const String &p_path) {
// Adjust index so tab is back a the previous position.
editor_data.move_edited_scene_to_index(scene_idx);
- get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(scene_idx));
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_scene_history_id(scene_idx));
// Recover the tab.
scene_tabs->set_current_tab(current_tab);
@@ -6053,6 +6072,9 @@ EditorNode::EditorNode() {
singleton = this;
+ EditorUndoRedoManager::get_singleton()->connect("version_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed));
+ EditorUndoRedoManager::get_singleton()->connect("history_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed));
+
TranslationServer::get_singleton()->set_enabled(false);
// Load settings.
if (!EditorSettings::get_singleton()) {
@@ -6094,7 +6116,10 @@ EditorNode::EditorNode() {
}
// Define a minimum window size to prevent UI elements from overlapping or being cut off.
- DisplayServer::get_singleton()->window_set_min_size(Size2(1024, 600) * EDSCALE);
+ Window *w = Object::cast_to<Window>(SceneTree::get_singleton()->get_root());
+ if (w) {
+ w->set_min_size(Size2(1024, 600) * EDSCALE);
+ }
FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
@@ -6233,6 +6258,7 @@ EditorNode::EditorNode() {
// Exporters might need the theme.
EditorColorMap::create();
theme = create_custom_theme();
+ DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), SNAME("Editor")));
register_exporters();
@@ -6287,7 +6313,6 @@ EditorNode::EditorNode() {
resource_preview = memnew(EditorResourcePreview);
add_child(resource_preview);
progress_dialog = memnew(ProgressDialog);
- gui_base->add_child(progress_dialog);
// Take up all screen.
gui_base->set_anchor(SIDE_RIGHT, Control::ANCHOR_END);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index f3dad8e223..fb2544c141 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -292,6 +292,7 @@ private:
Vector<EditorPlugin *> editor_plugins;
bool _initializing_plugins = false;
HashMap<String, EditorPlugin *> addon_name_to_plugin;
+ LocalVector<String> pending_addons;
PanelContainer *scene_root_parent = nullptr;
Control *theme_base = nullptr;
@@ -535,6 +536,7 @@ private:
static void _resource_loaded(Ref<Resource> p_resource, const String &p_path);
void _build_icon_type_cache();
+ void _enable_pending_addons();
void _dialog_action(String p_file);
@@ -577,6 +579,8 @@ private:
void _titlebar_resized();
void _version_button_pressed();
+ void _update_undo_redo_allowed();
+
int _save_external_resources();
bool _validate_scene_recursive(const String &p_filename, Node *p_node);
@@ -727,7 +731,6 @@ public:
static EditorLog *get_log() { return singleton->log; }
static EditorData &get_editor_data() { return singleton->editor_data; }
static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
- static Ref<EditorUndoRedoManager> &get_undo_redo();
static HBoxContainer *get_menu_hb() { return singleton->menu_hb; }
static VSplitContainer *get_top_split() { return singleton->top_split; }
@@ -912,7 +915,7 @@ public:
bool ensure_main_scene(bool p_from_native);
- Error run_play_native(int p_idx, int p_platform);
+ Error run_play_native(int p_id);
void run_play();
void run_play_current();
void run_play_custom(const String &p_custom);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index a5d1ddb2fa..ec866ad999 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -977,8 +977,8 @@ void EditorPlugin::_bind_methods() {
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
}
-Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() {
- return EditorNode::get_undo_redo();
+EditorUndoRedoManager *EditorPlugin::get_undo_redo() {
+ return EditorUndoRedoManager::get_singleton();
}
EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS];
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 33081e867a..21eb4b89ef 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -148,7 +148,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- Ref<EditorUndoRedoManager> get_undo_redo();
+ EditorUndoRedoManager *get_undo_redo();
void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
void remove_custom_type(const String &p_type);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index ba42bbf3bd..f528053fee 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -68,7 +68,7 @@ EditorPropertyNil::EditorPropertyNil() {
void EditorPropertyText::_set_read_only(bool p_read_only) {
text->set_editable(!p_read_only);
-};
+}
void EditorPropertyText::_text_submitted(const String &p_string) {
if (updating) {
@@ -133,7 +133,7 @@ EditorPropertyText::EditorPropertyText() {
void EditorPropertyMultilineText::_set_read_only(bool p_read_only) {
text->set_editable(!p_read_only);
open_big_text->set_disabled(p_read_only);
-};
+}
void EditorPropertyMultilineText::_big_text_changed() {
text->set_text(big_text->get_text());
@@ -236,7 +236,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
void EditorPropertyTextEnum::_set_read_only(bool p_read_only) {
option_button->set_disabled(p_read_only);
edit_button->set_disabled(p_read_only);
-};
+}
void EditorPropertyTextEnum::_emit_changed_value(String p_string) {
if (string_name) {
@@ -450,7 +450,7 @@ EditorPropertyLocale::EditorPropertyLocale() {
void EditorPropertyPath::_set_read_only(bool p_read_only) {
path->set_editable(!p_read_only);
path_edit->set_disabled(p_read_only);
-};
+}
void EditorPropertyPath::_path_selected(const String &p_path) {
emit_changed(get_edited_property(), p_path);
@@ -569,7 +569,7 @@ EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
path = memnew(LineEdit);
- path->set_drag_forwarding(this);
+ path->set_drag_forwarding_compat(this);
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
path_hb->add_child(path);
path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected));
@@ -588,7 +588,7 @@ EditorPropertyPath::EditorPropertyPath() {
void EditorPropertyClassName::_set_read_only(bool p_read_only) {
property->set_disabled(p_read_only);
-};
+}
void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) {
base_type = p_base_type;
@@ -629,112 +629,11 @@ EditorPropertyClassName::EditorPropertyClassName() {
add_child(dialog);
}
-///////////////////// MEMBER /////////////////////////
-
-void EditorPropertyMember::_set_read_only(bool p_read_only) {
- property->set_disabled(p_read_only);
-};
-
-void EditorPropertyMember::_property_selected(const String &p_selected) {
- emit_changed(get_edited_property(), p_selected);
- update_property();
-}
-
-void EditorPropertyMember::_property_select() {
- if (!selector) {
- selector = memnew(PropertySelector);
- selector->connect("selected", callable_mp(this, &EditorPropertyMember::_property_selected));
- add_child(selector);
- }
-
- String current = get_edited_object()->get(get_edited_property());
-
- if (hint == MEMBER_METHOD_OF_VARIANT_TYPE) {
- Variant::Type type = Variant::NIL;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (hint_text == Variant::get_type_name(Variant::Type(i))) {
- type = Variant::Type(i);
- }
- }
- if (type != Variant::NIL) {
- selector->select_method_from_basic_type(type, current);
- }
-
- } else if (hint == MEMBER_METHOD_OF_BASE_TYPE) {
- selector->select_method_from_base_type(hint_text, current);
-
- } else if (hint == MEMBER_METHOD_OF_INSTANCE) {
- Object *instance = ObjectDB::get_instance(ObjectID(hint_text.to_int()));
- if (instance) {
- selector->select_method_from_instance(instance, current);
- }
-
- } else if (hint == MEMBER_METHOD_OF_SCRIPT) {
- Object *obj = ObjectDB::get_instance(ObjectID(hint_text.to_int()));
- if (Object::cast_to<Script>(obj)) {
- selector->select_method_from_script(Object::cast_to<Script>(obj), current);
- }
-
- } else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) {
- Variant::Type type = Variant::NIL;
- String tname = hint_text;
- if (tname.contains(".")) {
- tname = tname.get_slice(".", 0);
- }
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (tname == Variant::get_type_name(Variant::Type(i))) {
- type = Variant::Type(Variant::Type(i));
- }
- }
-
- if (type != Variant::NIL) {
- selector->select_property_from_basic_type(type, current);
- }
-
- } else if (hint == MEMBER_PROPERTY_OF_BASE_TYPE) {
- selector->select_property_from_base_type(hint_text, current);
-
- } else if (hint == MEMBER_PROPERTY_OF_INSTANCE) {
- Object *instance = ObjectDB::get_instance(ObjectID(hint_text.to_int()));
- if (instance) {
- selector->select_property_from_instance(instance, current);
- }
-
- } else if (hint == MEMBER_PROPERTY_OF_SCRIPT) {
- Object *obj = ObjectDB::get_instance(ObjectID(hint_text.to_int()));
- if (Object::cast_to<Script>(obj)) {
- selector->select_property_from_script(Object::cast_to<Script>(obj), current);
- }
- }
-}
-
-void EditorPropertyMember::setup(Type p_hint, const String &p_hint_text) {
- hint = p_hint;
- hint_text = p_hint_text;
-}
-
-void EditorPropertyMember::update_property() {
- String full_path = get_edited_object()->get(get_edited_property());
- property->set_text(full_path);
-}
-
-void EditorPropertyMember::_bind_methods() {
-}
-
-EditorPropertyMember::EditorPropertyMember() {
- selector = nullptr;
- property = memnew(Button);
- property->set_clip_text(true);
- add_child(property);
- add_focusable(property);
- property->connect("pressed", callable_mp(this, &EditorPropertyMember::_property_select));
-}
-
///////////////////// CHECK /////////////////////////
void EditorPropertyCheck::_set_read_only(bool p_read_only) {
checkbox->set_disabled(p_read_only);
-};
+}
void EditorPropertyCheck::_checkbox_pressed() {
emit_changed(get_edited_property(), checkbox->is_pressed());
@@ -761,7 +660,7 @@ EditorPropertyCheck::EditorPropertyCheck() {
void EditorPropertyEnum::_set_read_only(bool p_read_only) {
options->set_disabled(p_read_only);
-};
+}
void EditorPropertyEnum::_option_selected(int p_which) {
int64_t val = options->get_item_metadata(p_which);
@@ -820,7 +719,7 @@ void EditorPropertyFlags::_set_read_only(bool p_read_only) {
for (CheckBox *check : flags) {
check->set_disabled(p_read_only);
}
-};
+}
void EditorPropertyFlags::_flag_toggled(int p_index) {
uint32_t value = get_edited_object()->get(get_edited_property());
@@ -1186,7 +1085,7 @@ void EditorPropertyLayers::_notification(int p_what) {
void EditorPropertyLayers::_set_read_only(bool p_read_only) {
button->set_disabled(p_read_only);
grid->set_read_only(p_read_only);
-};
+}
void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {
emit_changed(get_edited_property(), p_grid);
@@ -1359,7 +1258,7 @@ EditorPropertyLayers::EditorPropertyLayers() {
void EditorPropertyInteger::_set_read_only(bool p_read_only) {
spin->set_read_only(p_read_only);
-};
+}
void EditorPropertyInteger::_value_changed(int64_t val) {
if (setting) {
@@ -1406,7 +1305,7 @@ EditorPropertyInteger::EditorPropertyInteger() {
void EditorPropertyObjectID::_set_read_only(bool p_read_only) {
edit->set_disabled(p_read_only);
-};
+}
void EditorPropertyObjectID::_edit_pressed() {
emit_signal(SNAME("object_id_selected"), get_edited_property(), get_edited_object()->get(get_edited_property()));
@@ -1496,7 +1395,7 @@ EditorPropertyCallable::EditorPropertyCallable() {
void EditorPropertyFloat::_set_read_only(bool p_read_only) {
spin->set_read_only(p_read_only);
-};
+}
void EditorPropertyFloat::_value_changed(double val) {
if (setting) {
@@ -1546,7 +1445,7 @@ EditorPropertyFloat::EditorPropertyFloat() {
void EditorPropertyEasing::_set_read_only(bool p_read_only) {
spin->set_read_only(p_read_only);
-};
+}
void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
if (is_read_only()) {
@@ -1577,7 +1476,7 @@ void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
const Ref<InputEventMouseMotion> mm = p_ev;
- if (dragging && mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (dragging && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
float rel = mm->get_relative().x;
if (rel == 0) {
return;
@@ -1769,7 +1668,7 @@ void EditorPropertyVector2::_set_read_only(bool p_read_only) {
for (int i = 0; i < 2; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector2::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -1900,7 +1799,7 @@ void EditorPropertyRect2::_set_read_only(bool p_read_only) {
for (int i = 0; i < 4; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyRect2::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2003,7 +1902,7 @@ void EditorPropertyVector3::_set_read_only(bool p_read_only) {
for (int i = 0; i < 3; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2182,7 +2081,7 @@ void EditorPropertyVector2i::_set_read_only(bool p_read_only) {
for (int i = 0; i < 2; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector2i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2312,7 +2211,7 @@ void EditorPropertyRect2i::_set_read_only(bool p_read_only) {
for (int i = 0; i < 4; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2414,7 +2313,7 @@ void EditorPropertyVector3i::_set_read_only(bool p_read_only) {
for (int i = 0; i < 3; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2564,7 +2463,7 @@ void EditorPropertyPlane::_set_read_only(bool p_read_only) {
for (int i = 0; i < 4; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyPlane::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2660,7 +2559,7 @@ void EditorPropertyQuaternion::_set_read_only(bool p_read_only) {
for (int i = 0; i < 3; i++) {
euler[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyQuaternion::_edit_custom_value() {
if (edit_button->is_pressed()) {
@@ -2873,7 +2772,7 @@ void EditorPropertyVector4::_set_read_only(bool p_read_only) {
for (int i = 0; i < 4; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector4::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2963,7 +2862,7 @@ void EditorPropertyVector4i::_set_read_only(bool p_read_only) {
for (int i = 0; i < 4; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyVector4i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3052,7 +2951,7 @@ void EditorPropertyAABB::_set_read_only(bool p_read_only) {
for (int i = 0; i < 6; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyAABB::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3135,7 +3034,7 @@ void EditorPropertyTransform2D::_set_read_only(bool p_read_only) {
for (int i = 0; i < 6; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3226,7 +3125,7 @@ void EditorPropertyBasis::_set_read_only(bool p_read_only) {
for (int i = 0; i < 9; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyBasis::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3316,7 +3215,7 @@ void EditorPropertyTransform3D::_set_read_only(bool p_read_only) {
for (int i = 0; i < 12; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3414,7 +3313,7 @@ void EditorPropertyProjection::_set_read_only(bool p_read_only) {
for (int i = 0; i < 12; i++) {
spin[i]->set_read_only(p_read_only);
}
-};
+}
void EditorPropertyProjection::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -3517,7 +3416,7 @@ EditorPropertyProjection::EditorPropertyProjection() {
void EditorPropertyColor::_set_read_only(bool p_read_only) {
picker->set_disabled(p_read_only);
-};
+}
void EditorPropertyColor::_color_changed(const Color &p_color) {
// Cancel the color change if the current color is identical to the new one.
@@ -3787,7 +3686,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
assign->set_h_size_flags(SIZE_EXPAND_FILL);
assign->set_clip_text(true);
assign->connect("pressed", callable_mp(this, &EditorPropertyNodePath::_node_assign));
- assign->set_drag_forwarding(this);
+ assign->set_drag_forwarding_compat(this);
hbc->add_child(assign);
clear = memnew(Button);
@@ -3819,7 +3718,7 @@ EditorPropertyRID::EditorPropertyRID() {
void EditorPropertyResource::_set_read_only(bool p_read_only) {
resource_picker->set_editable(!p_read_only);
-};
+}
void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {
if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) {
@@ -3847,13 +3746,93 @@ void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource,
}
}
+static bool _find_recursive_resources(const Variant &v, HashSet<Resource *> &resources_found) {
+ switch (v.get_type()) {
+ case Variant::ARRAY: {
+ Array a = v;
+ for (int i = 0; i < a.size(); i++) {
+ Variant v2 = a[i];
+ if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {
+ continue;
+ }
+ if (_find_recursive_resources(v2, resources_found)) {
+ return true;
+ }
+ }
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = v;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for (const Variant &k : keys) {
+ if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
+ if (_find_recursive_resources(k, resources_found)) {
+ return true;
+ }
+ }
+ Variant v2 = d[k];
+ if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
+ if (_find_recursive_resources(v2, resources_found)) {
+ return true;
+ }
+ }
+ }
+ } break;
+ case Variant::OBJECT: {
+ Ref<Resource> r = v;
+
+ if (r.is_null()) {
+ return false;
+ }
+
+ if (resources_found.has(r.ptr())) {
+ return true;
+ }
+
+ resources_found.insert(r.ptr());
+
+ List<PropertyInfo> plist;
+ r->get_property_list(&plist);
+ for (const PropertyInfo &pinfo : plist) {
+ if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+
+ if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {
+ continue;
+ }
+ if (_find_recursive_resources(r->get(pinfo.name), resources_found)) {
+ return true;
+ }
+ }
+
+ resources_found.erase(r.ptr());
+ } break;
+ default: {
+ }
+ }
+ return false;
+}
+
void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) {
- // Make visual script the correct type.
- Ref<Script> s = p_resource;
+ Resource *r = Object::cast_to<Resource>(get_edited_object());
+ if (r) {
+ // Check for recursive setting of resource
+ HashSet<Resource *> resources_found;
+ resources_found.insert(r);
+ bool found = _find_recursive_resources(p_resource, resources_found);
+ if (found) {
+ EditorNode::get_singleton()->show_warning(TTR("Recursion detected, unable to assign resource to property."));
+ emit_changed(get_edited_property(), Ref<Resource>());
+ update_property();
+ return;
+ }
+ }
// The bool is_script applies only to an object's main script.
// Changing the value of Script-type exported variables of the main script should not trigger saving/reloading properties.
bool is_script = false;
+ Ref<Script> s = p_resource;
if (get_edited_object() && s.is_valid() && get_edited_property() == CoreStringNames::get_singleton()->_script) {
is_script = true;
InspectorDock::get_singleton()->store_script_properties(get_edited_object());
@@ -3863,7 +3842,7 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource)
// Prevent the creation of invalid ViewportTextures when possible.
Ref<ViewportTexture> vpt = p_resource;
if (vpt.is_valid()) {
- Resource *r = Object::cast_to<Resource>(get_edited_object());
+ r = Object::cast_to<Resource>(get_edited_object());
if (r && r->get_path().is_resource_file()) {
EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene."));
emit_changed(get_edited_property(), Ref<Resource>());
@@ -4409,45 +4388,6 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
editor->set_save_mode();
}
return editor;
- } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE ||
- p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE ||
- p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE ||
- p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT ||
- p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE ||
- p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE ||
- p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE ||
- p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) {
- EditorPropertyMember *editor = memnew(EditorPropertyMember);
-
- EditorPropertyMember::Type type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE;
- switch (p_hint) {
- case PROPERTY_HINT_METHOD_OF_BASE_TYPE:
- type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE;
- break;
- case PROPERTY_HINT_METHOD_OF_INSTANCE:
- type = EditorPropertyMember::MEMBER_METHOD_OF_INSTANCE;
- break;
- case PROPERTY_HINT_METHOD_OF_SCRIPT:
- type = EditorPropertyMember::MEMBER_METHOD_OF_SCRIPT;
- break;
- case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE:
- type = EditorPropertyMember::MEMBER_PROPERTY_OF_VARIANT_TYPE;
- break;
- case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE:
- type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE;
- break;
- case PROPERTY_HINT_PROPERTY_OF_INSTANCE:
- type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE;
- break;
- case PROPERTY_HINT_PROPERTY_OF_SCRIPT:
- type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT;
- break;
- default: {
- }
- }
- editor->setup(type, p_hint_text);
- return editor;
-
} else {
EditorPropertyText *editor = memnew(EditorPropertyText);
if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {
@@ -4646,6 +4586,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
return editor;
} else {
EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);
+ editor->setup(p_hint);
return editor;
}
} break;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 03e318b706..a255af30ee 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -203,40 +203,6 @@ public:
EditorPropertyClassName();
};
-class EditorPropertyMember : public EditorProperty {
- GDCLASS(EditorPropertyMember, EditorProperty);
-
-public:
- enum Type {
- MEMBER_METHOD_OF_VARIANT_TYPE, ///< a method of a type
- MEMBER_METHOD_OF_BASE_TYPE, ///< a method of a base type
- MEMBER_METHOD_OF_INSTANCE, ///< a method of an instance
- MEMBER_METHOD_OF_SCRIPT, ///< a method of a script & base
- MEMBER_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type
- MEMBER_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
- MEMBER_PROPERTY_OF_INSTANCE, ///< a property of an instance
- MEMBER_PROPERTY_OF_SCRIPT, ///< a property of a script & base
- };
-
-private:
- Type hint;
- PropertySelector *selector = nullptr;
- Button *property = nullptr;
- String hint_text;
-
- void _property_selected(const String &p_selected);
- void _property_select();
-
-protected:
- virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
-
-public:
- void setup(Type p_hint, const String &p_hint_text);
- virtual void update_property() override;
- EditorPropertyMember();
-};
-
class EditorPropertyCheck : public EditorProperty {
GDCLASS(EditorPropertyCheck, EditorProperty);
CheckBox *checkbox = nullptr;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 10f46283e6..069e80fc05 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -715,7 +715,7 @@ EditorPropertyArray::EditorPropertyArray() {
edit->set_clip_text(true);
edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_edit_pressed));
edit->set_toggle_mode(true);
- edit->set_drag_forwarding(this);
+ edit->set_drag_forwarding_compat(this);
edit->connect("draw", callable_mp(this, &EditorPropertyArray::_button_draw));
add_child(edit);
add_focusable(edit);
@@ -818,6 +818,10 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) {
update_property();
}
+void EditorPropertyDictionary::setup(PropertyHint p_hint) {
+ property_hint = p_hint;
+}
+
void EditorPropertyDictionary::update_property() {
Variant updated_val = get_edited_object()->get(get_edited_property());
@@ -929,7 +933,13 @@ void EditorPropertyDictionary::update_property() {
prop = editor;
} break;
case Variant::STRING: {
- prop = memnew(EditorPropertyText);
+ if (i != amount && property_hint == PROPERTY_HINT_MULTILINE_TEXT) {
+ // If this is NOT the new key field and there's a multiline hint,
+ // show the field as multiline
+ prop = memnew(EditorPropertyMultilineText);
+ } else {
+ prop = memnew(EditorPropertyText);
+ }
} break;
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 96fc2dce24..73a16e3687 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -154,6 +154,7 @@ class EditorPropertyDictionary : public EditorProperty {
EditorSpinSlider *size_sliderv = nullptr;
Button *button_add_item = nullptr;
EditorPaginator *paginator = nullptr;
+ PropertyHint property_hint;
void _page_changed(int p_page);
void _edit_pressed();
@@ -169,6 +170,7 @@ protected:
void _notification(int p_what);
public:
+ void setup(PropertyHint p_hint);
virtual void update_property() override;
EditorPropertyDictionary();
};
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index 195457c239..18ba19f5f6 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -211,6 +211,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["rmb"] = "RMB";
capitalize_string_remaps["rpc"] = "RPC";
capitalize_string_remaps["s3tc"] = "S3TC";
+ capitalize_string_remaps["scp"] = "SCP";
capitalize_string_remaps["sdf"] = "SDF";
capitalize_string_remaps["sdfgi"] = "SDFGI";
capitalize_string_remaps["sdk"] = "SDK";
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 94152dfd49..81b2fff97a 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -950,7 +950,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button->set_flat(true);
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
assign_button->set_clip_text(true);
- assign_button->set_drag_forwarding(this);
+ assign_button->set_drag_forwarding_compat(this);
add_child(assign_button);
assign_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_resource_selected));
assign_button->connect("draw", callable_mp(this, &EditorResourcePicker::_button_draw));
@@ -958,7 +958,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
if (!p_hide_assign_button_controls) {
preview_rect = memnew(TextureRect);
- preview_rect->set_ignore_texture_size(true);
+ preview_rect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
preview_rect->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
preview_rect->set_offset(SIDE_TOP, 1);
preview_rect->set_offset(SIDE_BOTTOM, -1);
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index b0eb4c3d55..49723aa169 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -37,53 +37,27 @@
void EditorRunNative::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
- Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(i);
- if (eep.is_null()) {
- continue;
- }
- Ref<ImageTexture> icon = eep->get_run_icon();
- if (!icon.is_null()) {
- Ref<Image> im = icon->get_image();
- im = im->duplicate();
- im->clear_mipmaps();
- if (!im->is_empty()) {
- im->resize(16 * EDSCALE, 16 * EDSCALE);
- Ref<ImageTexture> small_icon = ImageTexture::create_from_image(im);
- MenuButton *mb = memnew(MenuButton);
- mb->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::run_native).bind(i));
- mb->connect("pressed", callable_mp(this, &EditorRunNative::run_native).bind(-1, i));
- mb->set_icon(small_icon);
- add_child(mb);
- menus[i] = mb;
- }
- }
- }
+ case NOTIFICATION_THEME_CHANGED: {
+ remote_debug->set_icon(get_theme_icon(SNAME("PlayRemote"), SNAME("EditorIcons")));
} break;
-
case NOTIFICATION_PROCESS: {
bool changed = EditorExport::get_singleton()->poll_export_platforms() || first;
if (changed) {
- for (KeyValue<int, MenuButton *> &E : menus) {
- Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(E.key);
- MenuButton *mb = E.value;
- int dc = eep->get_options_count();
-
- if (dc == 0) {
- mb->hide();
- } else {
- mb->get_popup()->clear();
- mb->show();
- if (dc == 1) {
- mb->set_tooltip_text(eep->get_option_tooltip(0));
- } else {
- mb->set_tooltip_text(eep->get_options_tooltip());
- for (int i = 0; i < dc; i++) {
- mb->get_popup()->add_icon_item(eep->get_option_icon(i), eep->get_option_label(i));
- mb->get_popup()->set_item_tooltip(-1, eep->get_option_tooltip(i));
- }
+ remote_debug->get_popup()->clear();
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
+ Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(i);
+ if (eep.is_null()) {
+ continue;
+ }
+ int dc = MIN(eep->get_options_count(), 9000);
+ if (dc > 0) {
+ remote_debug->get_popup()->add_icon_item(eep->get_run_icon(), eep->get_name(), -1);
+ remote_debug->get_popup()->set_item_disabled(-1, true);
+ for (int j = 0; j < dc; j++) {
+ remote_debug->get_popup()->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), 10000 * i + j);
+ remote_debug->get_popup()->set_item_tooltip(-1, eep->get_option_tooltip(j));
+ remote_debug->get_popup()->set_item_indent(-1, 2);
}
}
}
@@ -94,25 +68,22 @@ void EditorRunNative::_notification(int p_what) {
}
}
-Error EditorRunNative::run_native(int p_idx, int p_platform) {
- if (!EditorNode::get_singleton()->ensure_main_scene(true)) {
- resume_idx = p_idx;
- resume_platform = p_platform;
+Error EditorRunNative::run_native(int p_id) {
+ if (p_id < 0) {
return OK;
}
- Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(p_platform);
- ERR_FAIL_COND_V(eep.is_null(), ERR_UNAVAILABLE);
+ int platform = p_id / 10000;
+ int idx = p_id % 10000;
- if (p_idx == -1) {
- if (eep->get_options_count() == 1) {
- menus[p_platform]->get_popup()->hide();
- p_idx = 0;
- } else {
- return ERR_INVALID_PARAMETER;
- }
+ if (!EditorNode::get_singleton()->ensure_main_scene(true)) {
+ resume_id = p_id;
+ return OK;
}
+ Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(platform);
+ ERR_FAIL_COND_V(eep.is_null(), ERR_UNAVAILABLE);
+
Ref<EditorExportPreset> preset;
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
@@ -151,16 +122,18 @@ Error EditorRunNative::run_native(int p_idx, int p_platform) {
}
eep->clear_messages();
- Error err = eep->run(preset, p_idx, flags);
+ Error err = eep->run(preset, idx, flags);
result_dialog_log->clear();
if (eep->fill_log_messages(result_dialog_log, err)) {
- result_dialog->popup_centered_ratio(0.5);
+ if (eep->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_ERROR) {
+ result_dialog->popup_centered_ratio(0.5);
+ }
}
return err;
}
void EditorRunNative::resume_run_native() {
- run_native(resume_idx, resume_platform);
+ run_native(resume_id);
}
void EditorRunNative::_bind_methods() {
@@ -172,6 +145,13 @@ 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->set_icon(get_theme_icon(SNAME("PlayRemote"), SNAME("EditorIcons")));
+ remote_debug->set_tooltip_text(TTR("Remote Debug"));
+
+ add_child(remote_debug);
+
result_dialog = memnew(AcceptDialog);
result_dialog->set_title(TTR("Project Run"));
result_dialog_log = memnew(RichTextLabel);
@@ -182,6 +162,4 @@ EditorRunNative::EditorRunNative() {
result_dialog->hide();
set_process(true);
- resume_idx = 0;
- resume_platform = 0;
}
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index a87e27de97..2a5431e54b 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -42,18 +42,17 @@ class EditorRunNative : public HBoxContainer {
RichTextLabel *result_dialog_log = nullptr;
AcceptDialog *result_dialog = nullptr;
- HashMap<int, MenuButton *> menus;
+ MenuButton *remote_debug = nullptr;
bool first = true;
- int resume_idx;
- int resume_platform;
+ int resume_id = -1;
protected:
static void _bind_methods();
void _notification(int p_what);
public:
- Error run_native(int p_idx, int p_platform);
+ Error run_native(int p_id);
bool is_deploy_debug_remote_enabled() const;
void resume_run_native();
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 5985af21cd..f6054529f9 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -132,7 +132,7 @@ void EditorSettingsDialog::_notification(int p_what) {
} break;
case NOTIFICATION_READY: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr);
undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr);
undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_commit_notify_callback(_undo_redo_callback, this);
@@ -161,7 +161,7 @@ void EditorSettingsDialog::_notification(int p_what) {
void EditorSettingsDialog::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
const Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed()) {
@@ -232,7 +232,7 @@ void EditorSettingsDialog::_event_config_confirmed() {
void EditorSettingsDialog::_update_builtin_action(const String &p_name, const Array &p_events) {
Array old_input_array = EditorSettings::get_singleton()->get_builtin_action_overrides(p_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Built-in Action") + " '" + p_name + "'");
undo_redo->add_do_method(EditorSettings::get_singleton(), "mark_setting_changed", "builtin_action_overrides");
undo_redo->add_undo_method(EditorSettings::get_singleton(), "mark_setting_changed", "builtin_action_overrides");
@@ -248,7 +248,7 @@ void EditorSettingsDialog::_update_builtin_action(const String &p_name, const Ar
void EditorSettingsDialog::_update_shortcut_events(const String &p_path, const Array &p_events) {
Ref<Shortcut> current_sc = EditorSettings::get_singleton()->get_shortcut(p_path);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Shortcut") + " '" + p_path + "'");
undo_redo->add_do_method(current_sc.ptr(), "set_events", p_events);
undo_redo->add_undo_method(current_sc.ptr(), "set_events", current_sc->get_events());
@@ -793,7 +793,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
shortcuts->connect("item_activated", callable_mp(this, &EditorSettingsDialog::_shortcut_cell_double_clicked));
tab_shortcuts->add_child(shortcuts);
- shortcuts->set_drag_forwarding(this);
+ shortcuts->set_drag_forwarding_compat(this);
// Adding event dialog
shortcut_editor = memnew(InputEventConfigurationDialog);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index ea03b1c744..177266e366 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -253,6 +253,12 @@ void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) {
value_input_dirty = true;
set_process_internal(true);
} break;
+ case Key::ESCAPE: {
+ value_input_closed_frame = Engine::get_singleton()->get_frames_drawn();
+ if (value_input_popup) {
+ value_input_popup->hide();
+ }
+ } break;
default:
break;
}
@@ -479,10 +485,10 @@ void EditorSpinSlider::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && !value_input_just_closed) {
+ if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
_focus_entered();
}
- value_input_just_closed = false;
+ value_input_closed_frame = 0;
} break;
}
}
@@ -553,7 +559,7 @@ void EditorSpinSlider::_evaluate_input_text() {
//text_submitted signal
void EditorSpinSlider::_value_input_submitted(const String &p_text) {
- value_input_just_closed = true;
+ value_input_closed_frame = Engine::get_singleton()->get_frames_drawn();
if (value_input_popup) {
value_input_popup->hide();
}
@@ -562,7 +568,7 @@ void EditorSpinSlider::_value_input_submitted(const String &p_text) {
//modal_closed signal
void EditorSpinSlider::_value_input_closed() {
_evaluate_input_text();
- value_input_just_closed = true;
+ value_input_closed_frame = Engine::get_singleton()->get_frames_drawn();
}
//focus_exited signal
@@ -578,7 +584,7 @@ void EditorSpinSlider::_value_focus_exited() {
// -> TAB was pressed
// -> modal_close was not called
// -> need to close/hide manually
- if (!value_input_just_closed) { //value_input_just_closed should do the same
+ if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
if (value_input_popup) {
value_input_popup->hide();
}
@@ -672,6 +678,7 @@ void EditorSpinSlider::_ensure_input_popup() {
add_child(value_input_popup);
value_input = memnew(LineEdit);
+ value_input->set_focus_mode(FOCUS_CLICK);
value_input_popup->add_child(value_input);
value_input->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
value_input_popup->connect("hidden", callable_mp(this, &EditorSpinSlider::_value_input_closed));
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
index 99eb953166..a4d810b18b 100644
--- a/editor/editor_spin_slider.h
+++ b/editor/editor_spin_slider.h
@@ -65,7 +65,7 @@ class EditorSpinSlider : public Range {
Control *value_input_popup = nullptr;
LineEdit *value_input = nullptr;
- bool value_input_just_closed = false;
+ uint64_t value_input_closed_frame = 0;
bool value_input_dirty = false;
bool hide_slider = false;
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 96c8aa6fb5..7a880d2ab3 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -133,25 +133,26 @@ void EditorColorMap::create() {
add_conversion_color_pair("#0e71fc", "#0350bd"); // New Autotile
add_conversion_color_pair("#c6ced4", "#828f9b"); // New Atlas
- // Visual script
- add_conversion_color_pair("#41ecad", "#25e3a0"); // VisualScript variant
- add_conversion_color_pair("#6f91f0", "#6d8eeb"); // VisualScript bool
- add_conversion_color_pair("#5abbef", "#4fb2e9"); // VisualScript int
- add_conversion_color_pair("#35d4f4", "#27ccf0"); // VisualScript float
- add_conversion_color_pair("#4593ec", "#4690e7"); // VisualScript String
- add_conversion_color_pair("#ac73f1", "#ad76ee"); // VisualScript Vector2
- add_conversion_color_pair("#f1738f", "#ee758e"); // VisualScript Rect2
- add_conversion_color_pair("#de66f0", "#dc6aed"); // VisualScript Vector3
- add_conversion_color_pair("#b9ec41", "#96ce1a"); // VisualScript Transform2D
- add_conversion_color_pair("#f74949", "#f77070"); // VisualScript Plane
- add_conversion_color_pair("#ec418e", "#ec69a3"); // VisualScript Quat
- add_conversion_color_pair("#ee5677", "#ee7991"); // VisualScript AABB
- add_conversion_color_pair("#e1ec41", "#b2bb19"); // VisualScript Basis
- add_conversion_color_pair("#f68f45", "#f49047"); // VisualScript Transform
- add_conversion_color_pair("#417aec", "#6993ec"); // VisualScript NodePath
- add_conversion_color_pair("#41ec80", "#2ce573"); // VisualScript RID
- add_conversion_color_pair("#55f3e3", "#12d5c3"); // VisualScript Object
- add_conversion_color_pair("#54ed9e", "#57e99f"); // VisualScript Dictionary
+ // Variant types
+ add_conversion_color_pair("#41ecad", "#25e3a0"); // Variant
+ add_conversion_color_pair("#6f91f0", "#6d8eeb"); // bool
+ add_conversion_color_pair("#5abbef", "#4fb2e9"); // int
+ add_conversion_color_pair("#35d4f4", "#27ccf0"); // float
+ add_conversion_color_pair("#4593ec", "#4690e7"); // String
+ add_conversion_color_pair("#ac73f1", "#ad76ee"); // Vector2
+ add_conversion_color_pair("#f1738f", "#ee758e"); // Rect2
+ add_conversion_color_pair("#de66f0", "#dc6aed"); // Vector3
+ add_conversion_color_pair("#b9ec41", "#96ce1a"); // Transform2D
+ add_conversion_color_pair("#f74949", "#f77070"); // Plane
+ add_conversion_color_pair("#ec418e", "#ec69a3"); // Quaternion
+ add_conversion_color_pair("#ee5677", "#ee7991"); // AABB
+ add_conversion_color_pair("#e1ec41", "#b2bb19"); // Basis
+ add_conversion_color_pair("#f68f45", "#f49047"); // Transform3D
+ add_conversion_color_pair("#417aec", "#6993ec"); // NodePath
+ add_conversion_color_pair("#41ec80", "#2ce573"); // RID
+ add_conversion_color_pair("#55f3e3", "#12d5c3"); // Object
+ add_conversion_color_pair("#54ed9e", "#57e99f"); // Dictionary
+
// Visual shaders
add_conversion_color_pair("#77ce57", "#67c046"); // Vector funcs
add_conversion_color_pair("#ea686c", "#d95256"); // Vector transforms
@@ -752,6 +753,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Editor background
Color background_color_opaque = background_color;
background_color_opaque.a = 1.0;
+ theme->set_color("background", "Editor", background_color_opaque);
theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color_opaque, default_margin_size, default_margin_size, default_margin_size, default_margin_size));
// Focus
diff --git a/editor/editor_title_bar.cpp b/editor/editor_title_bar.cpp
index d756606029..0271bbd64a 100644
--- a/editor/editor_title_bar.cpp
+++ b/editor/editor_title_bar.cpp
@@ -37,7 +37,7 @@ void EditorTitleBar::input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && moving) {
- if ((mm->get_button_mask() & MouseButton::LEFT) == MouseButton::LEFT) {
+ if (mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
Window *w = Object::cast_to<Window>(get_viewport());
if (w) {
Point2 mouse = DisplayServer::get_singleton()->mouse_get_position();
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index dd5d68a08e..558423df78 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -90,11 +90,12 @@ void EditorToaster::_notification(int p_what) {
}
// Hide element if it is not visible anymore.
- if (modulate_fade.a <= 0) {
- if (element.key->is_visible()) {
- element.key->hide();
- needs_update = true;
- }
+ if (modulate_fade.a <= 0 && element.key->is_visible()) {
+ element.key->hide();
+ needs_update = true;
+ } else if (modulate_fade.a >= 0 && !element.key->is_visible()) {
+ element.key->show();
+ needs_update = true;
}
}
@@ -419,12 +420,21 @@ void EditorToaster::_popup_str(String p_message, Severity p_severity, String p_t
// Create a new message if needed.
if (control == nullptr) {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->add_theme_constant_override("separation", 0);
+
Label *label = memnew(Label);
+ hb->add_child(label);
- control = popup(label, p_severity, default_message_duration, p_tooltip);
+ Label *count_label = memnew(Label);
+ hb->add_child(count_label);
+
+ control = popup(hb, p_severity, default_message_duration, p_tooltip);
toasts[control].message = p_message;
toasts[control].tooltip = p_tooltip;
toasts[control].count = 1;
+ toasts[control].message_label = label;
+ toasts[control].message_count_label = count_label;
} else {
if (toasts[control].popped) {
toasts[control].count += 1;
@@ -441,14 +451,31 @@ void EditorToaster::_popup_str(String p_message, Severity p_severity, String p_t
main_button->queue_redraw();
}
- // Retrieve the label back then update the text.
- Label *label = Object::cast_to<Label>(control->get_child(0)->get_child(0));
- ERR_FAIL_COND(!label);
+ // Retrieve the label back, then update the text.
+ Label *message_label = toasts[control].message_label;
+ ERR_FAIL_COND(!message_label);
+ message_label->set_text(p_message);
+ message_label->set_text_overrun_behavior(TextServer::OVERRUN_NO_TRIMMING);
+ message_label->set_custom_minimum_size(Size2());
+
+ Size2i size = message_label->get_combined_minimum_size();
+ int limit_width = get_viewport_rect().size.x / 2; // Limit label size to half the viewport size.
+ if (size.x > limit_width) {
+ message_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
+ message_label->set_custom_minimum_size(Size2(limit_width, 0));
+ }
+
+ // Retrieve the count label back, then update the text.
+ Label *message_count_label = toasts[control].message_count_label;
if (toasts[control].count == 1) {
- label->set_text(p_message);
+ message_count_label->hide();
} else {
- label->set_text(vformat("%s (%d)", p_message, toasts[control].count));
+ message_count_label->set_text(vformat("(%d)", toasts[control].count));
+ message_count_label->show();
}
+
+ vbox_container->reset_size();
+
is_processing_error = false;
}
diff --git a/editor/editor_toaster.h b/editor/editor_toaster.h
index acd2a8fbf3..6b834f8288 100644
--- a/editor/editor_toaster.h
+++ b/editor/editor_toaster.h
@@ -79,6 +79,8 @@ private:
String message;
String tooltip;
int count = 0;
+ Label *message_label = nullptr;
+ Label *message_count_label = nullptr;
};
HashMap<Control *, Toast> toasts;
diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp
index 2ef52bd85a..f65f905b25 100644
--- a/editor/editor_undo_redo_manager.cpp
+++ b/editor/editor_undo_redo_manager.cpp
@@ -39,6 +39,8 @@
#include "editor/editor_node.h"
#include "scene/main/node.h"
+EditorUndoRedoManager *EditorUndoRedoManager::singleton = nullptr;
+
EditorUndoRedoManager::History &EditorUndoRedoManager::get_or_create_history(int p_idx) {
if (!history_map.has(p_idx)) {
History history;
@@ -503,6 +505,16 @@ void EditorUndoRedoManager::_bind_methods() {
BIND_ENUM_CONSTANT(INVALID_HISTORY);
}
+EditorUndoRedoManager *EditorUndoRedoManager::get_singleton() {
+ return singleton;
+}
+
+EditorUndoRedoManager::EditorUndoRedoManager() {
+ if (!singleton) {
+ singleton = this;
+ }
+}
+
EditorUndoRedoManager::~EditorUndoRedoManager() {
for (const KeyValue<int, History> &E : history_map) {
discard_history(E.key, false);
diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h
index 960d17d410..10daeae807 100644
--- a/editor/editor_undo_redo_manager.h
+++ b/editor/editor_undo_redo_manager.h
@@ -32,11 +32,13 @@
#define EDITOR_UNDO_REDO_MANAGER_H
#include "core/object/class_db.h"
-#include "core/object/ref_counted.h"
+#include "core/object/object.h"
#include "core/object/undo_redo.h"
-class EditorUndoRedoManager : public RefCounted {
- GDCLASS(EditorUndoRedoManager, RefCounted);
+class EditorUndoRedoManager : public Object {
+ GDCLASS(EditorUndoRedoManager, Object);
+
+ static EditorUndoRedoManager *singleton;
public:
enum SpecialHistory {
@@ -132,6 +134,9 @@ public:
int get_current_action_history_id();
void discard_history(int p_idx, bool p_erase_from_map = true);
+
+ static EditorUndoRedoManager *get_singleton();
+ EditorUndoRedoManager();
~EditorUndoRedoManager();
};
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index 3a99c4e264..274fe34c52 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -168,8 +168,8 @@ void EventListenerLineEdit::clear_event() {
}
}
-void EventListenerLineEdit::set_allowed_input_types(int input_types) {
- allowed_input_types = input_types;
+void EventListenerLineEdit::set_allowed_input_types(int p_type_masks) {
+ allowed_input_types = p_type_masks;
}
int EventListenerLineEdit::get_allowed_input_types() const {
diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h
index 0679733b6a..aa0cc91d47 100644
--- a/editor/event_listener_line_edit.h
+++ b/editor/event_listener_line_edit.h
@@ -67,7 +67,7 @@ public:
Ref<InputEvent> get_event() const;
void clear_event();
- void set_allowed_input_types(int input_types);
+ void set_allowed_input_types(int p_type_masks);
int get_allowed_input_types() const;
void grab_focus();
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index f48cabf207..cc96107f97 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -170,6 +170,12 @@ void EditorExport::_notification(int p_what) {
case NOTIFICATION_PROCESS: {
update_export_presets();
} break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ for (int i = 0; i < export_platforms.size(); i++) {
+ export_platforms.write[i]->cleanup();
+ }
+ } break;
}
}
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 028071ff62..cacd181ad8 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -444,8 +444,10 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset>
result.insert("template");
if (p_debug) {
+ result.insert("debug");
result.insert("template_debug");
} else {
+ result.insert("release");
result.insert("template_release");
}
@@ -784,6 +786,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
HashSet<String> paths;
Vector<String> path_remaps;
+ paths.insert(ProjectSettings::get_singleton()->get_project_data_path().path_join("global_script_class_cache.cfg"));
if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) {
//find stuff
_export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths);
@@ -1320,6 +1323,121 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj
return OK;
}
+void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
+ String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);
+
+ Ref<DirAccess> da = DirAccess::open(dir);
+ da->list_dir_begin();
+ String f = da->get_next();
+ while (!f.is_empty()) {
+ if (f == "." || f == "..") {
+ f = da->get_next();
+ continue;
+ }
+ if (da->is_link(f)) {
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
+
+ zip_fileinfo zipfi;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
+ zipfi.dosDate = 0;
+ // 0120000: symbolic link type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = 0120644;
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.path_join(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ String target = da->read_link(f);
+ zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
+ zipCloseFileInZip(p_zip);
+ } else if (da->current_is_dir()) {
+ zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
+ } else {
+ bool _is_executable = is_executable(dir.path_join(f));
+
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
+
+ zip_fileinfo zipfi;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
+ zipfi.dosDate = 0;
+ // 0100000: regular file type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = (_is_executable ? 0100755 : 0100644);
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.path_join(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
+ if (fa.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
+ return;
+ }
+ const int bufsize = 16384;
+ uint8_t buf[bufsize];
+
+ while (true) {
+ uint64_t got = fa->get_buffer(buf, bufsize);
+ if (got == 0) {
+ break;
+ }
+ zipWriteInFileInZip(p_zip, buf, got);
+ }
+
+ zipCloseFileInZip(p_zip);
+ }
+ f = da->get_next();
+ }
+ da->list_dir_end();
+}
+
Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
EditorProgress ep("savepack", TTR("Packing"), 102, true);
@@ -1640,5 +1758,123 @@ bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, S
return valid;
}
+Error EditorExportPlatform::ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out, int p_port_fwd) const {
+ String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh");
+ if (ssh_path.is_empty()) {
+ ssh_path = "ssh";
+ }
+
+ List<String> args;
+ args.push_back("-p");
+ args.push_back(p_port);
+ for (const String &E : p_ssh_args) {
+ args.push_back(E);
+ }
+ if (p_port_fwd > 0) {
+ args.push_back("-R");
+ args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd));
+ }
+ args.push_back(p_host);
+ args.push_back(p_cmd_args);
+
+ String out;
+ int exit_code = -1;
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data());
+ for (const String &arg : args) {
+ OS::get_singleton()->print(" %s", arg.utf8().get_data());
+ }
+ OS::get_singleton()->print("\n");
+ }
+
+ Error err = OS::get_singleton()->execute(ssh_path, args, &out, &exit_code, true);
+ if (out.is_empty()) {
+ print_verbose(vformat("Exit code: %d", exit_code));
+ } else {
+ print_verbose(vformat("Exit code: %d, Output: %s", exit_code, out.replace("\r\n", "\n")));
+ }
+ if (r_out) {
+ *r_out = out.replace("\r\n", "\n").get_slice("\n", 0);
+ }
+ if (err != OK) {
+ return err;
+ } else if (exit_code != 0) {
+ if (!out.is_empty()) {
+ print_line(out);
+ }
+ return FAILED;
+ }
+ return OK;
+}
+
+Error EditorExportPlatform::ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid, int p_port_fwd) const {
+ String ssh_path = EditorSettings::get_singleton()->get("export/ssh/ssh");
+ if (ssh_path.is_empty()) {
+ ssh_path = "ssh";
+ }
+
+ List<String> args;
+ args.push_back("-p");
+ args.push_back(p_port);
+ for (const String &E : p_ssh_args) {
+ args.push_back(E);
+ }
+ if (p_port_fwd > 0) {
+ args.push_back("-R");
+ args.push_back(vformat("%d:localhost:%d", p_port_fwd, p_port_fwd));
+ }
+ args.push_back(p_host);
+ args.push_back(p_cmd_args);
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Executing: %s", ssh_path.utf8().get_data());
+ for (const String &arg : args) {
+ OS::get_singleton()->print(" %s", arg.utf8().get_data());
+ }
+ OS::get_singleton()->print("\n");
+ }
+
+ return OS::get_singleton()->create_process(ssh_path, args, r_pid);
+}
+
+Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const {
+ String scp_path = EditorSettings::get_singleton()->get("export/ssh/scp");
+ if (scp_path.is_empty()) {
+ scp_path = "scp";
+ }
+
+ List<String> args;
+ args.push_back("-P");
+ args.push_back(p_port);
+ for (const String &E : p_scp_args) {
+ args.push_back(E);
+ }
+ args.push_back(p_src_file);
+ args.push_back(vformat("%s:%s", p_host, p_dst_file));
+
+ String out;
+ int exit_code = -1;
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Executing: %s", scp_path.utf8().get_data());
+ for (const String &arg : args) {
+ OS::get_singleton()->print(" %s", arg.utf8().get_data());
+ }
+ OS::get_singleton()->print("\n");
+ }
+
+ Error err = OS::get_singleton()->execute(scp_path, args, &out, &exit_code, true);
+ if (err != OK) {
+ return err;
+ } else if (exit_code != 0) {
+ if (!out.is_empty()) {
+ print_line(out);
+ }
+ return FAILED;
+ }
+ return OK;
+}
+
EditorExportPlatform::EditorExportPlatform() {
}
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index d3d8a8f186..1fb35759ac 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -35,6 +35,7 @@ class EditorFileSystemDirectory;
struct EditorProgress;
#include "core/io/dir_access.h"
+#include "core/io/zip_io.h"
#include "editor_export_preset.h"
#include "editor_export_shared_object.h"
#include "scene/gui/rich_text_label.h"
@@ -92,7 +93,6 @@ private:
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
- void gen_debug_flags(Vector<String> &r_flags, int p_flags);
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
@@ -126,6 +126,13 @@ protected:
bool exists_export_template(String template_file_name, String *err) const;
String find_export_template(String template_file_name, String *err = nullptr) const;
void gen_export_flags(Vector<String> &r_flags, int p_flags);
+ void gen_debug_flags(Vector<String> &r_flags, int p_flags);
+
+ virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
+
+ Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const;
+ Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const;
+ Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const = 0;
@@ -215,6 +222,7 @@ public:
DEBUG_FLAG_VIEW_NAVIGATION = 16,
};
+ virtual void cleanup() {}
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
diff --git a/editor/export/editor_export_platform_pc.h b/editor/export/editor_export_platform_pc.h
index c71a62ee5c..8c24f2fc68 100644
--- a/editor/export/editor_export_platform_pc.h
+++ b/editor/export/editor_export_platform_pc.h
@@ -62,7 +62,6 @@ public:
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; };
virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
- void set_extension(const String &p_extension, const String &p_feature_key = "default");
void set_name(const String &p_name);
void set_os_name(const String &p_name);
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index f34ebfa541..784dbc116a 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -34,6 +34,7 @@
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "editor/editor_paths.h"
+#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "scene/resources/resource_format_text.h"
@@ -226,4 +227,8 @@ void EditorExportPlugin::_bind_methods() {
}
EditorExportPlugin::EditorExportPlugin() {
+ GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false);
+
+ EDITOR_DEF("export/ssh/ssh", "");
+ EDITOR_DEF("export/ssh/scp", "");
}
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 3e06633cc8..7136b4ceb6 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -480,7 +480,7 @@ void ProjectExportDialog::_enc_filters_changed(const String &p_filters) {
}
void ProjectExportDialog::_open_key_help_link() {
- OS::get_singleton()->shell_open(vformat("%s/development/compiling/compiling_with_script_encryption_key.html", VERSION_DOCS_URL));
+ OS::get_singleton()->shell_open(vformat("%s/contributing/development/compiling/compiling_with_script_encryption_key.html", VERSION_DOCS_URL));
}
void ProjectExportDialog::_enc_pck_changed(bool p_pressed) {
@@ -1023,7 +1023,7 @@ ProjectExportDialog::ProjectExportDialog() {
mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
presets = memnew(ItemList);
// TODO: Must reimplement drag forwarding.
- //presets->set_drag_forwarding(this);
+ //presets->set_drag_forwarding_compat(this);
mc->add_child(presets);
presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset));
duplicate_preset = memnew(Button);
diff --git a/editor/fbx_importer_manager.cpp b/editor/fbx_importer_manager.cpp
index dfea2e706a..2a005034a5 100644
--- a/editor/fbx_importer_manager.cpp
+++ b/editor/fbx_importer_manager.cpp
@@ -144,12 +144,8 @@ FBXImporterManager::FBXImporterManager() {
browse_dialog = memnew(EditorFileDialog);
browse_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
browse_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
-#if defined(X11_ENABLED)
- browse_dialog->add_filter("FBX2glTF-linux-x86_64");
-#elif defined(OSX_ENABLED)
- browse_dialog->add_filter("FBX2glTF-macos-x86_64");
-#elif defined(WINDOWS_ENABLED)
- browse_dialog->add_filter("FBX2glTF-windows-x86_64");
+#ifdef WINDOWS_ENABLED
+ browse_dialog->add_filter("*.exe");
#endif
browse_dialog->connect("file_selected", callable_mp(this, &FBXImporterManager::_select_file));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 5537e55f20..9db352c3fd 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2051,7 +2051,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
- ScriptEditor::get_singleton()->open_text_file_create_dialog(fpath);
+ String dir = ProjectSettings::get_singleton()->globalize_path(fpath);
+ ScriptEditor::get_singleton()->open_text_file_create_dialog(dir);
} break;
}
}
@@ -2599,7 +2600,8 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
String fpath = p_paths[0];
String item_text = fpath.ends_with("/") ? TTR("Open in File Manager") : TTR("Show in File Manager");
- p_popup->add_icon_item(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), item_text, FILE_SHOW_IN_EXPLORER);
+ p_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
+ p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), item_text);
path = fpath;
}
}
@@ -2644,7 +2646,7 @@ void FileSystemDock::_tree_empty_click(const Vector2 &p_pos, MouseButton p_butto
tree_popup->add_icon_item(get_theme_icon(SNAME("Object"), SNAME("EditorIcons")), TTR("New Resource..."), FILE_NEW_RESOURCE);
tree_popup->add_icon_item(get_theme_icon(SNAME("TextFile"), SNAME("EditorIcons")), TTR("New TextFile..."), FILE_NEW_TEXTFILE);
tree_popup->add_separator();
- tree_popup->add_icon_item(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), TTR("Open in File Manager"), FILE_SHOW_IN_EXPLORER);
+ tree_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
tree_popup->set_position(tree->get_screen_position() + p_pos);
tree_popup->reset_size();
@@ -2704,7 +2706,7 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton
file_list_popup->add_icon_item(get_theme_icon(SNAME("Object"), SNAME("EditorIcons")), TTR("New Resource..."), FILE_NEW_RESOURCE);
file_list_popup->add_icon_item(get_theme_icon(SNAME("TextFile"), SNAME("EditorIcons")), TTR("New TextFile..."), FILE_NEW_TEXTFILE);
file_list_popup->add_separator();
- file_list_popup->add_icon_item(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), TTR("Open in File Manager"), FILE_SHOW_IN_EXPLORER);
+ file_list_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER);
file_list_popup->set_position(files->get_screen_position() + p_pos);
file_list_popup->reset_size();
file_list_popup->popup();
@@ -2816,6 +2818,8 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) {
_tree_rmb_option(FILE_REMOVE);
} else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) {
_tree_rmb_option(FILE_RENAME);
+ } else if (ED_IS_SHORTCUT("filesystem_dock/show_in_explorer", p_event)) {
+ _tree_rmb_option(FILE_SHOW_IN_EXPLORER);
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
focus_on_filter();
} else {
@@ -2874,6 +2878,8 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) {
_file_list_rmb_option(FILE_REMOVE);
} else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) {
_file_list_rmb_option(FILE_RENAME);
+ } else if (ED_IS_SHORTCUT("filesystem_dock/show_in_explorer", p_event)) {
+ _file_list_rmb_option(FILE_SHOW_IN_EXPLORER);
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
focus_on_filter();
} else {
@@ -3045,6 +3051,7 @@ FileSystemDock::FileSystemDock() {
ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), Key::KEY_DELETE);
ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2);
ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER);
+ ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"));
VBoxContainer *top_vbc = memnew(VBoxContainer);
add_child(top_vbc);
@@ -3116,7 +3123,7 @@ FileSystemDock::FileSystemDock() {
tree = memnew(Tree);
tree->set_hide_root(true);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
tree->set_allow_rmb_select(true);
tree->set_select_mode(Tree::SELECT_MULTI);
tree->set_custom_minimum_size(Size2(0, 15 * EDSCALE));
@@ -3154,7 +3161,7 @@ FileSystemDock::FileSystemDock() {
files = memnew(ItemList);
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_select_mode(ItemList::SELECT_MULTI);
- files->set_drag_forwarding(this);
+ files->set_drag_forwarding_compat(this);
files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked));
files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input));
files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected));
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index 3ee7d5ddd8..7f0417ea29 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -130,7 +130,7 @@ void GroupDialog::_add_pressed() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add to Group"));
while (selected) {
@@ -160,7 +160,7 @@ void GroupDialog::_removed_pressed() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove from Group"));
while (selected) {
@@ -248,7 +248,7 @@ void GroupDialog::_group_renamed() {
renamed_group->set_text(0, name); // Spaces trimmed.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Group"));
List<Node *> nodes;
@@ -325,7 +325,7 @@ void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id,
case DELETE_GROUP: {
String name = ti->get_text(0);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Group"));
List<Node *> nodes;
@@ -597,7 +597,7 @@ void GroupsEditor::_add_group(const String &p_group) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add to Group"));
undo_redo->add_do_method(node, "add_to_group", name, true);
@@ -652,7 +652,7 @@ void GroupsEditor::_group_renamed() {
ti->set_text(0, name); // Spaces trimmed.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Group"));
undo_redo->add_do_method(node, "remove_from_group", selected_group);
@@ -688,7 +688,7 @@ void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseBu
switch (p_id) {
case DELETE_GROUP: {
const String name = ti->get_text(0);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove from Group"));
undo_redo->add_do_method(node, "remove_from_group", name);
diff --git a/editor/history_dock.cpp b/editor/history_dock.cpp
index f9fe1093b8..41ca519400 100644
--- a/editor/history_dock.cpp
+++ b/editor/history_dock.cpp
@@ -221,7 +221,7 @@ void HistoryDock::_notification(int p_notification) {
HistoryDock::HistoryDock() {
set_name("History");
- ur_manager = EditorNode::get_undo_redo();
+ ur_manager = EditorUndoRedoManager::get_singleton();
ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed));
ur_manager->connect("version_changed", callable_mp(this, &HistoryDock::on_version_changed));
diff --git a/editor/history_dock.h b/editor/history_dock.h
index 81d1d02b79..d507f19b07 100644
--- a/editor/history_dock.h
+++ b/editor/history_dock.h
@@ -40,7 +40,7 @@ class EditorUndoRedoManager;
class HistoryDock : public VBoxContainer {
GDCLASS(HistoryDock, VBoxContainer);
- Ref<EditorUndoRedoManager> ur_manager;
+ EditorUndoRedoManager *ur_manager;
ItemList *action_list = nullptr;
CheckBox *current_scene_checkbox = nullptr;
diff --git a/editor/icons/PlayRemote.svg b/editor/icons/PlayRemote.svg
new file mode 100644
index 0000000000..ea2195294f
--- /dev/null
+++ b/editor/icons/PlayRemote.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path d="m12.998 2.548-9.996.005a2.006 2.006 0 0 0-2.006 2v4.61c0 1.107.898 2.003 2.006 2.003h3.996v.9h-1.68v.381H3.001v2h2.315v.38h5.366v-.38H13v-2.004h-2.315v-.378H9.004v-.9h3.994a2.006 2.006 0 0 0 2.006-2.003v-4.61c0-1.106-.9-2.003-2.006-2.003zm-7.496 1.31 5 3-5 3z" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/editor/icons/README.md b/editor/icons/README.md
index 3159565180..d191000985 100644
--- a/editor/icons/README.md
+++ b/editor/icons/README.md
@@ -3,5 +3,5 @@
This folder contains all the icons used by Godot editor (except for platform
icons which are located in their respective platform folder).
-See [Editor icons](https://docs.godotengine.org/en/latest/development/editor/creating_icons.html)
+See [Editor icons](https://docs.godotengine.org/en/latest/contributing/development/editor/creating_icons.html)
in the documentation for details on creating icons for the Godot editor.
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index a67da0c2b1..ad6d41e10c 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -217,7 +217,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
- String name;
+ Vector<Color> colors;
+ const String default_name = "Mesh";
+ String name = default_name;
HashMap<String, HashMap<String, Ref<StandardMaterial3D>>> material_map;
@@ -249,6 +251,19 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
vtx.y = v[2].to_float() * scale_mesh.y + offset_mesh.y;
vtx.z = v[3].to_float() * scale_mesh.z + offset_mesh.z;
vertices.push_back(vtx);
+ //vertex color
+ if (v.size() == 7) {
+ while (colors.size() < vertices.size() - 1) {
+ colors.push_back(Color(1.0, 1.0, 1.0));
+ }
+ Color c;
+ c.r = v[4].to_float();
+ c.g = v[5].to_float();
+ c.b = v[6].to_float();
+ colors.push_back(c);
+ } else if (!colors.is_empty()) {
+ colors.push_back(Color(1.0, 1.0, 1.0));
+ }
} else if (l.begins_with("vt ")) {
//uv
Vector<String> v = l.split(" ", false);
@@ -317,6 +332,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
ERR_FAIL_INDEX_V(vtx, vertices.size(), ERR_FILE_CORRUPT);
Vector3 vertex = vertices[vtx];
+ if (!colors.is_empty()) {
+ surf_tool->set_color(colors[vtx]);
+ }
if (!smoothing) {
smooth_group++;
}
@@ -356,7 +374,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
print_verbose("OBJ: Current material " + current_material + " has " + itos(material_map.has(current_material_library) && material_map[current_material_library].has(current_material)));
if (material_map.has(current_material_library) && material_map[current_material_library].has(current_material)) {
- surf_tool->set_material(material_map[current_material_library][current_material]);
+ Ref<StandardMaterial3D> &material = material_map[current_material_library][current_material];
+ if (!colors.is_empty()) {
+ material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ }
+ surf_tool->set_material(material);
}
mesh = surf_tool->commit(mesh, mesh_flags);
@@ -374,9 +396,12 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
if (l.begins_with("o ") || f->eof_reached()) {
if (!p_single_mesh) {
- mesh->set_name(name);
- r_meshes.push_back(mesh);
- mesh.instantiate();
+ if (mesh->get_surface_count() > 0) {
+ mesh->set_name(name);
+ r_meshes.push_back(mesh);
+ mesh.instantiate();
+ }
+ name = default_name;
current_group = "";
current_material = "";
}
@@ -439,6 +464,7 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, co
for (const Ref<Mesh> &m : meshes) {
Ref<ImporterMesh> mesh;
mesh.instantiate();
+ mesh->set_name(m->get_name());
for (int i = 0; i < m->get_surface_count(); i++) {
mesh->add_surface(m->surface_get_primitive_type(i), m->surface_get_arrays(i), Array(), Dictionary(), m->surface_get_material(i));
}
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index 7187e85df0..ba48fc9029 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -91,6 +91,7 @@ static String _include_function(const String &p_path, void *userpointer) {
Error ResourceImporterShaderFile::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
/* STEP 1, Read shader code */
+ ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders.");
Error err;
Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err);
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index a9b43ba5f1..60415ff926 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -703,15 +703,17 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
}
MeshData &md = mesh_map[p_id];
- if (p_from != mesh_tree) {
- md.mesh_node->uncollapse_tree();
- md.mesh_node->select(0);
- mesh_tree->ensure_cursor_is_visible();
- }
- if (p_from != scene_tree) {
- md.scene_node->uncollapse_tree();
- md.scene_node->select(0);
- scene_tree->ensure_cursor_is_visible();
+ if (md.mesh_node != nullptr) {
+ if (p_from != mesh_tree) {
+ md.mesh_node->uncollapse_tree();
+ md.mesh_node->select(0);
+ mesh_tree->ensure_cursor_is_visible();
+ }
+ if (p_from != scene_tree) {
+ md.scene_node->uncollapse_tree();
+ md.scene_node->select(0);
+ scene_tree->ensure_cursor_is_visible();
+ }
}
mesh_preview->set_mesh(md.mesh);
@@ -849,7 +851,7 @@ void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
zoom = &md.cam_zoom;
}
Ref<InputEventMouseMotion> mm = p_input;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
(*rot_x) -= mm->get_relative().y * 0.01 * EDSCALE;
(*rot_y) -= mm->get_relative().x * 0.01 * EDSCALE;
(*rot_x) = CLAMP((*rot_x), -Math_PI / 2, Math_PI / 2);
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index b137f6f668..08d4bfff4a 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -515,6 +515,7 @@ Ref<InputEvent> InputEventConfigurationDialog::get_event() const {
void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) {
allowed_input_types = p_type_masks;
+ event_listener->set_allowed_input_types(p_type_masks);
}
InputEventConfigurationDialog::InputEventConfigurationDialog() {
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index fbe01c26b0..b28373e308 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/filesystem_dock.h"
#include "editor/plugins/script_editor_plugin.h"
InspectorDock *InspectorDock::singleton = nullptr;
@@ -86,6 +87,11 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
case RESOURCE_EDIT_CLIPBOARD: {
_paste_resource();
} break;
+ case RESOURCE_SHOW_IN_FILESYSTEM: {
+ Ref<Resource> current_res = _get_current_resource();
+ ERR_FAIL_COND(current_res.is_null());
+ FileSystemDock::get_singleton()->navigate_to_path(current_res->get_path());
+ } break;
case OBJECT_REQUEST_HELP: {
if (current) {
@@ -180,8 +186,8 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
}
}
- int history_id = editor_data->get_undo_redo()->get_history_for_object(current).id;
- editor_data->get_undo_redo()->clear_history(true, history_id);
+ int history_id = EditorUndoRedoManager::get_singleton()->get_history_for_object(current).id;
+ EditorUndoRedoManager::get_singleton()->clear_history(true, history_id);
EditorNode::get_singleton()->get_editor_plugins_over()->edit(nullptr);
EditorNode::get_singleton()->get_editor_plugins_over()->edit(current);
@@ -257,12 +263,8 @@ void InspectorDock::_resource_file_selected(String p_file) {
}
void InspectorDock::_save_resource(bool save_as) {
- ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
- Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
-
- Ref<Resource> current_res = Ref<Resource>(Object::cast_to<Resource>(current_obj));
+ Ref<Resource> current_res = _get_current_resource();
+ ERR_FAIL_COND(current_res.is_null());
if (save_as) {
EditorNode::get_singleton()->save_resource_as(current_res);
@@ -272,24 +274,15 @@ void InspectorDock::_save_resource(bool save_as) {
}
void InspectorDock::_unref_resource() {
- ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
- Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
-
- Ref<Resource> current_res = Ref<Resource>(Object::cast_to<Resource>(current_obj));
+ Ref<Resource> current_res = _get_current_resource();
+ ERR_FAIL_COND(current_res.is_null());
current_res->set_path("");
EditorNode::get_singleton()->edit_current();
}
void InspectorDock::_copy_resource() {
- ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
- Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj));
-
- Ref<Resource> current_res = Ref<Resource>(Object::cast_to<Resource>(current_obj));
-
+ Ref<Resource> current_res = _get_current_resource();
+ ERR_FAIL_COND(current_res.is_null());
EditorSettings::get_singleton()->set_resource_clipboard(current_res);
}
@@ -304,6 +297,16 @@ void InspectorDock::_prepare_resource_extra_popup() {
Ref<Resource> r = EditorSettings::get_singleton()->get_resource_clipboard();
PopupMenu *popup = resource_extra_button->get_popup();
popup->set_item_disabled(popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), r.is_null());
+
+ Ref<Resource> current_res = _get_current_resource();
+ ERR_FAIL_COND(current_res.is_null());
+ popup->set_item_disabled(popup->get_item_index(RESOURCE_SHOW_IN_FILESYSTEM), current_res->is_built_in());
+}
+
+Ref<Resource> InspectorDock::_get_current_resource() const {
+ ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
+ Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
+ return Ref<Resource>(Object::cast_to<Resource>(current_obj));
}
void InspectorDock::_prepare_history() {
@@ -662,6 +665,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
resource_extra_button->get_popup()->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY);
resource_extra_button->get_popup()->set_item_disabled(1, true);
resource_extra_button->get_popup()->add_separator();
+ resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/show_in_filesystem", TTR("Show in FileSystem")), RESOURCE_SHOW_IN_FILESYSTEM);
resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Resource Built-In")), RESOURCE_MAKE_BUILT_IN);
resource_extra_button->get_popup()->set_item_disabled(3, true);
resource_extra_button->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_menu_option));
diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h
index fb2e1d53b1..c2c1b55fb1 100644
--- a/editor/inspector_dock.h
+++ b/editor/inspector_dock.h
@@ -51,6 +51,7 @@ class InspectorDock : public VBoxContainer {
RESOURCE_LOAD,
RESOURCE_SAVE,
RESOURCE_SAVE_AS,
+ RESOURCE_SHOW_IN_FILESYSTEM,
RESOURCE_MAKE_BUILT_IN,
RESOURCE_COPY,
RESOURCE_EDIT_CLIPBOARD,
@@ -119,6 +120,7 @@ class InspectorDock : public VBoxContainer {
void _copy_resource();
void _paste_resource();
void _prepare_resource_extra_popup();
+ Ref<Resource> _get_current_resource() const;
void _info_pressed();
void _resource_created();
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 6fa980a8e5..5503645930 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -33,7 +33,6 @@
#include "core/config/project_settings.h"
#include "core/string/translation.h"
#include "editor/editor_file_dialog.h"
-#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
@@ -81,7 +80,7 @@ void LocalizationEditor::_translation_add(const PackedStringArray &p_paths) {
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add %d Translations"), p_paths.size()));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", GLOBAL_GET("internationalization/locale/translations"));
@@ -112,7 +111,7 @@ void LocalizationEditor::_translation_delete(Object *p_item, int p_column, int p
translations.remove_at(idx);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Translation"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", GLOBAL_GET("internationalization/locale/translations"));
@@ -143,7 +142,7 @@ void LocalizationEditor::_translation_res_add(const PackedStringArray &p_paths)
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Path(s)"), p_paths.size()));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", prev);
@@ -175,7 +174,7 @@ void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_
}
remaps[key] = r;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Remap(s)"), p_paths.size()));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps"));
@@ -239,7 +238,7 @@ void LocalizationEditor::_translation_res_option_changed() {
updating_translations = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Resource Remap Language"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps"));
@@ -273,7 +272,7 @@ void LocalizationEditor::_translation_res_delete(Object *p_item, int p_column, i
remaps.erase(key);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Resource Remap"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps"));
@@ -313,7 +312,7 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co
r.remove_at(idx);
remaps[key] = r;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Resource Remap Option"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps"));
@@ -332,7 +331,7 @@ void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) {
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add %d file(s) for POT generation"), p_paths.size()));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", GLOBAL_GET("internationalization/locale/translations_pot_files"));
@@ -359,7 +358,7 @@ void LocalizationEditor::_pot_delete(Object *p_item, int p_column, int p_button,
pot_translations.remove_at(idx);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove file from POT generation"));
undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations);
undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", GLOBAL_GET("internationalization/locale/translations_pot_files"));
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index dbbcd57742..2a55ac949f 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -55,7 +55,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
node_path_target = es->get_node(p_value);
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(vformat(TTR("Set %s on %d nodes"), name, get_node_count()), UndoRedo::MERGE_ENDS);
for (const NodePath &E : nodes) {
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index 3a83d9b9c2..7c23e19564 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -91,7 +91,7 @@ void AbstractPolygon2DEditor::_set_polygon(int p_idx, const Variant &p_polygon)
void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
Node2D *node = _get_node();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(node, "set_polygon", p_polygon);
undo_redo->add_undo_method(node, "set_polygon", p_previous);
}
@@ -101,7 +101,7 @@ Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const {
}
void AbstractPolygon2DEditor::_commit_action() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
@@ -205,7 +205,7 @@ void AbstractPolygon2DEditor::_wip_close() {
if (_is_line()) {
_set_polygon(0, wip);
} else if (wip.size() >= (_is_line() ? 2 : 3)) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Polygon"));
_action_add_polygon(wip);
if (_has_uv()) {
@@ -257,7 +257,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
return false;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<InputEventMouseButton> mb = p_event;
if (!_has_resource()) {
@@ -410,7 +410,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
if (mm.is_valid()) {
Vector2 gpoint = mm->get_position();
- if (edited_point.valid() && (wip_active || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE)) {
+ if (edited_point.valid() && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
//Move the point in a single axis. Should only work when editing a polygon and while holding shift.
@@ -619,7 +619,7 @@ void AbstractPolygon2DEditor::_bind_methods() {
}
void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Vector<Vector2> vertices = _get_polygon(p_vertex.polygon);
if (vertices.size() > (_is_line() ? 2 : 3)) {
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index ec3f2b0f63..33aebe5883 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -157,7 +157,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
@@ -200,7 +200,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
_update_edited_point_pos();
}
- if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
float blend_pos = mm->get_position().x / blend_space_draw->get_size().x;
blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
blend_pos += blend_space->get_min_space();
@@ -351,7 +351,7 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace1D Config"));
undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value());
undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
@@ -375,7 +375,7 @@ void AnimationNodeBlendSpace1DEditor::_labels_changed(String) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace1D Labels"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text());
undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label());
@@ -431,7 +431,7 @@ void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
@@ -450,7 +450,7 @@ void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) {
anim->set_animation(animations_to_add[p_index]);
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Animation Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
@@ -524,7 +524,7 @@ void AnimationNodeBlendSpace1DEditor::_erase_selected() {
if (selected_point != -1) {
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove BlendSpace1D Point"));
undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
@@ -544,7 +544,7 @@ void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move BlendSpace1D Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value());
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 00e7f28bb5..7f8137f8d5 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -228,7 +228,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Triangle"));
undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]);
undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count());
@@ -254,7 +254,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
if (!read_only) {
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
@@ -308,7 +308,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->queue_redraw();
}
- if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size());
blend_pos.y = 1.0 - blend_pos.y;
blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
@@ -362,7 +362,7 @@ void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
@@ -381,7 +381,7 @@ void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) {
anim->set_animation(animations_to_add[p_index]);
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Animation Point"));
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
@@ -676,7 +676,7 @@ void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace2D Config"));
undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value()));
undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
@@ -702,7 +702,7 @@ void AnimationNodeBlendSpace2DEditor::_labels_changed(String) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change BlendSpace2D Labels"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text());
undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label());
@@ -715,7 +715,7 @@ void AnimationNodeBlendSpace2DEditor::_labels_changed(String) {
}
void AnimationNodeBlendSpace2DEditor::_erase_selected() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (selected_point != -1) {
updating = true;
undo_redo->create_action(TTR("Remove BlendSpace2D Point"));
@@ -778,7 +778,7 @@ void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
return;
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node Point"));
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value()));
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
@@ -856,7 +856,7 @@ void AnimationNodeBlendSpace2DEditor::_open_editor() {
}
void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Toggle Auto Triangles"));
undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed());
undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles());
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 0ebc780604..14e3cb4b97 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -103,7 +103,7 @@ void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_propert
return;
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Parameter Changed:") + " " + String(p_property), UndoRedo::MERGE_ENDS);
undo_redo->add_do_property(tree, p_property, p_value);
undo_redo->add_undo_property(tree, p_property, tree->get(p_property));
@@ -363,7 +363,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
name = base_name + " " + itos(base);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node to BlendTree"));
undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE);
undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name);
@@ -425,9 +425,14 @@ void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, in
}
}
+void AnimationNodeBlendTreeEditor::_popup_hide() {
+ to_node = "";
+ to_slot = -1;
+}
+
void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) {
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Node Moved"));
undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE);
undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE);
@@ -449,7 +454,7 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Nodes Connected"));
undo_redo->add_do_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from);
undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index);
@@ -466,7 +471,7 @@ void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from,
graph->disconnect_node(p_from, p_from_index, p_to, p_to_index);
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Nodes Disconnected"));
undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index);
undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from);
@@ -482,7 +487,7 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options,
Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node);
ERR_FAIL_COND(!anim.is_valid());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Animation"));
undo_redo->add_do_method(anim.ptr(), "set_animation", option);
undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation());
@@ -496,7 +501,7 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Node"));
undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which);
undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which), blend_tree.ptr()->get_node_position(p_which));
@@ -541,7 +546,7 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Node(s)"));
for (const StringName &F : to_erase) {
@@ -575,7 +580,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) {
void AnimationNodeBlendTreeEditor::_filter_toggled() {
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Toggle Filter On/Off"));
undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed());
undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled());
@@ -593,7 +598,7 @@ void AnimationNodeBlendTreeEditor::_filter_edited() {
bool filtered = edited->is_checked(0);
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Filter"));
undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered);
undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path));
@@ -976,7 +981,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
String base_path = AnimationTreeEditor::get_singleton()->get_base_path();
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Node Renamed"));
undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name);
undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name);
@@ -1096,6 +1101,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
add_node->set_text(TTR("Add Node..."));
graph->get_zoom_hbox()->move_child(add_node, 0);
add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node));
+ add_node->get_popup()->connect("popup_hide", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_hide), CONNECT_DEFERRED);
add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu).bind(false));
add_node->set_disabled(read_only);
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
index b471d47df6..afb3394238 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.h
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -121,6 +121,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _popup_request(const Vector2 &p_position);
void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
+ void _popup_hide();
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp
index 9decbef51b..d66982f31b 100644
--- a/editor/plugins/animation_library_editor.cpp
+++ b/editor/plugins/animation_library_editor.cpp
@@ -94,7 +94,7 @@ void AnimationLibraryEditor::_add_library_validate(const String &p_name) {
void AnimationLibraryEditor::_add_library_confirm() {
if (adding_animation) {
String anim_name = add_library_name->get_text();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library);
ERR_FAIL_COND(!al.is_valid());
@@ -111,7 +111,7 @@ void AnimationLibraryEditor::_add_library_confirm() {
} else {
String lib_name = add_library_name->get_text();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<AnimationLibrary> al;
al.instantiate();
@@ -211,7 +211,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
ald->add_animation(animation_name, animation);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name));
undo_redo->add_do_method(player, "remove_animation_library", lib_name);
undo_redo->add_do_method(player, "add_animation_library", lib_name, ald);
@@ -280,7 +280,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
Ref<Animation> animd = anim->duplicate();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name));
undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd);
@@ -328,7 +328,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
name = p_path.get_file().get_basename() + " " + itos(attempt);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), name));
undo_redo->add_do_method(player, "add_animation_library", name, al);
@@ -366,7 +366,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
name = p_path.get_file().get_basename() + " " + itos(attempt);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Load Animation into Library: %s"), name));
undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
@@ -382,7 +382,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
EditorNode::get_singleton()->save_resource_in_path(al, p_path);
if (al->get_path() != prev_path) { // Save successful.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library));
undo_redo->add_do_method(al.ptr(), "set_path", al->get_path());
@@ -403,7 +403,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
String prev_path = anim->get_path();
EditorNode::get_singleton()->save_resource_in_path(anim, p_path);
if (anim->get_path() != prev_path) { // Save successful.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation));
undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path());
@@ -421,7 +421,7 @@ void AnimationLibraryEditor::_item_renamed() {
String text = ti->get_text(0);
String old_text = ti->get_metadata(0);
bool restore_text = false;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) {
restore_text = true;
@@ -535,7 +535,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
name = base_name + " (" + itos(attempt) + ")";
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name));
undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
@@ -561,7 +561,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
file_dialog_library = lib_name;
} break;
case LIB_BUTTON_DELETE: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name));
undo_redo->add_do_method(player, "remove_animation_library", lib_name);
undo_redo->add_undo_method(player, "add_animation_library", lib_name, al);
@@ -602,7 +602,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
} break;
case ANIM_BUTTON_DELETE: {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name));
undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index ec3eb1a494..caee7514d0 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -94,6 +94,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
// Need the last frame after it stopped.
frame->set_value(player->get_current_animation_position());
track_editor->set_anim_pos(player->get_current_animation_position());
+ stop->set_icon(stop_icon);
}
last_active = player->is_playing();
@@ -119,8 +120,15 @@ void AnimationPlayerEditor::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
- autoplay->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")));
+ stop_icon = get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ pause_icon = get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"));
+ if (player && player->is_playing()) {
+ stop->set_icon(pause_icon);
+ } else {
+ stop->set_icon(stop_icon);
+ }
+ autoplay->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")));
play->set_icon(get_theme_icon(SNAME("PlayStart"), SNAME("EditorIcons")));
play_from->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons")));
play_bw->set_icon(get_theme_icon(SNAME("PlayStartBackwards"), SNAME("EditorIcons")));
@@ -137,7 +145,6 @@ void AnimationPlayerEditor::_notification(int p_what) {
autoplay_reset_img->blit_rect(reset_img, Rect2i(Point2i(), icon_size), Point2i(icon_size.x, 0));
autoplay_reset_icon = ImageTexture::create_from_image(autoplay_reset_img);
}
- stop->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
onion_toggle->set_icon(get_theme_icon(SNAME("Onion"), SNAME("EditorIcons")));
onion_skinning->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
@@ -170,7 +177,7 @@ void AnimationPlayerEditor::_autoplay_pressed() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
String current = animation->get_item_text(animation->get_selected());
if (player->get_autoplay() == current) {
//unset
@@ -204,7 +211,7 @@ void AnimationPlayerEditor::_play_pressed() {
}
//unstop
- stop->set_pressed(false);
+ stop->set_icon(pause_icon);
}
void AnimationPlayerEditor::_play_from_pressed() {
@@ -221,7 +228,7 @@ void AnimationPlayerEditor::_play_from_pressed() {
}
//unstop
- stop->set_pressed(false);
+ stop->set_icon(pause_icon);
}
String AnimationPlayerEditor::_get_current() const {
@@ -242,7 +249,7 @@ void AnimationPlayerEditor::_play_bw_pressed() {
}
//unstop
- stop->set_pressed(false);
+ stop->set_icon(pause_icon);
}
void AnimationPlayerEditor::_play_bw_from_pressed() {
@@ -259,7 +266,7 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
}
//unstop
- stop->set_pressed(false);
+ stop->set_icon(pause_icon);
}
void AnimationPlayerEditor::_stop_pressed() {
@@ -267,9 +274,16 @@ void AnimationPlayerEditor::_stop_pressed() {
return;
}
- player->stop(false);
- play->set_pressed(false);
- stop->set_pressed(true);
+ if (player->is_playing()) {
+ player->pause();
+ } else {
+ String current = _get_current();
+ player->stop();
+ player->set_assigned_animation(current);
+ frame->set_value(0);
+ track_editor->set_anim_pos(0);
+ }
+ stop->set_icon(stop_icon);
}
void AnimationPlayerEditor::_animation_selected(int p_which) {
@@ -388,7 +402,7 @@ void AnimationPlayerEditor::_animation_remove_confirmed() {
if (current.contains("/")) {
current = current.get_slice("/", 1);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Animation"));
if (player->get_autoplay() == current) {
undo_redo->add_do_method(player, "set_autoplay", "");
@@ -464,7 +478,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (name_dialog_op) {
case TOOL_RENAME_ANIM: {
String current = animation->get_item_text(animation->get_selected());
@@ -599,7 +613,7 @@ void AnimationPlayerEditor::_blend_editor_next_changed(const int p_idx) {
String current = animation->get_item_text(animation->get_selected());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Blend Next Changed"));
undo_redo->add_do_method(player, "animation_set_next", current, blend_editor.next->get_item_text(p_idx));
undo_redo->add_undo_method(player, "animation_set_next", current, player->animation_get_next(current));
@@ -686,7 +700,7 @@ void AnimationPlayerEditor::_blend_edited() {
float blend_time = selected->get_range(1);
float prev_blend_time = player->get_blend_time(current, to);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Blend Time"));
undo_redo->add_do_method(player, "set_blend_time", current, to, blend_time);
undo_redo->add_undo_method(player, "set_blend_time", current, to, prev_blend_time);
@@ -798,12 +812,9 @@ void AnimationPlayerEditor::_update_animation() {
updating = true;
if (player->is_playing()) {
- play->set_pressed(true);
- stop->set_pressed(false);
-
+ stop->set_icon(pause_icon);
} else {
- play->set_pressed(false);
- stop->set_pressed(true);
+ stop->set_icon(stop_icon);
}
scale->set_text(String::num(player->get_speed_scale(), 2));
@@ -1155,7 +1166,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool
player->seek_delta(pos, pos - cpos);
} else {
- player->stop(true);
+ player->stop();
player->seek(pos, true);
}
}
@@ -1663,7 +1674,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
stop = memnew(Button);
stop->set_flat(true);
- stop->set_toggle_mode(true);
hb->add_child(stop);
stop->set_tooltip_text(TTR("Stop animation playback. (S)"));
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index 6c690c812c..327200506f 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -101,6 +101,8 @@ class AnimationPlayerEditor : public VBoxContainer {
OptionButton *library = nullptr;
Label *name_title = nullptr;
+ Ref<Texture2D> stop_icon;
+ Ref<Texture2D> pause_icon;
Ref<Texture2D> autoplay_icon;
Ref<Texture2D> reset_icon;
Ref<ImageTexture> autoplay_reset_icon;
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 4ea2f0c561..a675495429 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -246,7 +246,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
Ref<AnimationNode> an = state_machine->get_node(selected_node);
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node"));
for (int i = 0; i < node_rects.size(); i++) {
@@ -337,7 +337,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
Ref<InputEventMouseMotion> mm = p_event;
// Pan window
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
+ if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::MIDDLE)) {
h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
}
@@ -543,7 +543,7 @@ void AnimationNodeStateMachineEditor::_group_selected_nodes() {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Group");
// Move selected nodes to the new state machine
@@ -658,7 +658,7 @@ void AnimationNodeStateMachineEditor::_ungroup_selected_nodes() {
Vector<TransitionUR> transitions_ur;
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Ungroup");
// Move all child nodes to current state machine
@@ -935,7 +935,7 @@ void AnimationNodeStateMachineEditor::_stop_connecting() {
void AnimationNodeStateMachineEditor::_delete_selected() {
TreeItem *item = delete_tree->get_next_selected(nullptr);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
while (item) {
if (!updating) {
updating = true;
@@ -963,7 +963,7 @@ void AnimationNodeStateMachineEditor::_delete_all() {
selected_multi_transition = TransitionLine();
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action("Transition(s) Removed");
_erase_selected(true);
for (int i = 0; i < multi_transitions.size(); i++) {
@@ -1043,7 +1043,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node and Transition"));
undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos);
undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
@@ -1070,7 +1070,7 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node and Transition"));
undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos);
undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name);
@@ -1100,7 +1100,7 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action
tr->set_advance_mode(auto_advance->is_pressed() ? AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_AUTO : AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_ENABLED);
tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (!p_nested_action) {
updating = true;
undo_redo->create_action(TTR("Add Transition"));
@@ -1778,7 +1778,7 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
}
updating = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Node Renamed"));
undo_redo->add_do_method(state_machine.ptr(), "rename_node", prev_name, name);
undo_redo->add_undo_method(state_machine.ptr(), "rename_node", name, prev_name);
@@ -1813,7 +1813,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
if (!p_nested_action) {
updating = true;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Node Removed"));
for (int i = 0; i < node_rects.size(); i++) {
@@ -1882,7 +1882,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
if (!p_nested_action) {
updating = true;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Transition Removed"));
undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to);
undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr);
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index c2dac83416..eab5eb0404 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -293,7 +293,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
preview = memnew(TextureRect);
previews_vbox->add_child(preview);
- preview->set_ignore_texture_size(true);
+ preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
preview->set_custom_minimum_size(Size2(640 * EDSCALE, 345 * EDSCALE));
preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -705,6 +705,12 @@ const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = {
"testing",
};
+const char *EditorAssetLibrary::support_text[SUPPORT_MAX] = {
+ TTRC("Official"),
+ TTRC("Community"),
+ TTRC("Testing"),
+};
+
void EditorAssetLibrary::_select_author(int p_id) {
// Open author window.
}
@@ -1242,15 +1248,28 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
library_vb->add_child(asset_bottom_page);
if (result.is_empty()) {
+ String support_list;
+ for (int i = 0; i < SUPPORT_MAX; i++) {
+ if (support->get_popup()->is_item_checked(i)) {
+ if (!support_list.is_empty()) {
+ support_list += ", ";
+ }
+ support_list += TTRGET(support_text[i]);
+ }
+ }
+ if (support_list.is_empty()) {
+ support_list = "-";
+ }
+
if (!filter->get_text().is_empty()) {
library_info->set_text(
- vformat(TTR("No results for \"%s\"."), filter->get_text()));
+ vformat(TTR("No results for \"%s\" for support level(s): %s."), filter->get_text(), support_list));
} else {
// No results, even though the user didn't search for anything specific.
// This is typically because the version number changed recently
// and no assets compatible with the new version have been published yet.
library_info->set_text(
- vformat(TTR("No results compatible with %s %s."), String(VERSION_SHORT_NAME).capitalize(), String(VERSION_BRANCH)));
+ vformat(TTR("No results compatible with %s %s for support level(s): %s.\nCheck the enabled support levels using the 'Support' button in the top-right corner."), String(VERSION_SHORT_NAME).capitalize(), String(VERSION_BRANCH), support_list));
}
library_info->show();
} else {
@@ -1510,9 +1529,9 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
search_hb2->add_child(support);
support->set_text(TTR("Support"));
support->get_popup()->set_hide_on_checkable_item_selection(false);
- support->get_popup()->add_check_item(TTR("Official"), SUPPORT_OFFICIAL);
- support->get_popup()->add_check_item(TTR("Community"), SUPPORT_COMMUNITY);
- support->get_popup()->add_check_item(TTR("Testing"), SUPPORT_TESTING);
+ support->get_popup()->add_check_item(TTRGET(support_text[SUPPORT_OFFICIAL]), SUPPORT_OFFICIAL);
+ support->get_popup()->add_check_item(TTRGET(support_text[SUPPORT_COMMUNITY]), SUPPORT_COMMUNITY);
+ support->get_popup()->add_check_item(TTRGET(support_text[SUPPORT_TESTING]), SUPPORT_TESTING);
support->get_popup()->set_item_checked(SUPPORT_OFFICIAL, true);
support->get_popup()->set_item_checked(SUPPORT_COMMUNITY, true);
support->get_popup()->connect("id_pressed", callable_mp(this, &EditorAssetLibrary::_support_toggled));
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 0667f474da..8c74da0e2a 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -234,6 +234,7 @@ class EditorAssetLibrary : public PanelContainer {
static const char *sort_key[SORT_MAX];
static const char *sort_text[SORT_MAX];
static const char *support_key[SUPPORT_MAX];
+ static const char *support_text[SUPPORT_MAX];
///MainListing
diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
index acd3271f97..bb3b8a124e 100644
--- a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
@@ -44,8 +44,8 @@ void AudioStreamRandomizerEditorPlugin::make_visible(bool p_visible) {
}
void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
- Ref<EditorUndoRedoManager> undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(undo_redo_man.is_null());
+ EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo_man);
AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited);
if (!randomizer) {
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index dfcf9256c4..c913a9b0ab 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -316,7 +316,7 @@ void BoneMapper::create_editor() {
profile_texture = memnew(TextureRect);
profile_texture->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
- profile_texture->set_ignore_texture_size(true);
+ profile_texture->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
profile_texture->set_h_size_flags(Control::SIZE_FILL);
profile_texture->set_v_size_flags(Control::SIZE_FILL);
bone_mapper_field->add_child(profile_texture);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 24e4aeb358..4c14755b03 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -868,7 +868,7 @@ void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_ite
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(action_name);
for (CanvasItem *ci : modified_canvas_items) {
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci);
@@ -933,7 +933,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Node(s) to Position"));
for (Node *node : nodes_to_move) {
CanvasItem *ci = Object::cast_to<CanvasItem>(node);
@@ -1022,7 +1022,7 @@ void CanvasItemEditor::_on_grid_menu_id_pressed(int p_id) {
}
bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_event) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<InputEventMouseButton> b = p_event;
Ref<InputEventMouseMotion> m = p_event;
@@ -1322,7 +1322,7 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
// Drag the pivot (in pivot mode / with V key)
if (drag_type == DRAG_NONE) {
if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
- (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask() == Key::NONE)) {
+ (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask().is_empty())) {
List<CanvasItem *> selection = _get_edited_canvas_items();
// Filters the selection with nodes that allow setting the pivot
@@ -1850,7 +1850,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
// Drag resize handles
if (drag_type == DRAG_NONE) {
- if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_ctrl_pressed()) || tool == TOOL_SCALE)) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *ci = selection[0];
@@ -1897,7 +1897,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform;
bool uniform = m->is_shift_pressed();
- bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+ bool is_ctrl = m->is_ctrl_pressed();
Point2 drag_from_local = simple_xform.xform(drag_from);
Point2 drag_to_local = simple_xform.xform(drag_to);
@@ -1989,7 +1989,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_NONE) {
//Start moving the nodes
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
- if ((b->is_alt_pressed() && !b->is_ctrl_pressed()) || tool == TOOL_MOVE) {
+ if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) {
List<CanvasItem *> selection = _get_edited_canvas_items();
drag_selection.clear();
@@ -3418,7 +3418,7 @@ void CanvasItemEditor::_draw_selection() {
}
// Draw the move handles
- bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+ bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL);
bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
if (tool == TOOL_MOVE && show_transformation_gizmos) {
if (_is_node_movable(ci)) {
@@ -4263,7 +4263,7 @@ void CanvasItemEditor::_update_override_camera_button(bool p_game_running) {
}
void CanvasItemEditor::_popup_callback(int p_op) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
last_option = MenuOption(p_op);
switch (p_op) {
case SHOW_ORIGIN: {
@@ -4370,8 +4370,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
show_rulers = !show_rulers;
int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
view_menu->get_popup()->set_item_checked(idx, show_rulers);
- _update_scrollbars();
- viewport->queue_redraw();
+ update_viewport();
} break;
case SHOW_GUIDES: {
show_guides = !show_guides;
@@ -4683,25 +4682,18 @@ void CanvasItemEditor::_focus_selection(int p_op) {
} else {
rect = rect.merge(canvas_item_rect);
}
- };
-
- if (p_op == VIEW_CENTER_TO_SELECTION) {
- center = rect.get_center();
- Vector2 offset = viewport->get_size() / 2 - EditorNode::get_singleton()->get_scene_root()->get_global_canvas_transform().xform(center);
- view_offset -= (offset / zoom).round();
- update_viewport();
-
- } else { // VIEW_FRAME_TO_SELECTION
+ }
- if (rect.size.x > CMP_EPSILON && rect.size.y > CMP_EPSILON) {
- real_t scale_x = viewport->get_size().x / rect.size.x;
- real_t scale_y = viewport->get_size().y / rect.size.y;
- zoom = scale_x < scale_y ? scale_x : scale_y;
- zoom *= 0.90;
- viewport->queue_redraw();
- zoom_widget->set_zoom(zoom);
- call_deferred(SNAME("_popup_callback"), VIEW_CENTER_TO_SELECTION);
- }
+ if (p_op == VIEW_FRAME_TO_SELECTION && rect.size.x > CMP_EPSILON && rect.size.y > CMP_EPSILON) {
+ real_t scale_x = viewport->get_size().x / rect.size.x;
+ real_t scale_y = viewport->get_size().y / rect.size.y;
+ zoom = scale_x < scale_y ? scale_x : scale_y;
+ zoom *= 0.90;
+ zoom_widget->set_zoom(zoom);
+ viewport->queue_redraw(); // Redraw to update the global canvas transform after zoom changes.
+ call_deferred(SNAME("center_at"), rect.get_center()); // Defer because the updated transform is needed.
+ } else {
+ center_at(rect.get_center());
}
}
@@ -4715,6 +4707,7 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
+ ClassDB::bind_method(D_METHOD("center_at", "position"), &CanvasItemEditor::center_at);
ClassDB::bind_method("_set_owner_for_node_and_children", &CanvasItemEditor::_set_owner_for_node_and_children);
@@ -4961,6 +4954,12 @@ void CanvasItemEditor::focus_selection() {
_focus_selection(VIEW_CENTER_TO_SELECTION);
}
+void CanvasItemEditor::center_at(const Point2 &p_pos) {
+ Vector2 offset = viewport->get_size() / 2 - EditorNode::get_singleton()->get_scene_root()->get_global_canvas_transform().xform(p_pos);
+ view_offset -= (offset / zoom).round();
+ update_viewport();
+}
+
CanvasItemEditor::CanvasItemEditor() {
zoom = 1.0 / MAX(1, EDSCALE);
view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH);
@@ -5571,7 +5570,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
String name = path.get_file().get_basename();
child->set_name(Node::adjust_name_casing(name));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<Texture2D> texture = ResourceCache::get_ref(path);
if (parent) {
@@ -5650,7 +5649,7 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene);
undo_redo->add_do_reference(instantiated_scene);
@@ -5691,7 +5690,7 @@ void CanvasItemEditorViewport::_perform_drop_data() {
Vector<String> error_files;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Node"));
for (int i = 0; i < selected_files.size(); i++) {
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 8f02f02c32..f4fcc8a3d2 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -550,6 +550,7 @@ public:
void edit(CanvasItem *p_canvas_item);
void focus_selection();
+ void center_at(const Point2 &p_pos);
EditorSelection *editor_selection = nullptr;
diff --git a/editor/plugins/cast_2d_editor_plugin.cpp b/editor/plugins/cast_2d_editor_plugin.cpp
index 078198e64b..723082c293 100644
--- a/editor/plugins/cast_2d_editor_plugin.cpp
+++ b/editor/plugins/cast_2d_editor_plugin.cpp
@@ -77,7 +77,7 @@ bool Cast2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
return false;
}
} else if (pressed) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set target_position"));
undo_redo->add_do_property(node, "target_position", target_position);
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index bed4658976..c2d5885e43 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -219,7 +219,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
}
void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Handle"));
switch (shape_type) {
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
index 0fff16bf24..470b90aa7f 100644
--- a/editor/plugins/control_editor_plugin.cpp
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -721,12 +721,13 @@ void ControlEditorToolbar::_anchors_preset_selected(int p_preset) {
LayoutPreset preset = (LayoutPreset)p_preset;
List<Node *> selection = editor_selection->get_selected_node_list();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Anchors, Offsets, Grow Direction"));
for (Node *E : selection) {
Control *control = Object::cast_to<Control>(E);
if (control) {
+ undo_redo->add_do_property(control, "layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS);
undo_redo->add_do_property(control, "anchors_preset", preset);
undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
}
@@ -741,7 +742,7 @@ void ControlEditorToolbar::_anchors_preset_selected(int p_preset) {
void ControlEditorToolbar::_anchors_to_current_ratio() {
List<Node *> selection = editor_selection->get_selected_node_list();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Anchors, Offsets (Keep Ratio)"));
for (Node *E : selection) {
@@ -792,7 +793,7 @@ void ControlEditorToolbar::_anchor_mode_toggled(bool p_status) {
void ControlEditorToolbar::_container_flags_selected(int p_flags, bool p_vertical) {
List<Node *> selection = editor_selection->get_selected_node_list();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_vertical) {
undo_redo->create_action(TTR("Change Vertical Size Flags"));
} else {
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index b0bcda946e..20710bac19 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -140,7 +140,7 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) {
_dragging = false;
if (_has_undo_data) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent"));
undo_redo->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
undo_redo->add_undo_method(*_curve_ref, "_set_data", _undo_data);
@@ -301,7 +301,7 @@ void CurveEditor::on_preset_item_selected(int preset_id) {
break;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Load Curve Preset"));
undo_redo->add_do_method(&curve, "_set_data", curve.get_data());
undo_redo->add_undo_method(&curve, "_set_data", previous_data);
@@ -433,7 +433,7 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const {
void CurveEditor::add_point(Vector2 pos) {
ERR_FAIL_COND(_curve_ref.is_null());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Curve Point"));
Vector2 point_pos = get_world_pos(pos);
@@ -455,7 +455,7 @@ void CurveEditor::add_point(Vector2 pos) {
void CurveEditor::remove_point(int index) {
ERR_FAIL_COND(_curve_ref.is_null());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Curve Point"));
Curve::Point p = _curve_ref->get_point(index);
@@ -477,7 +477,7 @@ void CurveEditor::remove_point(int index) {
void CurveEditor::toggle_linear(TangentIndex tangent) {
ERR_FAIL_COND(_curve_ref.is_null());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Toggle Curve Linear Tangent"));
if (tangent == TANGENT_NONE) {
@@ -621,8 +621,8 @@ struct CanvasItemPlotCurve {
color2(p_color2) {}
void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
- // FIXME: Using a line width greater than 1 breaks curve rendering
- ci.draw_line(pos0, pos1, in_definition ? color1 : color2, 1);
+ // FIXME: Using a quad line breaks curve rendering.
+ ci.draw_line(pos0, pos1, in_definition ? color1 : color2, -1);
}
};
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 1a51224198..04b2a9337e 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -113,7 +113,7 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
cpu_particles->set_process_mode(particles->get_process_mode());
cpu_particles->set_z_index(particles->get_z_index());
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
@@ -161,7 +161,7 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() {
particles->set_emitting(false);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Generate Visibility Rect"));
undo_redo->add_do_method(particles, "set_visibility_rect", rect);
undo_redo->add_undo_method(particles, "set_visibility_rect", particles->get_visibility_rect());
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 19ac553708..65f66c2661 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -274,7 +274,7 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
cpu_particles->set_visible(node->is_visible());
cpu_particles->set_process_mode(node->get_process_mode());
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles3D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
@@ -324,7 +324,7 @@ void GPUParticles3DEditor::_generate_aabb() {
node->set_emitting(false);
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Generate Visibility AABB"));
ur->add_do_method(node, "set_visibility_aabb", rect);
ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());
diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp
index 68aafd6fa8..3676c2c222 100644
--- a/editor/plugins/gradient_editor.cpp
+++ b/editor/plugins/gradient_editor.cpp
@@ -99,7 +99,7 @@ void GradientEditor::_gradient_changed() {
void GradientEditor::_ramp_changed() {
editing = true;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp
index 703a44403d..7bd159a5b8 100644
--- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp
+++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp
@@ -55,7 +55,7 @@ void GradientTexture2DEditorRect::_update_fill_position() {
String property_name = handle == HANDLE_FILL_FROM ? "fill_from" : "fill_to";
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Set %s"), property_name), UndoRedo::MERGE_ENDS);
undo_redo->add_do_property(texture.ptr(), property_name, percent);
undo_redo->add_undo_property(texture.ptr(), property_name, handle == HANDLE_FILL_FROM ? texture->get_fill_from() : texture->get_fill_to());
@@ -178,7 +178,7 @@ void GradientTexture2DEditorRect::_notification(int p_what) {
GradientTexture2DEditorRect::GradientTexture2DEditorRect() {
checkerboard = memnew(TextureRect);
checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE);
- checkerboard->set_ignore_texture_size(true);
+ checkerboard->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
checkerboard->set_draw_behind_parent(true);
add_child(checkerboard, false, INTERNAL_MODE_FRONT);
@@ -188,7 +188,7 @@ GradientTexture2DEditorRect::GradientTexture2DEditorRect() {
///////////////////////
void GradientTexture2DEditor::_reverse_button_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Swap GradientTexture2D Fill Points"));
undo_redo->add_do_property(texture.ptr(), "fill_from", texture->get_fill_to());
undo_redo->add_do_property(texture.ptr(), "fill_to", texture->get_fill_from());
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp
index bee1206b90..429add4540 100644
--- a/editor/plugins/light_occluder_2d_editor_plugin.cpp
+++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -84,7 +84,7 @@ void LightOccluder2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) co
void LightOccluder2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
Ref<OccluderPolygon2D> occluder = _ensure_occluder();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(occluder.ptr(), "set_polygon", p_polygon);
undo_redo->add_undo_method(occluder.ptr(), "set_polygon", p_previous);
}
@@ -98,7 +98,7 @@ void LightOccluder2DEditor::_create_resource() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Occluder Polygon"));
undo_redo->add_do_method(node, "set_occluder_polygon", Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D)));
undo_redo->add_undo_method(node, "set_occluder_polygon", Variant(Ref<RefCounted>()));
diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp
index f2c487dd14..0185617c36 100644
--- a/editor/plugins/line_2d_editor_plugin.cpp
+++ b/editor/plugins/line_2d_editor_plugin.cpp
@@ -55,7 +55,7 @@ void Line2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) const {
void Line2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
Node2D *_node = _get_node();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(_node, "set_points", p_polygon);
undo_redo->add_undo_method(_node, "set_points", p_previous);
}
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index e66c5214a9..36c143ca8d 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -44,7 +44,7 @@ void MaterialEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
rot.x -= mm->get_relative().y * 0.01;
rot.y -= mm->get_relative().x * 0.01;
@@ -291,8 +291,8 @@ void EditorInspectorPluginMaterial::parse_begin(Object *p_object) {
}
void EditorInspectorPluginMaterial::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
- Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(!undo_redo.is_valid());
+ EditorUndoRedoManager *undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo);
// For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot,
// set the respective metallic or roughness factor to 1.0 as a convenience feature
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index a9cbec00b5..ee555ae8d4 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -39,7 +39,7 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
rot_x -= mm->get_relative().y * 0.01;
rot_y -= mm->get_relative().x * 0.01;
if (rot_x < -Math_PI / 2) {
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
index 6cc551d367..35b7367393 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
@@ -65,7 +65,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
switch (p_option) {
case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
List<Node *> selection = editor_selection->get_selected_node_list();
@@ -152,7 +152,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
Node *owner = get_tree()->get_edited_scene_root();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Trimesh Static Shape"));
@@ -182,7 +182,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
err_dialog->popup_centered();
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (simplify) {
ur->create_action(TTR("Create Simplified Convex Shape"));
@@ -222,7 +222,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
err_dialog->popup_centered();
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Multiple Convex Shapes"));
@@ -259,7 +259,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
Node *owner = get_tree()->get_edited_scene_root();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Navigation Mesh"));
ur->add_do_method(node, "add_child", nmi, true);
@@ -275,7 +275,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
outline_dialog->popup_centered(Vector2(200, 90));
} break;
case MENU_OPTION_CREATE_DEBUG_TANGENTS: {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Debug Tangents"));
MeshInstance3D *tangents = node->create_debug_tangents_node();
@@ -334,7 +334,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Unwrap UV2"));
ur->add_do_method(node, "set_mesh", unwrapped_mesh);
@@ -493,7 +493,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
mi->set_mesh(mesho);
Node *owner = get_tree()->get_edited_scene_root();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Outline"));
diff --git a/editor/plugins/navigation_link_2d_editor_plugin.cpp b/editor/plugins/navigation_link_2d_editor_plugin.cpp
index 53f9ff019f..21a1d839f0 100644
--- a/editor/plugins/navigation_link_2d_editor_plugin.cpp
+++ b/editor/plugins/navigation_link_2d_editor_plugin.cpp
@@ -85,7 +85,7 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
end_grabbed = false;
}
} else {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (start_grabbed) {
undo_redo->create_action(TTR("Set start_location"));
undo_redo->add_do_method(node, "set_start_location", node->get_start_location());
diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp
index cbc225a7ff..957a520d8a 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.cpp
+++ b/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -76,7 +76,7 @@ void NavigationPolygonEditor::_set_polygon(int p_idx, const Variant &p_polygon)
void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) {
Ref<NavigationPolygon> navpoly = _ensure_navpoly();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "add_outline", p_polygon);
undo_redo->add_undo_method(navpoly.ptr(), "remove_outline", navpoly->get_outline_count());
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
@@ -85,7 +85,7 @@ void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) {
void NavigationPolygonEditor::_action_remove_polygon(int p_idx) {
Ref<NavigationPolygon> navpoly = _ensure_navpoly();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "remove_outline", p_idx);
undo_redo->add_undo_method(navpoly.ptr(), "add_outline_at_index", navpoly->get_outline(p_idx), p_idx);
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
@@ -94,7 +94,7 @@ void NavigationPolygonEditor::_action_remove_polygon(int p_idx) {
void NavigationPolygonEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
Ref<NavigationPolygon> navpoly = _ensure_navpoly();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(navpoly.ptr(), "set_outline", p_idx, p_polygon);
undo_redo->add_undo_method(navpoly.ptr(), "set_outline", p_idx, p_previous);
undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines");
@@ -110,7 +110,7 @@ void NavigationPolygonEditor::_create_resource() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Navigation Polygon"));
undo_redo->add_do_method(node, "set_navigation_polygon", Ref<NavigationPolygon>(memnew(NavigationPolygon)));
undo_redo->add_undo_method(node, "set_navigation_polygon", Variant(Ref<RefCounted>()));
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 74db91a309..bb71c27bff 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -1337,13 +1337,13 @@ void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i
light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore);
} else if (p_id == 0) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Light Radius"));
ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE));
ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore);
ur->commit_action();
} else if (p_id == 1) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Light Radius"));
ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE));
ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore);
@@ -1562,7 +1562,7 @@ void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gi
player->set_emission_angle(p_restore);
} else {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle"));
ur->add_do_method(player, "set_emission_angle", player->get_emission_angle());
ur->add_undo_method(player, "set_emission_angle", p_restore);
@@ -1823,7 +1823,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_
if (p_cancel) {
camera->set("fov", p_restore);
} else {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Camera FOV"));
ur->add_do_property(camera, "fov", camera->get_fov());
ur->add_undo_property(camera, "fov", p_restore);
@@ -1834,7 +1834,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_
if (p_cancel) {
camera->set("size", p_restore);
} else {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Camera Size"));
ur->add_do_property(camera, "size", camera->get_size());
ur->add_undo_property(camera, "size", p_restore);
@@ -2160,7 +2160,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(so.ptr(), "set_radius", so->get_radius());
ur->add_undo_method(so.ptr(), "set_radius", p_restore);
@@ -2174,7 +2174,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(bo.ptr(), "set_size", bo->get_size());
ur->add_undo_method(bo.ptr(), "set_size", p_restore);
@@ -2188,7 +2188,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(qo.ptr(), "set_size", qo->get_size());
ur->add_undo_method(qo.ptr(), "set_size", p_restore);
@@ -2902,7 +2902,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Notifier AABB"));
ur->add_do_method(notifier, "set_aabb", notifier->get_aabb());
ur->add_undo_method(notifier, "set_aabb", p_restore);
@@ -3093,7 +3093,7 @@ void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo,
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Particles AABB"));
ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb());
ur->add_undo_method(particles, "set_visibility_aabb", p_restore);
@@ -3259,7 +3259,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Radius"));
ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
ur->add_undo_method(sn, "set_radius", p_restore);
@@ -3272,7 +3272,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Extents"));
ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
ur->add_undo_method(sn, "set_extents", p_restore);
@@ -3531,7 +3531,7 @@ void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo,
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Probe Extents"));
ur->add_do_method(probe, "set_extents", probe->get_extents());
ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset());
@@ -3683,7 +3683,7 @@ void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id,
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Decal Extents"));
ur->add_do_method(decal, "set_extents", decal->get_extents());
ur->add_undo_method(decal, "set_extents", restore);
@@ -3823,7 +3823,7 @@ void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Probe Extents"));
ur->add_do_method(probe, "set_extents", probe->get_extents());
ur->add_undo_method(probe, "set_extents", restore);
@@ -4438,7 +4438,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
@@ -4452,7 +4452,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(ss.ptr(), "set_size", ss->get_size());
ur->add_undo_method(ss.ptr(), "set_size", p_restore);
@@ -4469,7 +4469,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Capsule Shape Radius"));
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
@@ -4494,7 +4494,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Cylinder Shape Radius"));
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
@@ -4519,7 +4519,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Separation Ray Shape Length"));
ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
ur->add_undo_method(ss.ptr(), "set_length", p_restore);
@@ -5184,7 +5184,7 @@ void NavigationLink3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Start Location"));
ur->add_do_method(link, "set_start_location", link->get_start_location());
@@ -5946,7 +5946,7 @@ void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Fog Volume Extents"));
ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
ur->add_undo_method(sn, "set_extents", p_restore);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 1147d5ff8c..9198ebeca8 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1911,7 +1911,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
- if (spatial_editor->get_current_hover_gizmo().is_null() && (m->get_button_mask() & MouseButton::MASK_LEFT) == MouseButton::NONE && !_edit.gizmo.is_valid()) {
+ if (spatial_editor->get_current_hover_gizmo().is_null() && !m->get_button_mask().has_flag(MouseButtonMask::LEFT) && !_edit.gizmo.is_valid()) {
_transform_gizmo_select(_edit.mouse_pos, true);
}
@@ -1924,7 +1924,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle, _edit.gizmo_handle_secondary);
set_message(n + ": " + String(v));
- } else if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE || _edit.instant) {
+ } else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT) || _edit.instant) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) {
@@ -1963,7 +1963,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
update_transform(m->get_position(), _get_key_modifier(m) == Key::SHIFT);
}
- } else if ((m->get_button_mask() & MouseButton::MASK_RIGHT) != MouseButton::NONE || freelook_active) {
+ } else if (m->get_button_mask().has_flag(MouseButtonMask::RIGHT) || freelook_active) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ZOOM;
} else if (freelook_active) {
@@ -1972,7 +1972,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
nav_mode = NAVIGATION_PAN;
}
- } else if ((m->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
+ } else if (m->get_button_mask().has_flag(MouseButtonMask::MIDDLE)) {
const Key mod = _get_key_modifier(m);
if (nav_scheme == NAVIGATION_GODOT) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
@@ -3104,7 +3104,7 @@ void Node3DEditorViewport::_draw() {
}
void Node3DEditorViewport::_menu_option(int p_option) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_option) {
case VIEW_TOP: {
cursor.y_rot = 0;
@@ -4257,7 +4257,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(instantiated_scene);
@@ -4286,7 +4286,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
}
void Node3DEditorViewport::_perform_drop_data() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (spatial_editor->get_preview_material_target().is_valid()) {
GeometryInstance3D *geometry_instance = Object::cast_to<GeometryInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
@@ -4496,7 +4496,7 @@ void Node3DEditorViewport::commit_transform() {
TTRC("Translate"),
TTRC("Scale"),
};
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(_transform_name[_edit.mode]);
List<Node *> &selection = editor_selection->get_selected_node_list();
@@ -4921,7 +4921,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
c->add_child(viewport);
surface = memnew(Control);
- surface->set_drag_forwarding(this);
+ surface->set_drag_forwarding_compat(this);
add_child(surface);
surface->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
surface->set_clip_contents(true);
@@ -6014,7 +6014,7 @@ void Node3DEditor::_xform_dialog_action() {
t.basis.rotate(rotate);
t.origin = translate;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("XForm Dialog"));
const List<Node *> &selection = editor_selection->get_selected_node_list();
@@ -6126,7 +6126,7 @@ void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) {
}
void Node3DEditor::_menu_item_pressed(int p_option) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_option) {
case MENU_TOOL_SELECT:
case MENU_TOOL_MOVE:
@@ -7312,7 +7312,7 @@ void Node3DEditor::_snap_selected_nodes_to_floor() {
}
if (snapped_to_floor) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Snap Nodes to Floor"));
// Perform snapping if at least one node can be snapped
@@ -7382,7 +7382,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
ERR_FAIL_COND(!base);
Node *new_sun = preview_sun->duplicate();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Preview Sun to Scene"));
undo_redo->add_do_method(base, "add_child", new_sun, true);
// Move to the beginning of the scene tree since more "global" nodes
@@ -7416,7 +7416,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
new_env->set_camera_attributes(preview_environment->get_camera_attributes()->duplicate(true));
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Preview Environment to Scene"));
undo_redo->add_do_method(base, "add_child", new_env, true);
// Move to the beginning of the scene tree since more "global" nodes
@@ -7995,7 +7995,7 @@ void Node3DEditor::_update_preview_environment() {
void Node3DEditor::_sun_direction_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
sun_rotation.x += mm->get_relative().y * (0.02 * EDSCALE);
sun_rotation.y -= mm->get_relative().x * (0.02 * EDSCALE);
sun_rotation.x = CLAMP(sun_rotation.x, -Math_TAU / 4, Math_TAU / 4);
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index b78ffb7612..63f6643ba3 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -120,7 +120,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
// Check for point deletion.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if ((mb->get_button_index() == MouseButton::RIGHT && mode == MODE_EDIT) || (mb->get_button_index() == MouseButton::LEFT && mode == MODE_DELETE)) {
if (dist_to_p < grab_threshold) {
undo_redo->create_action(TTR("Remove Point from Curve"));
@@ -155,7 +155,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && ((mb->is_command_or_control_pressed() && mode == MODE_EDIT) || mode == MODE_CREATE)) {
Ref<Curve2D> curve = node->get_curve();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Point to Curve"));
undo_redo->add_do_method(curve.ptr(), "add_point", cpoint);
undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count());
@@ -191,7 +191,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
insertion_point = curve->get_point_count() - 2;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Split Curve"));
undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint2), Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1);
@@ -215,7 +215,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && action != ACTION_NONE) {
Ref<Curve2D> curve = node->get_curve();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
switch (action) {
case ACTION_NONE:
@@ -491,7 +491,7 @@ void Path2DEditor::_mode_selected(int p_mode) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Point from Curve"));
undo_redo->add_do_method(node->get_curve().ptr(), "add_point", begin);
undo_redo->add_undo_method(node->get_curve().ptr(), "remove_point", node->get_curve()->get_point_count());
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 917b8245a3..75cd04bee8 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -174,7 +174,7 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (!p_secondary) {
if (p_cancel) {
@@ -409,7 +409,7 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p
}
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (closest_seg != -1) {
//subdivide
@@ -451,21 +451,21 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p
// Find the offset and point index of the place to break up.
// Also check for the control points.
if (dist_to_p < click_dist) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Path Point"));
ur->add_do_method(c.ptr(), "remove_point", i);
ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
ur->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_out < click_dist) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Out-Control Point"));
ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
ur->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_in < click_dist) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove In-Control Point"));
ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
@@ -544,7 +544,7 @@ void Path3DEditorPlugin::_close_curve() {
if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Close Curve"));
ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 9d8f1a1578..05fc464226 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -156,7 +156,7 @@ void Polygon2DEditor::_sync_bones() {
Array new_bones = node->call("_get_bones");
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Sync Bones"));
undo_redo->add_do_method(node, "_set_bones", new_bones);
undo_redo->add_undo_method(node, "_set_bones", prev_bones);
@@ -286,7 +286,7 @@ void Polygon2DEditor::_uv_edit_popup_hide() {
}
void Polygon2DEditor::_menu_option(int p_option) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_option) {
case MODE_EDIT_UV: {
if (node->get_texture().is_null()) {
@@ -399,7 +399,7 @@ void Polygon2DEditor::_update_polygon_editing_state() {
void Polygon2DEditor::_commit_action() {
// Makes that undo/redoing actions made outside of the UV editor still affect its polygon.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(uv_edit_draw, "queue_redraw");
undo_redo->add_undo_method(uv_edit_draw, "queue_redraw");
undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "update_viewport");
@@ -467,7 +467,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
mtx.columns[2] = -uv_draw_ofs;
mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index 294aa32444..9defb4de9b 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -96,7 +96,7 @@ void Polygon3DEditor::_menu_option(int p_option) {
void Polygon3DEditor::_wip_close() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
ERR_FAIL_COND_MSG(!obj, "Edited object is not valid.");
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Polygon3D"));
undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));
undo_redo->add_do_method(obj, "set_polygon", wip);
@@ -186,7 +186,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_ca
if (mb->is_pressed()) {
if (mb->is_ctrl_pressed()) {
if (poly.size() < 3) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.push_back(cpoint);
@@ -265,7 +265,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_ca
ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ 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);
@@ -294,7 +294,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_ca
}
if (closest_idx >= 0) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ 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);
@@ -313,7 +313,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_ca
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- if (edited_point != -1 && (wip_active || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE)) {
+ 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);
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index 27cad1cb7e..250f1403af 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -71,7 +71,7 @@ void ResourcePreloaderEditor::_files_load_request(const Vector<String> &p_paths)
name = basename + " " + itos(counter);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Resource"));
undo_redo->add_do_method(preloader, "add_resource", name, resource);
undo_redo->add_undo_method(preloader, "remove_resource", name);
@@ -116,7 +116,7 @@ void ResourcePreloaderEditor::_item_edited() {
}
Ref<Resource> samp = preloader->get_resource(old_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Resource"));
undo_redo->add_do_method(preloader, "remove_resource", old_name);
undo_redo->add_do_method(preloader, "add_resource", new_name, samp);
@@ -129,7 +129,7 @@ void ResourcePreloaderEditor::_item_edited() {
}
void ResourcePreloaderEditor::_remove_resource(const String &p_to_remove) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Resource"));
undo_redo->add_do_method(preloader, "remove_resource", p_to_remove);
undo_redo->add_undo_method(preloader, "add_resource", p_to_remove, preloader->get_resource(p_to_remove));
@@ -163,7 +163,7 @@ void ResourcePreloaderEditor::_paste_pressed() {
name = basename + " " + itos(counter);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Paste Resource"));
undo_redo->add_do_method(preloader, "add_resource", name, r);
undo_redo->add_undo_method(preloader, "remove_resource", name);
@@ -322,7 +322,7 @@ void ResourcePreloaderEditor::drop_data_fw(const Point2 &p_point, const Variant
name = basename + "_" + itos(counter);
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Resource"));
undo_redo->add_do_method(preloader, "add_resource", name, r);
undo_redo->add_undo_method(preloader, "remove_resource", name);
@@ -379,7 +379,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() {
tree->set_column_expand(1, true);
tree->set_v_size_flags(SIZE_EXPAND_FILL);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
vbc->add_child(tree);
dialog = memnew(AcceptDialog);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 61bf51b6dc..8777b73540 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -223,7 +223,6 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
- // TODO: This signal is no use for VisualScript.
ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text")));
ADD_SIGNAL(MethodInfo("go_to_method", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::STRING, "method")));
@@ -440,6 +439,8 @@ void ScriptEditor::_goto_script_line(Ref<RefCounted> p_script, int p_line) {
} else if (current) {
current->goto_line(p_line, true);
}
+
+ _save_history();
}
}
}
@@ -2205,8 +2206,7 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
if (use_external_editor &&
(EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) &&
- p_resource->get_path().is_resource_file() &&
- !p_resource->is_class("VisualScript")) {
+ p_resource->get_path().is_resource_file()) {
String path = EDITOR_GET("text_editor/external/exec_path");
String flags = EDITOR_GET("text_editor/external/exec_flags");
@@ -2256,11 +2256,14 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
args.push_back(script_path);
}
- Error err = OS::get_singleton()->create_process(path, args);
- if (err == OK) {
- return false;
+ if (!path.is_empty()) {
+ Error err = OS::get_singleton()->create_process(path, args);
+ if (err == OK) {
+ return false;
+ }
}
- WARN_PRINT("Couldn't open external text editor, using internal");
+
+ ERR_PRINT("Couldn't open external text editor, falling back to the internal editor. Review your `text_editor/external/` editor settings.");
}
for (int i = 0; i < tab_container->get_tab_count(); i++) {
@@ -2305,21 +2308,20 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
se->set_edited_resource(p_resource);
- if (!p_resource->is_class("VisualScript")) {
- bool highlighter_set = false;
- for (int i = 0; i < syntax_highlighters.size(); i++) {
- Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
- if (highlighter.is_null()) {
- continue;
- }
- se->add_syntax_highlighter(highlighter);
+ // Syntax highlighting.
+ bool highlighter_set = false;
+ for (int i = 0; i < syntax_highlighters.size(); i++) {
+ Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
+ if (highlighter.is_null()) {
+ continue;
+ }
+ se->add_syntax_highlighter(highlighter);
- if (scr != nullptr && !highlighter_set) {
- PackedStringArray languages = highlighter->_get_supported_languages();
- if (languages.has(scr->get_language()->get_name())) {
- se->set_syntax_highlighter(highlighter);
- highlighter_set = true;
- }
+ if (scr != nullptr && !highlighter_set) {
+ PackedStringArray languages = highlighter->_get_supported_languages();
+ if (languages.has(scr->get_language()->get_name())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
}
}
}
@@ -2556,9 +2558,9 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St
}
void ScriptEditor::open_text_file_create_dialog(const String &p_base_path, const String &p_base_name) {
- file_dialog->set_current_file(p_base_name);
- file_dialog->set_current_dir(p_base_path);
_menu_option(FILE_NEW_TEXTFILE);
+ file_dialog->set_current_dir(p_base_path);
+ file_dialog->set_current_file(p_base_name);
open_textfile_after_create = false;
}
@@ -2954,15 +2956,18 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
Ref<Resource> res = open_file(file);
if (res.is_valid()) {
- if (tab_container->get_tab_count() > num_tabs_before) {
+ const int num_tabs = tab_container->get_tab_count();
+ if (num_tabs > num_tabs_before) {
tab_container->move_child(tab_container->get_tab_control(tab_container->get_tab_count() - 1), new_index);
- num_tabs_before = tab_container->get_tab_count();
- } else { /* Maybe script was already open */
+ num_tabs_before = num_tabs;
+ } else if (num_tabs > 0) { /* Maybe script was already open */
tab_container->move_child(tab_container->get_tab_control(tab_container->get_current_tab()), new_index);
}
}
}
- tab_container->set_current_tab(new_index);
+ if (tab_container->get_tab_count() > 0) {
+ tab_container->set_current_tab(new_index);
+ }
_update_script_names();
}
}
@@ -3669,7 +3674,7 @@ ScriptEditor::ScriptEditor() {
_sort_list_on_update = true;
script_list->connect("item_clicked", callable_mp(this, &ScriptEditor::_script_list_clicked), CONNECT_DEFERRED);
script_list->set_allow_rmb_select(true);
- script_list->set_drag_forwarding(this);
+ script_list->set_drag_forwarding_compat(this);
context_menu = memnew(PopupMenu);
add_child(context_menu);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 9406701ade..4d525cc5a9 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2167,7 +2167,7 @@ ScriptTextEditor::ScriptTextEditor() {
connection_info_dialog = memnew(ConnectionInfoDialog);
- code_editor->get_text_editor()->set_drag_forwarding(this);
+ code_editor->get_text_editor()->set_drag_forwarding_compat(this);
}
ScriptTextEditor::~ScriptTextEditor() {
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index da6a1ea0af..709ca30858 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -229,7 +229,7 @@ void ShaderEditorPlugin::_close_shader(int p_index) {
memdelete(c);
edited_shaders.remove_at(p_index);
_update_shader_list();
- EditorNode::get_undo_redo()->clear_history(); // To prevent undo on deleted graphs.
+ EditorUndoRedoManager::get_singleton()->clear_history(); // To prevent undo on deleted graphs.
}
void ShaderEditorPlugin::_resource_saved(Object *obj) {
@@ -451,7 +451,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
vb->add_child(shader_list);
shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected));
shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked));
- shader_list->set_drag_forwarding(this);
+ shader_list->set_drag_forwarding_compat(this);
main_split->add_child(vb);
vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE);
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index d90d6b6756..06db696330 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -61,7 +61,7 @@ void Skeleton2DEditor::_menu_option(int p_option) {
err_dialog->popup_centered();
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Rest Pose to Bones"));
for (int i = 0; i < node->get_bone_count(); i++) {
Bone2D *bone = node->get_bone(i);
@@ -77,7 +77,7 @@ void Skeleton2DEditor::_menu_option(int p_option) {
err_dialog->popup_centered();
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Rest Pose from Bones"));
for (int i = 0; i < node->get_bone_count(); i++) {
Bone2D *bone = node->get_bone(i);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 0c8fc08795..53486d9509 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -116,7 +116,7 @@ void BoneTransformEditor::_value_changed(const String &p_property, Variant p_val
return;
}
if (skeleton) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));
undo_redo->add_do_property(skeleton, p_property, p_value);
@@ -271,7 +271,7 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) {
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
if (p_all_bones) {
for (int i = 0; i < bone_len; i++) {
@@ -338,7 +338,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
if (p_all_bones) {
for (int i = 0; i < bone_len; i++) {
@@ -358,7 +358,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
}
void Skeleton3DEditor::create_physical_skeleton() {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ERR_FAIL_COND(!get_tree());
Node *owner = get_tree()->get_edited_scene_root();
@@ -593,7 +593,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
Node *node = get_node_or_null(p_skeleton_path);
Skeleton3D *skeleton_node = Object::cast_to<Skeleton3D>(node);
ERR_FAIL_NULL(skeleton_node);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Parentage"));
// If the target is a child of ourselves, we move only *us* and not our children.
if (skeleton_node->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {
@@ -813,7 +813,7 @@ void Skeleton3DEditor::create_editors() {
joint_tree->set_v_size_flags(SIZE_EXPAND_FILL);
joint_tree->set_h_size_flags(SIZE_EXPAND_FILL);
joint_tree->set_allow_rmb_select(true);
- joint_tree->set_drag_forwarding(this);
+ joint_tree->set_drag_forwarding_compat(this);
s_con->add_child(joint_tree);
pose_editor = memnew(BoneTransformEditor(skeleton));
@@ -1323,7 +1323,7 @@ void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, c
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
Node3DEditor *ne = Node3DEditor::get_singleton();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Bone Transform"));
if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
for (int i = 0; i < p_ids.size(); i++) {
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 2a8994d239..0d00bfa8fd 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -344,7 +344,7 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
mesh_instance->set_mesh(mesh);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to MeshInstance2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
ur->add_do_reference(mesh_instance);
@@ -402,7 +402,7 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
polygon_2d_instance->set_polygon(polygon);
polygon_2d_instance->set_polygons(polys);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Polygon2D"));
ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
ur->add_do_reference(polygon_2d_instance);
@@ -424,7 +424,7 @@ void Sprite2DEditor::_create_collision_polygon_2d_node() {
CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);
collision_polygon_2d_instance->set_polygon(outline);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create CollisionPolygon2D Sibling"));
ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance);
ur->add_do_reference(collision_polygon_2d_instance);
@@ -457,7 +457,7 @@ void Sprite2DEditor::_create_light_occluder_2d_node() {
LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D);
light_occluder_2d_instance->set_occluder_polygon(polygon);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create LightOccluder2D Sibling"));
ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance);
ur->add_do_reference(light_occluder_2d_instance);
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 4fa56189bb..d5365068c3 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -197,7 +197,7 @@ void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
}
const Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
// Select by holding down the mouse button on frames.
const int idx = _sheet_preview_position_to_frame_index(mm->get_position());
@@ -238,7 +238,7 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
}
const Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
+ if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::MIDDLE)) {
const Vector2 dragged = Input::get_singleton()->warp_mouse_motion(mm, split_sheet_scroll->get_global_rect());
split_sheet_scroll->set_h_scroll(split_sheet_scroll->get_h_scroll() - dragged.x);
split_sheet_scroll->set_v_scroll(split_sheet_scroll->get_v_scroll() - dragged.y);
@@ -251,7 +251,7 @@ void SpriteFramesEditor::_sheet_add_frames() {
const Size2i offset = _get_offset();
const Size2i separation = _get_separation();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Frame"));
int fc = frames->get_frame_count(edited_anim);
@@ -470,7 +470,7 @@ void SpriteFramesEditor::_file_load_request(const Vector<String> &p_path, int p_
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Frame"));
int fc = frames->get_frame_count(edited_anim);
@@ -541,7 +541,7 @@ void SpriteFramesEditor::_paste_pressed() {
return; ///beh should show an error i guess
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Paste Frame"));
undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, duration);
undo_redo->add_undo_method(frames, "remove_frame", edited_anim, frames->get_frame_count(edited_anim));
@@ -584,7 +584,7 @@ void SpriteFramesEditor::_empty_pressed() {
Ref<Texture2D> texture;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Empty"));
undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, 1.0, from);
undo_redo->add_undo_method(frames, "remove_frame", edited_anim, from);
@@ -608,7 +608,7 @@ void SpriteFramesEditor::_empty2_pressed() {
Ref<Texture2D> texture;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Empty"));
undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, 1.0, from + 1);
undo_redo->add_undo_method(frames, "remove_frame", edited_anim, from + 1);
@@ -632,7 +632,7 @@ void SpriteFramesEditor::_up_pressed() {
sel = to_move;
sel -= 1;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Frame"));
undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1));
undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
@@ -658,7 +658,7 @@ void SpriteFramesEditor::_down_pressed() {
sel = to_move;
sel += 1;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Frame"));
undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1));
undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move));
@@ -681,7 +681,7 @@ void SpriteFramesEditor::_delete_pressed() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Resource"));
undo_redo->add_do_method(frames, "remove_frame", edited_anim, to_delete);
undo_redo->add_undo_method(frames, "add_frame", edited_anim, frames->get_frame_texture(edited_anim, to_delete), frames->get_frame_duration(edited_anim, to_delete), to_delete);
@@ -768,7 +768,7 @@ void SpriteFramesEditor::_animation_name_edited() {
List<Node *> nodes;
_find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref<SpriteFrames>(frames));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Animation"));
undo_redo->add_do_method(frames, "rename_animation", edited_anim, name);
undo_redo->add_undo_method(frames, "rename_animation", name, edited_anim);
@@ -798,7 +798,7 @@ void SpriteFramesEditor::_animation_add() {
List<Node *> nodes;
_find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref<SpriteFrames>(frames));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Animation"));
undo_redo->add_do_method(frames, "add_animation", name);
undo_redo->add_undo_method(frames, "remove_animation", name);
@@ -831,7 +831,7 @@ void SpriteFramesEditor::_animation_remove() {
}
void SpriteFramesEditor::_animation_remove_confirmed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Animation"));
undo_redo->add_do_method(frames, "remove_animation", edited_anim);
undo_redo->add_undo_method(frames, "add_animation", edited_anim);
@@ -860,7 +860,7 @@ void SpriteFramesEditor::_animation_loop_changed() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Loop"));
undo_redo->add_do_method(frames, "set_animation_loop", edited_anim, anim_loop->is_pressed());
undo_redo->add_undo_method(frames, "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim));
@@ -874,7 +874,7 @@ void SpriteFramesEditor::_animation_speed_changed(double p_value) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation FPS"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(frames, "set_animation_speed", edited_anim, p_value);
undo_redo->add_undo_method(frames, "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim));
@@ -926,7 +926,7 @@ void SpriteFramesEditor::_frame_duration_changed(double p_value) {
Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, index);
float old_duration = frames->get_frame_duration(edited_anim, index);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Frame Duration"));
undo_redo->add_do_method(frames, "set_frame", edited_anim, index, texture, p_value);
undo_redo->add_undo_method(frames, "set_frame", edited_anim, index, texture, old_duration);
@@ -1206,7 +1206,7 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
reorder = true;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (reorder) { //drop is from reordering frames
int from_frame = -1;
float duration = 1.0;
@@ -1408,7 +1408,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
frame_list->set_max_columns(0);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
frame_list->set_max_text_lines(2);
- frame_list->set_drag_forwarding(this);
+ frame_list->set_drag_forwarding_compat(this);
frame_list->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_frame_list_gui_input));
frame_list->connect("item_selected", callable_mp(this, &SpriteFramesEditor::_frame_list_item_selected));
@@ -1551,7 +1551,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_vb->add_child(split_sheet_panel);
split_sheet_preview = memnew(TextureRect);
- split_sheet_preview->set_ignore_texture_size(true);
+ split_sheet_preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS);
split_sheet_preview->connect("draw", callable_mp(this, &SpriteFramesEditor::_sheet_preview_draw));
split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input));
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index f3f0542b9b..ad5881c76f 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -650,7 +650,7 @@ TextEditor::TextEditor() {
goto_line_dialog = memnew(GotoLineDialog);
add_child(goto_line_dialog);
- code_editor->get_text_editor()->set_drag_forwarding(this);
+ code_editor->get_text_editor()->set_drag_forwarding_compat(this);
}
TextEditor::~TextEditor() {
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index cd160f8b14..1b29999796 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -301,7 +301,9 @@ void ShaderTextEditor::_load_theme_settings() {
syntax_highlighter->clear_color_regions();
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
- syntax_highlighter->set_disabled_branch_color(comment_color);
+
+ // Disabled preprocessor branches use translucent text color to be easier to distinguish from comments.
+ syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5));
te->clear_comment_delimiters();
te->add_comment_delimiter("/*", "*/", false);
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 2dab3fa278..2fb624b713 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -128,7 +128,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
texture_display->set_texture(p_texture);
texture_display->set_anchors_preset(TextureRect::PRESET_FULL_RECT);
texture_display->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
- texture_display->set_ignore_texture_size(true);
+ texture_display->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
add_child(texture_display);
if (p_show_metadata) {
diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp
index 9a3b157f5b..816d081617 100644
--- a/editor/plugins/texture_layered_editor_plugin.cpp
+++ b/editor/plugins/texture_layered_editor_plugin.cpp
@@ -36,7 +36,7 @@ void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
y_rot += -mm->get_relative().x * 0.01;
x_rot += mm->get_relative().y * 0.01;
_update_material();
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 59f2159167..9bad2f2fbf 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -300,7 +300,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
mtx.xform(rect.position + Vector2(0, rect.size.y / 2)) + Vector2(-handle_offset, 0)
};
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT) {
@@ -349,7 +349,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
for (const Rect2 &E : autoslice_cache) {
if (E.has_point(point)) {
rect = E;
- if (Input::get_singleton()->is_key_pressed(Key::CTRL) && !(Input::get_singleton()->is_key_pressed(Key(Key::SHIFT | Key::ALT)))) {
+ if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !(Input::get_singleton()->is_key_pressed(Key(Key::SHIFT | Key::ALT)))) {
Rect2 r;
if (atlas_tex.is_valid()) {
r = atlas_tex->get_region();
@@ -824,10 +824,18 @@ void TextureRegionEditor::_update_autoslice() {
void TextureRegionEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_EXIT_TREE: {
+ get_tree()->disconnect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed));
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ get_tree()->connect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed));
+ [[fallthrough]];
+ }
case NOTIFICATION_THEME_CHANGED: {
edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
+
case NOTIFICATION_READY: {
zoom_out->set_icon(get_theme_icon(SNAME("ZoomLess"), SNAME("EditorIcons")));
zoom_reset->set_icon(get_theme_icon(SNAME("ZoomReset"), SNAME("EditorIcons")));
@@ -840,11 +848,13 @@ void TextureRegionEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) {
_update_autoslice();
}
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
// This happens when the user leaves the Editor and returns,
// they could have changed the textures, so the cache is cleared.
@@ -867,7 +877,6 @@ void TextureRegionEditor::_node_removed(Object *p_obj) {
void TextureRegionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_region"), &TextureRegionEditor::_edit_region);
- ClassDB::bind_method(D_METHOD("_node_removed"), &TextureRegionEditor::_node_removed);
ClassDB::bind_method(D_METHOD("_zoom_on_position"), &TextureRegionEditor::_zoom_on_position);
ClassDB::bind_method(D_METHOD("_update_rect"), &TextureRegionEditor::_update_rect);
}
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 68c1041a0b..40aac77a99 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -800,7 +800,7 @@ void ThemeItemImportTree::_import_selected() {
ProgressDialog::get_singleton()->end_task("import_theme_items");
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Import Theme Items"));
ur->add_do_method(*edited_theme, "clear");
@@ -1498,7 +1498,7 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu
String item_name = item->get_text(0);
int data_type = item->get_parent()->get_metadata(0);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Theme Item"));
ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type);
ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type));
@@ -1517,7 +1517,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
const String new_type = edit_add_type_value->get_text().strip_edges();
edit_add_type_value->clear();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Add Theme Type"));
ur->add_do_method(*edited_theme, "add_type", new_type);
@@ -1529,7 +1529,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
}
void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Theme Item"));
switch (p_data_type) {
@@ -1574,7 +1574,7 @@ void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) {
Ref<Theme> old_snapshot = edited_theme->duplicate();
Ref<Theme> new_snapshot = edited_theme->duplicate();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Theme Type"));
new_snapshot->remove_type(p_theme_type);
@@ -1597,7 +1597,7 @@ void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type,
Ref<Theme> old_snapshot = edited_theme->duplicate();
Ref<Theme> new_snapshot = edited_theme->duplicate();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Data Type Items From Theme"));
new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names);
@@ -1626,7 +1626,7 @@ void ThemeItemEditorDialog::_remove_class_items() {
Ref<Theme> old_snapshot = edited_theme->duplicate();
Ref<Theme> new_snapshot = edited_theme->duplicate();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Class Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1662,7 +1662,7 @@ void ThemeItemEditorDialog::_remove_custom_items() {
Ref<Theme> old_snapshot = edited_theme->duplicate();
Ref<Theme> new_snapshot = edited_theme->duplicate();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Custom Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1698,7 +1698,7 @@ void ThemeItemEditorDialog::_remove_all_items() {
Ref<Theme> old_snapshot = edited_theme->duplicate();
Ref<Theme> new_snapshot = edited_theme->duplicate();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove All Items From Theme"));
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1802,7 +1802,7 @@ void ThemeItemEditorDialog::_confirm_edit_theme_item() {
if (item_popup_mode == CREATE_THEME_ITEM) {
_add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type);
} else if (item_popup_mode == RENAME_THEME_ITEM) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Rename Theme Item"));
ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type);
@@ -2828,7 +2828,7 @@ void ThemeTypeEditor::_add_default_type_items() {
updating = false;
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Override All Default Theme Items"));
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
@@ -2848,7 +2848,7 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
}
String item_name = le->get_text().strip_edges();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Add Theme Item"));
switch (p_data_type) {
@@ -2893,7 +2893,7 @@ void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Co
}
void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Override Theme Item"));
switch (p_data_type) {
@@ -2932,7 +2932,7 @@ void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
}
void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Remove Theme Item"));
switch (p_data_type) {
@@ -3006,7 +3006,7 @@ void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Rename Theme Item"));
switch (p_data_type) {
@@ -3062,7 +3062,7 @@ void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name,
}
void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS);
ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value);
ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
@@ -3070,7 +3070,7 @@ void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
}
void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Constant Item in Theme"));
ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value);
ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
@@ -3078,7 +3078,7 @@ void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name)
}
void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Font Size Item in Theme"));
ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value);
ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
@@ -3090,7 +3090,7 @@ void ThemeTypeEditor::_edit_resource_item(Ref<Resource> p_resource, bool p_edit)
}
void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Font Item in Theme"));
ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>());
@@ -3107,7 +3107,7 @@ void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name)
}
void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Icon Item in Theme"));
ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>());
@@ -3124,7 +3124,7 @@ void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_n
}
void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Stylebox Item in Theme"));
ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>());
@@ -3167,7 +3167,7 @@ void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_
stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource();
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Pin Stylebox"));
ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox);
@@ -3200,7 +3200,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_
}
void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Unpin Stylebox"));
ur->add_do_method(this, "_unpin_leading_stylebox");
ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
@@ -3269,7 +3269,7 @@ void ThemeTypeEditor::_update_stylebox_from_leading() {
}
void ThemeTypeEditor::_type_variation_changed(const String p_value) {
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Set Theme Type Variation"));
if (p_value.is_empty()) {
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index 7edf6e3a30..eaf72d36ba 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -31,7 +31,6 @@
#include "atlas_merging_dialog.h"
#include "editor/editor_file_dialog.h"
-#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
@@ -56,7 +55,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
new_texture_region_size = new_texture_region_size.max(atlas_source->get_texture_region_size());
}
- // Generate the merged TileSetAtlasSource.
+ // Generate the new texture.
Vector2i atlas_offset;
int line_height = 0;
for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
@@ -72,28 +71,6 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
Rect2i new_tile_rect_in_altas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id));
- // Create tiles and alternatives, then copy their properties.
- for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_id); alternative_index++) {
- int alternative_id = atlas_source->get_alternative_tile_id(tile_id, alternative_index);
- if (alternative_id == 0) {
- merged->create_tile(new_tile_rect_in_altas.position, new_tile_rect_in_altas.size);
- } else {
- merged->create_alternative_tile(new_tile_rect_in_altas.position, alternative_index);
- }
-
- // Copy the properties.
- TileData *original_tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
- List<PropertyInfo> properties;
- original_tile_data->get_property_list(&properties);
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- const StringName &property_name = E->get().name;
- merged->set(property_name, original_tile_data->get(property_name));
- }
-
- // Add to the mapping.
- merged_mapping[source_index][tile_id] = new_tile_rect_in_altas.position;
- }
-
// Copy the texture.
for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
@@ -103,6 +80,9 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
+
+ // Add to the mapping.
+ merged_mapping[source_index][tile_id] = new_tile_rect_in_altas.position;
}
// Compute the atlas offset.
@@ -115,8 +95,43 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
}
- merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture(ImageTexture::create_from_image(output_image));
+
+ // Copy the tiles to the merged TileSetAtlasSource.
+ for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) {
+ Ref<TileSetAtlasSource> atlas_source = p_atlas_sources[source_index];
+ for (KeyValue<Vector2i, Vector2i> tile_mapping : merged_mapping[source_index]) {
+ // Create tiles and alternatives, then copy their properties.
+ for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) {
+ int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index);
+ if (alternative_id == 0) {
+ merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
+ } else {
+ merged->create_alternative_tile(tile_mapping.value, alternative_index);
+ }
+
+ // Copy the properties.
+ TileData *src_tile_data = atlas_source->get_tile_data(tile_mapping.key, alternative_id);
+ List<PropertyInfo> properties;
+ src_tile_data->get_property_list(&properties);
+
+ TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, alternative_id);
+ for (PropertyInfo property : properties) {
+ if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+ Variant value = src_tile_data->get(property.name);
+ Variant default_value = ClassDB::class_get_default_property_value("TileData", property.name);
+ if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
+ continue;
+ }
+ dst_tile_data->set(property.name, value);
+ }
+ }
+ }
+ }
+
+ merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture_region_size(new_texture_region_size);
}
}
@@ -151,10 +166,12 @@ void AtlasMergingDialog::_merge_confirmed(String p_path) {
Ref<ImageTexture> output_image_texture = merged->get_texture();
output_image_texture->get_image()->save_png(p_path);
+ ResourceLoader::import(p_path);
+
Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D");
merged->set_texture(new_texture_resource);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Merge TileSetAtlasSource"));
int next_id = tile_set->get_next_source_id();
undo_redo->add_do_method(*tile_set, "add_source", merged, next_id);
@@ -194,7 +211,7 @@ void AtlasMergingDialog::ok_pressed() {
}
void AtlasMergingDialog::cancel_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < commited_actions_count; i++) {
undo_redo->undo();
}
@@ -225,6 +242,14 @@ bool AtlasMergingDialog::_get(const StringName &p_name, Variant &r_ret) const {
return false;
}
+void AtlasMergingDialog::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ _update_texture();
+ } break;
+ }
+}
+
void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) {
ERR_FAIL_COND(!p_tile_set.is_valid());
tile_set = p_tile_set;
@@ -300,7 +325,7 @@ AtlasMergingDialog::AtlasMergingDialog() {
preview = memnew(TextureRect);
preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
preview->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- preview->set_ignore_texture_size(true);
+ preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
preview->hide();
preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
atlas_merging_right_panel->add_child(preview);
diff --git a/editor/plugins/tiles/atlas_merging_dialog.h b/editor/plugins/tiles/atlas_merging_dialog.h
index 032541c90e..bf1b56894f 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.h
+++ b/editor/plugins/tiles/atlas_merging_dialog.h
@@ -75,6 +75,8 @@ protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _notification(int p_what);
+
public:
void update_tile_set(Ref<TileSet> p_tile_set);
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 3c6ed0f049..0ac375407c 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -454,21 +454,31 @@ void TileAtlasView::set_padding(Side p_side, int p_padding) {
margin_container_paddings[p_side] = p_padding;
}
-Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const {
+Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
- if (texture.is_valid()) {
- Vector2i margins = tile_set_atlas_source->get_margins();
- Vector2i separation = tile_set_atlas_source->get_separation();
- Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+ if (!texture.is_valid()) {
+ return TileSetSource::INVALID_ATLAS_COORDS;
+ }
+
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
- // Compute index in atlas
- Vector2 pos = p_pos - margins;
- Vector2i ret = (pos / (texture_region_size + separation)).floor();
+ // Compute index in atlas
+ Vector2 pos = p_pos - margins;
+ Vector2i ret = (pos / (texture_region_size + separation)).floor();
- return ret;
+ // Return invalid value (without clamp).
+ Rect2i rect = Rect2(Vector2i(), tile_set_atlas_source->get_atlas_grid_size());
+ if (!p_clamp && !rect.has_point(ret)) {
+ return TileSetSource::INVALID_ATLAS_COORDS;
}
- return TileSetSource::INVALID_ATLAS_COORDS;
+ // Clamp.
+ ret.x = CLAMP(ret.x, 0, rect.size.x - 1);
+ ret.y = CLAMP(ret.y, 0, rect.size.y - 1);
+
+ return ret;
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 166abca2a3..f719bee704 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -128,7 +128,7 @@ public:
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
- Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
+ Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const;
void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index fb4787f13c..81aa9bf272 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -265,12 +265,12 @@ void GenericTilePolygonEditor::_zoom_changed() {
}
void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
if (use_undo_redo) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
} else {
// This nice hack allows for discarding undo actions without making code too complex.
- undo_redo.instantiate();
+ undo_redo = memnew(EditorUndoRedoManager);
}
switch (p_item_pressed) {
@@ -361,6 +361,10 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
default:
break;
}
+
+ if (!use_undo_redo) {
+ memdelete(undo_redo);
+ }
}
void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) {
@@ -445,12 +449,12 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) {
}
void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
if (use_undo_redo) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
} else {
// This nice hack allows for discarding undo actions without making code too complex.
- undo_redo.instantiate();
+ undo_redo = memnew(EditorUndoRedoManager);
}
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
@@ -493,11 +497,11 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_ctrl_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_command_or_control_pressed()) {
editor_zoom_widget->set_zoom_by_increments(1);
_zoom_changed();
accept_event();
- } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_ctrl_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_command_or_control_pressed()) {
editor_zoom_widget->set_zoom_by_increments(-1);
_zoom_changed();
accept_event();
@@ -643,6 +647,10 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
}
base_control->queue_redraw();
+
+ if (!use_undo_redo) {
+ memdelete(undo_redo);
+ }
}
void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {
@@ -902,7 +910,7 @@ Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_s
}
void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E.key.alternative_tile, property), E.value);
@@ -918,8 +926,8 @@ void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_
p_canvas_item->draw_set_transform_matrix(p_transform);
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position())));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true));
rect = rect.abs();
RBSet<TileMapCell> edited;
@@ -953,7 +961,7 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (drag_type == DRAG_TYPE_PAINT) {
- Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position()));
+ Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true));
for (int i = 0; i < line.size(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
@@ -971,19 +979,19 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti
}
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
- if (picker_button->is_pressed()) {
- Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
+ if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) {
+ Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true);
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
_set_painted_value(p_tile_set_atlas_source, coords, 0);
picker_button->set_pressed(false);
}
- } else if (mb->is_ctrl_pressed()) {
+ } else if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) {
drag_type = DRAG_TYPE_PAINT_RECT;
drag_modified.clear();
drag_painted_value = _get_painted_value();
@@ -992,7 +1000,7 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti
drag_type = DRAG_TYPE_PAINT;
drag_modified.clear();
drag_painted_value = _get_painted_value();
- Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
+ Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true);
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
TileMapCell cell;
@@ -1007,8 +1015,8 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti
} else {
if (drag_type == DRAG_TYPE_PAINT_RECT) {
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true));
rect = rect.abs();
drag_modified.clear();
@@ -1095,7 +1103,7 @@ void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasVie
drag_last_pos = mb->get_position();
}
} else {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Painting Tiles Property"));
_setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value);
undo_redo->commit_action(false);
@@ -1345,7 +1353,7 @@ Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_
}
void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, occlusion_layer), E.value);
@@ -1524,26 +1532,32 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas
}
void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) {
- Array new_array = p_new_value;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ Dictionary new_dict = p_new_value;
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (KeyValue<TileMapCell, Variant> &E : p_previous_values) {
- Array old_array = E.value;
-
Vector2i coords = E.key.get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_array.size());
- for (int i = 0; i < old_array.size(); i++) {
- Dictionary dict = old_array[i];
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
+
+ Dictionary old_dict = E.value;
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["linear_velocity"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["angular_velocity"]);
+ Array old_polygon_array = old_dict["polygons"];
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_polygon_array.size());
+ for (int i = 0; i < old_polygon_array.size(); i++) {
+ Dictionary polygon_dict = old_polygon_array[i];
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin"]);
}
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_array.size());
- for (int i = 0; i < new_array.size(); i++) {
- Dictionary dict = new_array[i];
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["linear_velocity"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["angular_velocity"]);
+ Array new_polygon_array = new_dict["polygons"];
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_polygon_array.size());
+ for (int i = 0; i < new_polygon_array.size(); i++) {
+ Dictionary polygon_dict = new_polygon_array[i];
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin"]);
}
}
}
@@ -1804,8 +1818,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
p_canvas_item->draw_set_transform_matrix(p_transform);
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position())));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true));
rect = rect.abs();
RBSet<TileMapCell> edited;
@@ -1834,8 +1848,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
int terrain_set = int(painted["terrain_set"]);
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position())));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true));
rect = rect.abs();
RBSet<TileMapCell> edited;
@@ -1995,7 +2009,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
- Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position()));
+ Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true));
for (int i = 0; i < line.size(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
@@ -2029,7 +2043,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) {
int terrain_set = Dictionary(drag_painted_value)["terrain_set"];
int terrain = Dictionary(drag_painted_value)["terrain"];
- Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position()));
+ Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true));
for (int i = 0; i < line.size(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
@@ -2083,7 +2097,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
- if (mb->get_button_index() == MouseButton::LEFT && picker_button->is_pressed()) {
+ if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) {
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
@@ -2126,7 +2140,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (mb->get_button_index() == MouseButton::RIGHT) {
terrain_set = -1;
}
- if (mb->is_ctrl_pressed()) {
+ if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) {
// Paint terrain set with rect.
drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT;
drag_modified.clear();
@@ -2167,7 +2181,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (mb->get_button_index() == MouseButton::RIGHT) {
terrain = -1;
}
- if (mb->is_ctrl_pressed()) {
+ if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) {
// Paint terrain bits with rect.
drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT;
drag_modified.clear();
@@ -2227,11 +2241,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
}
} else {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) {
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true));
rect = rect.abs();
RBSet<TileMapCell> edited;
@@ -2318,8 +2332,8 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
int terrain = int(painted["terrain"]);
Rect2i rect;
- rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos));
- rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()));
+ rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true));
+ rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true));
rect = rect.abs();
RBSet<TileMapCell> edited;
@@ -2600,7 +2614,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
}
}
} else {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Tiles Property"));
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
@@ -2747,7 +2761,7 @@ Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atla
}
void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, navigation_layer), E.value);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index e7dc5d825b..23b6da438c 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -273,7 +273,7 @@ void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEven
if (ED_IS_SHORTCUT("tiles_editor/paste", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
select_last_pattern = true;
int new_pattern_index = tile_set->get_patterns_count();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add TileSet pattern"));
undo_redo->add_do_method(*tile_set, "add_pattern", tile_map_clipboard, new_pattern_index);
undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
@@ -283,7 +283,7 @@ void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEven
if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
Vector<int> selected = patterns_item_list->get_selected_items();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove TileSet patterns"));
for (int i = 0; i < selected.size(); i++) {
int pattern_index = selected[i];
@@ -515,7 +515,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) {
// Delete selected tiles.
if (!tile_map_selection.is_empty()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete tiles"));
for (const Vector2i &E : tile_map_selection) {
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
@@ -547,7 +547,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (ED_IS_SHORTCUT("tiles_editor/delete", p_event)) {
// Delete selected tiles.
if (!tile_map_selection.is_empty()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete tiles"));
for (const Vector2i &E : tile_map_selection) {
undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
@@ -646,12 +646,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
} else {
// Check if we are picking a tile.
- if (picker_button->is_pressed() || (Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
+ if (picker_button->is_pressed() || (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
drag_type = DRAG_TYPE_PICK;
drag_start_mouse_pos = mpos;
} else {
// Paint otherwise.
- if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
@@ -667,11 +667,11 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
_fix_invalid_tiles_in_tile_map_selection();
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
drag_type = DRAG_TYPE_RECT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
@@ -742,7 +742,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Draw the selection.
if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && tool_buttons_group->get_pressed_button() == select_tool_button) {
// In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
- if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
+ if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
// Do nothing
} else {
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
@@ -812,7 +812,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Vector2i coords = tile_map->map_pattern(tile_map->local_to_map(drag_last_mouse_pos - mouse_offset), clipboard_used_cells[i], tile_map_clipboard);
preview[coords] = TileMapCell(tile_map_clipboard->get_cell_source_id(clipboard_used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(clipboard_used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(clipboard_used_cells[i]));
}
- } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
bool expand_grid = false;
if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
@@ -1240,20 +1240,20 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (drag_type) {
case DRAG_TYPE_SELECT: {
undo_redo->create_action(TTR("Change selection"));
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
tile_map_selection.clear();
}
Rect2i rect = Rect2i(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos) - tile_map->local_to_map(drag_start_mouse_pos)).abs();
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
if (tile_map_selection.has(coords)) {
tile_map_selection.erase(coords);
}
@@ -1716,8 +1716,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
// Draw the selection rect.
if (tile_set_dragging_selection) {
- Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos);
- Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true);
+ Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
region.size += Vector2i(1, 1);
@@ -1812,8 +1812,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
tile_set_selection.clear();
}
// Compute the covered area.
- Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos);
- Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true);
+ Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
if (start_tile != TileSetSource::INVALID_ATLAS_COORDS && end_tile != TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
region.size += Vector2i(1, 1);
@@ -2236,7 +2236,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
scene_tiles_list = memnew(ItemList);
scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- scene_tiles_list->set_drag_forwarding(this);
+ scene_tiles_list->set_drag_forwarding_compat(this);
scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected));
scene_tiles_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked));
@@ -2641,7 +2641,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (drag_type) {
case DRAG_TYPE_PICK: {
Vector2i coords = tile_map->local_to_map(mpos);
@@ -2875,7 +2875,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
drag_type = DRAG_TYPE_PICK;
} else {
// Paint otherwise.
- if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
@@ -2890,14 +2890,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
@@ -2982,7 +2982,7 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o
tile_xform.set_scale(tile_shape_size);
tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
}
- } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
bool expand_grid = false;
if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a single tile.
@@ -3303,12 +3303,16 @@ void TileMapEditorTerrainsPlugin::_update_theme() {
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
_stop_dragging(); // Avoids staying in a wrong drag state.
- tile_map_id = p_tile_map_id;
- tile_map_layer = p_tile_map_layer;
+ if (tile_map_id != p_tile_map_id) {
+ tile_map_id = p_tile_map_id;
- _update_terrains_cache();
- _update_terrains_tree();
- _update_tiles_list();
+ // Clear the selection.
+ _update_terrains_cache();
+ _update_terrains_tree();
+ _update_tiles_list();
+ }
+
+ tile_map_layer = p_tile_map_layer;
}
TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
@@ -3485,7 +3489,7 @@ void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) {
}
if (p_id == 0) { // Replace Tile Proxies
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Replace Tiles with Proxies"));
for (int layer_index = 0; layer_index < tile_map->get_layers_count(); layer_index++) {
TypedArray<Vector2i> used_cells = tile_map->get_used_cells(layer_index);
@@ -3702,8 +3706,8 @@ void TileMapEditor::_update_layers_selection() {
}
void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
- Ref<EditorUndoRedoManager> undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(undo_redo_man.is_null());
+ EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo_man);
TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
if (!tile_map) {
diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
index ef82e748a8..f6aeffa13f 100644
--- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
+++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
@@ -30,10 +30,11 @@
#include "tile_proxies_manager_dialog.h"
-#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/popup_menu.h"
#include "scene/gui/separator.h"
void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index, Object *p_item_list) {
@@ -55,7 +56,7 @@ void TileProxiesManagerDialog::_menu_id_pressed(int p_id) {
}
void TileProxiesManagerDialog::_delete_selected_bindings() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Tile Proxies"));
Vector<int> source_level_selected = source_level_list->get_selected_items();
@@ -155,7 +156,7 @@ void TileProxiesManagerDialog::_property_changed(const String &p_path, const Var
}
void TileProxiesManagerDialog::_add_button_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (from.source_id != TileSet::INVALID_SOURCE && to.source_id != TileSet::INVALID_SOURCE) {
Vector2i from_coords = from.get_atlas_coords();
Vector2i to_coords = to.get_atlas_coords();
@@ -196,7 +197,7 @@ void TileProxiesManagerDialog::_add_button_pressed() {
}
void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Invalid Tile Proxies"));
undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies");
@@ -224,7 +225,7 @@ void TileProxiesManagerDialog::_clear_invalid_button_pressed() {
}
void TileProxiesManagerDialog::_clear_all_button_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete All Tile Proxies"));
undo_redo->add_do_method(*tile_set, "clear_tile_proxies");
@@ -305,7 +306,7 @@ void TileProxiesManagerDialog::_unhandled_key_input(Ref<InputEvent> p_event) {
}
void TileProxiesManagerDialog::cancel_pressed() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < commited_actions_count; i++) {
undo_redo->undo();
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 146a53f3dd..9e2cb47c23 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1015,44 +1015,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Handle the event.
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- if (drag_type == DRAG_TYPE_NONE) {
- if (selection.size() == 1) {
- // Change the cursor depending on the hovered thing.
- TileSelection selected = selection.front()->get();
- if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
- Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position();
- Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
- Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
- const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
- const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
- CursorShape cursor_shape = CURSOR_ARROW;
- bool can_grow[4];
- for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
- can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
- }
- for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + rect.size * coords[i];
- if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
- cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
- }
- Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
- if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
- cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
- }
- }
- tile_atlas_control->set_default_cursor_shape(cursor_shape);
- }
- }
- } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) {
+ if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) {
// Create big tile.
new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
@@ -1097,7 +1066,6 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
// Move tile.
Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset);
- coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords);
selection.clear();
@@ -1243,7 +1211,6 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
- CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
@@ -1257,7 +1224,6 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_last_mouse_pos = drag_start_mouse_pos;
drag_current_tile = selected.tile;
drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
- cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
}
Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
@@ -1266,10 +1232,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_last_mouse_pos = drag_start_mouse_pos;
drag_current_tile = selected.tile;
drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
- cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
}
}
- tile_atlas_control->set_default_cursor_shape(cursor_shape);
}
}
@@ -1292,7 +1256,6 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_last_mouse_pos = drag_start_mouse_pos;
drag_current_tile = selected.tile;
drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
- tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE);
} else {
// Start selection dragging.
drag_type = DRAG_TYPE_RECT_SELECT;
@@ -1332,7 +1295,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
}
void TileSetAtlasSourceEditor::_end_dragging() {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (drag_type) {
case DRAG_TYPE_CREATE_TILES:
undo_redo->create_action(TTR("Create tiles"));
@@ -1370,8 +1333,8 @@ void TileSetAtlasSourceEditor::_end_dragging() {
undo_redo->commit_action();
} break;
case DRAG_TYPE_CREATE_TILES_USING_RECT: {
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
undo_redo->create_action(TTR("Create tiles"));
@@ -1387,8 +1350,8 @@ void TileSetAtlasSourceEditor::_end_dragging() {
undo_redo->commit_action();
} break;
case DRAG_TYPE_REMOVE_TILES_USING_RECT: {
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
List<PropertyInfo> list;
@@ -1438,8 +1401,8 @@ void TileSetAtlasSourceEditor::_end_dragging() {
}
break;
case DRAG_TYPE_RECT_SELECT: {
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
ERR_FAIL_COND(start_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS);
ERR_FAIL_COND(new_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS);
@@ -1543,7 +1506,67 @@ void TileSetAtlasSourceEditor::_end_dragging() {
drag_modified_tiles.clear();
drag_type = DRAG_TYPE_NONE;
- tile_atlas_control->set_default_cursor_shape(CURSOR_ARROW);
+ // Change mouse accordingly.
+}
+
+Control::CursorShape TileSetAtlasSourceEditor::get_cursor_shape(const Point2 &p_pos) const {
+ Control::CursorShape cursor_shape = get_default_cursor_shape();
+ if (drag_type == DRAG_TYPE_NONE) {
+ if (selection.size() == 1) {
+ // Change the cursor depending on the hovered thing.
+ TileSelection selected = selection.front()->get();
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ Transform2D xform = tile_atlas_control->get_global_transform().affine_inverse() * get_global_transform();
+ Vector2 mouse_local_pos = xform.xform(p_pos);
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
+ }
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + rect.size * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
+ }
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
+ if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
+ }
+ }
+ }
+ }
+ } else {
+ switch (drag_type) {
+ case DRAG_TYPE_RESIZE_TOP_LEFT:
+ case DRAG_TYPE_RESIZE_BOTTOM_RIGHT:
+ cursor_shape = CURSOR_FDIAGSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_TOP:
+ case DRAG_TYPE_RESIZE_BOTTOM:
+ cursor_shape = CURSOR_VSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_TOP_RIGHT:
+ case DRAG_TYPE_RESIZE_BOTTOM_LEFT:
+ cursor_shape = CURSOR_BDIAGSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_LEFT:
+ case DRAG_TYPE_RESIZE_RIGHT:
+ cursor_shape = CURSOR_HSIZE;
+ break;
+ case DRAG_TYPE_MOVE_TILE:
+ cursor_shape = CURSOR_MOVE;
+ break;
+ default:
+ break;
+ }
+ }
+ return cursor_shape;
}
HashMap<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas) {
@@ -1563,7 +1586,7 @@ HashMap<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_p
}
void TileSetAtlasSourceEditor::_menu_option(int p_option) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_option) {
case TILE_DELETE: {
@@ -1747,8 +1770,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
}
} else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
// Draw tiles to be removed.
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
@@ -1777,8 +1800,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
for (int x = area.get_position().x; x < area.get_end().x; x++) {
@@ -1795,8 +1818,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
// Draw the hovered tile.
if (drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT || drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) {
// Draw the rect.
- Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
- Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
Vector2i margins = tile_set_atlas_source->get_margins();
@@ -2079,8 +2102,8 @@ void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what)
}
void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
- Ref<EditorUndoRedoManager> undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(!undo_redo_man.is_valid());
+ EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo_man);
#define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
@@ -2210,7 +2233,7 @@ void TileSetAtlasSourceEditor::_auto_create_tiles() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create tiles in non-transparent texture regions"));
for (int y = 0; y < grid_size.y; y++) {
for (int x = 0; x < grid_size.x; x++) {
@@ -2256,7 +2279,7 @@ void TileSetAtlasSourceEditor::_auto_remove_tiles() {
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove tiles in fully transparent texture regions"));
List<PropertyInfo> list;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 85f780ea26..bcab1296ad 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -277,6 +277,8 @@ public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_source();
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
+
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
};
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index d269a7c0ec..4f6522f130 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -67,7 +67,7 @@ void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data,
// Actually create the new source.
Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
atlas_source->set_texture(resource);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a new atlas source"));
undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
@@ -260,7 +260,7 @@ void TileSetEditor::_source_delete_pressed() {
Ref<TileSetSource> source = tile_set->get_source(to_delete);
// Remove the source.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove source"));
undo_redo->add_do_method(*tile_set, "remove_source", to_delete);
undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete);
@@ -279,7 +279,7 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
// Add a new source.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add atlas source"));
undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
@@ -294,7 +294,7 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
Ref<TileSetScenesCollectionSource> scene_collection_source = memnew(TileSetScenesCollectionSource);
// Add a new source.
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add atlas source"));
undo_redo->add_do_method(*tile_set, "add_source", scene_collection_source, source_id);
undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
@@ -369,7 +369,7 @@ void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event
if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
Vector<int> selected = patterns_item_list->get_selected_items();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove TileSet patterns"));
for (int i = 0; i < selected.size(); i++) {
int pattern_index = selected[i];
@@ -416,8 +416,8 @@ void TileSetEditor::_tab_changed(int p_tab_changed) {
}
void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
- Ref<EditorUndoRedoManager> undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(undo_redo_man.is_null());
+ EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo_man);
TileSet *ed_tile_set = Object::cast_to<TileSet>(p_edited);
if (!ed_tile_set) {
@@ -597,8 +597,8 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
}
void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
- Ref<EditorUndoRedoManager> undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
- ERR_FAIL_COND(undo_redo_man.is_null());
+ EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+ ERR_FAIL_NULL(undo_redo_man);
#define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
TileSet *ed_tile_set = Object::cast_to<TileSet>(p_edited);
@@ -728,7 +728,7 @@ TileSetEditor::TileSetEditor() {
sources_list->add_user_signal(MethodInfo("sort_request"));
sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list).bind(-1));
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
- sources_list->set_drag_forwarding(this);
+ sources_list->set_drag_forwarding_compat(this);
split_container_left_side->add_child(sources_list);
HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
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 5af338caa0..8d48302f1a 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -237,7 +237,7 @@ void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_inde
void TileSetScenesCollectionSourceEditor::_source_add_pressed() {
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", Ref<PackedScene>(), scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
@@ -252,7 +252,7 @@ void TileSetScenesCollectionSourceEditor::_source_delete_pressed() {
ERR_FAIL_COND(selected_indices.size() <= 0);
int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id);
@@ -404,7 +404,7 @@ void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, c
Ref<PackedScene> resource = ResourceLoader::load(files[i]);
if (resource.is_valid()) {
int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add a Scene Tile"));
undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id);
undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id);
@@ -509,7 +509,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scene_tiles_list = memnew(ItemList);
scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
- scene_tiles_list->set_drag_forwarding(this);
+ scene_tiles_list->set_drag_forwarding_compat(this);
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_tile_inspector).unbind(1));
scene_tiles_list->connect("item_selected", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1));
scene_tiles_list->connect("item_activated", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_list_item_activated));
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index b40e588e07..19ee0ae98d 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -185,26 +185,34 @@ void TilesEditorPlugin::_notification(int p_what) {
}
void TilesEditorPlugin::make_visible(bool p_visible) {
- is_visible = p_visible;
-
- if (is_visible) {
+ if (p_visible || is_tile_map_selected()) {
// Disable and hide invalid editors.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
tileset_editor_button->set_visible(tile_set.is_valid());
tilemap_editor_button->set_visible(tile_map);
- if (tile_map && !is_editing_tile_set) {
+ if (tile_map && (!is_editing_tile_set || !p_visible)) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(tilemap_editor);
} else {
EditorNode::get_singleton()->make_bottom_panel_item_visible(tileset_editor);
}
-
+ is_visible = true;
} else {
tileset_editor_button->hide();
tilemap_editor_button->hide();
EditorNode::get_singleton()->hide_bottom_panel();
+ is_visible = false;
}
}
+bool TilesEditorPlugin::is_tile_map_selected() {
+ TypedArray<Node> selection = get_editor_interface()->get_selection()->get_selected_nodes();
+ if (selection.size() == 1 && Object::cast_to<TileMap>(selection[0])) {
+ return true;
+ }
+
+ return false;
+}
+
void TilesEditorPlugin::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_pattern.is_valid());
@@ -362,19 +370,24 @@ void TilesEditorPlugin::edit(Object *p_object) {
} else if (p_object->is_class("TileSet")) {
tile_set = Ref<TileSet>(p_object);
if (tile_map) {
- if (tile_map->get_tileset() != tile_set || !tile_map->is_inside_tree()) {
+ if (tile_map->get_tileset() != tile_set || !tile_map->is_inside_tree() || !is_tile_map_selected()) {
tile_map = nullptr;
tile_map_id = ObjectID();
}
}
is_editing_tile_set = true;
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tileset_editor);
}
}
// Update the editors.
_update_editors();
+ // If the tileset is being edited, the visibility function must be called
+ // here after _update_editors has been called.
+ if (is_editing_tile_set) {
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(tileset_editor);
+ }
+
// Add change listener.
if (tile_map) {
tile_map->connect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 5b91584aa3..50073e59c6 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -110,6 +110,8 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tilemap_editor->forward_canvas_gui_input(p_event); }
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+ bool is_tile_map_selected();
+
// Pattern preview API.
void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 4cf397ffd0..2c93d46e38 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -129,30 +129,32 @@ void VisualShaderGraphPlugin::set_connections(const List<VisualShader::Connectio
void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id) {
if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) {
- for (const KeyValue<int, Port> &E : links[p_node_id].output_ports) {
+ Link &link = links[p_node_id];
+
+ for (const KeyValue<int, Port> &E : link.output_ports) {
if (E.value.preview_button != nullptr) {
E.value.preview_button->set_pressed(false);
}
}
+ bool is_dirty = link.preview_pos < 0;
- if (links[p_node_id].preview_visible && !is_dirty() && links[p_node_id].preview_box != nullptr) {
- links[p_node_id].graph_node->remove_child(links[p_node_id].preview_box);
- memdelete(links[p_node_id].preview_box);
- links[p_node_id].graph_node->reset_size();
- links[p_node_id].preview_visible = false;
+ if (!is_dirty && link.preview_visible && link.preview_box != nullptr) {
+ link.graph_node->remove_child(link.preview_box);
+ memdelete(link.preview_box);
+ link.preview_box = nullptr;
+ link.graph_node->reset_size();
+ link.preview_visible = false;
}
- if (p_port_id != -1 && links[p_node_id].output_ports[p_port_id].preview_button != nullptr) {
- if (is_dirty()) {
- links[p_node_id].preview_pos = links[p_node_id].graph_node->get_child_count();
+ if (p_port_id != -1 && link.output_ports[p_port_id].preview_button != nullptr) {
+ if (is_dirty) {
+ link.preview_pos = link.graph_node->get_child_count();
}
VBoxContainer *vbox = memnew(VBoxContainer);
- links[p_node_id].graph_node->add_child(vbox);
- if (links[p_node_id].preview_pos != -1) {
- links[p_node_id].graph_node->move_child(vbox, links[p_node_id].preview_pos);
- }
- links[p_node_id].graph_node->set_slot_draw_stylebox(vbox->get_index(), false);
+ link.graph_node->add_child(vbox);
+ link.graph_node->move_child(vbox, link.preview_pos);
+ link.graph_node->set_slot_draw_stylebox(vbox->get_index(), false);
Control *offset = memnew(Control);
offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE));
@@ -162,9 +164,9 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p
port_preview->setup(visual_shader, visual_shader->get_shader_type(), p_node_id, p_port_id);
port_preview->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
vbox->add_child(port_preview);
- links[p_node_id].preview_visible = true;
- links[p_node_id].preview_box = vbox;
- links[p_node_id].output_ports[p_port_id].preview_button->set_pressed(true);
+ link.preview_visible = true;
+ link.preview_box = vbox;
+ link.output_ports[p_port_id].preview_button->set_pressed(true);
}
}
}
@@ -177,8 +179,8 @@ void VisualShaderGraphPlugin::update_node(VisualShader::Type p_type, int p_node_
if (p_type != visual_shader->get_shader_type() || !links.has(p_node_id)) {
return;
}
- remove_node(p_type, p_node_id);
- add_node(p_type, p_node_id);
+ remove_node(p_type, p_node_id, true);
+ add_node(p_type, p_node_id, true);
}
void VisualShaderGraphPlugin::set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, Variant p_value) {
@@ -305,8 +307,8 @@ void VisualShaderGraphPlugin::update_parameter_refs() {
for (KeyValue<int, Link> &E : links) {
VisualShaderNodeParameterRef *ref = Object::cast_to<VisualShaderNodeParameterRef>(E.value.visual_node);
if (ref) {
- remove_node(E.value.type, E.key);
- add_node(E.value.type, E.key);
+ remove_node(E.value.type, E.key, true);
+ add_node(E.value.type, E.key, true);
}
}
}
@@ -329,14 +331,6 @@ void VisualShaderGraphPlugin::clear_links() {
links.clear();
}
-bool VisualShaderGraphPlugin::is_dirty() const {
- return dirty;
-}
-
-void VisualShaderGraphPlugin::make_dirty(bool p_enabled) {
- dirty = p_enabled;
-}
-
void VisualShaderGraphPlugin::register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphNode *p_graph_node) {
links.insert(p_id, { p_type, p_visual_node, p_graph_node, p_visual_node->get_output_port_for_preview() != -1, -1, HashMap<int, InputPort>(), HashMap<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } });
}
@@ -356,7 +350,7 @@ void VisualShaderGraphPlugin::update_theme() {
vector_expanded_color[3] = editor->get_theme_color(SNAME("axis_w_color"), SNAME("Editor")); // alpha
}
-void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
+void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool p_just_update) {
if (!visual_shader.is_valid() || p_type != visual_shader->get_shader_type()) {
return;
}
@@ -424,7 +418,18 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
graph->add_child(node);
node->set_theme(vstheme);
editor->_update_created_node(node);
- register_link(p_type, p_id, vsnode.ptr(), node);
+
+ if (p_just_update) {
+ Link &link = links[p_id];
+
+ link.graph_node = node;
+ link.preview_box = nullptr;
+ link.preview_pos = -1;
+ link.output_ports.clear();
+ link.input_ports.clear();
+ } else {
+ register_link(p_type, p_id, vsnode.ptr(), node);
+ }
if (is_resizable) {
size = resizable_node->get_size();
@@ -1038,11 +1043,13 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
}
}
-void VisualShaderGraphPlugin::remove_node(VisualShader::Type p_type, int p_id) {
+void VisualShaderGraphPlugin::remove_node(VisualShader::Type p_type, int p_id, bool p_just_update) {
if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
links[p_id].graph_node->get_parent()->remove_child(links[p_id].graph_node);
memdelete(links[p_id].graph_node);
- links.erase(p_id);
+ if (!p_just_update) {
+ links.erase(p_id);
+ }
}
}
@@ -1854,7 +1861,7 @@ void VisualShaderEditor::_update_parameters(bool p_update_refs) {
}
void VisualShaderEditor::_update_parameter_refs(HashSet<String> &p_deleted_names) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
VisualShader::Type type = VisualShader::Type(i);
@@ -1909,15 +1916,12 @@ void VisualShaderEditor::_update_graph() {
_update_varyings();
graph_plugin->clear_links();
- graph_plugin->make_dirty(true);
graph_plugin->update_theme();
for (int n_i = 0; n_i < nodes.size(); n_i++) {
- graph_plugin->add_node(type, nodes[n_i]);
+ graph_plugin->add_node(type, nodes[n_i], false);
}
- graph_plugin->make_dirty(false);
-
for (const VisualShader::Connection &E : node_connections) {
int from = E.from_node;
int from_idx = E.from_port;
@@ -1954,7 +1958,7 @@ void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Input Port"));
undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name);
undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port);
@@ -1970,7 +1974,7 @@ void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_typ
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Output Port"));
undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name);
undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port);
@@ -1986,7 +1990,7 @@ void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_p
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Input Port Type"));
undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type);
undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port));
@@ -2002,7 +2006,7 @@ void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Output Port Type"));
undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type);
undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port));
@@ -2031,7 +2035,7 @@ void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *p
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Input Port Name"));
undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, validated_name);
undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id));
@@ -2058,7 +2062,7 @@ void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Output Port Name"));
undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, validated_name);
undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, prev_name);
@@ -2071,7 +2075,7 @@ void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expa
Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
ERR_FAIL_COND(!node.is_valid());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_expand) {
undo_redo->create_action(TTR("Expand Output Port"));
} else {
@@ -2169,7 +2173,7 @@ void VisualShaderEditor::_remove_input_port(int p_node, int p_port) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Input Port"));
List<VisualShader::Connection> conns;
@@ -2219,7 +2223,7 @@ void VisualShaderEditor::_remove_output_port(int p_node, int p_port) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Output Port"));
List<VisualShader::Connection> conns;
@@ -2286,7 +2290,7 @@ void VisualShaderEditor::_expression_focus_out(Object *code_edit, int p_node) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set VisualShader Expression"));
undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text());
undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression());
@@ -2350,7 +2354,7 @@ void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, in
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Resize VisualShader Node"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size);
undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size());
@@ -2367,7 +2371,7 @@ void VisualShaderEditor::_preview_select_port(int p_node, int p_port) {
if (node->get_output_port_for_preview() == p_port) {
p_port = -1; //toggle it
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(p_port == -1 ? TTR("Hide Port Preview") : TTR("Show Port Preview"));
undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port);
undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", prev_port);
@@ -2413,7 +2417,7 @@ void VisualShaderEditor::_comment_title_popup_hide() {
if (node->get_title() == comment_title_change_edit->get_text()) {
return; // nothing changed - ignored
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Comment Node Title"));
undo_redo->add_do_method(node.ptr(), "set_title", comment_title_change_edit->get_text());
undo_redo->add_undo_method(node.ptr(), "set_title", node->get_title());
@@ -2456,7 +2460,7 @@ void VisualShaderEditor::_comment_desc_popup_hide() {
if (node->get_description() == comment_desc_change_edit->get_text()) {
return; // nothing changed - ignored
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Comment Node Description"));
undo_redo->add_do_method(node.ptr(), "set_description", comment_desc_change_edit->get_text());
undo_redo->add_undo_method(node.ptr(), "set_description", node->get_title());
@@ -2477,7 +2481,7 @@ void VisualShaderEditor::_parameter_line_edit_changed(const String &p_text, int
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Parameter Name"));
undo_redo->add_do_method(node.ptr(), "set_parameter_name", validated_name);
undo_redo->add_undo_method(node.ptr(), "set_parameter_name", node->get_parameter_name());
@@ -2513,7 +2517,7 @@ void VisualShaderEditor::_port_edited(const StringName &p_property, const Varian
Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node);
ERR_FAIL_COND(!vsn.is_valid());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Input Default Port"));
Ref<VisualShaderNodeCustom> custom = Object::cast_to<VisualShaderNodeCustom>(vsn.ptr());
@@ -2959,7 +2963,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
int id_to_use = visual_shader->get_valid_node_id(type);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_resource_path.is_empty()) {
undo_redo->create_action(TTR("Add Node to Visual Shader"));
} else {
@@ -2967,8 +2971,8 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
}
undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use);
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use);
- undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_to_use);
- undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_to_use);
+ undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_to_use, false);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_to_use, false);
VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr());
if (expr) {
@@ -3126,7 +3130,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
}
void VisualShaderEditor::_add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Add Varying to Visual Shader: %s"), p_name));
undo_redo->add_do_method(visual_shader.ptr(), "add_varying", p_name, p_mode, p_type);
@@ -3161,7 +3165,7 @@ void VisualShaderEditor::_add_varying(const String &p_name, VisualShader::Varyin
}
void VisualShaderEditor::_remove_varying(const String &p_name) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Remove Varying from Visual Shader: %s"), p_name));
VisualShader::VaryingMode var_mode = visual_shader->get_varying_mode(p_name);
@@ -3249,7 +3253,7 @@ void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_t
void VisualShaderEditor::_nodes_dragged() {
drag_dirty = false;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Node(s) Moved"));
for (const DragOp &E : drag_buffer) {
@@ -3273,7 +3277,7 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Nodes Connected"));
List<VisualShader::Connection> conns;
@@ -3305,7 +3309,7 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from
int from = p_from.to_int();
int to = p_to.to_int();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Nodes Disconnected"));
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index);
@@ -3345,7 +3349,7 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
List<VisualShader::Connection> conns;
visual_shader->get_node_connections(type, &conns);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
for (const int &F : p_nodes) {
for (const VisualShader::Connection &E : conns) {
if (E.from_node == F || E.to_node == F) {
@@ -3361,7 +3365,7 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F);
undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F), F);
- undo_redo->add_undo_method(graph_plugin.ptr(), "add_node", type, F);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "add_node", type, F, false);
VisualShaderNodeParameter *parameter = Object::cast_to<VisualShaderNodeParameter>(node.ptr());
if (parameter) {
@@ -3391,7 +3395,7 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
// delete nodes from the graph
for (const int &F : p_nodes) {
- undo_redo->add_do_method(graph_plugin.ptr(), "remove_node", type, F);
+ undo_redo->add_do_method(graph_plugin.ptr(), "remove_node", type, F, false);
}
// update parameter refs if any parameter has been deleted
@@ -3404,7 +3408,7 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
}
void VisualShaderEditor::_replace_node(VisualShader::Type p_type_id, int p_node_id, const StringName &p_from, const StringName &p_to) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(visual_shader.ptr(), "replace_node", p_type_id, p_node_id, p_to);
undo_redo->add_undo_method(visual_shader.ptr(), "replace_node", p_type_id, p_node_id, p_from);
}
@@ -3439,7 +3443,7 @@ void VisualShaderEditor::_update_parameter(VisualShader::Type p_type_id, int p_n
void VisualShaderEditor::_convert_constants_to_parameters(bool p_vice_versa) {
VisualShader::Type type_id = get_current_shader_type();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (!p_vice_versa) {
undo_redo->create_action(TTR("Convert Constant Node(s) To Parameter(s)"));
} else {
@@ -3638,7 +3642,7 @@ void VisualShaderEditor::_delete_node_request(int p_type, int p_node) {
List<int> to_erase;
to_erase.push_back(p_node);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete VisualShader Node"));
_delete_nodes(p_type, to_erase);
undo_redo->commit_action();
@@ -3667,7 +3671,7 @@ void VisualShaderEditor::_delete_nodes_request(const TypedArray<StringName> &p_n
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete VisualShader Node(s)"));
_delete_nodes(get_current_shader_type(), to_erase);
undo_redo->commit_action();
@@ -4080,7 +4084,7 @@ void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, Li
}
void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_duplicate) {
undo_redo->create_action(TTR("Duplicate VisualShader Node(s)"));
} else {
@@ -4131,7 +4135,7 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c
}
undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, node, item.position + p_offset, id_from);
- undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from);
+ undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from, false);
added_set.insert(id_from);
id_from++;
@@ -4153,7 +4157,7 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c
continue;
}
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
- undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_from);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_from, false);
id_from++;
}
@@ -4199,7 +4203,7 @@ void VisualShaderEditor::_copy_nodes(bool p_cut) {
_dup_copy_nodes(get_current_shader_type(), copy_items_buffer, copy_connections_buffer);
if (p_cut) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Cut VisualShader Node(s)"));
List<int> ids;
@@ -4286,7 +4290,7 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input,
bool type_changed = next_input_type != prev_input_type;
- Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo_man = EditorUndoRedoManager::get_singleton();
undo_redo_man->create_action(TTR("Visual Shader Input Type Changed"));
undo_redo_man->add_do_method(p_input.ptr(), "set_input_name", p_name);
@@ -4355,7 +4359,7 @@ void VisualShaderEditor::_parameter_ref_select_item(Ref<VisualShaderNodeParamete
bool type_changed = p_parameter_ref->get_parameter_type_by_name(p_name) != p_parameter_ref->get_parameter_type_by_name(prev_name);
- Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo_man = EditorUndoRedoManager::get_singleton();
undo_redo_man->create_action(TTR("ParameterRef Name Changed"));
undo_redo_man->add_do_method(p_parameter_ref.ptr(), "set_parameter_name", p_name);
@@ -4399,7 +4403,7 @@ void VisualShaderEditor::_varying_select_item(Ref<VisualShaderNodeVarying> p_var
bool is_getter = Ref<VisualShaderNodeVaryingGetter>(p_varying.ptr()).is_valid();
- Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo_man = EditorUndoRedoManager::get_singleton();
undo_redo_man->create_action(TTR("Varying Name Changed"));
undo_redo_man->add_do_method(p_varying.ptr(), "set_varying_name", p_name);
@@ -4470,7 +4474,7 @@ void VisualShaderEditor::_float_constant_selected(int p_which) {
return; // same
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Set Constant: %s"), float_constant_defs[p_which].name));
undo_redo->add_do_method(node.ptr(), "set_constant", float_constant_defs[p_which].value);
undo_redo->add_undo_method(node.ptr(), "set_constant", node->get_constant());
@@ -4734,7 +4738,7 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
saved_node_pos_dirty = true;
_add_node(idx, add_options[idx].ops);
} else if (d.has("files")) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Node(s) to Visual Shader"));
if (d["files"].get_type() == Variant::PACKED_STRING_ARRAY) {
@@ -4915,7 +4919,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->set_h_size_flags(SIZE_EXPAND_FILL);
graph->set_show_zoom_label(true);
add_child(graph);
- graph->set_drag_forwarding(this);
+ graph->set_drag_forwarding_compat(this);
float graph_minimap_opacity = EDITOR_GET("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
float graph_lines_curvature = EDITOR_GET("editors/visual_editors/lines_curvature");
@@ -5146,7 +5150,7 @@ VisualShaderEditor::VisualShaderEditor() {
members = memnew(Tree);
members_vb->add_child(members);
- members->set_drag_forwarding(this);
+ members->set_drag_forwarding_compat(this);
members->set_h_size_flags(SIZE_EXPAND_FILL);
members->set_v_size_flags(SIZE_EXPAND_FILL);
members->set_hide_root(true);
@@ -6165,7 +6169,7 @@ public:
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
updating = true;
undo_redo->create_action(TTR("Edit Visual Property:") + " " + p_property, UndoRedo::MERGE_ENDS);
@@ -6367,7 +6371,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Visual Shader Mode Changed"));
//do is easy
undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which);
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index a43d44473f..c4f6b4952c 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -95,7 +95,6 @@ private:
Ref<VisualShader> visual_shader;
HashMap<int, Link> links;
List<VisualShader::Connection> connections;
- bool dirty = false;
Color vector_expanded_color[4];
@@ -115,12 +114,10 @@ public:
void clear_links();
void set_shader_type(VisualShader::Type p_type);
bool is_preview_visible(int p_id) const;
- bool is_dirty() const;
- void make_dirty(bool p_enabled);
void update_node(VisualShader::Type p_type, int p_id);
void update_node_deferred(VisualShader::Type p_type, int p_node_id);
- void add_node(VisualShader::Type p_type, int p_id);
- void remove_node(VisualShader::Type p_type, int p_id);
+ void add_node(VisualShader::Type p_type, int p_id, bool p_just_update);
+ void remove_node(VisualShader::Type p_type, int p_id, bool p_just_update);
void connect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id);
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index 4ecf3338bc..db11f80826 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -32,6 +32,7 @@
#include "core/object/message_queue.h"
#include "core/os/os.h"
+#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "main/main.h"
#include "servers/display_server.h"
@@ -133,6 +134,14 @@ void BackgroundProgress::end_task(const String &p_task) {
ProgressDialog *ProgressDialog::singleton = nullptr;
void ProgressDialog::_notification(int p_what) {
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (!is_visible()) {
+ Node *p = get_parent();
+ if (p) {
+ p->remove_child(this);
+ }
+ }
+ }
}
void ProgressDialog::_popup() {
@@ -146,8 +155,17 @@ void ProgressDialog::_popup() {
main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP));
main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM));
- //raise();
- popup_centered(ms);
+ EditorNode *ed = EditorNode::get_singleton();
+ if (ed && !is_inside_tree()) {
+ Window *w = ed->get_window();
+ while (w && w->get_exclusive_child()) {
+ w = w->get_exclusive_child();
+ }
+ if (w && w != this) {
+ w->add_child(this);
+ popup_centered(ms);
+ }
+ }
}
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index 22d118cd90..8396d50a0e 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -122,9 +122,6 @@ static const char *enum_renames[][2] = {
{ "JOINT_SLIDER", "JOINT_TYPE_SLIDER" }, // PhysicsServer3D
{ "KEY_CONTROL", "KEY_CTRL" }, // Globals
{ "LOOP_PING_PONG", "LOOP_PINGPONG" }, // AudioStreamWAV
- { "MATH_RAND", "MATH_RANDF_RANGE" }, // VisualScriptBuiltinFunc
- { "MATH_RANDOM", "MATH_RANDI_RANGE" }, // VisualScriptBuiltinFunc
- { "MATH_STEPIFY", "MATH_STEP_DECIMALS" }, // VisualScriptBuiltinFunc
{ "MODE_KINEMATIC", "FREEZE_MODE_KINEMATIC" }, // RigidBody
{ "MODE_OPEN_ANY", "FILE_MODE_OPEN_ANY" }, // FileDialog
{ "MODE_OPEN_DIR", "FILE_MODE_OPEN_DIR" }, // FileDialog
@@ -239,6 +236,7 @@ static const char *gdscript_function_renames[][2] = {
{ "body_add_torque", "body_apply_torque" }, // PhysicsServer2D
{ "bumpmap_to_normalmap", "bump_map_to_normal_map" }, // Image
{ "can_be_hidden", "_can_be_hidden" }, // EditorNode3DGizmoPlugin
+ { "can_drop_data", "_can_drop_data" }, // Control
{ "can_drop_data_fw", "_can_drop_data_fw" }, // ScriptEditor
{ "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator
{ "can_instance", "can_instantiate" }, // PackedScene, Script
@@ -265,6 +263,7 @@ static const char *gdscript_function_renames[][2] = {
{ "delete_char_at_cursor", "delete_char_at_caret" }, // LineEdit
{ "deselect_items", "deselect_all" }, // FileDialog
{ "disable_plugin", "_disable_plugin" }, // EditorPlugin
+ { "drop_data", "_drop_data" }, // Control
{ "drop_data_fw", "_drop_data_fw" }, // ScriptEditor
{ "exclude_polygons_2d", "exclude_polygons" }, // Geometry2D
{ "find_node", "find_child" }, // Node
@@ -694,6 +693,7 @@ static const char *csharp_function_renames[][2] = {
{ "BindChildNodeToBone", "SetBoneChildren" }, // Skeleton3D
{ "BumpmapToNormalmap", "BumpMapToNormalMap" }, // Image
{ "CanBeHidden", "_CanBeHidden" }, // EditorNode3DGizmoPlugin
+ { "CanDropData", "_CanDropData" }, // Control
{ "CanDropDataFw", "_CanDropDataFw" }, // ScriptEditor
{ "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator
{ "CanInstance", "CanInstantiate" }, // PackedScene, Script
@@ -717,6 +717,7 @@ static const char *csharp_function_renames[][2] = {
{ "DampedStringJointSetParam", "DampedSpringJointSetParam" }, // PhysicsServer2D
{ "DeleteCharAtCursor", "DeleteCharAtCaret" }, // LineEdit
{ "DeselectItems", "DeselectAll" }, // FileDialog
+ { "DropData", "_DropData" }, // Control
{ "DropDataFw", "_DropDataFw" }, // ScriptEditor
{ "ExcludePolygons2d", "ExcludePolygons" }, // Geometry2D
{ "FindScancodeFromString", "FindKeycodeFromString" }, // OS
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index dc755cab41..c4f5eb777e 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1233,7 +1233,7 @@ void ProjectList::load_project_icon(int p_index) {
// The default project icon is 128Ɨ128 to look crisp on hiDPI displays,
// but we want the actual displayed size to be 64Ɨ64 on loDPI displays.
- item.control->icon->set_ignore_texture_size(true);
+ item.control->icon->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
item.control->icon->set_custom_minimum_size(Size2(64, 64) * EDSCALE);
item.control->icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
@@ -1273,7 +1273,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features);
uint64_t last_edited = 0;
- if (FileAccess::exists(conf)) {
+ if (cf_err == OK) {
// The modification date marks the date the project was last edited.
// This is because the `project.godot` file will always be modified
// when editing a project (but not when running it).
@@ -2653,8 +2653,9 @@ ProjectManager::ProjectManager() {
AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2);
}
- set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- set_theme(create_custom_theme());
+ Ref<Theme> theme = create_custom_theme();
+ set_theme(theme);
+ DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), SNAME("Editor")));
set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
@@ -3008,7 +3009,10 @@ ProjectManager::ProjectManager() {
SceneTree::get_singleton()->get_root()->connect("files_dropped", callable_mp(this, &ProjectManager::_files_dropped));
// Define a minimum window size to prevent UI elements from overlapping or being cut off.
- DisplayServer::get_singleton()->window_set_min_size(Size2(520, 350) * EDSCALE);
+ Window *w = Object::cast_to<Window>(SceneTree::get_singleton()->get_root());
+ if (w) {
+ w->set_min_size(Size2(520, 350) * EDSCALE);
+ }
// Resize the bootsplash window based on Editor display scale EDSCALE.
float scale_factor = MAX(1, EDSCALE);
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 69c21177f9..560549d249 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -114,7 +114,7 @@ void ProjectSettingsEditor::_add_setting() {
Variant value;
Variant::construct(Variant::Type(type_box->get_selected_id()), value, nullptr, 0, ce);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Project Setting"));
undo_redo->add_do_property(ps, setting, value);
undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant());
@@ -134,7 +134,7 @@ void ProjectSettingsEditor::_delete_setting() {
Variant value = ps->get(setting);
int order = ps->get_order(setting);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Delete Item"));
undo_redo->add_do_method(ps, "clear", setting);
@@ -223,7 +223,7 @@ void ProjectSettingsEditor::_select_type(Variant::Type p_type) {
void ProjectSettingsEditor::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
const Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed()) {
@@ -345,7 +345,7 @@ void ProjectSettingsEditor::_action_added(const String &p_name) {
action["events"] = Array();
action["deadzone"] = 0.5f;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Input Action"));
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name);
@@ -361,7 +361,7 @@ void ProjectSettingsEditor::_action_edited(const String &p_name, const Dictionar
const String property_name = "input/" + p_name;
Dictionary old_val = GLOBAL_GET(property_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (old_val["deadzone"] != p_action["deadzone"]) {
// Deadzone Changed
undo_redo->create_action(TTR("Change Action deadzone"));
@@ -398,7 +398,7 @@ void ProjectSettingsEditor::_action_removed(const String &p_name) {
Dictionary old_val = GLOBAL_GET(property_name);
int order = ProjectSettings::get_singleton()->get_order(property_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Erase Input Action"));
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property_name);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);
@@ -421,7 +421,7 @@ void ProjectSettingsEditor::_action_renamed(const String &p_old_name, const Stri
int order = ProjectSettings::get_singleton()->get_order(old_property_name);
Dictionary action = GLOBAL_GET(old_property_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Input Action Event"));
// Do: clear old, set new
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", old_property_name);
@@ -451,7 +451,7 @@ void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const
HashMap<String, Variant> action_values;
ProjectSettings::get_singleton()->get_property_list(&props);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Update Input Action Order"));
for (const PropertyInfo &prop : props) {
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index b7b316df64..c3a99e96ef 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -583,7 +583,7 @@ void RenameDialog::rename() {
_iterate_scene(root_node, selected_node_list, &global_count);
if (!to_rename.is_empty()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Batch Rename"));
// Make sure to iterate reversed so that child nodes will find parents.
diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp
index c1dbd4d6bc..c861fadcf0 100644
--- a/editor/scene_create_dialog.cpp
+++ b/editor/scene_create_dialog.cpp
@@ -171,7 +171,9 @@ Node *SceneCreateDialog::create_scene_root() {
break;
case ROOT_USER_INTERFACE: {
Control *gui_ctl = memnew(Control);
+ // Making the root control full rect by default is more useful for resizable UIs.
gui_ctl->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ gui_ctl->set_grow_direction_preset(Control::PRESET_FULL_RECT);
root = gui_ctl;
} break;
case ROOT_OTHER:
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index d2d5f249dd..733f140a56 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -231,7 +231,7 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Instantiate Scene(s)"));
for (int i = 0; i < instances.size(); i++) {
@@ -275,7 +275,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Replace with Branch Scene"));
Node *parent = base->get_parent();
@@ -540,7 +540,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Detach Script"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_method(EditorNode::get_singleton(), "push_item", (Script *)nullptr);
@@ -611,7 +611,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break; // one or more nodes can not be moved
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (selection.size() == 1) {
undo_redo->create_action(TTR("Move Node In Parent"));
}
@@ -661,7 +661,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Duplicate Node(s)"), UndoRedo::MERGE_DISABLE, selection.front()->get());
undo_redo->add_do_method(editor_selection, "clear");
@@ -778,7 +778,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Make node as Root"));
undo_redo->add_do_method(node->get_parent(), "remove_child", node);
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", node);
@@ -1023,7 +1023,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *node = e->get();
if (node) {
Node *root = EditorNode::get_singleton()->get_edited_scene();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (!root) {
break;
}
@@ -1099,7 +1099,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
bool enabling = !first_selected->get()->is_unique_name_in_owner();
List<Node *> full_selection = editor_selection->get_full_selected_node_list();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (enabling) {
Vector<Node *> new_unique_nodes;
@@ -1184,7 +1184,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
case TOOL_CREATE_USER_INTERFACE: {
Control *node = memnew(Control);
- node->set_anchors_and_offsets_preset(PRESET_FULL_RECT); //more useful for resizable UIs.
+ // Making the root control full rect by default is more useful for resizable UIs.
+ node->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ node->set_grow_direction_preset(PRESET_FULL_RECT);
new_node = node;
} break;
@@ -1224,7 +1226,7 @@ void SceneTreeDock::_property_selected(int p_idx) {
}
void SceneTreeDock::_perform_property_drop(Node *p_node, String p_property, Ref<Resource> p_res) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(TTR("Set %s"), p_property));
undo_redo->add_do_property(p_node, p_property, p_res);
undo_redo->add_undo_property(p_node, p_property, p_node->get(p_property));
@@ -1232,7 +1234,7 @@ void SceneTreeDock::_perform_property_drop(Node *p_node, String p_property, Ref<
}
void SceneTreeDock::add_root_node(Node *p_node) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action_for_history(TTR("New Scene Root"), editor_data->get_current_edited_scene_history_id());
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_node);
undo_redo->add_do_method(scene_tree, "update_tree");
@@ -1383,7 +1385,7 @@ void SceneTreeDock::_notification(int p_what) {
void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) {
if (p_node->get_owner() == p_base && p_node != p_root) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (p_mode) {
case MODE_BIDI: {
bool disable_unique = p_node->is_unique_name_in_owner() && p_root->get_node_or_null(UNIQUE_NODE_PREFIX + String(p_node->get_name())) != nullptr;
@@ -1617,7 +1619,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
Variant old_variant = p_base->get(propertyname);
Variant updated_variant = old_variant;
if (_check_node_path_recursive(p_base, updated_variant, p_renames)) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_property(p_base, propertyname, updated_variant);
undo_redo->add_undo_property(p_base, propertyname, old_variant);
p_base->set(propertyname, updated_variant);
@@ -1664,7 +1666,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
}
HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (found_path) {
if (found_path->value == NodePath()) {
//will be erased
@@ -1831,7 +1833,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
// Sort by tree order, so re-adding is easy.
p_nodes.sort_custom<Node::Comparator>();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Reparent Node"), UndoRedo::MERGE_DISABLE, p_nodes[0]);
HashMap<Node *, NodePath> path_renames;
@@ -1966,7 +1968,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, selected.front()->get());
for (Node *E : selected) {
Ref<Script> existing = E->get_script();
@@ -1992,7 +1994,7 @@ void SceneTreeDock::_shader_created(Ref<Shader> p_shader) {
Ref<Shader> existing = selected_shader_material->get_shader();
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Shader"));
undo_redo->add_do_method(selected_shader_material.ptr(), "set_shader", p_shader);
undo_redo->add_undo_method(selected_shader_material.ptr(), "set_shader", existing);
@@ -2061,7 +2063,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
EditorNode::get_singleton()->get_editor_plugins_over()->make_visible(false);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ 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;
@@ -2191,7 +2193,7 @@ void SceneTreeDock::_do_create(Node *p_parent) {
}
child->set_name(new_name);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action_for_history(TTR("Create Node"), editor_data->get_current_edited_scene_history_id());
if (edited_scene) {
@@ -2260,7 +2262,7 @@ void SceneTreeDock::_create() {
List<Node *> selection = editor_selection->get_selected_node_list();
ERR_FAIL_COND(selection.size() <= 0);
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, selection.front()->get());
for (Node *n : selection) {
@@ -2394,7 +2396,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
}
//p_remove_old was added to support undo
if (p_remove_old) {
- EditorNode::get_undo_redo()->clear_history();
+ EditorUndoRedoManager::get_singleton()->clear_history();
}
newnode->set_name(newname);
@@ -2616,7 +2618,7 @@ void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
Object *obj = ClassDB::instantiate(scr->get_instance_base_type());
ERR_FAIL_NULL(obj);
@@ -3188,7 +3190,7 @@ List<Node *> SceneTreeDock::paste_nodes() {
owner = paste_parent;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Paste Node(s)"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
ur->add_do_method(editor_selection, "clear");
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index dad584ea92..148dcd862a 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -68,7 +68,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
Node *n = get_node(np);
ERR_FAIL_COND(!n);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_id == BUTTON_SUBSCENE) {
if (n == get_scene_node()) {
if (n && n->get_scene_inherited_state().is_valid()) {
@@ -176,7 +176,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
void SceneTreeEditor::_toggle_visible(Node *p_node) {
if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) {
bool v = bool(p_node->call("is_visible"));
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->add_do_method(p_node, "set_visible", !v);
undo_redo->add_undo_method(p_node, "set_visible", v);
}
@@ -993,6 +993,10 @@ void SceneTreeEditor::_renamed() {
}
if (new_name == n->get_name()) {
+ if (which->get_text(0).is_empty()) {
+ which->set_text(0, new_name);
+ }
+
return;
}
@@ -1011,7 +1015,7 @@ void SceneTreeEditor::_renamed() {
which->set_metadata(0, n->get_path());
emit_signal(SNAME("node_renamed"));
} else {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Node"));
emit_signal(SNAME("node_prerename"), n, new_name);
undo_redo->add_do_method(this, "_rename_node", n->get_instance_id(), new_name);
@@ -1415,7 +1419,7 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope
add_child(tree);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
if (p_can_rename) {
tree->set_allow_rmb_select(true);
tree->connect("item_mouse_selected", callable_mp(this, &SceneTreeEditor::_rmb_select));
diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp
index 4ac0a5b672..800de431eb 100644
--- a/editor/shader_globals_editor.cpp
+++ b/editor/shader_globals_editor.cpp
@@ -70,7 +70,7 @@ class ShaderGlobalsEditorInterface : public Object {
GDCLASS(ShaderGlobalsEditorInterface, Object)
void _set_var(const StringName &p_name, const Variant &p_value, const Variant &p_prev_value) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set Shader Global Variable"));
undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_value);
@@ -399,7 +399,7 @@ void ShaderGlobalsEditor::_variable_added() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Variant value = create_var(RS::GlobalShaderParameterType(variable_type->get_selected()));
@@ -418,7 +418,7 @@ void ShaderGlobalsEditor::_variable_added() {
}
void ShaderGlobalsEditor::_variable_deleted(const String &p_variable) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Shader Global Parameter"));
undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_remove", p_variable);
diff --git a/main/main.cpp b/main/main.cpp
index 4cab469672..00c6b1fecd 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1385,7 +1385,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#ifdef TOOLS_ENABLED
if (editor) {
packed_data->set_disabled(true);
- globals->set_disable_feature_overrides(true);
Engine::get_singleton()->set_editor_hint(true);
main_args.push_back("--editor");
if (!init_windowed) {
@@ -1920,6 +1919,9 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
window_position = &position;
}
+ Color boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", boot_splash_bg_color);
+ DisplayServer::set_early_window_clear_color_override(true, boot_bg_color);
+
// rendering_driver now held in static global String in main and initialized in setup()
Error err;
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, err);
@@ -2085,7 +2087,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0));
}
- Color boot_bg_color = GLOBAL_DEF_BASIC("application/boot_splash/bg_color", boot_splash_bg_color);
+ Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color");
#if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH)
boot_bg_color =
@@ -2120,6 +2122,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
#endif
}
+ DisplayServer::set_early_window_clear_color_override(false);
+
MAIN_PRINT("Main: DCC");
RenderingServer::get_singleton()->set_default_clear_color(
GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
@@ -2607,7 +2611,7 @@ bool Main::start() {
if (debug_navigation) {
sml->set_debug_navigation_hint(true);
NavigationServer3D::get_singleton()->set_active(true);
- NavigationServer3D::get_singleton_mut()->set_debug_enabled(true);
+ NavigationServer3D::get_singleton()->set_debug_enabled(true);
}
#endif
@@ -2769,10 +2773,6 @@ bool Main::start() {
DisplayServer::get_singleton()->window_set_title(appname);
#endif
- // Define a very small minimum window size to prevent bugs such as GH-37242.
- // It can still be overridden by the user in a script.
- DisplayServer::get_singleton()->window_set_min_size(Size2i(64, 64));
-
bool snap_controls = GLOBAL_GET("gui/common/snap_controls_to_pixels");
sml->get_root()->set_snap_controls_to_pixels(snap_controls);
@@ -2968,6 +2968,7 @@ bool Main::is_iterating() {
// For performance metrics.
static uint64_t physics_process_max = 0;
static uint64_t process_max = 0;
+static uint64_t navigation_process_max = 0;
bool Main::iteration() {
//for now do not error on this
@@ -2996,6 +2997,7 @@ bool Main::iteration() {
uint64_t physics_process_ticks = 0;
uint64_t process_ticks = 0;
+ uint64_t navigation_process_ticks = 0;
frame += ticks_elapsed;
@@ -3028,11 +3030,19 @@ bool Main::iteration() {
PhysicsServer2D::get_singleton()->flush_queries();
if (OS::get_singleton()->get_main_loop()->physics_process(physics_step * time_scale)) {
+ PhysicsServer3D::get_singleton()->end_sync();
+ PhysicsServer2D::get_singleton()->end_sync();
+
exit = true;
break;
}
- NavigationServer3D::get_singleton_mut()->process(physics_step * time_scale);
+ uint64_t navigation_begin = OS::get_singleton()->get_ticks_usec();
+
+ NavigationServer3D::get_singleton()->process(physics_step * time_scale);
+
+ navigation_process_ticks = MAX(navigation_process_ticks, OS::get_singleton()->get_ticks_usec() - navigation_begin); // keep the largest one for reference
+ navigation_process_max = MAX(OS::get_singleton()->get_ticks_usec() - navigation_begin, navigation_process_max);
message_queue->flush();
@@ -3112,8 +3122,10 @@ bool Main::iteration() {
Engine::get_singleton()->_fps = frames;
performance->set_process_time(USEC_TO_SEC(process_max));
performance->set_physics_process_time(USEC_TO_SEC(physics_process_max));
+ performance->set_navigation_process_time(USEC_TO_SEC(navigation_process_max));
process_max = 0;
physics_process_max = 0;
+ navigation_process_max = 0;
frame %= 1000000;
frames = 0;
diff --git a/main/performance.cpp b/main/performance.cpp
index c2624c708f..e24a3673b9 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -36,6 +36,7 @@
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "servers/audio_server.h"
+#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
#include "servers/physics_server_3d.h"
#include "servers/rendering_server.h"
@@ -54,6 +55,7 @@ void Performance::_bind_methods() {
BIND_ENUM_CONSTANT(TIME_FPS);
BIND_ENUM_CONSTANT(TIME_PROCESS);
BIND_ENUM_CONSTANT(TIME_PHYSICS_PROCESS);
+ BIND_ENUM_CONSTANT(TIME_NAVIGATION_PROCESS);
BIND_ENUM_CONSTANT(MEMORY_STATIC);
BIND_ENUM_CONSTANT(MEMORY_STATIC_MAX);
BIND_ENUM_CONSTANT(MEMORY_MESSAGE_BUFFER_MAX);
@@ -74,7 +76,15 @@ void Performance::_bind_methods() {
BIND_ENUM_CONSTANT(PHYSICS_3D_COLLISION_PAIRS);
BIND_ENUM_CONSTANT(PHYSICS_3D_ISLAND_COUNT);
BIND_ENUM_CONSTANT(AUDIO_OUTPUT_LATENCY);
-
+ BIND_ENUM_CONSTANT(NAVIGATION_ACTIVE_MAPS);
+ BIND_ENUM_CONSTANT(NAVIGATION_REGION_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_AGENT_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_LINK_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_POLYGON_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_EDGE_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT);
BIND_ENUM_CONSTANT(MONITOR_MAX);
}
@@ -93,6 +103,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
"time/fps",
"time/process",
"time/physics_process",
+ "time/navigation_process",
"memory/static",
"memory/static_max",
"memory/msg_buf_max",
@@ -113,6 +124,15 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
"physics_3d/collision_pairs",
"physics_3d/islands",
"audio/driver/output_latency",
+ "navigation/active_maps",
+ "navigation/regions",
+ "navigation/agents",
+ "navigation/links",
+ "navigation/polygons",
+ "navigation/edges",
+ "navigation/edges_merged",
+ "navigation/edges_connected",
+ "navigation/edges_free",
};
@@ -127,6 +147,8 @@ double Performance::get_monitor(Monitor p_monitor) const {
return _process_time;
case TIME_PHYSICS_PROCESS:
return _physics_process_time;
+ case TIME_NAVIGATION_PROCESS:
+ return _navigation_process_time;
case MEMORY_STATIC:
return Memory::get_mem_usage();
case MEMORY_STATIC_MAX:
@@ -167,6 +189,24 @@ double Performance::get_monitor(Monitor p_monitor) const {
return PhysicsServer3D::get_singleton()->get_process_info(PhysicsServer3D::INFO_ISLAND_COUNT);
case AUDIO_OUTPUT_LATENCY:
return AudioServer::get_singleton()->get_output_latency();
+ case NAVIGATION_ACTIVE_MAPS:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS);
+ case NAVIGATION_REGION_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_REGION_COUNT);
+ case NAVIGATION_AGENT_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_AGENT_COUNT);
+ case NAVIGATION_LINK_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_LINK_COUNT);
+ case NAVIGATION_POLYGON_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_POLYGON_COUNT);
+ case NAVIGATION_EDGE_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_COUNT);
+ case NAVIGATION_EDGE_MERGE_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_MERGE_COUNT);
+ case NAVIGATION_EDGE_CONNECTION_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT);
+ case NAVIGATION_EDGE_FREE_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT);
default: {
}
@@ -182,6 +222,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_TIME,
MONITOR_TYPE_TIME,
+ MONITOR_TYPE_TIME,
MONITOR_TYPE_MEMORY,
MONITOR_TYPE_MEMORY,
MONITOR_TYPE_MEMORY,
@@ -202,6 +243,15 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_TIME,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
};
@@ -216,6 +266,10 @@ void Performance::set_physics_process_time(double p_pt) {
_physics_process_time = p_pt;
}
+void Performance::set_navigation_process_time(double p_pt) {
+ _navigation_process_time = p_pt;
+}
+
void Performance::add_custom_monitor(const StringName &p_id, const Callable &p_callable, const Vector<Variant> &p_args) {
ERR_FAIL_COND_MSG(has_custom_monitor(p_id), "Custom monitor with id '" + String(p_id) + "' already exists.");
_monitor_map.insert(p_id, MonitorCall(p_callable, p_args));
@@ -262,6 +316,7 @@ uint64_t Performance::get_monitor_modification_time() {
Performance::Performance() {
_process_time = 0;
_physics_process_time = 0;
+ _navigation_process_time = 0;
_monitor_modification_time = 0;
singleton = this;
}
diff --git a/main/performance.h b/main/performance.h
index 14695ab5a3..34162b2da9 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -50,6 +50,7 @@ class Performance : public Object {
double _process_time;
double _physics_process_time;
+ double _navigation_process_time;
class MonitorCall {
Callable _callable;
@@ -69,6 +70,7 @@ public:
TIME_FPS,
TIME_PROCESS,
TIME_PHYSICS_PROCESS,
+ TIME_NAVIGATION_PROCESS,
MEMORY_STATIC,
MEMORY_STATIC_MAX,
MEMORY_MESSAGE_BUFFER_MAX,
@@ -89,6 +91,15 @@ public:
PHYSICS_3D_COLLISION_PAIRS,
PHYSICS_3D_ISLAND_COUNT,
AUDIO_OUTPUT_LATENCY,
+ NAVIGATION_ACTIVE_MAPS,
+ NAVIGATION_REGION_COUNT,
+ NAVIGATION_AGENT_COUNT,
+ NAVIGATION_LINK_COUNT,
+ NAVIGATION_POLYGON_COUNT,
+ NAVIGATION_EDGE_COUNT,
+ NAVIGATION_EDGE_MERGE_COUNT,
+ NAVIGATION_EDGE_CONNECTION_COUNT,
+ NAVIGATION_EDGE_FREE_COUNT,
MONITOR_MAX
};
@@ -105,6 +116,7 @@ public:
void set_process_time(double p_pt);
void set_physics_process_time(double p_pt);
+ void set_navigation_process_time(double p_pt);
void add_custom_monitor(const StringName &p_id, const Callable &p_callable, const Vector<Variant> &p_args);
void remove_custom_monitor(const StringName &p_id);
diff --git a/methods.py b/methods.py
index 99a59b49e3..7ede2592ff 100644
--- a/methods.py
+++ b/methods.py
@@ -111,6 +111,10 @@ def get_version_info(module_version_string="", silent=False):
head = open(os.path.join(gitfolder, "HEAD"), "r", encoding="utf8").readline().strip()
if head.startswith("ref: "):
ref = head[5:]
+ # If this directory is a Git worktree instead of a root clone.
+ parts = gitfolder.split("/")
+ if len(parts) > 2 and parts[-2] == "worktrees":
+ gitfolder = "/".join(parts[0:-2])
head = os.path.join(gitfolder, ref)
packedrefs = os.path.join(gitfolder, "packed-refs")
if os.path.isfile(head):
@@ -484,29 +488,29 @@ def use_windows_spawn_fix(self, platform=None):
def save_active_platforms(apnames, ap):
for x in ap:
- names = ["logo"]
- if os.path.isfile(x + "/run_icon.png"):
- names.append("run_icon")
-
- for name in names:
- pngf = open(x + "/" + name + ".png", "rb")
- b = pngf.read(1)
- str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
- str += " static const unsigned char _" + x[9:] + "_" + name + "[]={"
+ svg_names = []
+ if os.path.isfile(x + "/logo.svg"):
+ svg_names.append("logo")
+ if os.path.isfile(x + "/run_icon.svg"):
+ svg_names.append("run_icon")
+
+ for name in svg_names:
+ svgf = open(x + "/" + name + ".svg", "rb")
+ b = svgf.read(1)
+ svg_str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
+ svg_str += " static const char *_" + x[9:] + "_" + name + '_svg = "'
while len(b) == 1:
- str += hex(ord(b))
- b = pngf.read(1)
- if len(b) == 1:
- str += ","
+ svg_str += "\\" + hex(ord(b))[1:]
+ b = svgf.read(1)
- str += "};\n"
+ svg_str += '";\n'
- pngf.close()
+ svgf.close()
# NOTE: It is safe to generate this file here, since this is still executed serially
- wf = x + "/" + name + ".gen.h"
- with open(wf, "w") as pngw:
- pngw.write(str)
+ wf = x + "/" + name + "_svg.gen.h"
+ with open(wf, "w") as svgw:
+ svgw.write(svg_str)
def no_verbose(sys, env):
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index e717b501f4..16e58172b2 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -328,7 +328,11 @@
"$(PROJECT_DIR)/**",
);
PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier;
- PRODUCT_NAME = "$(TARGET_NAME)";
+ INFOPLIST_KEY_CFBundleDisplayName = "$name";
+ PRODUCT_NAME = "$binary";
+ EXECUTABLE_NAME = "$binary";
+ MARKETING_VERSION = $short_version;
+ CURRENT_PROJECT_VERSION = $version;
PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug";
TARGETED_DEVICE_FAMILY = "$targeted_device_family";
VALID_ARCHS = "arm64 x86_64";
@@ -360,7 +364,11 @@
"$(PROJECT_DIR)/**",
);
PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier;
- PRODUCT_NAME = "$(TARGET_NAME)";
+ INFOPLIST_KEY_CFBundleDisplayName = "$name";
+ PRODUCT_NAME = "$binary";
+ EXECUTABLE_NAME = "$binary";
+ MARKETING_VERSION = $short_version;
+ CURRENT_PROJECT_VERSION = $version;
PROVISIONING_PROFILE = "$provisioning_profile_uuid_release";
TARGETED_DEVICE_FAMILY = "$targeted_device_family";
VALID_ARCHS = "arm64 x86_64";
diff --git a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
index b88dfae5b2..ee5f1d35ae 100644
--- a/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
+++ b/misc/dist/ios_xcode/godot_ios/godot_ios-Info.plist
@@ -5,9 +5,9 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
- <string>$name</string>
+ <string>$(INFOPLIST_KEY_CFBundleDisplayName)</string>
<key>CFBundleExecutable</key>
- <string>$binary</string>
+ <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIcons</key>
<dict/>
<key>CFBundleIcons~ipad</key>
@@ -17,15 +17,15 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
- <string>$name</string>
+ <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>$short_version</string>
+ <string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>$signature</string>
<key>CFBundleVersion</key>
- <string>$version</string>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false />
<key>LSRequiresIPhoneOS</key>
diff --git a/misc/hooks/asmessage.applescript b/misc/hooks/asmessage.applescript
new file mode 100644
index 0000000000..15ba94dc37
--- /dev/null
+++ b/misc/hooks/asmessage.applescript
@@ -0,0 +1,59 @@
+on run argv
+ set vButtons to { "OK" }
+ set vButtonCodes to { 0 }
+ set vDbutton to "OK"
+ set vText to ""
+ set vTitle to ""
+ set vTimeout to -1
+
+ repeat with i from 1 to length of argv
+ try
+ set vArg to item i of argv
+ if vArg = "-buttons" then
+ set vButtonsAndCodes to my fSplit(item (i + 1) of argv, ",")
+ set vButtons to {}
+ set vButtonCodes to {}
+ repeat with j from 1 to length of vButtonsAndCodes
+ set vBtn to my fSplit(item j of vButtonsAndCodes, ":")
+ copy (item 1 of vBtn) to the end of the vButtons
+ copy (item 2 of vBtn) to the end of the vButtonCodes
+ end repeat
+ else if vArg = "-title" then
+ set vTitle to item (i + 1) of argv
+ else if vArg = "-center" then
+ -- not supported
+ else if vArg = "-default" then
+ set vDbutton to item (i + 1) of argv
+ else if vArg = "-geometry" then
+ -- not supported
+ else if vArg = "-nearmouse" then
+ -- not supported
+ else if vArg = "-timeout" then
+ set vTimeout to item (i + 1) of argv as integer
+ else if vArg = "-file" then
+ set vText to read (item (i + 1) of argv) as string
+ else if vArg = "-text" then
+ set vText to item (i + 1) of argv
+ end if
+ end try
+ end repeat
+
+ set vDlg to display dialog vText buttons vButtons default button vDbutton with title vTitle giving up after vTimeout with icon stop
+ set vRet to button returned of vDlg
+ repeat with i from 1 to length of vButtons
+ set vBtn to item i of vButtons
+ if vBtn = vRet
+ return item i of vButtonCodes
+ end if
+ end repeat
+
+ return 0
+end run
+
+on fSplit(vString, vDelimiter)
+ set oldDelimiters to AppleScript's text item delimiters
+ set AppleScript's text item delimiters to vDelimiter
+ set vArray to every text item of vString
+ set AppleScript's text item delimiters to oldDelimiters
+ return vArray
+end fSplit
diff --git a/misc/hooks/pre-commit-black b/misc/hooks/pre-commit-black
index b7335685ae..bbad6a690a 100755
--- a/misc/hooks/pre-commit-black
+++ b/misc/hooks/pre-commit-black
@@ -34,6 +34,9 @@ XMSG=`which xmessage 2>/dev/null`
# Path to powershell (Windows only)
PWSH=`which powershell 2>/dev/null`
+# Path to osascript (macOS only)
+OSA=`which osascript 2>/dev/null`
+
##################################################################
# There should be no need to change anything below this line.
@@ -69,6 +72,10 @@ if [ ! -x "$BLACK" ] ; then
elif [ -x "$XMSG" ] ; then
$XMSG -center -title "Error" "Error: black executable not found."
exit 1
+ elif [ -x "$OSA" ] ; then
+ asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")"
+ $OSA "$asmessage" -center -title "Error" --text "Error: black executable not found."
+ exit 1
elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
$PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: black executable not found."
@@ -159,6 +166,16 @@ while true; do
else
yn="N"
fi
+ elif [ -x "$OSA" ] ; then
+ asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")"
+ choice=`$OSA "$asmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"`
+ if [ "$choice" = "100" ] ; then
+ yn="Y"
+ elif [ "$choice" = "200" ] ; then
+ yn="S"
+ else
+ yn="N"
+ fi
elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
$PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
@@ -171,7 +188,7 @@ while true; do
yn="N"
fi
else
- printf "Error: zenity, xmessage, or powershell executable not found.\n"
+ printf "Error: zenity, xmessage, osascript, or powershell executable not found.\n"
exit 1
fi
else
diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format
index 9570d5120b..fd0213c175 100755
--- a/misc/hooks/pre-commit-clang-format
+++ b/misc/hooks/pre-commit-clang-format
@@ -47,6 +47,9 @@ XMSG=`which xmessage 2>/dev/null`
# Path to powershell (Windows only)
PWSH=`which powershell 2>/dev/null`
+# Path to osascript (macOS only)
+OSA=`which osascript 2>/dev/null`
+
##################################################################
# There should be no need to change anything below this line.
@@ -89,6 +92,10 @@ if [ ! -x "$CLANG_FORMAT" ] ; then
elif [ -x "$XMSG" ] ; then
$XMSG -center -title "Error" "$message"
exit 1
+ elif [ -x "$OSA" ] ; then
+ asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")"
+ $OSA "$asmessage" -center -title "Error" --text "$message"
+ exit 1
elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
$PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "$message"
@@ -199,6 +206,16 @@ while true; do
else
yn="N"
fi
+ elif [ -x "$OSA" ] ; then
+ asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")"
+ choice=`$OSA "$asmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"`
+ if [ "$choice" = "100" ] ; then
+ yn="Y"
+ elif [ "$choice" = "200" ] ; then
+ yn="S"
+ else
+ yn="N"
+ fi
elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
$PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
@@ -211,7 +228,7 @@ while true; do
yn="N"
fi
else
- printf "Error: zenity, xmessage, or powershell executable not found.\n"
+ printf "Error: zenity, xmessage, osascript, or powershell executable not found.\n"
exit 1
fi
else
diff --git a/misc/scripts/file_format.sh b/misc/scripts/file_format.sh
index a2f33692f9..3ff5798e22 100755
--- a/misc/scripts/file_format.sh
+++ b/misc/scripts/file_format.sh
@@ -4,9 +4,9 @@
# This is supplementary to clang_format.sh and black_format.sh, but should be
# run before them.
-# We need dos2unix and recode.
-if [ ! -x "$(command -v dos2unix)" -o ! -x "$(command -v recode)" ]; then
- printf "Install 'dos2unix' and 'recode' to use this script.\n"
+# We need dos2unix and isutf8.
+if [ ! -x "$(command -v dos2unix)" -o ! -x "$(command -v isutf8)" ]; then
+ printf "Install 'dos2unix' and 'isutf8' (moreutils package) to use this script.\n"
fi
set -uo pipefail
@@ -53,22 +53,24 @@ done
diff=$(git diff --color)
-# If no UTF-8 violations were collected and no diff has been
-# generated all is OK, clean up, and exit.
if [ ! -s utf8-validation.txt ] && [ -z "$diff" ] ; then
+ # If no UTF-8 violations were collected (the file is empty) and
+ # no diff has been generated all is OK, clean up, and exit.
printf "Files in this commit comply with the formatting rules.\n"
- rm -f utf8-violations.txt
+ rm -f utf8-validation.txt
exit 0
fi
-# Violations detected, notify the user, clean up, and exit.
if [ -s utf8-validation.txt ]
then
+ # If the file has content and is not empty, violations
+ # detected, notify the user, clean up, and exit.
printf "\n*** The following files contain invalid UTF-8 character sequences:\n\n"
cat utf8-validation.txt
- rm -f utf8-validation.txt
fi
+rm -f utf8-validation.txt
+
if [ ! -z "$diff" ]
then
printf "\n*** The following differences were found between the code "
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 61787691a3..2c533cb36d 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -217,7 +217,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
@@ -231,7 +231,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(s, "set_size", s->get_size());
ur->add_undo_method(s, "set_size", p_restore);
@@ -249,7 +249,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Cylinder Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
@@ -274,7 +274,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Torus Inner Radius"));
ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 1fd98a43d4..b5192bd664 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -118,6 +118,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
target_format = Image::FORMAT_ETC2_RA_AS_RG;
r_img->convert_rg_to_ra_rgba8();
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
target_format = Image::FORMAT_ETC2_RGBA8;
r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
@@ -222,9 +223,9 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
}
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 8dc47cbfd5..28f478e9cd 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1037,7 +1037,7 @@ String GDScript::get_script_path() const {
}
Error GDScript::load_source_code(const String &p_path) {
- if (p_path.is_empty() || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
+ if (p_path.is_empty() || p_path.begins_with("gdscript://") || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
return OK;
}
@@ -1307,7 +1307,7 @@ GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
}
void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
- if (skip_dependencies || p_dependencies.has(this)) {
+ if (p_dependencies.has(this)) {
return;
}
p_dependencies.insert(this);
@@ -1363,9 +1363,11 @@ GDScript::GDScript() :
GDScriptLanguage::get_singleton()->script_list.add(&script_list);
}
+
+ path = vformat("gdscript://%d.gd", get_instance_id());
}
-void GDScript::_save_orphaned_subclasses() {
+void GDScript::_save_orphaned_subclasses(GDScript::ClearData *p_clear_data) {
struct ClassRefWithName {
ObjectID id;
String fully_qualified_name;
@@ -1381,8 +1383,17 @@ void GDScript::_save_orphaned_subclasses() {
}
// clear subclasses to allow unused subclasses to be deleted
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ p_clear_data->scripts.insert(E.value);
+ }
subclasses.clear();
// subclasses are also held by constants, clear those as well
+ for (KeyValue<StringName, Variant> &E : constants) {
+ GDScript *gdscr = _get_gdscript_from_variant(E.value);
+ if (gdscr != nullptr) {
+ p_clear_data->scripts.insert(gdscr);
+ }
+ }
constants.clear();
// keep orphan subclass only for subclasses that are still in use
@@ -1413,60 +1424,50 @@ void GDScript::_init_rpc_methods_properties() {
}
}
-void GDScript::clear() {
+void GDScript::clear(GDScript::ClearData *p_clear_data) {
if (clearing) {
return;
}
clearing = true;
- RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
- HashMap<GDScript *, ObjectID> must_clear_dependencies_objectids;
+ GDScript::ClearData data;
+ GDScript::ClearData *clear_data = p_clear_data;
+ bool is_root = false;
- // Log the objectids before clearing, as a cascade of clear could
- // remove instances that are still in the clear loop
- for (GDScript *E : must_clear_dependencies) {
- must_clear_dependencies_objectids.insert(E, E->get_instance_id());
+ // If `clear_data` is `nullptr`, it means that it's the root.
+ // The root is in charge to clear functions and scripts of itself and its dependencies
+ if (clear_data == nullptr) {
+ clear_data = &data;
+ is_root = true;
}
+ RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
for (GDScript *E : must_clear_dependencies) {
- Object *obj = ObjectDB::get_instance(must_clear_dependencies_objectids[E]);
- if (obj == nullptr) {
- continue;
- }
-
- E->skip_dependencies = true;
- E->clear();
- E->skip_dependencies = false;
- GDScriptCache::remove_script(E->get_path());
+ clear_data->scripts.insert(E);
+ E->clear(clear_data);
}
- RBSet<StringName> member_function_names;
for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- member_function_names.insert(E.key);
+ clear_data->functions.insert(E.value);
}
- for (const StringName &E : member_function_names) {
- if (member_functions.has(E)) {
- memdelete(member_functions[E]);
- }
- }
- member_function_names.clear();
member_functions.clear();
for (KeyValue<StringName, GDScript::MemberInfo> &E : member_indices) {
+ clear_data->scripts.insert(E.value.data_type.script_type_ref);
E.value.data_type.script_type_ref = Ref<Script>();
}
if (implicit_initializer) {
- memdelete(implicit_initializer);
+ clear_data->functions.insert(implicit_initializer);
+ implicit_initializer = nullptr;
}
- implicit_initializer = nullptr;
if (implicit_ready) {
- memdelete(implicit_ready);
+ clear_data->functions.insert(implicit_ready);
+ implicit_ready = nullptr;
}
- implicit_ready = nullptr;
- _save_orphaned_subclasses();
+ _save_orphaned_subclasses(clear_data);
#ifdef TOOLS_ENABLED
// Clearing inner class doc, script doc only cleared when the script source deleted.
@@ -1474,7 +1475,21 @@ void GDScript::clear() {
_clear_doc();
}
#endif
- clearing = false;
+
+ // If it's not the root, skip clearing the data
+ if (is_root) {
+ // All dependencies have been accounted for
+ for (GDScriptFunction *E : clear_data->functions) {
+ memdelete(E);
+ }
+ for (Ref<Script> &E : clear_data->scripts) {
+ Ref<GDScript> gdscr = E;
+ if (gdscr.is_valid()) {
+ GDScriptCache::remove_script(gdscr->get_path());
+ }
+ }
+ clear_data->clear();
+ }
}
GDScript::~GDScript() {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 24ef67ddaf..a53785a98d 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -62,7 +62,6 @@ class GDScript : public Script {
bool tool = false;
bool valid = false;
bool reloading = false;
- bool skip_dependencies = false;
struct MemberInfo {
int index = 0;
@@ -71,6 +70,15 @@ class GDScript : public Script {
GDScriptDataType data_type;
};
+ struct ClearData {
+ RBSet<GDScriptFunction *> functions;
+ RBSet<Ref<Script>> scripts;
+ void clear() {
+ functions.clear();
+ scripts.clear();
+ }
+ };
+
friend class GDScriptInstance;
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
@@ -157,7 +165,7 @@ class GDScript : public Script {
bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false, PlaceHolderScriptInstance *p_instance_to_update = nullptr);
- void _save_orphaned_subclasses();
+ void _save_orphaned_subclasses(GDScript::ClearData *p_clear_data);
void _init_rpc_methods_properties();
void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const;
@@ -180,7 +188,7 @@ protected:
static void _bind_methods();
public:
- void clear();
+ void clear(GDScript::ClearData *p_clear_data = nullptr);
virtual bool is_valid() const override { return valid; }
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 8a07d509a1..edd94da824 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -42,6 +42,9 @@
#include "gdscript_utility_functions.h"
#include "scene/resources/packed_scene.h"
+#define UNNAMED_ENUM "<anonymous enum>"
+#define ENUM_SEPARATOR "::"
+
static MethodInfo info_from_utility_func(const StringName &p_function) {
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
@@ -65,9 +68,6 @@ static MethodInfo info_from_utility_func(const StringName &p_function) {
pi.name = "arg" + itos(i + 1);
#endif
pi.type = Variant::get_utility_function_argument_type(p_function, i);
- if (pi.type == Variant::NIL) {
- pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
info.arguments.push_back(pi);
}
}
@@ -100,19 +100,45 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::NATIVE;
type.builtin_type = Variant::OBJECT;
- type.is_constant = true;
type.native_type = p_class_name;
+ type.is_constant = true;
type.is_meta_type = true;
return type;
}
-static GDScriptParser::DataType make_native_enum_type(const StringName &p_native_class, const StringName &p_enum_name) {
+static GDScriptParser::DataType make_script_meta_type(const Ref<Script> &p_script) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::ENUM;
- type.builtin_type = Variant::INT;
+ type.kind = GDScriptParser::DataType::SCRIPT;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = p_script->get_instance_base_type();
+ type.script_type = p_script;
+ type.script_path = p_script->get_path();
type.is_constant = true;
type.is_meta_type = true;
+ return type;
+}
+
+// In enum types, native_type is used to store the class (native or otherwise) that the enum belongs to.
+// This disambiguates between similarly named enums in base classes or outer classes
+static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, const String &p_base_name, const bool p_meta = false) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::ENUM;
+ type.builtin_type = p_meta ? Variant::DICTIONARY : Variant::INT;
+ type.enum_type = p_enum_name;
+ type.is_constant = true;
+ type.is_meta_type = p_meta;
+
+ // For enums, native_type is only used to check compatibility in is_type_compatible()
+ // We can set anything readable here for error messages, as long as it uniquely identifies the type of the enum
+ type.native_type = p_base_name + ENUM_SEPARATOR + p_enum_name;
+
+ return type;
+}
+
+static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, const bool p_meta = true) {
+ GDScriptParser::DataType type = make_enum_type(p_enum_name, p_native_class, p_meta);
List<StringName> enum_values;
ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values);
@@ -134,6 +160,19 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
return type;
}
+static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) {
+ // Check that an enum has a given value, not key.
+ // Make sure that implicit conversion to int64_t is sensible before calling!
+ HashMap<StringName, int64_t>::ConstIterator i = p_type.enum_values.begin();
+ while (i) {
+ if (i->value == p_val) {
+ return i->key;
+ }
+ ++i;
+ }
+ return StringName();
+}
+
bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) {
if (p_class->members_indices.has(p_member_name)) {
int index = p_class->members_indices[p_member_name];
@@ -192,6 +231,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
}
Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) {
+ // TODO check outer classes for static members only
const GDScriptParser::DataType *current_data_type = &p_class_node->base_type;
while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
@@ -220,9 +260,13 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
}
void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) {
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_NULL(p_list);
+
if (p_list->find(p_node) != nullptr) {
return;
}
+
p_list->push_back(p_node);
// TODO: Try to solve class inheritance if not yet resolving.
@@ -387,7 +431,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
return err;
}
base = info_parser->get_parser()->head->get_datatype();
- } else if (class_exists(name) && ClassDB::can_instantiate(name)) {
+ } else if (class_exists(name)) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
@@ -547,11 +591,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.builtin_type = GDScriptParser::get_builtin_type(first);
if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
-
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
- container_type.is_meta_type = false;
- container_type.is_constant = false;
result.set_container_element_type(container_type);
}
}
@@ -573,12 +614,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
result = ref->get_parser()->head->get_datatype();
} else {
- result.kind = GDScriptParser::DataType::SCRIPT;
- result.script_type = ResourceLoader::load(path, "Script");
- result.native_type = result.script_type->get_instance_base_type();
- result.script_path = path;
- result.is_constant = true;
- result.is_meta_type = false;
+ result = make_script_meta_type(ResourceLoader::load(path, "Script"));
}
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
@@ -591,12 +627,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = ref->get_parser()->head->get_datatype();
} else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
// Native enum in current class.
- result = make_native_enum_type(parser->current_class->base_type.native_type, first);
+ result = make_native_enum_type(first, parser->current_class->base_type.native_type);
} else {
// Classes in current scope.
List<GDScriptParser::ClassNode *> script_classes;
+ bool found = false;
get_class_node_current_scope_classes(parser->current_class, &script_classes);
for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (found) {
+ break;
+ }
+
if (script_class->identifier && script_class->identifier->name == first) {
result = script_class->get_datatype();
break;
@@ -608,14 +649,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
switch (member.type) {
case GDScriptParser::ClassNode::Member::CLASS:
result = member.get_datatype();
+ found = true;
break;
case GDScriptParser::ClassNode::Member::ENUM:
result = member.get_datatype();
+ found = true;
break;
case GDScriptParser::ClassNode::Member::CONSTANT:
if (member.get_datatype().is_meta_type) {
result = member.get_datatype();
- result.is_meta_type = false;
+ found = true;
break;
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
@@ -626,16 +669,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return bad_type;
}
result = ref->get_parser()->head->get_datatype();
- result.is_meta_type = false;
} else {
- Ref<Script> script = member.constant->initializer->reduced_value;
- result.kind = GDScriptParser::DataType::SCRIPT;
- result.builtin_type = Variant::OBJECT;
- result.script_type = script;
- result.script_path = script->get_path();
- result.native_type = script->get_instance_base_type();
- result.is_meta_type = false;
+ result = make_script_meta_type(member.constant->initializer->reduced_value);
}
+ found = true;
break;
}
[[fallthrough]];
@@ -667,15 +704,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if (result.kind == GDScriptParser::DataType::NATIVE) {
// Only enums allowed for native.
- if (!ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
- push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
- return bad_type;
- }
- if (p_type->type_chain.size() > 2) {
- push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
+ if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
+ if (p_type->type_chain.size() > 2) {
+ push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
+ return bad_type;
+ } else {
+ result = make_native_enum_type(p_type->type_chain[1]->name, result.native_type);
+ }
+ } else {
+ push_error(vformat(R"(Could not find type "%s" in "%s".)", p_type->type_chain[1]->name, first), p_type->type_chain[1]);
return bad_type;
}
- result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name);
} else {
push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
return bad_type;
@@ -787,8 +826,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
for (int j = 0; j < member.signal->parameters.size(); j++) {
GDScriptParser::ParameterNode *param = member.signal->parameters[j];
- GDScriptParser::DataType param_type = resolve_datatype(param->datatype_specifier);
- param_type.is_meta_type = false;
+ GDScriptParser::DataType param_type = type_from_metatype(resolve_datatype(param->datatype_specifier));
param->set_datatype(param_type);
mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name));
// TODO: add signal parameter default values
@@ -804,15 +842,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
member.m_enum->set_datatype(resolving_datatype);
-
- GDScriptParser::DataType enum_type;
- enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- enum_type.kind = GDScriptParser::DataType::ENUM;
- enum_type.builtin_type = Variant::DICTIONARY;
- enum_type.enum_type = member.m_enum->identifier->name;
- enum_type.native_type = p_class->fqcn + "." + member.m_enum->identifier->name;
- enum_type.is_meta_type = true;
- enum_type.is_constant = true;
+ GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true);
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
@@ -846,6 +876,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
current_enum = prev_enum;
+ dictionary.set_read_only(true);
member.m_enum->set_datatype(enum_type);
member.m_enum->dictionary = dictionary;
@@ -858,11 +889,11 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
resolve_function_signature(member.function, p_source);
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ member.enum_value.identifier->set_datatype(resolving_datatype);
+
if (member.enum_value.custom_value) {
check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value);
- member.enum_value.identifier->set_datatype(resolving_datatype);
-
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.enum_value.parent_enum;
reduce_expression(member.enum_value.custom_value);
@@ -892,11 +923,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Also update the original references.
member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
- GDScriptParser::DataType datatype;
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- datatype.kind = GDScriptParser::DataType::BUILTIN;
- datatype.builtin_type = Variant::INT;
- member.enum_value.identifier->set_datatype(datatype);
+ member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false));
} break;
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
@@ -1330,7 +1357,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
} else {
if (p_function->return_type != nullptr) {
- p_function->set_datatype(resolve_datatype(p_function->return_type));
+ p_function->set_datatype(type_from_metatype(resolve_datatype(p_function->return_type)));
} else {
// In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here.
// It's not "undetected" to not mix up with unknown functions.
@@ -1364,7 +1391,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!valid) {
// Compute parent signature as a string to show in the error message.
- String parent_signature = function_name.operator String() + "(";
+ String parent_signature = String(function_name) + "(";
int j = 0;
for (const GDScriptParser::DataType &par_type : parameters_types) {
if (j > 0) {
@@ -1484,14 +1511,12 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT;
- bool is_variable = p_assignable->type == GDScriptParser::Node::VARIABLE;
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
- specified_type = resolve_datatype(p_assignable->datatype_specifier);
- specified_type.is_meta_type = false;
+ specified_type = type_from_metatype(resolve_datatype(p_assignable->datatype_specifier));
type = specified_type;
}
@@ -1507,9 +1532,9 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
if (is_constant) {
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true);
} else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true);
}
if (!p_assignable->initializer->is_constant) {
push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
@@ -1547,13 +1572,14 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else if (!specified_type.is_variant()) {
if (initializer_type.is_variant() || !initializer_type.is_hard_type()) {
mark_node_unsafe(p_assignable->initializer);
- if (is_variable) {
- static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+ p_assignable->use_conversion_assign = true;
+ if (!initializer_type.is_variant() && !is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
+ downgrade_node_type_source(p_assignable->initializer);
}
} else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
- if (is_variable && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
+ if (!is_constant && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
mark_node_unsafe(p_assignable->initializer);
- static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+ p_assignable->use_conversion_assign = true;
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
@@ -1696,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (list_resolved) {
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
variable_type.kind = GDScriptParser::DataType::BUILTIN;
- variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
- p_for->variable->set_datatype(variable_type);
+ variable_type.builtin_type = Variant::INT;
} else if (p_for->list) {
resolve_node(p_for->list, false);
- if (p_for->list->datatype.has_container_element_type()) {
- variable_type = p_for->list->datatype.get_container_element_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else if (p_for->list->datatype.is_typed_container_type()) {
- variable_type = p_for->list->datatype.get_typed_container_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else {
- // Last resort
- // TODO: Must other cases be handled? Must we mark as unsafe?
- variable_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ GDScriptParser::DataType list_type = p_for->list->get_datatype();
+ if (!list_type.is_hard_type()) {
+ mark_node_unsafe(p_for->list);
+ }
+ if (list_type.is_variant()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_for->list);
+ } else if (list_type.has_container_element_type()) {
+ variable_type = list_type.get_container_element_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.is_typed_container_type()) {
+ variable_type = list_type.get_typed_container_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = list_type.builtin_type;
+ } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::INT;
+ } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::FLOAT;
+ } else if (list_type.builtin_type == Variant::OBJECT) {
+ GDScriptParser::DataType return_type;
+ List<GDScriptParser::DataType> par_types;
+ int default_arg_count = 0;
+ bool is_static = false;
+ bool is_vararg = false;
+ if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ variable_type = return_type;
+ variable_type.type_source = list_type.type_source;
+ } else if (!list_type.is_hard_type()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list);
+ }
+ } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list);
}
}
if (p_for->variable) {
@@ -2063,6 +2120,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+ if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) {
+ push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
+ }
+
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) {
update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
@@ -2070,86 +2131,86 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
- if (assignee_type.is_constant) {
- push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
- }
-
+ bool assignee_is_variant = assignee_type.is_variant();
+ bool assignee_is_hard = assignee_type.is_hard_type();
+ bool assigned_is_variant = assigned_value_type.is_variant();
+ bool assigned_is_hard = assigned_value_type.is_hard_type();
bool compatible = true;
+ bool downgrades_assignee = false;
+ bool downgrades_assigned = false;
GDScriptParser::DataType op_type = assigned_value_type;
- if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE && !op_type.is_variant()) {
op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
+
+ if (assignee_is_variant) {
+ // variant assignee
+ mark_node_unsafe(p_assignment);
+ } else if (!compatible) {
+ // incompatible hard types and non-variant assignee
+ mark_node_unsafe(p_assignment);
+ if (assigned_is_variant) {
+ // incompatible hard non-variant assignee and hard variant assigned
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // incompatible hard non-variant types
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
+ }
+ } else if (op_type.type_source == GDScriptParser::DataType::UNDETECTED && !assigned_is_variant) {
+ // incompatible non-variant types (at least one weak)
+ downgrades_assignee = !assignee_is_hard;
+ downgrades_assigned = !assigned_is_hard;
+ }
}
p_assignment->set_datatype(op_type);
- if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type()) {
- if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
- if (!compatible) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true)) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
- }
- }
- } else {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
+ if (assignee_is_variant) {
+ if (!assignee_is_hard) {
+ // weak variant assignee
+ mark_node_unsafe(p_assignment);
}
- } else if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
- mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
} else {
- mark_node_unsafe(p_assignment);
- if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
+ if (assignee_is_hard && !assigned_is_hard) {
+ // hard non-variant assignee and weak assigned
+ mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
- }
- }
-
- if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
- // Change source type so it's not wrongly detected later.
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee);
-
- switch (identifier->source) {
- case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
- GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->parameter_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
+ downgrades_assigned = downgrades_assigned || (!assigned_is_variant && !is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value));
+ } else if (compatible) {
+ if (op_type.is_variant()) {
+ // non-variant assignee and variant result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ // hard non-variant assignee and variant result
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // weak non-variant assignee and variant result
+ downgrades_assignee = true;
}
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
- GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
+ } else if (!is_type_compatible(assignee_type, op_type, assignee_is_hard, p_assignment->assigned_value)) {
+ // non-variant assignee and incompatible result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ if (is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
+ // hard non-variant assignee and maybe compatible result
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // hard non-variant assignee and incompatible result
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
+ }
+ } else {
+ // weak non-variant assignee and incompatible result
+ downgrades_assignee = true;
}
- } break;
- default:
- // Nothing to do.
- break;
+ }
}
}
+ if (downgrades_assignee) {
+ downgrade_node_type_source(p_assignment->assignee);
+ }
+ if (downgrades_assigned) {
+ downgrade_node_type_source(p_assignment->assigned_value);
+ }
+
#ifdef DEBUG_ENABLED
if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
@@ -2368,7 +2429,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
- push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be %s but is %s.)", Variant::get_type_name(builtin_type), err.argument + 1,
+ push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1,
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
break;
@@ -2428,7 +2489,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool types_match = true;
for (int i = 0; i < p_call->arguments.size(); i++) {
- GDScriptParser::DataType par_type = type_from_property(info.arguments[i]);
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
types_match = false;
@@ -2466,7 +2527,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
- if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ if (!p_is_root && !p_is_await && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
}
@@ -2484,8 +2545,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
- type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
+ type_from_property(wrong_arg, true).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
@@ -2513,7 +2574,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (Variant::has_utility_function(function_name)) {
MethodInfo function_info = info_from_utility_func(function_name);
- if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ if (!p_is_root && !p_is_await && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
}
@@ -2532,12 +2593,12 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
String expected_type_name;
if (err.argument < function_info.arguments.size()) {
- expected_type_name = type_from_property(function_info.arguments[err.argument]).to_string();
+ expected_type_name = type_from_property(function_info.arguments[err.argument], true).to_string();
} else {
expected_type_name = Variant::get_type_name((Variant::Type)err.expected);
}
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
expected_type_name, p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
@@ -2613,6 +2674,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else {
reduce_expression(subscript->base);
base_type = subscript->base->get_datatype();
+ is_self = subscript->base->type == GDScriptParser::Node::SELF;
}
} else {
// Invalid call. Error already sent in parser.
@@ -2659,7 +2721,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
mark_lambda_use_self();
}
- if (!p_is_root && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
+ if (!p_is_root && !p_is_await && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call);
}
@@ -2683,8 +2745,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else {
bool found = false;
- // Check if the name exists as something else.
- if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
+ // Enums do not have functions other than the built-in dictionary ones.
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
+ push_error(vformat(R"*(Enums only have Dictionary built-in methods. Function "%s()" does not exist for enum "%s".)*", p_call->function_name, base_type.enum_type), p_call->callee);
+ } else if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { // Check if the name exists as something else.
GDScriptParser::IdentifierNode *callee_id;
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
callee_id = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
@@ -2714,7 +2778,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();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
- push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call);
+ push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
}
}
@@ -2728,33 +2792,61 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
reduce_expression(p_cast->operand);
- GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
+ GDScriptParser::DataType cast_type = type_from_metatype(resolve_datatype(p_cast->cast_type));
if (!cast_type.is_set()) {
mark_node_unsafe(p_cast);
return;
}
- cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (!op_type.is_variant()) {
bool valid = false;
+ bool more_informative_error = false;
if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Enum types are compatible between each other, so it's a safe cast.
- valid = true;
+ // Enum casts are compatible when value from operand exists in target enum
+ if (p_cast->operand->is_constant && p_cast->operand->reduced) {
+ if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
+ valid = true;
+ } else {
+ valid = false;
+ more_informative_error = true;
+ push_error(vformat(R"(Invalid cast. Enum "%s" does not have value corresponding to "%s.%s" (%d).)",
+ cast_type.to_string(), op_type.enum_type,
+ enum_get_value_name(op_type, p_cast->operand->reduced_value), // Can never be null
+ p_cast->operand->reduced_value.operator uint64_t()),
+ p_cast->cast_type);
+ }
+ } else {
+ // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
+ mark_node_unsafe(p_cast);
+ valid = true;
+ }
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Convertint int to enum is always valid.
- valid = true;
+ // Int assignment to enum not valid when exact int assigned is known but is not an enum value
+ if (p_cast->operand->is_constant && p_cast->operand->reduced) {
+ if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
+ valid = true;
+ } else {
+ valid = false;
+ more_informative_error = true;
+ push_error(vformat(R"(Invalid cast. Enum "%s" does not have enum value %d.)", cast_type.to_string(), p_cast->operand->reduced_value.operator uint64_t()), p_cast->cast_type);
+ }
+ } else {
+ // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
+ mark_node_unsafe(p_cast);
+ valid = true;
+ }
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
}
- if (!valid) {
+ if (!valid && !more_informative_error) {
push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type);
}
}
@@ -2840,18 +2932,22 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
return ref->get_parser()->head->get_datatype();
} else {
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::SCRIPT;
- type.builtin_type = Variant::OBJECT;
- type.script_type = ResourceLoader::load(path, "Script");
- type.native_type = type.script_type->get_instance_base_type();
- type.script_path = path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
+ return make_script_meta_type(ResourceLoader::load(path, "Script"));
}
}
+void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) {
+ ERR_FAIL_NULL(p_identifier);
+
+ p_identifier->set_datatype(p_identifier_datatype);
+ Error err = OK;
+ GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr();
+ ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path));
+ scr = scr->find_class(p_identifier_datatype.class_type->fqcn);
+ p_identifier->reduced_value = scr;
+ p_identifier->is_constant = true;
+}
+
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
if (!p_identifier->get_datatype().has_no_type()) {
return;
@@ -2869,26 +2965,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (base.kind == GDScriptParser::DataType::ENUM) {
if (base.is_meta_type) {
if (base.enum_values.has(name)) {
+ p_identifier->set_datatype(type_from_metatype(base));
p_identifier->is_constant = true;
p_identifier->reduced_value = base.enum_values[name];
-
- GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::ENUM;
- result.is_constant = true;
- result.builtin_type = Variant::INT;
- result.native_type = base.native_type;
- result.enum_type = base.enum_type;
- result.enum_values = base.enum_values;
- p_identifier->set_datatype(result);
return;
- } else {
- // Consider as a Dictionary, so it can be anything.
- // This will be evaluated in the next if block.
- base.kind = GDScriptParser::DataType::BUILTIN;
- base.builtin_type = Variant::DICTIONARY;
- base.is_meta_type = false;
}
+
+ // Enum does not have this value, return.
+ return;
} else {
push_error(R"(Cannot get property from enum value.)", p_identifier);
return;
@@ -2942,102 +3026,91 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
GDScriptParser::ClassNode *base_class = base.class_type;
+ List<GDScriptParser::ClassNode *> script_classes;
+ bool is_base = true;
- // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
- while (base_class != nullptr) {
- if (base_class->identifier && base_class->identifier->name == name) {
- p_identifier->set_datatype(base_class->get_datatype());
+ if (base_class != nullptr) {
+ get_class_node_current_scope_classes(base_class, &script_classes);
+ }
+
+ for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
+ reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
return;
}
- if (base_class->has_member(name)) {
- resolve_class_member(base_class, name, p_identifier);
+ if (script_class->has_member(name)) {
+ resolve_class_member(script_class, name, p_identifier);
- GDScriptParser::ClassNode::Member member = base_class->get_member(name);
- p_identifier->set_datatype(member.get_datatype());
+ GDScriptParser::ClassNode::Member member = script_class->get_member(name);
switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT:
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.constant->initializer->reduced_value;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
p_identifier->constant_source = member.constant;
- break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- break;
- case GDScriptParser::ClassNode::Member::ENUM:
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.m_enum->dictionary;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- break;
- case GDScriptParser::ClassNode::Member::VARIABLE:
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
- p_identifier->variable_source = member.variable;
- member.variable->usages += 1;
- break;
- case GDScriptParser::ClassNode::Member::SIGNAL:
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
- break;
- case GDScriptParser::ClassNode::Member::FUNCTION:
- p_identifier->set_datatype(make_callable_type(member.function->info));
- break;
- case GDScriptParser::ClassNode::Member::CLASS:
- if (p_base != nullptr && p_base->is_constant) {
- p_identifier->is_constant = true;
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
-
- Error err = OK;
- GDScript *scr = GDScriptCache::get_full_script(base.script_path, err).ptr();
- ERR_FAIL_COND_MSG(err != OK, "Error while getting subscript full script.");
- scr = scr->find_class(p_identifier->get_datatype().class_type->fqcn);
- p_identifier->reduced_value = scr;
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ p_identifier->variable_source = member.variable;
+ member.variable->usages += 1;
+ return;
}
- break;
- default:
- break; // Type already set.
- }
- return;
- }
- // Check outer constants.
- // TODO: Allow outer static functions.
- if (base_class->outer != nullptr) {
- List<GDScriptParser::ClassNode *> script_classes;
- get_class_node_current_scope_classes(base_class->outer, &script_classes);
- for (GDScriptParser::ClassNode *script_class : script_classes) {
- if (script_class->has_member(name)) {
- resolve_class_member(script_class, name, p_identifier);
-
- GDScriptParser::ClassNode::Member member = script_class->get_member(name);
- switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT:
- // TODO: Make sure loops won't cause problem. And make special error message for those.
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.constant->initializer->reduced_value;
- return;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.enum_value.value;
- return;
- case GDScriptParser::ClassNode::Member::ENUM:
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.m_enum->dictionary;
- return;
- case GDScriptParser::ClassNode::Member::CLASS:
- p_identifier->set_datatype(member.get_datatype());
- return;
- default:
- break;
+ } break;
+
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ return;
+ }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::FUNCTION: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(make_callable_type(member.function->info));
+ return;
}
+ } break;
+
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ return;
+ }
+
+ default: {
+ // Do nothing
}
}
}
- base_class = base_class->base_type.class_type;
+ if (is_base) {
+ is_base = script_class->base_type.class_type != nullptr;
+ if (!is_base && p_base != nullptr) {
+ break;
+ }
+ }
}
// Check native members. No need for native class recursion because Node exposes all Object's properties.
@@ -3067,35 +3140,39 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
if (ClassDB::has_enum(native, name)) {
- p_identifier->set_datatype(make_native_enum_type(native, name));
+ p_identifier->set_datatype(make_native_enum_type(name, native));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
bool valid = false;
+
int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid);
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
- p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- return;
+
+ // Check whether this constant, which exists, belongs to an enum
+ StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
+ if (enum_name != StringName()) {
+ p_identifier->set_datatype(make_native_enum_type(enum_name, native, false));
+ } else {
+ p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+ }
}
}
}
void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) {
- // TODO: This is opportunity to further infer types.
+ // TODO: This is an opportunity to further infer types.
- // Check if we are inside and enum. This allows enum values to access other elements of the same enum.
+ // Check if we are inside an enum. This allows enum values to access other elements of the same enum.
if (current_enum) {
for (int i = 0; i < current_enum->values.size(); i++) {
const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
if (element.identifier->name == p_identifier->name) {
- GDScriptParser::DataType type;
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
- type.builtin_type = Variant::INT;
- type.is_constant = true;
+ StringName enum_name = current_enum->identifier->name ? current_enum->identifier->name : UNNAMED_ENUM;
+ GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false);
if (element.parent_enum->identifier) {
type.enum_type = element.parent_enum->identifier->name;
}
@@ -3164,18 +3241,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+ 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;
}
- push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->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()) {
- // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
- if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+ // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (source_is_variable || source_is_signal) {
mark_lambda_use_self();
return; // No need to capture.
}
@@ -3411,9 +3490,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_expression(p_subscript->base);
if (p_subscript->base->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false);
} else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false);
}
}
@@ -3473,12 +3552,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
Variant value = p_subscript->base->reduced_value.get(p_subscript->index->reduced_value, &valid);
if (!valid) {
push_error(vformat(R"(Cannot get index "%s" from "%s".)", p_subscript->index->reduced_value, p_subscript->base->reduced_value), p_subscript->index);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
- result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
GDScriptParser::DataType index_type = p_subscript->index->get_datatype();
@@ -3738,20 +3817,17 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
-void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
- bool all_is_constant = true;
-
+void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) {
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
if (element->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const);
} else if (element->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const);
}
- all_is_constant = all_is_constant && element->is_constant;
- if (!all_is_constant) {
+ if (!element->is_constant) {
return;
}
}
@@ -3761,24 +3837,24 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
for (int i = 0; i < p_array->elements.size(); i++) {
array[i] = p_array->elements[i]->reduced_value;
}
+ if (p_is_const) {
+ array.set_read_only(true);
+ }
p_array->is_constant = true;
p_array->reduced_value = array;
}
-void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- bool all_is_constant = true;
-
+void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) {
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
if (element.value->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const);
} else if (element.value->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const);
}
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
- if (!all_is_constant) {
+ if (!element.key->is_constant || !element.value->is_constant) {
return;
}
}
@@ -3788,6 +3864,9 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
dict[element.key->reduced_value] = element.value->reduced_value;
}
+ if (p_is_const) {
+ dict.set_read_only(true);
+ }
p_dictionary->is_constant = true;
p_dictionary->reduced_value = dict;
}
@@ -3865,20 +3944,21 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
return result;
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) const {
+GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) {
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
- result.is_constant = false;
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
result.builtin_type = Variant::INT;
+ } else {
+ result.is_constant = false;
}
return result;
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property) const {
+GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg) const {
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- if (p_property.type == Variant::NIL && (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ if (p_property.type == Variant::NIL && (p_is_arg || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
// Variant
result.kind = GDScriptParser::DataType::VARIANT;
return result;
@@ -3928,11 +4008,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_default_arg_count = 0;
StringName function_name = p_function;
+ bool was_enum = false;
if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
+ was_enum = true;
if (p_base_type.is_meta_type) {
// Enum type can be treated as a dictionary value.
p_base_type.kind = GDScriptParser::DataType::BUILTIN;
- p_base_type.builtin_type = Variant::DICTIONARY;
p_base_type.is_meta_type = false;
} else {
push_error("Cannot call function on enum value.", p_source);
@@ -3955,6 +4036,10 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
if (E.name == p_function) {
function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name);
+ // Cannot use non-const methods on enums.
+ if (!r_static && was_enum && !(E.flags & METHOD_FLAG_CONST)) {
+ push_error(vformat(R"*(Cannot call non-const Dictionary function "%s()" on enum "%s".)*", p_function, p_base_type.enum_type), p_source);
+ }
return true;
}
}
@@ -3962,6 +4047,25 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
return false;
}
+ StringName base_native = p_base_type.native_type;
+ if (base_native != StringName()) {
+ // Empty native class might happen in some Script implementations.
+ // Just ignore it.
+ if (!class_exists(base_native)) {
+ push_error(vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native), p_source);
+ return false;
+ } else if (p_is_constructor && !ClassDB::can_instantiate(base_native)) {
+ if (p_base_type.kind == GDScriptParser::DataType::CLASS) {
+ push_error(vformat(R"(Class "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.class_type->fqcn.get_file(), base_native), p_source);
+ } else if (p_base_type.kind == GDScriptParser::DataType::SCRIPT) {
+ push_error(vformat(R"(Script "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.script_path.get_file(), base_native), p_source);
+ } else {
+ push_error(vformat(R"(Native class "%s" cannot be constructed as it is abstract.)", base_native), p_source);
+ }
+ return false;
+ }
+ }
+
if (p_is_constructor) {
function_name = "_init";
r_static = true;
@@ -4022,17 +4126,6 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
}
}
- StringName base_native = p_base_type.native_type;
-#ifdef DEBUG_ENABLED
- if (base_native != StringName()) {
- // Empty native class might happen in some Script implementations.
- // Just ignore it.
- if (!class_exists(base_native)) {
- ERR_FAIL_V_MSG(false, vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native));
- }
- }
-#endif
-
if (p_is_constructor) {
// Native types always have a default constructor.
r_return_type = p_base_type;
@@ -4060,7 +4153,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
for (const PropertyInfo &E : p_info.arguments) {
- r_par_types.push_back(type_from_property(E));
+ r_par_types.push_back(type_from_property(E, true));
}
return true;
}
@@ -4069,7 +4162,7 @@ bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScr
List<GDScriptParser::DataType> arg_types;
for (const PropertyInfo &E : p_method.arguments) {
- arg_types.push_back(type_from_property(E));
+ arg_types.push_back(type_from_property(E, true));
}
return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
@@ -4102,7 +4195,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
mark_node_unsafe(p_call);
if (!is_type_compatible(arg_type, par_type)) {
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*",
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
p_call->arguments[i]);
valid = false;
@@ -4187,9 +4280,6 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- GDScriptParser::DataType result;
- result.kind = GDScriptParser::DataType::VARIANT;
-
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
@@ -4209,28 +4299,18 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
-
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
bool validated = op_eval != nullptr;
- if (hard_operation && !validated) {
- r_valid = false;
- return result;
- } else if (hard_operation && validated) {
+ GDScriptParser::DataType result;
+ if (validated) {
r_valid = true;
- result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
- } else if (!hard_operation && !validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::UNDETECTED;
+ } else {
+ r_valid = !hard_operation;
result.kind = GDScriptParser::DataType::VARIANT;
- result.builtin_type = Variant::NIL;
- } else if (!hard_operation && validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::INFERRED;
- result.kind = GDScriptParser::DataType::BUILTIN;
- result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
}
return result;
@@ -4406,6 +4486,46 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
+void GDScriptAnalyzer::downgrade_node_type_source(GDScriptParser::Node *p_node) {
+ GDScriptParser::IdentifierNode *identifier = nullptr;
+ if (p_node->type == GDScriptParser::Node::IDENTIFIER) {
+ identifier = static_cast<GDScriptParser::IdentifierNode *>(p_node);
+ } else if (p_node->type == GDScriptParser::Node::SUBSCRIPT) {
+ GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_node);
+ if (subscript->is_attribute) {
+ identifier = subscript->attribute;
+ }
+ }
+ if (identifier == nullptr) {
+ return;
+ }
+
+ GDScriptParser::Node *source = nullptr;
+ switch (identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
+ source = identifier->parameter_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
+ source = identifier->bind_source;
+ } break;
+ default:
+ break;
+ }
+ if (source == nullptr) {
+ return;
+ }
+
+ GDScriptParser::DataType datatype;
+ datatype.kind = GDScriptParser::DataType::VARIANT;
+ source->set_datatype(datatype);
+}
+
void GDScriptAnalyzer::mark_lambda_use_self() {
for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
lambda->use_self = true;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index a7f8e3b556..b22d47982f 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -102,13 +102,13 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
- void const_fold_array(GDScriptParser::ArrayNode *p_array);
- void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+ void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const);
+ void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const);
// Helpers.
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
- GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
- GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
+ static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type);
+ GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const;
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
@@ -120,9 +120,11 @@ class GDScriptAnalyzer {
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
+ void downgrade_node_type_source(GDScriptParser::Node *p_node);
void mark_lambda_use_self();
bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
+ static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 8b3ae17e5f..6c80fb7665 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -872,8 +872,12 @@ void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
append(p_target);
}
-void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
- write_assign(p_dst, p_src);
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) {
+ if (p_use_conversion) {
+ write_assign_with_conversion(p_dst, p_src);
+ } else {
+ write_assign(p_dst, p_src);
+ }
function->default_arguments.push_back(opcodes.size());
}
@@ -920,13 +924,29 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(index);
}
+GDScriptCodeGenerator::Address GDScriptByteCodeGenerator::get_call_target(const GDScriptCodeGenerator::Address &p_target, Variant::Type p_type) {
+ if (p_target.mode == Address::NIL) {
+ GDScriptDataType type;
+ if (p_type != Variant::NIL) {
+ type.has_type = true;
+ type.kind = GDScriptDataType::BUILTIN;
+ type.builtin_type = p_type;
+ }
+ uint32_t addr = add_temporary(type);
+ pop_temporary();
+ return Address(Address::TEMPORARY, addr, type);
+ } else {
+ return p_target;
+ }
+}
+
void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -936,7 +956,7 @@ void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -947,7 +967,7 @@ void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -957,7 +977,7 @@ void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_tar
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function);
}
@@ -979,11 +999,17 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
}
if (is_validated) {
+ Variant::Type result_type = Variant::has_utility_function_return_value(p_function) ? Variant::get_utility_function_return_type(p_function) : Variant::NIL;
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
+ }
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_utility_function(p_function));
} else {
@@ -991,13 +1017,13 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function);
}
}
-void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments) {
bool is_validated = false;
// Check if all types are correct.
@@ -1017,16 +1043,26 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
if (!is_validated) {
// Perform regular call.
- write_call(p_target, p_base, p_method, p_arguments);
+ if (p_is_static) {
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(get_call_target(p_target));
+ append(p_type);
+ append(p_method);
+ append(p_arguments.size());
+ } else {
+ write_call(p_target, p_base, p_method, p_arguments);
+ }
return;
}
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
}
append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
@@ -1035,59 +1071,17 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
- bool is_validated = false;
-
- // Check if all types are correct.
- if (Variant::is_builtin_method_vararg(p_type, p_method)) {
- is_validated = true; // Vararg works fine with any argument, since they can be any type.
- } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
- bool all_types_exact = true;
- for (int i = 0; i < p_arguments.size(); i++) {
- if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
- all_types_exact = false;
- break;
- }
- }
-
- is_validated = all_types_exact;
- }
-
- if (!is_validated) {
- // Perform regular call.
- append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(p_target);
- append(p_type);
- append(p_method);
- append(p_arguments.size());
- return;
- }
-
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
- }
-
- append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, p_base, p_type, p_method, false, p_arguments);
+}
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(Address()); // No base since it's static.
- append(p_target);
- append(p_arguments.size());
- append(Variant::get_validated_builtin_method(p_type, p_method));
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, Address(), p_type, p_method, true, p_arguments);
}
void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) {
@@ -1101,7 +1095,7 @@ void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(method);
append(p_arguments.size());
return;
@@ -1114,7 +1108,7 @@ void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target,
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_method);
}
@@ -1178,7 +1172,7 @@ void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, cons
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_method);
if (is_ptrcall) {
@@ -1194,7 +1188,7 @@ void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const S
append(p_arguments[i]);
}
append(GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -1205,7 +1199,7 @@ void GDScriptByteCodeGenerator::write_call_self_async(const Address &p_target, c
append(p_arguments[i]);
}
append(GDScriptFunction::ADDR_SELF);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -1216,7 +1210,7 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
@@ -1227,7 +1221,7 @@ void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFu
append(p_captures[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_captures.size());
append(p_function);
}
@@ -1266,7 +1260,7 @@ void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(Variant::get_validated_constructor(p_type, valid_constructor));
return;
@@ -1277,7 +1271,7 @@ void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_type);
}
@@ -1287,7 +1281,7 @@ void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, c
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
}
@@ -1296,7 +1290,7 @@ void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_tar
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
if (p_element_type.script_type) {
Variant script_type = Ref<Script>(p_element_type.script_type);
int addr = get_constant_pos(script_type);
@@ -1315,7 +1309,7 @@ void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_targ
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index ba4847813f..171c505116 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -309,6 +309,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
}
+ Address get_call_target(const Address &p_target, Variant::Type p_type = Variant::NIL);
+
int address_of(const Address &p_address) {
switch (p_address.mode) {
case Address::SELF:
@@ -458,7 +460,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
- virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
+ virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override;
virtual void write_store_global(const Address &p_dst, int p_global_index) override;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) override;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
@@ -467,6 +469,7 @@ public:
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
+ void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments);
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index c7a1bcb9e9..e885938eba 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -113,7 +113,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
- virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
+ virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0;
virtual void write_store_global(const Address &p_dst, int p_global_index) = 0;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) = 0;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 2d36692c3c..d63a1b4536 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -213,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr
}
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
- if (p_expression->is_constant) {
+ if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) {
return codegen.add_constant(p_expression->reduced_value);
}
@@ -520,10 +520,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
- GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
-
- GDScriptCodeGenerator::Address return_addr = p_root ? nil : result;
+ GDScriptCodeGenerator::Address result;
+ if (p_root) {
+ result = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
+ } else {
+ result = codegen.add_temporary(type);
+ }
Vector<GDScriptCodeGenerator::Address> arguments;
for (int i = 0; i < call->arguments.size(); i++) {
@@ -538,20 +540,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- gen->write_construct(return_addr, vtype, arguments);
+ gen->write_construct(result, vtype, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
// Variant utility function.
- gen->write_call_utility(return_addr, call->function_name, arguments);
+ gen->write_call_utility(result, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
// GDScript utility function.
- gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
+ gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
if (call->is_super) {
// Super call.
- gen->write_super_call(return_addr, call->function_name, arguments);
+ gen->write_super_call(result, call->function_name, arguments);
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
@@ -563,24 +565,24 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall.
- gen->write_call_ptrcall(return_addr, self, method, arguments);
+ gen->write_call_ptrcall(result, self, method, arguments);
} else {
// Not exact arguments, but still can use method bind call.
- gen->write_call_method_bind(return_addr, self, method, arguments);
+ gen->write_call_method_bind(result, self, method, arguments);
}
} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
if (within_await) {
- gen->write_call_async(return_addr, self, call->function_name, arguments);
+ gen->write_call_async(result, self, call->function_name, arguments);
} else {
- gen->write_call(return_addr, self, call->function_name, arguments);
+ gen->write_call(result, self, call->function_name, arguments);
}
} else {
if (within_await) {
- gen->write_call_self_async(return_addr, call->function_name, arguments);
+ gen->write_call_self_async(result, call->function_name, arguments);
} else {
- gen->write_call_self(return_addr, call->function_name, arguments);
+ gen->write_call_self(result, call->function_name, arguments);
}
}
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
@@ -589,18 +591,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (subscript->is_attribute) {
// May be static built-in method call.
if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
- gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
+ gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&
ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
// It's a static native method call.
- gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
+ gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
} else {
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
if (within_await) {
- gen->write_call_async(return_addr, base, call->function_name, arguments);
+ gen->write_call_async(result, base, call->function_name, arguments);
} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
// Native method, use faster path.
StringName class_name;
@@ -613,18 +615,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
MethodBind *method = ClassDB::get_method(class_name, call->function_name);
if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall.
- gen->write_call_ptrcall(return_addr, base, method, arguments);
+ gen->write_call_ptrcall(result, base, method, arguments);
} else {
// Not exact arguments, but still can use method bind call.
- gen->write_call_method_bind(return_addr, base, method, arguments);
+ gen->write_call_method_bind(result, base, method, arguments);
}
} else {
- gen->write_call(return_addr, base, call->function_name, arguments);
+ gen->write_call(result, base, call->function_name, arguments);
}
} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
- gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments);
+ gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
} else {
- gen->write_call(return_addr, base, call->function_name, arguments);
+ gen->write_call(result, base, call->function_name, arguments);
}
if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -2114,7 +2116,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
}
- codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 6f8b90bd06..3fc0924b4c 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -80,7 +80,7 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
}
processed_template = processed_template.replace("_BASE_", p_base_class_name)
- .replace("_CLASS_", p_class_name)
+ .replace("_CLASS_", p_class_name.to_pascal_case())
.replace("_TS_", _get_indentation());
scr->set_source_code(processed_template);
return scr;
@@ -995,9 +995,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
GDScriptParser::DataType base_type = p_base.type;
- bool _static = base_type.is_meta_type;
- if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {
ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);
option.insert_text += "(";
r_result.insert(option.display, option);
@@ -1006,7 +1005,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
while (!base_type.has_no_type()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
- _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1);
+ _find_identifiers_in_class(base_type.class_type, p_only_functions, base_type.is_meta_type, false, r_result, p_recursion_depth + 1);
// This already finds all parent identifiers, so we are done.
base_type = GDScriptParser::DataType();
} break;
@@ -1014,7 +1013,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
if (!p_only_functions) {
- if (!_static) {
+ if (!base_type.is_meta_type) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const PropertyInfo &E : members) {
@@ -1090,7 +1089,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
- if (!_static || Engine::get_singleton()->has_singleton(type)) {
+ if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
for (const PropertyInfo &E : pinfo) {
@@ -1107,7 +1106,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
- bool only_static = _static && !Engine::get_singleton()->has_singleton(type);
+ bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type);
List<MethodInfo> methods;
ClassDB::get_method_list(type, &methods, false, true);
@@ -1129,6 +1128,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
return;
} break;
+ case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
Variant tmp;
@@ -1156,6 +1156,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
tmp.get_method_list(&methods);
for (const MethodInfo &E : methods) {
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) {
+ // Enum types are static and cannot change, therefore we skip non-const dictionary methods.
+ continue;
+ }
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
if (E.arguments.size()) {
option.insert_text += "(";
@@ -1271,6 +1275,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}
r_result.insert(option.display, option);
}
+
+ // Global classes
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+ for (const StringName &E : global_classes) {
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
+ r_result.insert(option.display, option);
+ }
}
static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
@@ -1364,6 +1376,9 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (p_expression->is_constant) {
// Already has a value, so just use that.
r_type = _type_from_variant(p_expression->reduced_value);
+ if (p_expression->get_datatype().kind == GDScriptParser::DataType::ENUM) {
+ r_type.type = p_expression->get_datatype();
+ }
found = true;
} else {
switch (p_expression->type) {
@@ -2977,7 +2992,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// The path needs quotes if it's not a valid identifier (with an exception
// for "/" as path separator, which also doesn't require quotes).
if (!opt.replace("/", "_").is_valid_identifier()) {
- opt = opt.quote(quote_style); // Handle user preference.
+ // Ignore quote_style and just use double quotes for paths with apostrophes.
+ // Double quotes don't need to be checked because they're not valid in node and property names.
+ opt = opt.quote(opt.contains("'") ? "\"" : quote_style); // Handle user preference.
}
ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
options.insert(option.display, option);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 6107bb37c8..f5d3306376 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -540,43 +540,28 @@ void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
head->fqcn = script_path;
current_class = head;
+ bool can_have_class_or_extends = true;
- // If we happen to parse an annotation before extends or class_name keywords, track it.
- // @tool is allowed, but others should fail.
- AnnotationNode *premature_annotation = nullptr;
-
- if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @tool, script-level, or standalone annotation.
+ while (match(GDScriptTokenizer::Token::ANNOTATION)) {
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == SNAME("@tool")) {
- // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
- _is_tool = true;
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after "@tool" annotation.)");
- }
- // @tool annotation has no specific target.
- annotation->apply(this, nullptr);
- } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
- premature_annotation = annotation;
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after a standalone annotation.)");
- }
+ if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
annotation->apply(this, head);
} else {
- premature_annotation = annotation;
annotation_stack.push_back(annotation);
+ // This annotation must appear after script-level annotations
+ // and class_name/extends (ex: could be @onready or @export),
+ // so we stop looking for script-level stuff.
+ can_have_class_or_extends = false;
+ break;
}
}
}
- for (bool should_break = false; !should_break;) {
+ while (can_have_class_or_extends) {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (premature_annotation != nullptr) {
- push_error(R"("class_name" should be used before annotations (except @tool).)");
- }
advance();
if (head->identifier != nullptr) {
push_error(R"("class_name" can only be used once.)");
@@ -585,9 +570,6 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (premature_annotation != nullptr) {
- push_error(R"("extends" should be used before annotations (except @tool).)");
- }
advance();
if (head->extends_used) {
push_error(R"("extends" can only be used once.)");
@@ -597,7 +579,8 @@ void GDScriptParser::parse_program() {
}
break;
default:
- should_break = true;
+ // No tokens are allowed between script annotations and class/extends.
+ can_have_class_or_extends = false;
break;
}
@@ -606,21 +589,6 @@ void GDScriptParser::parse_program() {
}
}
- if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for a script-level, or standalone annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
- if (annotation != nullptr) {
- if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after a standalone annotation.)");
- }
- annotation->apply(this, head);
- } else {
- annotation_stack.push_back(annotation);
- }
- }
- }
-
parse_class_body(true);
complete_extents(head);
@@ -1299,16 +1267,18 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
EnumNode::Value item;
GDScriptParser::IdentifierNode *identifier = parse_identifier();
#ifdef DEBUG_ENABLED
- for (MethodInfo &info : gdscript_funcs) {
- if (info.name == identifier->name) {
+ if (!named) { // Named enum identifiers do not shadow anything since you can only access them with NamedEnum.ENUM_VALUE
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
}
}
- if (Variant::has_utility_function(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
- } else if (ClassDB::class_exists(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
- }
#endif
item.identifier = identifier;
item.parent_enum = enum_node;
@@ -1319,14 +1289,8 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
} else if (!named) {
- // TODO: Abstract this recursive member check.
- ClassNode *parent = current_class;
- while (parent != nullptr) {
- if (parent->members_indices.has(item.identifier->name)) {
- push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, parent->get_member(item.identifier->name).get_type_name()));
- break;
- }
- parent = parent->outer;
+ if (current_class->members_indices.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, current_class->get_member(item.identifier->name).get_type_name()));
}
}
@@ -1799,24 +1763,29 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
// TODO: Add assert message.
AssertNode *assert = alloc_node<AssertNode>();
+ push_multiline(true);
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "assert".)");
+
assert->condition = parse_expression(false);
if (assert->condition == nullptr) {
push_error("Expected expression to assert.");
+ pop_multiline();
complete_extents(assert);
return nullptr;
}
- if (match(GDScriptTokenizer::Token::COMMA)) {
- // Error message.
+ if (match(GDScriptTokenizer::Token::COMMA) && !check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
assert->message = parse_expression(false);
if (assert->message == nullptr) {
push_error(R"(Expected error message for assert after ",".)");
+ pop_multiline();
complete_extents(assert);
return nullptr;
}
+ match(GDScriptTokenizer::Token::COMMA);
}
+ pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
complete_extents(assert);
@@ -3793,6 +3762,26 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::INT;
}
}
+ if (p_annotation->name == SNAME("@export_multiline")) {
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
+ DataType inner_type = export_type.get_container_element_type();
+ if (inner_type.builtin_type != Variant::STRING) {
+ push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
+ return false;
+ }
+
+ String hint_prefix = itos(inner_type.builtin_type) + "/" + itos(variable->export_info.hint);
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::ARRAY;
+
+ return true;
+ } else if (export_type.builtin_type == Variant::DICTIONARY) {
+ variable->export_info.type = Variant::DICTIONARY;
+
+ return true;
+ }
+ }
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
@@ -4087,8 +4076,11 @@ String GDScriptParser::DataType::to_string() const {
}
return native_type.operator String();
}
- case ENUM:
- return enum_type.operator String() + " (enum)";
+ case ENUM: {
+ // native_type contains either the native class defining the enum
+ // or the fully qualified class name of the script defining the enum
+ return String(native_type).get_file(); // Remove path, keep filename
+ }
case RESOLVING:
case UNRESOLVED:
return "<unresolved type>";
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 65eace8088..0903f62061 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -185,8 +185,8 @@ public:
case BUILTIN:
return builtin_type == p_other.builtin_type;
case NATIVE:
- case ENUM:
- return native_type == p_other.native_type && enum_type == p_other.enum_type;
+ case ENUM: // Enums use native_type to identify the enum and its base class.
+ return native_type == p_other.native_type;
case SCRIPT:
return script_type == p_other.script_type;
case CLASS:
@@ -360,6 +360,7 @@ public:
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
+ bool use_conversion_assign = false;
int usages = 0;
virtual ~AssignableNode() {}
@@ -1182,7 +1183,6 @@ public:
bool onready = false;
PropertyInfo export_info;
int assignments = 0;
- bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 04612c6793..e17a804003 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -164,6 +164,7 @@ bool GDScriptTokenizer::Token::is_identifier() const {
switch (type) {
case IDENTIFIER:
case MATCH: // Used in String.match().
+ case CONST_INF: // Used in Vector{2,3,4}.INF
return true;
default:
return false;
diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md
index 6e54085962..361d586d32 100644
--- a/modules/gdscript/tests/README.md
+++ b/modules/gdscript/tests/README.md
@@ -4,5 +4,5 @@ The `scripts/` folder contains integration tests in the form of GDScript files
and output files.
See the
-[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript)
+[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html#integration-tests-for-gdscript)
for information about creating and running GDScript integration tests.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
new file mode 100644
index 0000000000..38c2faa859
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
@@ -0,0 +1,2 @@
+func test():
+ CanvasItem.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
new file mode 100644
index 0000000000..9eff912b59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Native class "CanvasItem" cannot be constructed as it is abstract.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
new file mode 100644
index 0000000000..118e7e8a45
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
@@ -0,0 +1,9 @@
+class A extends CanvasItem:
+ func _init():
+ print('no')
+
+class B extends A:
+ pass
+
+func test():
+ B.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
new file mode 100644
index 0000000000..8b956f5974
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd
new file mode 100644
index 0000000000..8123fc53d9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd
@@ -0,0 +1,3 @@
+enum { V }
+func test():
+ V = 1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd
new file mode 100644
index 0000000000..da2b13d690
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd
@@ -0,0 +1,3 @@
+enum NamedEnum { V }
+func test():
+ NamedEnum.V = 1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd
new file mode 100644
index 0000000000..71616ea3af
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd
@@ -0,0 +1,5 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2, OTHER_ENUM_VALUE_3 }
+
+func test():
+ print(MyOtherEnum.OTHER_ENUM_VALUE_3 as MyEnum)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
new file mode 100644
index 0000000000..3a8d2a205a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid cast. Enum "cast_enum_bad_enum.gd::MyEnum" does not have value corresponding to "MyOtherEnum.OTHER_ENUM_VALUE_3" (2).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd
new file mode 100644
index 0000000000..60a31fb318
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd
@@ -0,0 +1,4 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ print(2 as MyEnum)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
new file mode 100644
index 0000000000..bc0d8b7834
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid cast. Enum "cast_enum_bad_int.gd::MyEnum" does not have enum value 2.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd
new file mode 100644
index 0000000000..b8603dd4ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd
@@ -0,0 +1,5 @@
+const array: Array = [0]
+
+func test():
+ var key: int = 0
+ array[key] = 0
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd
new file mode 100644
index 0000000000..9b5112b788
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd
@@ -0,0 +1,5 @@
+const dictionary := {}
+
+func test():
+ var key: int = 0
+ dictionary[key] = 0
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
new file mode 100644
index 0000000000..87fbe1229c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
@@ -0,0 +1,5 @@
+const base := [0]
+
+func test():
+ var sub := base[0]
+ if sub is String: pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd
new file mode 100644
index 0000000000..2940c03515
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd
@@ -0,0 +1,4 @@
+enum Enum {V1, V2}
+
+func test():
+ Enum.clear()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out
new file mode 100644
index 0000000000..9ca86eca9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call non-const Dictionary function "clear()" on enum "Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd
new file mode 100644
index 0000000000..a66e2714d9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd
@@ -0,0 +1,4 @@
+enum Enum {V1, V2}
+
+func test():
+ var bad = Enum.V3
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
new file mode 100644
index 0000000000..ddbdc17a42
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "V3" in base "enum_bad_value.gd::Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
index fde7e92f8c..02c4633586 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
+Value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
index 6fa2682d0a..441cccbf7b 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type MyOtherEnum (enum) to variable "class_var" with specified type MyEnum (enum).
+Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd
new file mode 100644
index 0000000000..2c7dfafd06
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd
@@ -0,0 +1,5 @@
+enum Enum {V1, V2}
+
+func test():
+ var Enum2 = Enum
+ Enum2.clear()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out
new file mode 100644
index 0000000000..9ca86eca9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call non-const Dictionary function "clear()" on enum "Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd
new file mode 100644
index 0000000000..62ac1c3108
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func enum_func(e : MyEnum) -> void:
+ print(e)
+
+func test():
+ enum_func(MyOtherEnum.OTHER_ENUM_VALUE_1)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
new file mode 100644
index 0000000000..e85f7d6f9f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid argument for "enum_func()" function: argument 1 should be "enum_function_parameter_wrong_type.gd::MyEnum" but is "enum_function_parameter_wrong_type.gd::MyOtherEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd
new file mode 100644
index 0000000000..18b3ffb0fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func enum_func() -> MyEnum:
+ return MyOtherEnum.OTHER_ENUM_VALUE_1
+
+func test():
+ print(enum_func())
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
new file mode 100644
index 0000000000..f7ea3267fa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot return value of type "enum_function_return_wrong_type.gd::MyOtherEnum" because the function return type is "enum_function_return_wrong_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..2b006f1f69
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd
@@ -0,0 +1,10 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+class InnerClass:
+ enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = InnerClass.MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
new file mode 100644
index 0000000000..38df5a0cd8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" cannot be assigned to a variable of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
index fde7e92f8c..2adcbd9edf 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
+Value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
index 07fb19f1ff..331113dd30 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type MyOtherEnum (enum) to variable "local_var" with specified type MyEnum (enum).
+Cannot assign a value of type enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "local_var" with specified type enum_local_var_init_with_wrong_enum_type.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd
new file mode 100644
index 0000000000..744c2e47ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd
@@ -0,0 +1,2 @@
+func test():
+ var _bad = TileSet.TileShape.THIS_DOES_NOT_EXIST
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
new file mode 100644
index 0000000000..49f041a2dd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "THIS_DOES_NOT_EXIST" in base "TileSet::TileShape".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
new file mode 100644
index 0000000000..81d5d59ae8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
@@ -0,0 +1,7 @@
+enum MyEnum { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+func test():
+ const P = preload("../features/enum_value_from_parent.gd")
+ var local_var: MyEnum
+ local_var = P.VALUE_B
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
new file mode 100644
index 0000000000..6298c026b4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_value_from_parent.gd::<anonymous enum>" cannot be assigned to a variable of type "enum_preload_unnamed_assign_to_named.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd
new file mode 100644
index 0000000000..96904c297a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd
@@ -0,0 +1,8 @@
+class A:
+ enum { V }
+
+class B extends A:
+ enum { V }
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out
new file mode 100644
index 0000000000..7961a1a481
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "V" already exists in parent class A.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd
new file mode 100644
index 0000000000..7e749db6b5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd
@@ -0,0 +1,7 @@
+enum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = ENUM_VALUE_1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
new file mode 100644
index 0000000000..b70121ed81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type enum_unnamed_assign_to_named.gd::<anonymous enum> to variable "local_var" with specified type enum_unnamed_assign_to_named.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
new file mode 100644
index 0000000000..cf56a0a933
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
@@ -0,0 +1,6 @@
+const constant_float = 1.0
+
+func test():
+ for x in constant_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
new file mode 100644
index 0000000000..5ee8ac19e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
@@ -0,0 +1,6 @@
+const constant_int = 1
+
+func test():
+ for x in constant_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
new file mode 100644
index 0000000000..b3db4f3b49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
@@ -0,0 +1,6 @@
+enum { enum_value = 1 }
+
+func test():
+ for x in enum_value:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
new file mode 100644
index 0000000000..87c54f7402
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_float := 1.0
+
+ for x in hard_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
new file mode 100644
index 0000000000..2a43f5a930
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_int := 1
+
+ for x in hard_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
new file mode 100644
index 0000000000..c3920d35b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
@@ -0,0 +1,14 @@
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_iterator := Iterator.new()
+
+ for x in hard_iterator:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
new file mode 100644
index 0000000000..a48591a3b4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "StringName" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
new file mode 100644
index 0000000000..b36d87aabe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_string := 'a'
+
+ for x in hard_string:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
new file mode 100644
index 0000000000..92c5ebc599
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "String" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
new file mode 100644
index 0000000000..060a8bedf9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
@@ -0,0 +1,3 @@
+func test():
+ for x in true:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
new file mode 100644
index 0000000000..94cb038885
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Unable to iterate on value of type "bool".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
new file mode 100644
index 0000000000..6cfc822482
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
@@ -0,0 +1,4 @@
+func test():
+ for x in 1:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
new file mode 100644
index 0000000000..664e364493
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
@@ -0,0 +1,3 @@
+func test():
+ var foo: bool = true
+ foo += 'bar'
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
new file mode 100644
index 0000000000..358b096a64
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands "bool" and "String" for assignment operator.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd
new file mode 100644
index 0000000000..e1bed94406
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd
@@ -0,0 +1,2 @@
+func test():
+ TileSet.this_does_not_exist # Does not exist
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out
new file mode 100644
index 0000000000..06180c3a55
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "this_does_not_exist" in base "TileSet".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
new file mode 100644
index 0000000000..1cf3870a8e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ print(type.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
new file mode 100644
index 0000000000..73a54d7820
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
new file mode 100644
index 0000000000..c1074df915
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ var type_v: Variant = type
+ print(type_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
new file mode 100644
index 0000000000..92e7b9316e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
new file mode 100644
index 0000000000..2631c3c500
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ print(instance.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
new file mode 100644
index 0000000000..892f8e2c3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
new file mode 100644
index 0000000000..cba788381e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ var instance_v: Variant = instance
+ print(instance_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
new file mode 100644
index 0000000000..8257e74f57
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
index 65c0d9dabc..200c352223 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
@@ -1,12 +1,12 @@
class A:
- class B:
- func test():
- print(A.B.D)
+ class B:
+ func test():
+ print(A.B.D)
class C:
- class D:
- pass
+ class D:
+ pass
func test():
- var inst = A.B.new()
- inst.test()
+ var inst = A.B.new()
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd
new file mode 100644
index 0000000000..4e75ded96a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd
@@ -0,0 +1,6 @@
+enum LocalNamed { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+func test():
+ const P = preload("../features/enum_from_outer.gd")
+ var x : LocalNamed
+ x = P.Named.VALUE_A
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
new file mode 100644
index 0000000000..5e3c446bf6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_from_outer.gd::Named" cannot be assigned to a variable of type "preload_enum_error.gd::LocalNamed".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
index bbadf1ce27..bf776029b9 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "String" to a target of type "int".
+Value of type "String" cannot be assigned to a variable of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
index 63587942f7..393b66c9f0 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
@@ -1,2 +1,2 @@
func test() -> void:
- return null
+ return null
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
index 0ee4e7ea36..6be2730bab 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
@@ -1,4 +1,4 @@
func test() -> void:
- var a
- a = 1
- return a
+ var a
+ a = 1
+ return a
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
new file mode 100644
index 0000000000..2d2c2bef19
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
@@ -0,0 +1,19 @@
+func test():
+ var one_0 = 0
+ one_0 = 1
+ var one_1 := one_0
+ print(one_1)
+
+ var two: Variant = 0
+ two += 2
+ print(two)
+
+ var three_0: Variant = 1
+ var three_1: int = 2
+ three_0 += three_1
+ print(three_0)
+
+ var four_0: int = 3
+ var four_1: Variant = 1
+ four_0 += four_1
+ print(four_0)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
index 7881a0feb6..a94487d989 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
@@ -11,4 +11,3 @@ func test() -> void:
Extend.InnerClass.InnerInnerClass.test_a_b_c(A.new(), B.new(), C.new())
Extend.InnerClass.InnerInnerClass.test_enum(C.TestEnum.HELLO_WORLD)
Extend.InnerClass.InnerInnerClass.test_a_prime(A.APrime.new())
-
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
index 30e7deb05a..7c846c59bd 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
@@ -1,19 +1,19 @@
class A:
- var x = 3
+ var x = 3
class B:
- var x = 4
+ var x = 4
class C:
- var x = 5
+ var x = 5
class Test:
- var a = A.new()
- var b: B = B.new()
- var c := C.new()
+ var a = A.new()
+ var b: B = B.new()
+ var c := C.new()
func test():
- var test_instance := Test.new()
- prints(test_instance.a.x)
- prints(test_instance.b.x)
- prints(test_instance.c.x)
+ var test_instance := Test.new()
+ prints(test_instance.a.x)
+ prints(test_instance.b.x)
+ prints(test_instance.c.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd
new file mode 100644
index 0000000000..9bc08f2dc5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd
@@ -0,0 +1,29 @@
+class_name EnumAccessOuterClass
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ static func print_enums():
+ print("Inner - Inner")
+ print(MyEnum.V0, MyEnum.V1, MyEnum.V2)
+ print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2)
+ print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2)
+
+ print("Inner - Outer")
+ print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2)
+
+
+enum MyEnum { V0, V1, V2 }
+
+func print_enums():
+ print("Outer - Outer")
+ print(MyEnum.V0, MyEnum.V1, MyEnum.V2)
+ print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2)
+
+ print("Outer - Inner")
+ print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2)
+ print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2)
+
+func test():
+ print_enums()
+ InnerClass.print_enums()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out
new file mode 100644
index 0000000000..02e2e2b396
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out
@@ -0,0 +1,13 @@
+GDTEST_OK
+Outer - Outer
+012
+012
+Outer - Inner
+021
+021
+Inner - Inner
+021
+021
+021
+Inner - Outer
+012
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd
new file mode 100644
index 0000000000..3076e7069f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd
@@ -0,0 +1,13 @@
+enum Enum {V1, V2}
+
+func test():
+ var enumAsDict : Dictionary = Enum.duplicate()
+ var enumAsVariant = Enum.duplicate()
+ print(Enum.has("V1"))
+ print(enumAsDict.has("V1"))
+ print(enumAsVariant.has("V1"))
+ enumAsDict.clear()
+ enumAsVariant.clear()
+ print(Enum.has("V1"))
+ print(enumAsDict.has("V1"))
+ print(enumAsVariant.has("V1"))
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out
new file mode 100644
index 0000000000..a41924d0c9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+true
+true
+true
+false
+false
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd
new file mode 100644
index 0000000000..b3f9941903
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd
@@ -0,0 +1,13 @@
+class A:
+ enum Named { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+class B extends A:
+ var a = Named.VALUE_A
+ var b = Named.VALUE_B
+ var c = Named.VALUE_C
+
+func test():
+ var test_instance = B.new()
+ prints("a", test_instance.a, test_instance.a == A.Named.VALUE_A)
+ prints("b", test_instance.b, test_instance.b == A.Named.VALUE_B)
+ prints("c", test_instance.c, test_instance.c == B.Named.VALUE_C)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out
index c160839da3..c160839da3 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd
index 5f57c5b8c2..4d6852a9be 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd
@@ -1,5 +1,3 @@
-extends Node
-
enum Named { VALUE_A, VALUE_B, VALUE_C = 42 }
class Test:
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out
new file mode 100644
index 0000000000..c160839da3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+a 0 true
+b 1 true
+c 42 true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd
new file mode 100644
index 0000000000..8a4e89d0d6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd
@@ -0,0 +1,112 @@
+class_name EnumFunctionTypecheckOuterClass
+
+enum MyEnum { V0, V1, V2 }
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ func inner_inner_no_class(e : MyEnum) -> MyEnum:
+ print(e)
+ return e
+
+ func inner_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum:
+ print(e)
+ return e
+
+ func inner_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum:
+ print(e)
+ return e
+
+ func inner_outer(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum:
+ print(e)
+ return e
+
+ func test():
+ var _d
+ print("Inner")
+
+ var o := EnumFunctionTypecheckOuterClass.new()
+
+ _d = o.outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = o.outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = o.outer_inner_class(MyEnum.V1)
+ _d = o.outer_inner_class(InnerClass.MyEnum.V1)
+ _d = o.outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = o.outer_inner_class_class(MyEnum.V1)
+ _d = o.outer_inner_class_class(InnerClass.MyEnum.V1)
+ _d = o.outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ print()
+
+
+ _d = inner_inner_no_class(MyEnum.V1)
+ _d = inner_inner_no_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_inner_class(MyEnum.V1)
+ _d = inner_inner_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_inner_class_class(MyEnum.V1)
+ _d = inner_inner_class_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ print()
+
+
+func outer_outer_no_class(e : MyEnum) -> MyEnum:
+ print(e)
+ return e
+
+func outer_outer_class(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum:
+ print(e)
+ return e
+
+func outer_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum:
+ print(e)
+ return e
+
+func outer_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum:
+ print(e)
+ return e
+
+func test():
+ var _d
+ print("Outer")
+
+ _d = outer_outer_no_class(MyEnum.V1)
+ _d = outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = outer_outer_class(MyEnum.V1)
+ _d = outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = outer_inner_class(InnerClass.MyEnum.V1)
+ _d = outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = outer_inner_class_class(InnerClass.MyEnum.V1)
+ _d = outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ print()
+
+ var i := EnumFunctionTypecheckOuterClass.InnerClass.new()
+
+ _d = i.inner_inner_no_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_inner_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_inner_class_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_outer(MyEnum.V1)
+ _d = i.inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ print()
+
+ i.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out
new file mode 100644
index 0000000000..2e3ce1aa10
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out
@@ -0,0 +1,55 @@
+GDTEST_OK
+Outer
+1
+1
+
+1
+1
+
+2
+2
+
+2
+2
+
+
+2
+2
+
+2
+2
+
+2
+2
+
+1
+1
+
+
+Inner
+1
+
+1
+
+2
+2
+2
+
+2
+2
+2
+
+
+2
+2
+2
+
+2
+2
+2
+
+2
+2
+2
+
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd
new file mode 100644
index 0000000000..b97d9bbb6b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd
@@ -0,0 +1,16 @@
+const A := 1
+enum { B }
+enum NamedEnum { C }
+
+class Parent:
+ const D := 2
+ enum { E }
+ enum NamedEnum2 { F }
+
+class Child extends Parent:
+ enum TestEnum { A, B, C, D, E, F, Node, Object, Child, Parent}
+
+func test():
+ print(A, B, NamedEnum.C, Parent.D, Parent.E, Parent.NamedEnum2.F)
+ print(Child.TestEnum.A, Child.TestEnum.B, Child.TestEnum.C, Child.TestEnum.D, Child.TestEnum.E, Child.TestEnum.F)
+ print(Child.TestEnum.Node, Child.TestEnum.Object, Child.TestEnum.Child, Child.TestEnum.Parent)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out
new file mode 100644
index 0000000000..864ba2a549
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+100200
+012345
+6789
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd
new file mode 100644
index 0000000000..6a0a1e1969
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd
@@ -0,0 +1,19 @@
+func print_enum(e : TileSet.TileShape) -> TileSet.TileShape:
+ print(e)
+ return e
+
+func test():
+ var v : TileSet.TileShape
+ v = TileSet.TILE_SHAPE_SQUARE
+ v = print_enum(v)
+ v = print_enum(TileSet.TILE_SHAPE_SQUARE)
+ v = TileSet.TileShape.TILE_SHAPE_SQUARE
+ v = print_enum(v)
+ v = print_enum(TileSet.TileShape.TILE_SHAPE_SQUARE)
+
+ v = TileSet.TILE_SHAPE_ISOMETRIC
+ v = print_enum(v)
+ v = print_enum(TileSet.TILE_SHAPE_ISOMETRIC)
+ v = TileSet.TileShape.TILE_SHAPE_ISOMETRIC
+ v = print_enum(v)
+ v = print_enum(TileSet.TileShape.TILE_SHAPE_ISOMETRIC)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out
new file mode 100644
index 0000000000..1126dcc6ec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+0
+0
+0
+0
+1
+1
+1
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd
new file mode 100644
index 0000000000..b05ae82048
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd
@@ -0,0 +1,86 @@
+class_name EnumTypecheckOuterClass
+
+enum MyEnum { V0, V1, V2 }
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ static func test_inner_from_inner():
+ print("Inner - Inner")
+ var e1 : MyEnum
+ var e2 : InnerClass.MyEnum
+ var e3 : EnumTypecheckOuterClass.InnerClass.MyEnum
+
+ print("Self ", e1, e2, e3)
+ e1 = MyEnum.V1
+ e2 = MyEnum.V1
+ e3 = MyEnum.V1
+ print("MyEnum ", e1, e2, e3)
+ e1 = InnerClass.MyEnum.V1
+ e2 = InnerClass.MyEnum.V1
+ e3 = InnerClass.MyEnum.V1
+ print("Inner.MyEnum ", e1, e2, e3)
+ e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e3 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ print("Outer.Inner.MyEnum ", e1, e2, e3)
+
+ e1 = e2
+ e1 = e3
+ e2 = e1
+ e2 = e3
+ e3 = e1
+ e3 = e2
+
+ print()
+
+ static func test_outer_from_inner():
+ print("Inner - Outer")
+ var e : EnumTypecheckOuterClass.MyEnum
+
+ e = EnumTypecheckOuterClass.MyEnum.V1
+ print("Outer.MyEnum ", e)
+
+ print()
+
+func test_outer_from_outer():
+ print("Outer - Outer")
+ var e1 : MyEnum
+ var e2 : EnumTypecheckOuterClass.MyEnum
+
+ print("Self ", e1, e2)
+ e1 = MyEnum.V1
+ e2 = MyEnum.V1
+ print("Outer ", e1, e2)
+ e1 = EnumTypecheckOuterClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.MyEnum.V1
+ print("Outer.MyEnum ", e1, e2)
+
+ e1 = e2
+ e2 = e1
+
+ print()
+
+func test_inner_from_outer():
+ print("Outer - Inner")
+ var e1 : InnerClass.MyEnum
+ var e2 : EnumTypecheckOuterClass.InnerClass.MyEnum
+
+ print("Inner ", e1, e2)
+ e1 = InnerClass.MyEnum.V1
+ e2 = InnerClass.MyEnum.V1
+ print("Outer.Inner ", e1, e2)
+ e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ print("Outer.Inner.MyEnum ", e1, e2)
+
+ e1 = e2
+ e2 = e1
+
+ print()
+
+func test():
+ test_outer_from_outer()
+ test_inner_from_outer()
+ InnerClass.test_outer_from_inner()
+ InnerClass.test_inner_from_inner()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out
new file mode 100644
index 0000000000..3b2dcade26
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out
@@ -0,0 +1,19 @@
+GDTEST_OK
+Outer - Outer
+Self 00
+Outer 11
+Outer.MyEnum 11
+
+Outer - Inner
+Inner 00
+Outer.Inner 22
+Outer.Inner.MyEnum 22
+
+Inner - Outer
+Outer.MyEnum 1
+
+Inner - Inner
+Self 000
+MyEnum 222
+Inner.MyEnum 222
+Outer.Inner.MyEnum 222
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
new file mode 100644
index 0000000000..95c3268130
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
@@ -0,0 +1,12 @@
+class A extends CanvasItem:
+ func _init():
+ pass
+
+class B extends A:
+ pass
+
+class C extends CanvasItem:
+ pass
+
+func test():
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
index 757744b6f1..0c740935b9 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
@@ -2,5 +2,5 @@ const External = preload("external_enum_as_constant_external.notest.gd")
const MyEnum = External.MyEnum
func test():
- print(MyEnum.WAITING == 0)
- print(MyEnum.GODOT == 1)
+ print(MyEnum.WAITING == 0)
+ print(MyEnum.GODOT == 1)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
index 7c090844d0..24c1e41aab 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
@@ -1,4 +1,4 @@
enum MyEnum {
- WAITING,
- GODOT
+ WAITING,
+ GODOT
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
new file mode 100644
index 0000000000..7b74be6f2c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
@@ -0,0 +1,15 @@
+func test():
+ var variant_int: Variant = 1
+ var weak_int = 1
+
+ for x in variant_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ for x in weak_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
new file mode 100644
index 0000000000..7677671cfd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+0
+0
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
new file mode 100644
index 0000000000..541da78332
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
@@ -0,0 +1,50 @@
+# Inner-outer class lookup
+class A:
+ const Q: = "right one"
+
+class X:
+ const Q: = "wrong one"
+
+class Y extends X:
+ class B extends A:
+ static func check() -> void:
+ print(Q)
+
+# External class lookup
+const External: = preload("lookup_class_external.notest.gd")
+
+class Internal extends External.A:
+ static func check() -> void:
+ print(TARGET)
+
+ class E extends External.E:
+ static func check() -> void:
+ print(TARGET)
+ print(WAITING)
+
+# Variable lookup
+class C:
+ var Q := 'right one'
+
+class D:
+ const Q := 'wrong one'
+
+class E extends D:
+ class F extends C:
+ func check() -> void:
+ print(Q)
+
+# Test
+func test() -> void:
+ # Inner-outer class lookup
+ Y.B.check()
+ print("---")
+
+ # External class lookup
+ Internal.check()
+ Internal.E.check()
+ print("---")
+
+ # Variable lookup
+ var f: = E.F.new()
+ f.check()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
new file mode 100644
index 0000000000..a0983c1438
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+right one
+---
+wrong
+right
+godot
+---
+right one
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
new file mode 100644
index 0000000000..a2904e20a8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
@@ -0,0 +1,15 @@
+class A:
+ const TARGET: = "wrong"
+
+ class B:
+ const TARGET: = "wrong"
+ const WAITING: = "godot"
+
+ class D extends C:
+ pass
+
+class C:
+ const TARGET: = "right"
+
+class E extends A.B.D:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
new file mode 100644
index 0000000000..26cf6c7322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
@@ -0,0 +1,41 @@
+signal hello
+
+func get_signal() -> Signal:
+ return hello
+
+class A:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+ class B:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+class C extends A.B:
+ func get_signal() -> Signal:
+ return hello
+
+func test():
+ var a: = A.new()
+ var b: = A.B.new()
+ var c: = C.new()
+
+ var hello_a_result: = hello == a.get_signal()
+ var hello_b_result: = hello == b.get_signal()
+ var hello_c_result: = hello == c.get_signal()
+ var a_b_result: = a.get_signal() == b.get_signal()
+ var a_c_result: = a.get_signal() == c.get_signal()
+ var b_c_result: = b.get_signal() == c.get_signal()
+ var c_c_result: = c.get_signal() == c.get_signal()
+
+ print("hello == A.hello? %s" % hello_a_result)
+ print("hello == A.B.hello? %s" % hello_b_result)
+ print("hello == C.hello? %s" % hello_c_result)
+ print("A.hello == A.B.hello? %s" % a_b_result)
+ print("A.hello == C.hello? %s" % a_c_result)
+ print("A.B.hello == C.hello? %s" % b_c_result)
+ print("C.hello == C.hello? %s" % c_c_result)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
new file mode 100644
index 0000000000..6b0d32eaf8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+hello == A.hello? false
+hello == A.B.hello? false
+hello == C.hello? false
+A.hello == A.B.hello? false
+A.hello == C.hello? false
+A.B.hello == C.hello? false
+C.hello == C.hello? true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
index c9caef7d7c..95f04421d1 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
@@ -1,5 +1,5 @@
func variant() -> Variant:
- return 'variant'
+ return 'variant'
func test():
- print(variant())
+ print(variant())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
new file mode 100644
index 0000000000..da24c06b2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
@@ -0,0 +1,6 @@
+class Check extends Node:
+ func _set(_property: StringName, _value: Variant) -> bool:
+ return true
+
+func test() -> void:
+ print('OK')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
index ada6030132..0085b3f367 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
@@ -1,6 +1,6 @@
-# Error here. `class_name` should be used *before* annotations, not after (except @tool).
-@icon("res://path/to/optional/icon.svg")
+# Error here. Annotations should be used before `class_name`, not after.
class_name HelloWorld
+@icon("res://path/to/optional/icon.svg")
func test():
- pass
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
index 02b33c8692..a598ff8424 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"class_name" should be used before annotations (except @tool).
+Annotation "@icon" is not allowed in this level.
diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
index 92dfb2366d..816783f239 100644
--- a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
@@ -1,2 +1,2 @@
func test():
- var dictionary = { hello = "world",, }
+ var dictionary = { hello = "world",, }
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
index 4608c778aa..7a745bd995 100644
--- a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
@@ -1,4 +1,4 @@
func test():
- match 1:
- [[[var a]]], 2:
- pass
+ match 1:
+ [[[var a]]], 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
index 43b513045b..a7197bf68f 100644
--- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
@@ -1,34 +1,34 @@
func foo(x):
- match x:
- 1 + 1:
- print("1+1")
- [1,2,[1,{1:2,2:var z,..}]]:
- print("[1,2,[1,{1:2,2:var z,..}]]")
- print(z)
- 1 if true else 2:
- print("1 if true else 2")
- 1 < 2:
- print("1 < 2")
- 1 or 2 and 1:
- print("1 or 2 and 1")
- 6 | 1:
- print("1 | 1")
- 1 >> 1:
- print("1 >> 1")
- 1, 2 or 3, 4:
- print("1, 2 or 3, 4")
- _:
- print("wildcard")
+ match x:
+ 1 + 1:
+ print("1+1")
+ [1,2,[1,{1:2,2:var z,..}]]:
+ print("[1,2,[1,{1:2,2:var z,..}]]")
+ print(z)
+ 1 if true else 2:
+ print("1 if true else 2")
+ 1 < 2:
+ print("1 < 2")
+ 1 or 2 and 1:
+ print("1 or 2 and 1")
+ 6 | 1:
+ print("1 | 1")
+ 1 >> 1:
+ print("1 >> 1")
+ 1, 2 or 3, 4:
+ print("1, 2 or 3, 4")
+ _:
+ print("wildcard")
func test():
- foo(6 | 1)
- foo(1 >> 1)
- foo(2)
- foo(1)
- foo(1+1)
- foo(1 < 2)
- foo([2, 1])
- foo(4)
- foo([1, 2, [1, {1 : 2, 2:3}]])
- foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
- foo([1, 2, [1, {1 : 2}]])
+ foo(6 | 1)
+ foo(1 >> 1)
+ foo(2)
+ foo(1)
+ foo(1+1)
+ foo(1 < 2)
+ foo([2, 1])
+ foo(4)
+ foo([1, 2, [1, {1 : 2, 2:3}]])
+ foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
+ foo([1, 2, [1, {1 : 2}]])
diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
index 2b46f1e88a..c959c6c6af 100644
--- a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
@@ -1,27 +1,27 @@
func foo(x):
- match x:
- 1:
- print("1")
- 2:
- print("2")
- [1, 2]:
- print("[1, 2]")
- 3 or 4:
- print("3 or 4")
- 4:
- print("4")
- {1 : 2, 2 : 3}:
- print("{1 : 2, 2 : 3}")
- _:
- print("wildcard")
+ match x:
+ 1:
+ print("1")
+ 2:
+ print("2")
+ [1, 2]:
+ print("[1, 2]")
+ 3 or 4:
+ print("3 or 4")
+ 4:
+ print("4")
+ {1 : 2, 2 : 3}:
+ print("{1 : 2, 2 : 3}")
+ _:
+ print("wildcard")
func test():
- foo(0)
- foo(1)
- foo(2)
- foo([1, 2])
- foo(3)
- foo(4)
- foo([4,4])
- foo({1 : 2, 2 : 3})
- foo({1 : 2, 4 : 3})
+ foo(0)
+ foo(1)
+ foo(2)
+ foo([1, 2])
+ foo(3)
+ foo(4)
+ foo([4,4])
+ foo({1 : 2, 2 : 3})
+ foo({1 : 2, 4 : 3})
diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd
index 8bd188e247..19009e433d 100644
--- a/modules/gdscript/tests/scripts/parser/features/class_name.gd
+++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd
@@ -1,5 +1,5 @@
-class_name HelloWorld
@icon("res://path/to/optional/icon.svg")
+class_name HelloWorld
func test():
pass
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
index c3b2506156..17d00bce3c 100644
--- a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
@@ -1,4 +1,4 @@
func test():
- var my_lambda = func(x):
- print(x)
- my_lambda.call("hello")
+ var my_lambda = func(x):
+ print(x)
+ my_lambda.call("hello")
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
index 377dd25e9e..75857fb8ff 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -1,43 +1,43 @@
func foo(x):
- match x:
- {"key1": "value1", "key2": "value2"}:
- print('{"key1": "value1", "key2": "value2"}')
- {"key1": "value1", "key2"}:
- print('{"key1": "value1", "key2"}')
- {"key1", "key2": "value2"}:
- print('{"key1", "key2": "value2"}')
- {"key1", "key2"}:
- print('{"key1", "key2"}')
- {"key1": "value1"}:
- print('{"key1": "value1"}')
- {"key1"}:
- print('{"key1"}')
- _:
- print("wildcard")
+ match x:
+ {"key1": "value1", "key2": "value2"}:
+ print('{"key1": "value1", "key2": "value2"}')
+ {"key1": "value1", "key2"}:
+ print('{"key1": "value1", "key2"}')
+ {"key1", "key2": "value2"}:
+ print('{"key1", "key2": "value2"}')
+ {"key1", "key2"}:
+ print('{"key1", "key2"}')
+ {"key1": "value1"}:
+ print('{"key1": "value1"}')
+ {"key1"}:
+ print('{"key1"}')
+ _:
+ print("wildcard")
func bar(x):
- match x:
- {0}:
- print("0")
- {1}:
- print("1")
- {2}:
- print("2")
- _:
- print("wildcard")
+ match x:
+ {0}:
+ print("0")
+ {1}:
+ print("1")
+ {2}:
+ print("2")
+ _:
+ print("wildcard")
func test():
- foo({"key1": "value1", "key2": "value2"})
- foo({"key1": "value1", "key2": ""})
- foo({"key1": "", "key2": "value2"})
- foo({"key1": "", "key2": ""})
- foo({"key1": "value1"})
- foo({"key1": ""})
- foo({"key1": "value1", "key2": "value2", "key3": "value3"})
- foo({"key1": "value1", "key3": ""})
- foo({"key2": "value2"})
- foo({"key3": ""})
- bar({0: "0"})
- bar({1: "1"})
- bar({2: "2"})
- bar({3: "3"})
+ foo({"key1": "value1", "key2": "value2"})
+ foo({"key1": "value1", "key2": ""})
+ foo({"key1": "", "key2": "value2"})
+ foo({"key1": "", "key2": ""})
+ foo({"key1": "value1"})
+ foo({"key1": ""})
+ foo({"key1": "value1", "key2": "value2", "key3": "value3"})
+ foo({"key1": "value1", "key3": ""})
+ foo({"key2": "value2"})
+ foo({"key3": ""})
+ bar({0: "0"})
+ bar({1: "1"})
+ bar({2: "2"})
+ bar({3: "3"})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
index dbe223f5f5..a278ea1154 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
@@ -1,26 +1,26 @@
func foo(x):
- match x:
- 1, [2]:
- print('1, [2]')
- _:
- print('wildcard')
+ match x:
+ 1, [2]:
+ print('1, [2]')
+ _:
+ print('wildcard')
func bar(x):
- match x:
- [1], [2], [3]:
- print('[1], [2], [3]')
- [4]:
- print('[4]')
- _:
- print('wildcard')
+ match x:
+ [1], [2], [3]:
+ print('[1], [2], [3]')
+ [4]:
+ print('[4]')
+ _:
+ print('wildcard')
func test():
- foo(1)
- foo([2])
- foo(2)
- bar([1])
- bar([2])
- bar([3])
- bar([4])
- bar([5])
+ foo(1)
+ foo([2])
+ foo(2)
+ bar([1])
+ bar([2])
+ bar([3])
+ bar([4])
+ bar([5])
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
index a0ae7fb17c..0a71f33c25 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
@@ -1,6 +1,6 @@
func test():
- match [1, 2, 3]:
- [var a, var b, var c]:
- print(a == 1)
- print(b == 2)
- print(c == 3)
+ match [1, 2, 3]:
+ [var a, var b, var c]:
+ print(a == 1)
+ print(b == 2)
+ print(c == 3)
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_assert.gd b/modules/gdscript/tests/scripts/parser/features/multiline_assert.gd
new file mode 100644
index 0000000000..8c699c604d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_assert.gd
@@ -0,0 +1,24 @@
+func test():
+ var x := 5
+
+ assert(x > 0)
+ assert(x > 0,)
+ assert(x > 0, 'message')
+ assert(x > 0, 'message',)
+
+ assert(
+ x > 0
+ )
+ assert(
+ x > 0,
+ )
+ assert(
+ x > 0,
+ 'message'
+ )
+ assert(
+ x > 0,
+ 'message',
+ )
+
+ print('OK')
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_assert.out b/modules/gdscript/tests/scripts/parser/features/multiline_assert.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_assert.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
new file mode 100644
index 0000000000..4cbb464f59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
@@ -0,0 +1,17 @@
+class A:
+ enum { X = 1 }
+
+ class B:
+ enum { X = 2 }
+
+class C:
+ const X = 3
+
+ class D:
+ enum { X = 4 }
+
+func test():
+ print(A.X)
+ print(A.B.X)
+ print(C.X)
+ print(C.D.X)
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.gd b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
new file mode 100644
index 0000000000..039d51d9ed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
@@ -0,0 +1,6 @@
+func test():
+ var vec2: = Vector2.INF
+ var vec3: = Vector3.INF
+
+ print(vec2.x == INF)
+ print(vec3.z == INF)
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.out b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..a72ac9b5ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
@@ -0,0 +1,8 @@
+var weakling = 'not float'
+func weak(x: float = weakling):
+ print(x)
+ print('typeof x is', typeof(x))
+
+func test():
+ print(typeof(weak()))
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
new file mode 100644
index 0000000000..8543cf976e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: weak()
+>> runtime/errors/bad_conversion_for_default_parameter.gd
+>> 2
+>> Trying to assign value of type 'String' to a variable of type 'float'.
+0
+not ok
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd
new file mode 100644
index 0000000000..a5ecaba38d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd
@@ -0,0 +1,6 @@
+const array: Array = [{}]
+
+func test():
+ var dictionary := array[0]
+ var key: int = 0
+ dictionary[key] = 0
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
new file mode 100644
index 0000000000..2a97eaea44
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/constant_array_is_deep.gd
+>> 6
+>> Invalid set index '0' (on base: 'Dictionary') with value of type 'int'
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd
new file mode 100644
index 0000000000..3e71cd0518
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd
@@ -0,0 +1,4 @@
+const array: Array = [0]
+
+func test():
+ array.push_back(0)
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out
new file mode 100644
index 0000000000..ba3e1c46c6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out
@@ -0,0 +1,7 @@
+GDTEST_RUNTIME_ERROR
+>> ERROR
+>> on function: push_back()
+>> core/variant/array.cpp
+>> 253
+>> Condition "_p->read_only" is true.
+>> Array is in read-only state.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
new file mode 100644
index 0000000000..7b350e81ad
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
@@ -0,0 +1,4 @@
+const dictionary := {}
+
+func test():
+ dictionary.erase(0)
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out
new file mode 100644
index 0000000000..3e7ca11a4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out
@@ -0,0 +1,7 @@
+GDTEST_RUNTIME_ERROR
+>> ERROR
+>> on function: erase()
+>> core/variant/dictionary.cpp
+>> 177
+>> Condition "_p->read_only" is true. Returning: false
+>> Dictionary is in read-only state.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd
new file mode 100644
index 0000000000..4763210a7f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd
@@ -0,0 +1,6 @@
+const dictionary := {0: [0]}
+
+func test():
+ var array := dictionary[0]
+ var key: int = 0
+ array[key] = 0
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
new file mode 100644
index 0000000000..c807db6b0c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/constant_dictionary_is_deep.gd
+>> 6
+>> Invalid set index '0' (on base: 'Array') with value of type 'int'
diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
index 9b64084fa6..bd38259cec 100644
--- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
@@ -9,7 +9,7 @@ func test():
array_sname.push_back(&"godot")
print("String in Array: ", "godot" in array_sname)
- # Not equal because the values are different types.
+ # Not equal because the values are different types.
print("Arrays not equal: ", array_str != array_sname)
var string_array: Array[String] = []
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
new file mode 100644
index 0000000000..46b9fbc951
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
@@ -0,0 +1,7 @@
+func wait() -> void:
+ pass
+
+func test():
+ @warning_ignore(redundant_await)
+ await wait()
+ print("end")
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.out b/modules/gdscript/tests/scripts/runtime/features/await_on_void.out
new file mode 100644
index 0000000000..5bc3dcf2db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+end
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..9d0c6317b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
@@ -0,0 +1,19 @@
+func literal(x: float = 1):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var inferring := 2
+func inferred(x: float = inferring):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var weakling = 3
+func weak(x: float = weakling):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+func test():
+ literal()
+ inferred()
+ weak()
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
new file mode 100644
index 0000000000..a9ef4919cf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+x is 1
+typeof x is 3
+x is 2
+typeof x is 3
+x is 3
+typeof x is 3
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
index 1f15026f17..94bac1974f 100644
--- a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
@@ -13,5 +13,5 @@ func test():
print("String gets StringName: ", stringname_dict.get("abc"))
stringname_dict[&"abc"] = 42
- # They compare equal because StringName keys are converted to String.
+ # They compare equal because StringName keys are converted to String.
print("String Dictionary == StringName Dictionary: ", string_dict == stringname_dict)
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
new file mode 100644
index 0000000000..1d4b400d81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
@@ -0,0 +1,17 @@
+# https://github.com/godotengine/godot/issues/71177
+
+func test():
+ builtin_method()
+ builtin_method_static()
+ print("done")
+
+func builtin_method():
+ var pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ pba.resize(1) # Built-in validated.
+
+
+func builtin_method_static():
+ var _pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ Vector2.from_angle(PI) # Static built-in validated.
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
new file mode 100644
index 0000000000..8e68c97774
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+done
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
new file mode 100644
index 0000000000..81355e0255
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
@@ -0,0 +1,51 @@
+const constant_float = 1.0
+const constant_int = 1
+enum { enum_value = 1 }
+
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_float := 1.0
+ var hard_int := 1
+ var hard_string := '0'
+ var hard_iterator := Iterator.new()
+
+ var variant_float: Variant = hard_float
+ var variant_int: Variant = hard_int
+ var variant_string: Variant = hard_string
+ var variant_iterator: Variant = hard_iterator
+
+ for i in 1.0:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in 1:
+ print(typeof(i) == TYPE_INT)
+ for i in 'a':
+ print(typeof(i) == TYPE_STRING)
+ for i in Iterator.new():
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in hard_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in hard_int:
+ print(typeof(i) == TYPE_INT)
+ for i in hard_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in hard_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in variant_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in variant_int:
+ print(typeof(i) == TYPE_INT)
+ for i in variant_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in variant_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
new file mode 100644
index 0000000000..b3e82d52ef
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
new file mode 100644
index 0000000000..f2368643de
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
@@ -0,0 +1,20 @@
+func test():
+ var gdscr: = GDScript.new()
+ gdscr.source_code = '''
+extends Resource
+
+func test() -> void:
+ prints("Outer")
+ var inner = InnerClass.new()
+
+class InnerClass:
+ func _init() -> void:
+ prints("Inner")
+'''
+ @warning_ignore(return_value_discarded)
+ gdscr.reload()
+
+ var inst = gdscr.new()
+
+ @warning_ignore(unsafe_method_access)
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.out b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
new file mode 100644
index 0000000000..16114f57f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Outer
+Inner
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
index f33ba7dffd..252e100bda 100644
--- a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
@@ -3,23 +3,23 @@
var a: int = 1
func shadow_regular_assignment(a: Variant, b: Variant) -> void:
- print(a)
- print(self.a)
- a = b
- print(a)
- print(self.a)
+ print(a)
+ print(self.a)
+ a = b
+ print(a)
+ print(self.a)
var v := Vector2(0.0, 0.0)
func shadow_subscript_assignment(v: Vector2, x: float) -> void:
- print(v)
- print(self.v)
- v.x += x
- print(v)
- print(self.v)
+ print(v)
+ print(self.v)
+ v.x += x
+ print(v)
+ print(self.v)
func test():
- shadow_regular_assignment('a', 'b')
- shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
+ shadow_regular_assignment('a', 'b')
+ shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
new file mode 100644
index 0000000000..cc34e71b01
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
@@ -0,0 +1,45 @@
+# https://github.com/godotengine/godot/issues/70964
+
+func test():
+ test_construct(0, false)
+ test_utility(0, false)
+ test_builtin_call(Vector2.UP, false)
+ test_builtin_call_validated(Vector2.UP, false)
+ test_object_call(RefCounted.new(), false)
+ test_object_call_method_bind(Resource.new(), false)
+ test_object_call_ptrcall(RefCounted.new(), false)
+
+ print("end")
+
+func test_construct(v, f):
+ Vector2(v, v) # Built-in type construct.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_utility(v, f):
+ abs(v) # Utility function.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_builtin_call(v, f):
+ @warning_ignore(unsafe_method_access)
+ v.angle() # Built-in method call.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_builtin_call_validated(v: Vector2, f):
+ @warning_ignore(return_value_discarded)
+ v.abs() # Built-in method call validated.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call(v, f):
+ @warning_ignore(unsafe_method_access)
+ v.get_reference_count() # Native type method call.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call_method_bind(v: Resource, f):
+ @warning_ignore(return_value_discarded)
+ v.duplicate() # Native type method call with MethodBind.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call_ptrcall(v: RefCounted, f):
+ @warning_ignore(return_value_discarded)
+ v.get_reference_count() # Native type method call with ptrcall.
+ assert(not f) # Test unary operator reading from `nil`.
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out
new file mode 100644
index 0000000000..5bc3dcf2db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+end
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
new file mode 100644
index 0000000000..af3f3cb941
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/71172
+
+func test():
+ @warning_ignore(narrowing_conversion)
+ var foo: int = 0.0
+ print(typeof(foo) == TYPE_INT)
+ var dict : Dictionary = {"a":0.0}
+ foo = dict.get("a")
+ print(typeof(foo) == TYPE_INT)
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index f313f4b28f..d7e8141eb1 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -15,7 +15,7 @@
<param index="2" name="state" type="GLTFState" />
<param index="3" name="flags" type="int" default="0" />
<description>
- Takes a [PackedByteArray] defining a gLTF and returns a [GLTFState] object through the [param state] parameter.
+ Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
</description>
</method>
@@ -26,7 +26,7 @@
<param index="2" name="flags" type="int" default="0" />
<param index="3" name="base_path" type="String" default="&quot;&quot;" />
<description>
- Takes a path to a gLTF file and returns a [GLTFState] object through the [param state] parameter.
+ Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
</description>
</method>
@@ -36,14 +36,14 @@
<param index="1" name="state" type="GLTFState" />
<param index="2" name="flags" type="int" default="0" />
<description>
- Takes a Godot Engine scene node and returns a [GLTFState] object through the [param state] parameter.
+ Takes a Godot Engine scene node and exports it and its descendants to the given [GLTFState] object through the [param state] parameter.
</description>
</method>
<method name="generate_buffer">
<return type="PackedByteArray" />
<param index="0" name="state" type="GLTFState" />
<description>
- Takes a [GLTFState] object through the [param state] parameter and returns a gLTF [PackedByteArray].
+ Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray].
</description>
</method>
<method name="generate_scene">
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 67bb99de16..183190460e 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -461,7 +461,7 @@ void GridMapEditor::_delete_selection() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Delete Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -482,7 +482,7 @@ void GridMapEditor::_fill_selection() {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Fill Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -576,7 +576,7 @@ void GridMapEditor::_do_paste() {
rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
Vector3 ofs = paste_indicator.current - paste_indicator.click;
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paste Selection"));
for (const ClipboardItem &item : clipboard_items) {
@@ -664,7 +664,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else {
if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paint"));
for (const SetItem &si : set_items) {
undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
@@ -686,7 +686,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) {
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Selection"));
undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index faee36fb82..3c0bd56e86 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -1310,8 +1310,8 @@ RID GridMap::get_bake_mesh_instance(int p_idx) {
GridMap::GridMap() {
set_notify_transform(true);
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
- NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
+ NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->connect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
#endif // DEBUG_ENABLED
}
@@ -1338,8 +1338,8 @@ GridMap::~GridMap() {
clear();
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
- NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
+ NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
#endif // DEBUG_ENABLED
}
@@ -1421,7 +1421,7 @@ void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const Octant
return;
}
- Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_edge_connections_material();
+ Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connections_material();
Array mesh_array;
mesh_array.resize(Mesh::ARRAY_MAX);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index c45fdda75c..e39127e0a1 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -688,7 +688,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
return false; // Already up to date
}
} else {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
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 7f1231dbcb..5fb29b86da 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -74,27 +74,19 @@ namespace Godot.SourceGenerators
ObjectId = 22,
TypeString = 23,
NodePathToEditedNode = 24,
- MethodOfVariantType = 25,
- MethodOfBaseType = 26,
- MethodOfInstance = 27,
- MethodOfScript = 28,
- PropertyOfVariantType = 29,
- PropertyOfBaseType = 30,
- PropertyOfInstance = 31,
- PropertyOfScript = 32,
- ObjectTooBig = 33,
- NodePathValidTypes = 34,
- SaveFile = 35,
- GlobalSaveFile = 36,
- IntIsObjectid = 37,
- IntIsPointer = 38,
- ArrayType = 39,
- LocaleId = 40,
- LocalizableString = 41,
- NodeType = 42,
- HideQuaternionEdit = 43,
- Password = 44,
- Max = 45
+ ObjectTooBig = 25,
+ NodePathValidTypes = 26,
+ SaveFile = 27,
+ GlobalSaveFile = 28,
+ IntIsObjectid = 29,
+ IntIsPointer = 30,
+ ArrayType = 31,
+ LocaleId = 32,
+ LocalizableString = 33,
+ NodeType = 34,
+ HideQuaternionEdit = 35,
+ Password = 36,
+ Max = 37
}
[Flags]
@@ -103,9 +95,9 @@ namespace Godot.SourceGenerators
None = 0,
Storage = 2,
Editor = 4,
- Checkable = 8,
- Checked = 16,
- Internationalized = 32,
+ Internal = 8,
+ Checkable = 16,
+ Checked = 32,
Group = 64,
Category = 128,
Subgroup = 256,
@@ -118,7 +110,7 @@ namespace Godot.SourceGenerators
ScriptDefaultValue = 32768,
ClassIsEnum = 65536,
NilIsVariant = 131072,
- Internal = 262144,
+ Array = 262144,
DoNotShareOnDuplicate = 524288,
HighEndGfx = 1048576,
NodePathFromSceneRoot = 2097152,
@@ -128,9 +120,7 @@ namespace Godot.SourceGenerators
EditorInstantiateObject = 33554432,
EditorBasicSetting = 67108864,
ReadOnly = 134217728,
- Array = 268435456,
Default = 6,
- DefaultIntl = 38,
NoEditor = 2
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index 4d40724a83..e439822666 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -356,19 +356,19 @@ namespace GodotTools.Build
{
base._Ready();
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+ SizeFlagsVertical = SizeFlags.ExpandFill;
var hsc = new HSplitContainer
{
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ SizeFlagsVertical = SizeFlags.ExpandFill
};
AddChild(hsc);
_issuesList = new ItemList
{
- SizeFlagsVertical = (int)SizeFlags.ExpandFill,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the build log
};
_issuesList.ItemActivated += IssueActivated;
_issuesList.AllowRmbSelect = true;
@@ -382,8 +382,8 @@ namespace GodotTools.Build
_buildLog = new TextEdit
{
Editable = false,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the issues list
};
hsc.AddChild(_buildLog);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 237ac85267..2e438f3f8f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -123,9 +123,9 @@ namespace GodotTools.Build
base._Ready();
CustomMinimumSize = new Vector2i(0, (int)(228 * EditorScale));
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+ SizeFlagsVertical = SizeFlags.ExpandFill;
- var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(toolBarHBox);
_buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index e40b17cecb..03cbfda1bd 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1864,12 +1864,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append("\n" OPEN_BLOCK_L1);
if (getter) {
- p_output.append(INDENT2 "get\n"
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
-
- OPEN_BLOCK_L2 INDENT3);
+ p_output.append(INDENT2 "get\n" OPEN_BLOCK_L2 INDENT3);
p_output.append("return ");
p_output.append(getter->proxy_name + "(");
@@ -1884,21 +1879,11 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(itos(p_iprop.index));
}
}
- p_output.append(");\n"
-
- CLOSE_BLOCK_L2
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning restore CS0618\n");
+ p_output.append(");\n" CLOSE_BLOCK_L2);
}
if (setter) {
- p_output.append(INDENT2 "set\n"
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
-
- OPEN_BLOCK_L2 INDENT3);
+ p_output.append(INDENT2 "set\n" OPEN_BLOCK_L2 INDENT3);
p_output.append(setter->proxy_name + "(");
if (p_iprop.index != -1) {
@@ -1912,12 +1897,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(itos(p_iprop.index) + ", ");
}
}
- p_output.append("value);\n"
-
- CLOSE_BLOCK_L2
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning restore CS0618\n");
+ p_output.append("value);\n" CLOSE_BLOCK_L2);
}
p_output.append(CLOSE_BLOCK_L1);
@@ -3056,12 +3036,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
if (accessor) {
- const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value);
-
- // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
- // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
- imethod.is_deprecated = true;
- imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead.";
+ // We only make internal an accessor method if it's in the same class as the property.
+ // It's easier this way, but also we don't know if an accessor method in a different class
+ // could have other purposes, so better leave those untouched.
+ imethod.is_internal = true;
}
if (itype.class_doc) {
@@ -3806,7 +3784,7 @@ void BindingsGenerator::_populate_global_constants() {
if (enum_name != StringName()) {
EnumInterface ienum(enum_name);
- // TODO: ienum.is_flags is always false for core constants since they don't seem to support bitfield enums
+ ienum.is_flags = CoreConstants::is_global_constant_bitfield(i);
List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
if (enum_match) {
enum_match->get().constants.push_back(iconstant);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index a2916d4ae8..0e46b63b59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -437,48 +437,25 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>
/// A <see langword="bool"/> for whether or not they are intersecting.
/// </returns>
- public readonly bool Intersects(AABB with, bool includeBorders = false)
+ public readonly bool Intersects(AABB with)
{
- if (includeBorders)
- {
- if (_position.x > with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x < with._position.x)
- return false;
- if (_position.y > with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y < with._position.y)
- return false;
- if (_position.z > with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z < with._position.z)
- return false;
- }
- else
- {
- if (_position.x >= with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x <= with._position.x)
- return false;
- if (_position.y >= with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y <= with._position.y)
- return false;
- if (_position.z >= with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z <= with._position.z)
- return false;
- }
+ if (_position.x >= with._position.x + with._size.x)
+ return false;
+ if (_position.x + _size.x <= with._position.x)
+ return false;
+ if (_position.y >= with._position.y + with._size.y)
+ return false;
+ if (_position.y + _size.y <= with._position.y)
+ return false;
+ if (_position.z >= with._position.z + with._size.z)
+ return false;
+ if (_position.z + _size.z <= with._position.z)
+ return false;
return true;
}
@@ -586,6 +563,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other <see cref="AABB"/>.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 5d390a298d..b57317e1d0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -120,31 +120,6 @@ namespace Godot
}
/// <summary>
- /// The scale of this basis.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value>
- public Vector3 Scale
- {
- readonly get
- {
- real_t detSign = Mathf.Sign(Determinant());
- return detSign * new Vector3
- (
- Column0.Length(),
- Column1.Length(),
- Column2.Length()
- );
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- Column0 *= value.x;
- Column1 *= value.y;
- Column2 *= value.z;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector3"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
@@ -493,12 +468,6 @@ namespace Godot
}
}
- /// <summary>
- /// Returns the basis's rotation in the form of a quaternion.
- /// See <see cref="GetEuler"/> if you need Euler angles, but keep in
- /// mind that quaternions should generally be preferred to Euler angles.
- /// </summary>
- /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns>
internal readonly Quaternion GetQuaternion()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -573,106 +542,18 @@ namespace Godot
}
/// <summary>
- /// Get rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
- /// </summary>
- /// <param name="index">Which row.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns>
- public readonly Vector3 GetRow(int index)
- {
- switch (index)
- {
- case 0:
- return Row0;
- case 1:
- return Row1;
- case 2:
- return Row2;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
- /// <summary>
- /// Sets rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
+ /// Assuming that the matrix is the combination of a rotation and scaling,
+ /// return the absolute value of scaling factors along each axis.
/// </summary>
- /// <param name="index">Which row.</param>
- /// <param name="value">The vector to set the row to.</param>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- public void SetRow(int index, Vector3 value)
+ public readonly Vector3 GetScale()
{
- switch (index)
- {
- case 0:
- Row0 = value;
- return;
- case 1:
- Row1 = value;
- return;
- case 2:
- Row2 = value;
- return;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
- /// <summary>
- /// This function considers a discretization of rotations into
- /// 24 points on unit sphere, lying along the vectors (x, y, z) with
- /// each component being either -1, 0, or 1, and returns the index
- /// of the point best representing the orientation of the object.
- /// It is mainly used by the <see cref="GridMap"/> editor.
- ///
- /// For further details, refer to the Godot source code.
- /// </summary>
- /// <returns>The orthogonal index.</returns>
- public readonly int GetOrthogonalIndex()
- {
- var orth = this;
-
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- var row = orth.GetRow(i);
-
- real_t v = row[j];
-
- if (v > 0.5f)
- {
- v = 1.0f;
- }
- else if (v < -0.5f)
- {
- v = -1.0f;
- }
- else
- {
- v = 0f;
- }
-
- row[j] = v;
-
- orth.SetRow(i, row);
- }
- }
-
- for (int i = 0; i < 24; i++)
- {
- if (orth == _orthoBases[i])
- {
- return i;
- }
- }
-
- return 0;
+ real_t detSign = Mathf.Sign(Determinant());
+ return detSign * new Vector3
+ (
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
+ );
}
/// <summary>
@@ -709,6 +590,16 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this basis is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Row0.IsFinite() && Row1.IsFinite() && Row2.IsFinite();
+ }
+
internal readonly Basis Lerp(Basis to, real_t weight)
{
Basis b = this;
@@ -896,7 +787,7 @@ namespace Godot
/// <param name="quaternion">The quaternion to create the basis from.</param>
public Basis(Quaternion quaternion)
{
- real_t s = 2.0f / quaternion.LengthSquared;
+ real_t s = 2.0f / quaternion.LengthSquared();
real_t xs = quaternion.x * s;
real_t ys = quaternion.y * s;
@@ -925,26 +816,26 @@ namespace Godot
public Basis(Vector3 axis, real_t angle)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- real_t cosine = Mathf.Cos(angle);
- Row0.x = axisSq.x + cosine * (1.0f - axisSq.x);
- Row1.y = axisSq.y + cosine * (1.0f - axisSq.y);
- Row2.z = axisSq.z + cosine * (1.0f - axisSq.z);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+
+ Row0.x = axisSq.x + cos * (1.0f - axisSq.x);
+ Row1.y = axisSq.y + cos * (1.0f - axisSq.y);
+ Row2.z = axisSq.z + cos * (1.0f - axisSq.z);
- real_t sine = Mathf.Sin(angle);
- real_t t = 1.0f - cosine;
+ real_t t = 1.0f - cos;
real_t xyzt = axis.x * axis.y * t;
- real_t zyxs = axis.z * sine;
+ real_t zyxs = axis.z * sin;
Row0.y = xyzt - zyxs;
Row1.x = xyzt + zyxs;
xyzt = axis.x * axis.z * t;
- zyxs = axis.y * sine;
+ zyxs = axis.y * sin;
Row0.z = xyzt + zyxs;
Row2.x = xyzt - zyxs;
xyzt = axis.y * axis.z * t;
- zyxs = axis.x * sine;
+ zyxs = axis.x * sin;
Row1.z = xyzt - zyxs;
Row2.y = xyzt + zyxs;
}
@@ -967,8 +858,20 @@ namespace Godot
// We need to assign the struct fields here first so we can't do it that way...
}
- // Arguments are named such that xy is equal to calling x.y
- internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
+ /// <summary>
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
+ /// </summary>
+ /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param>
+ public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
{
Row0 = new Vector3(xx, yx, zx);
Row1 = new Vector3(xy, yy, zy);
@@ -982,19 +885,29 @@ namespace Godot
/// <param name="order">The order to compose the Euler angles.</param>
public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz)
{
- real_t c, s;
-
- c = Mathf.Cos(euler.x);
- s = Mathf.Sin(euler.x);
- Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c));
+ (real_t sin, real_t cos) = Mathf.SinCos(euler.x);
+ Basis xmat = new Basis
+ (
+ new Vector3(1, 0, 0),
+ new Vector3(0, cos, sin),
+ new Vector3(0, -sin, cos)
+ );
- c = Mathf.Cos(euler.y);
- s = Mathf.Sin(euler.y);
- Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c));
+ (sin, cos) = Mathf.SinCos(euler.y);
+ Basis ymat = new Basis
+ (
+ new Vector3(cos, 0, -sin),
+ new Vector3(0, 1, 0),
+ new Vector3(sin, 0, cos)
+ );
- c = Mathf.Cos(euler.z);
- s = Mathf.Sin(euler.z);
- Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1));
+ (sin, cos) = Mathf.SinCos(euler.z);
+ Basis zmat = new Basis
+ (
+ new Vector3(cos, sin, 0),
+ new Vector3(-sin, cos, 0),
+ new Vector3(0, 0, 1)
+ );
switch (order)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
index 03996bafdd..8325af034c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
@@ -48,13 +48,8 @@ namespace Godot
}
/// <summary>
- /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
- /// the current node) or an absolute path (in the scene tree) to a node. If the path
- /// does not exist, a <see langword="null"/> instance is returned and an error
- /// is logged. Attempts to access methods on the return value will result in an
- /// "Attempt to call &lt;method&gt; on a null instance." error.
- /// Note: Fetching absolute paths only works when the node is inside the scene tree
- /// (see <see cref="IsInsideTree"/>).
+ /// Similar to <see cref="GetNode"/>, but does not log an error if <paramref name="path"/>
+ /// does not point to a valid <see cref="Node"/>.
/// </summary>
/// <example>
/// Example: Assume your current node is Character and the following tree:
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 3f9e986f62..b2cb0f5e6b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -440,6 +440,17 @@ namespace Godot
}
/// <summary>
+ /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not
+ /// <see cref="NaN"/>, positive infinite, or negative infinity.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns>
+ public static bool IsFinite(real_t s)
+ {
+ return real_t.IsFinite(s);
+ }
+
+ /// <summary>
/// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity).
/// </summary>
/// <param name="s">The value to check.</param>
@@ -460,10 +471,11 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero.
+ /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero.
/// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
///
- /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
+ /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with
+ /// one value as zero.
/// </summary>
/// <param name="s">The value to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index ea05c1547c..72a1868964 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -83,6 +83,17 @@ namespace Godot
}
/// <summary>
+ /// Returns the sine and cosine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine and cosine of that angle.</returns>
+ public static (real_t Sin, real_t Cos) SinCos(real_t s)
+ {
+ (double sin, double cos) = Math.SinCos(s);
+ return ((real_t)sin, (real_t)cos);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
/// equal to each other.
/// The comparison is done using the provided tolerance value.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 42c6b0a37e..8a125e3c73 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -15,7 +15,7 @@ namespace Godot
private Vector3 _normal;
/// <summary>
- /// The normal of the plane, which must be normalized.
+ /// The normal of the plane, which must be a unit vector.
/// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is
/// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property.
/// </summary>
@@ -85,23 +85,6 @@ namespace Godot
public real_t D { get; set; }
/// <summary>
- /// The center of the plane, the point where the normal line intersects the plane.
- /// </summary>
- /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
- public Vector3 Center
- {
- readonly get
- {
- return _normal * D;
- }
- set
- {
- _normal = value.Normalized();
- D = value.Length();
- }
- }
-
- /// <summary>
/// Returns the shortest distance from this plane to the position <paramref name="point"/>.
/// </summary>
/// <param name="point">The position to use for the calculation.</param>
@@ -112,6 +95,16 @@ namespace Godot
}
/// <summary>
+ /// Returns the center of the plane, the point on the plane closest to the origin.
+ /// The point where the normal line going through the origin intersects the plane.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
+ public readonly Vector3 GetCenter()
+ {
+ return _normal * D;
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if point is inside the plane.
/// Comparison uses a custom minimum tolerance threshold.
/// </summary>
@@ -155,7 +148,7 @@ namespace Godot
/// <param name="from">The start of the ray.</param>
/// <param name="dir">The direction of the ray, normalized.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public readonly Vector3? IntersectRay(Vector3 from, Vector3 dir)
+ public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
@@ -183,7 +176,7 @@ namespace Godot
/// <param name="begin">The start of the line segment.</param>
/// <param name="end">The end of the line segment.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public readonly Vector3? IntersectSegment(Vector3 begin, Vector3 end)
+ public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
real_t den = _normal.Dot(segment);
@@ -205,6 +198,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this plane is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _normal.IsFinite() && Mathf.IsFinite(D);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane.
/// </summary>
/// <param name="point">The point to check.</param>
@@ -280,10 +283,21 @@ namespace Godot
}
/// <summary>
+ /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector.
+ /// The plane will intersect the origin.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
+ public Plane(Vector3 normal)
+ {
+ _normal = normal;
+ D = 0;
+ }
+
+ /// <summary>
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
/// the plane's distance to the origin <paramref name="d"/>.
/// </summary>
- /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(Vector3 normal, real_t d)
{
@@ -295,7 +309,7 @@ namespace Godot
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
/// a <paramref name="point"/> on the plane.
/// </summary>
- /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
/// <param name="point">The point on the plane.</param>
public Plane(Vector3 normal, Vector3 point)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index fd37f8d9e8..f11b3c553a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -381,14 +381,14 @@ namespace Godot
}
real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0);
real_t deltaZ = zFar - zNear;
- real_t sine = Mathf.Sin(radians);
+ (real_t sin, real_t cos) = Mathf.SinCos(radians);
- if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
+ if ((deltaZ == 0) || (sin == 0) || (aspect == 0))
{
return Zero;
}
- real_t cotangent = Mathf.Cos(radians) / sine;
+ real_t cotangent = cos / sin;
Projection proj = Projection.Identity;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index bd0dea0c1c..8e4f9178f7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -97,27 +97,6 @@ namespace Godot
}
/// <summary>
- /// Returns the length (magnitude) of the quaternion.
- /// </summary>
- /// <seealso cref="LengthSquared"/>
- /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
- public readonly real_t Length
- {
- get { return Mathf.Sqrt(LengthSquared); }
- }
-
- /// <summary>
- /// Returns the squared length (squared magnitude) of the quaternion.
- /// This method runs faster than <see cref="Length"/>, so prefer it if
- /// you need to compare quaternions or need the squared length for some formula.
- /// </summary>
- /// <value>Equivalent to <c>Dot(this)</c>.</value>
- public readonly real_t LengthSquared
- {
- get { return Dot(this); }
- }
-
- /// <summary>
/// Returns the angle between this quaternion and <paramref name="to"/>.
/// This is the magnitude of the angle you would need to rotate
/// by to get from one to the other.
@@ -340,12 +319,22 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this quaternion is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
/// Returns whether the quaternion is normalized or not.
/// </summary>
/// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
public readonly bool IsNormalized()
{
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon;
}
public readonly Quaternion Log()
@@ -355,12 +344,33 @@ namespace Godot
}
/// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
+ public readonly real_t Length()
+ {
+ return Mathf.Sqrt(LengthSquared());
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of the quaternion.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare quaternions or need the squared length for some formula.
+ /// </summary>
+ /// <value>Equivalent to <c>Dot(this)</c>.</value>
+ public readonly real_t LengthSquared()
+ {
+ return Dot(this);
+ }
+
+ /// <summary>
/// Returns a copy of the quaternion, normalized to unit length.
/// </summary>
/// <returns>The normalized quaternion.</returns>
public readonly Quaternion Normalized()
{
- return this / Length;
+ return this / Length();
}
/// <summary>
@@ -532,14 +542,13 @@ namespace Godot
}
else
{
- real_t sinAngle = Mathf.Sin(angle * 0.5f);
- real_t cosAngle = Mathf.Cos(angle * 0.5f);
- real_t s = sinAngle / d;
+ (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f);
+ real_t s = sin / d;
x = axis.x * s;
y = axis.y * s;
z = axis.z * s;
- w = cosAngle;
+ w = cos;
}
}
@@ -583,12 +592,9 @@ namespace Godot
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
// a3 is the angle of the first rotation, following the notation in this reference.
- real_t cosA1 = Mathf.Cos(halfA1);
- real_t sinA1 = Mathf.Sin(halfA1);
- real_t cosA2 = Mathf.Cos(halfA2);
- real_t sinA2 = Mathf.Sin(halfA2);
- real_t cosA3 = Mathf.Cos(halfA3);
- real_t sinA3 = Mathf.Sin(halfA3);
+ (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1);
+ (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2);
+ (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3);
return new Quaternion(
(sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3),
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index b0e0e75a34..1a8696d3bc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -101,6 +101,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one.
/// </summary>
/// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index faee81a98a..cf8939a859 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -275,38 +275,19 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
- public readonly bool Intersects(Rect2i b, bool includeBorders = false)
+ public readonly bool Intersects(Rect2i b)
{
- if (includeBorders)
- {
- if (_position.x > b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x < b._position.x)
- return false;
- if (_position.y > b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y < b._position.y)
- return false;
- }
- else
- {
- if (_position.x >= b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x <= b._position.x)
- return false;
- if (_position.y >= b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y <= b._position.y)
- return false;
- }
+ if (_position.x >= b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x <= b._position.x)
+ return false;
+ if (_position.y >= b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y <= b._position.y)
+ return false;
return true;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 756f71e5b2..fa060e3a53 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -32,45 +32,6 @@ namespace Godot
public Vector2 origin;
/// <summary>
- /// The rotation of this transformation matrix.
- /// </summary>
- /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
- public real_t Rotation
- {
- readonly get
- {
- return Mathf.Atan2(x.y, x.x);
- }
- set
- {
- Vector2 scale = Scale;
- x.x = y.y = Mathf.Cos(value);
- x.y = y.x = Mathf.Sin(value);
- y.x *= -1;
- Scale = scale;
- }
- }
-
- /// <summary>
- /// The scale of this transformation matrix.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
- public Vector2 Scale
- {
- readonly get
- {
- real_t detSign = Mathf.Sign(BasisDeterminant());
- return new Vector2(x.Length(), detSign * y.Length());
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- x *= value.x;
- y *= value.y;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector2"/>.
/// The third column is the <see cref="origin"/> vector.
/// </summary>
@@ -203,6 +164,23 @@ namespace Godot
}
/// <summary>
+ /// Returns the transform's rotation (in radians).
+ /// </summary>
+ public readonly real_t GetRotation()
+ {
+ return Mathf.Atan2(x.y, x.x);
+ }
+
+ /// <summary>
+ /// Returns the scale.
+ /// </summary>
+ public readonly Vector2 GetScale()
+ {
+ real_t detSign = Mathf.Sign(BasisDeterminant());
+ return new Vector2(x.Length(), detSign * y.Length());
+ }
+
+ /// <summary>
/// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
/// </summary>
/// <param name="transform">The other transform.</param>
@@ -210,15 +188,17 @@ namespace Godot
/// <returns>The interpolated transform.</returns>
public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
- real_t r1 = Rotation;
- real_t r2 = transform.Rotation;
+ real_t r1 = GetRotation();
+ real_t r2 = transform.GetRotation();
- Vector2 s1 = Scale;
- Vector2 s2 = transform.Scale;
+ Vector2 s1 = GetScale();
+ Vector2 s2 = transform.GetScale();
// Slerp rotation
- var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
- var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
+ (real_t sin1, real_t cos1) = Mathf.SinCos(r1);
+ (real_t sin2, real_t cos2) = Mathf.SinCos(r2);
+ var v1 = new Vector2(cos1, sin1);
+ var v2 = new Vector2(cos2, sin2);
real_t dot = v1.Dot(v2);
@@ -235,7 +215,8 @@ namespace Godot
{
real_t angle = weight * Mathf.Acos(dot);
Vector2 v3 = (v2 - (v1 * dot)).Normalized();
- v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle));
+ (real_t sine, real_t cos) = Mathf.SinCos(angle);
+ v = (v1 * sine) + (v3 * cos);
}
// Extract parameters
@@ -271,6 +252,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return x.IsFinite() && y.IsFinite() && origin.IsFinite();
+ }
+
+ /// <summary>
/// Returns the transform with the basis orthogonal (90 degrees),
/// and normalized axis vectors (scale of 1 or -1).
/// </summary>
@@ -423,7 +414,7 @@ namespace Godot
/// <summary>
/// Constructs a transformation matrix from the given components.
- /// Arguments are named such that xy is equal to calling x.y
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
/// </summary>
/// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param>
/// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param>
@@ -446,13 +437,34 @@ namespace Godot
/// <param name="origin">The origin vector, or column index 2.</param>
public Transform2D(real_t rotation, Vector2 origin)
{
- x.x = y.y = Mathf.Cos(rotation);
- x.y = y.x = Mathf.Sin(rotation);
+ (real_t sin, real_t cos) = Mathf.SinCos(rotation);
+ x.x = y.y = cos;
+ x.y = y.x = sin;
y.x *= -1;
this.origin = origin;
}
/// <summary>
+ /// Constructs a transformation matrix from a <paramref name="rotation"/> value,
+ /// <paramref name="scale"/> vector, <paramref name="skew"/> value, and
+ /// <paramref name="origin"/> vector.
+ /// </summary>
+ /// <param name="rotation">The rotation of the new transform, in radians.</param>
+ /// <param name="scale">The scale of the new transform.</param>
+ /// <param name="skew">The skew of the new transform, in radians.</param>
+ /// <param name="origin">The origin vector, or column index 2.</param>
+ public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin)
+ {
+ (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation);
+ (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew);
+ x.x = rotationCos * scale.x;
+ y.y = rotationSkewCos * scale.y;
+ y.x = -rotationSkewSin * scale.y;
+ x.y = rotationSin * scale.x;
+ this.origin = origin;
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 39167bd116..6b2475fc59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -115,16 +115,30 @@ namespace Godot
}
/// <summary>
- /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
+ /// Returns a transform interpolated between this transform and another
+ /// <paramref name="transform"/> by a given <paramref name="weight"/>
+ /// (on the range of 0.0 to 1.0).
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated transform.</returns>
public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight)
{
- Basis retBasis = basis.Lerp(transform.basis, weight);
- Vector3 retOrigin = origin.Lerp(transform.origin, weight);
- return new Transform3D(retBasis, retOrigin);
+ Vector3 sourceScale = basis.GetScale();
+ Quaternion sourceRotation = basis.GetRotationQuaternion();
+ Vector3 sourceLocation = origin;
+
+ Vector3 destinationScale = transform.basis.GetScale();
+ Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
+ Vector3 destinationLocation = transform.origin;
+
+ var interpolated = new Transform3D();
+ Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
+ Vector3 scale = sourceScale.Lerp(destinationScale, weight);
+ interpolated.basis.SetQuaternionScale(quaternion, scale);
+ interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
+
+ return interpolated;
}
/// <summary>
@@ -140,6 +154,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return basis.IsFinite() && origin.IsFinite();
+ }
+
+ /// <summary>
/// Returns a copy of the transform rotated such that its
/// -Z axis (forward) points towards the <paramref name="target"/> position.
///
@@ -223,34 +247,6 @@ namespace Godot
return new Transform3D(basis * tmpBasis, origin);
}
- /// <summary>
- /// Returns a transform spherically interpolated between this transform and
- /// another <paramref name="transform"/> by <paramref name="weight"/>.
- /// </summary>
- /// <param name="transform">The other transform.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The interpolated transform.</returns>
- public readonly Transform3D SphericalInterpolateWith(Transform3D transform, real_t weight)
- {
- /* not sure if very "efficient" but good enough? */
-
- Vector3 sourceScale = basis.Scale;
- Quaternion sourceRotation = basis.GetRotationQuaternion();
- Vector3 sourceLocation = origin;
-
- Vector3 destinationScale = transform.basis.Scale;
- Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
- Vector3 destinationLocation = transform.origin;
-
- var interpolated = new Transform3D();
- Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
- Vector3 scale = sourceScale.Lerp(destinationScale, weight);
- interpolated.basis.SetQuaternionScale(quaternion, scale);
- interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
-
- return interpolated;
- }
-
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
@@ -346,15 +342,25 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from the given <paramref name="quaternion"/>
- /// and <paramref name="origin"/> vector.
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>.
/// </summary>
- /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param>
- /// <param name="origin">The origin vector, or column index 3.</param>
- public Transform3D(Quaternion quaternion, Vector3 origin)
+ /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param>
+ /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param>
+ public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz)
{
- basis = new Basis(quaternion);
- this.origin = origin;
+ basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz);
+ origin = new Vector3(ox, oy, oz);
}
/// <summary>
@@ -370,6 +376,29 @@ namespace Godot
}
/// <summary>
+ /// Constructs a transformation matrix from the given <paramref name="projection"/>
+ /// by trimming the last row of the projection matrix (<c>projection.x.w</c>,
+ /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c>
+ /// are not copied over).
+ /// </summary>
+ /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param>
+ public Transform3D(Projection projection)
+ {
+ basis = new Basis
+ (
+ projection.x.x, projection.y.x, projection.z.x,
+ projection.x.y, projection.y.y, projection.z.y,
+ projection.x.z, projection.y.z, projection.z.z
+ );
+ origin = new Vector3
+ (
+ projection.w.x,
+ projection.w.y,
+ projection.w.z
+ );
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 4c60080ee9..1e88e18b3d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -334,6 +334,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -529,11 +539,12 @@ namespace Godot
/// <returns>The rotated vector.</returns>
public readonly Vector2 Rotated(real_t angle)
{
- real_t sine = Mathf.Sin(angle);
- real_t cosi = Mathf.Cos(angle);
- return new Vector2(
- x * cosi - y * sine,
- x * sine + y * cosi);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2
+ (
+ x * cos - y * sin,
+ x * sin + y * cos
+ );
}
/// <summary>
@@ -683,7 +694,8 @@ namespace Godot
/// <returns>The resulting vector.</returns>
public static Vector2 FromAngle(real_t angle)
{
- return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2(cos, sin);
}
/// <summary>
@@ -989,6 +1001,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector2"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index fefdee33a5..031464dcc6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -331,6 +331,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -1060,6 +1070,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector3"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 3191e8adc0..0f4528bb40 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -280,6 +280,16 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
@@ -857,6 +867,18 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector4"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 6664dfb2dd..86b0e04206 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -289,7 +289,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
}
#else
static String get_assembly_name() {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
@@ -466,7 +466,7 @@ void GDMono::_init_godot_api_hashes() {
#ifdef TOOLS_ENABLED
bool GDMono::_load_project_assembly() {
- String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index a3ca2d6486..b6a31cf542 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -5,20 +5,12 @@
</brief_description>
<description>
Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]).
- Also supports custom node spawns through [method spawn], calling [method _spawn_custom] on all peers.
+ Also supports custom node spawns through [method spawn], calling [member spawn_function] on all peers.
Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way.
</description>
<tutorials>
</tutorials>
<methods>
- <method name="_spawn_custom" qualifiers="virtual">
- <return type="Node" />
- <param index="0" name="data" type="Variant" />
- <description>
- Method called on all peers when a custom spawn was requested by the authority using [method spawn]. Should return a [Node] that is not in the scene tree.
- [b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
- </description>
- </method>
<method name="add_spawnable_scene">
<return type="void" />
<param index="0" name="path" type="String" />
@@ -49,12 +41,16 @@
<return type="Node" />
<param index="0" name="data" type="Variant" default="null" />
<description>
- Requests a custom spawn, with [code]data[/code] passed to [method _spawn_custom] 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 [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].
[b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns.
</description>
</method>
</methods>
<members>
+ <member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function">
+ Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
+ [b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
+ </member>
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
When set to [code]0[/code] (the default), there is no limit.
diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
index e4e2b4f631..a688c5fd79 100644
--- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml
+++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
@@ -79,6 +79,7 @@
</member>
<member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
+ [b]Note:[/b] Changing this option while other peers are connected may lead to unexpected behaviors.
[b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported].
</member>
</members>
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index dbf1eecf0e..9b071ecc02 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -142,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) {
return;
}
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add property to synchronizer"));
if (config.is_null()) {
@@ -250,7 +250,7 @@ ReplicationEditor::ReplicationEditor() {
tree->add_child(drop_label);
drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- tree->set_drag_forwarding(this);
+ tree->set_drag_forwarding_compat(this);
}
void ReplicationEditor::_bind_methods() {
@@ -357,7 +357,7 @@ void ReplicationEditor::_tree_item_edited() {
int column = tree->get_edited_column();
ERR_FAIL_COND(column < 1 || column > 2);
const NodePath prop = ti->get_metadata(0);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
bool value = ti->is_checked(column);
String method;
if (column == 1) {
@@ -397,7 +397,7 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
int idx = config->property_get_index(prop);
bool spawn = config->property_get_spawn(prop);
bool sync = config->property_get_sync(prop);
- Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Property"));
undo_redo->add_do_method(config.ptr(), "remove_property", prop);
undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx);
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 52b874d280..0aa54b69f9 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -93,13 +93,6 @@ PackedStringArray MultiplayerSpawner::get_configuration_warnings() const {
if (spawn_path.is_empty() || !has_node(spawn_path)) {
warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
}
- bool has_scenes = get_spawnable_scene_count() > 0;
- // Can't check if method is overridden in placeholder scripts.
- bool has_placeholder_script = get_script_instance() && get_script_instance()->is_placeholder();
- if (!has_scenes && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom) && !has_placeholder_script) {
- warnings.push_back(RTR("A list of PackedScenes must be set in the \"Auto Spawn List\" property in order for MultiplayerSpawner to automatically spawn them remotely when added as child of \"spawn_path\"."));
- warnings.push_back(RTR("Alternatively, a Script implementing the function \"_spawn_custom\" must be set for this MultiplayerSpawner, and \"spawn\" must be called explicitly in code."));
- }
return warnings;
}
@@ -162,7 +155,9 @@ void MultiplayerSpawner::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
- GDVIRTUAL_BIND(_spawn_custom, "data");
+ ClassDB::bind_method(D_METHOD("get_spawn_function"), &MultiplayerSpawner::get_spawn_function);
+ ClassDB::bind_method(D_METHOD("set_spawn_function", "spawn_function"), &MultiplayerSpawner::set_spawn_function);
+ ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_spawn_function", "get_spawn_function");
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
@@ -183,7 +178,7 @@ void MultiplayerSpawner::_update_spawn_node() {
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
if (node) {
spawn_node = node->get_instance_id();
- if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
+ if (get_spawnable_scene_count()) {
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
}
} else {
@@ -204,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
ERR_CONTINUE(!node);
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
- // This is unlikely, but might still crash the engine.
- if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
- node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
- }
get_multiplayer()->object_configuration_remove(node, this);
}
tracked_nodes.clear();
@@ -249,11 +240,11 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s
if (!tracked_nodes.has(oid)) {
tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
- p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
+ _spawn_notify(p_node->get_instance_id());
}
}
-void MultiplayerSpawner::_node_ready(ObjectID p_id) {
+void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this);
}
@@ -298,23 +289,26 @@ Node *MultiplayerSpawner::instantiate_scene(int p_id) {
Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
- Node *node = nullptr;
- if (GDVIRTUAL_CALL(_spawn_custom, p_data, node)) {
- return node;
- }
- ERR_FAIL_V_MSG(nullptr, "Method '_spawn_custom' is not implemented on this peer.");
+ ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires a valid 'spawn_function'.");
+ const Variant *argv[1] = { &p_data };
+ Variant ret;
+ Callable::CallError ce;
+ spawn_function.callp(argv, 1, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, nullptr, "Failed to call spawn function.");
+ ERR_FAIL_COND_V_MSG(ret.get_type() != Variant::OBJECT, nullptr, "The spawn function must return a Node.");
+ return Object::cast_to<Node>(ret.operator Object *());
}
Node *MultiplayerSpawner::spawn(const Variant &p_data) {
ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
- ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script.");
+ ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires the 'spawn_function' property to be a valid callable.");
Node *parent = get_spawn_node();
ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
Node *node = instantiate_custom(p_data);
- ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node.");
+ ERR_FAIL_COND_V_MSG(!node, nullptr, "The 'spawn_function' callable must return a valid node.");
_track(node, p_data);
parent->add_child(node, true);
diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
index 3793c2d111..8a54140e32 100644
--- a/modules/multiplayer/multiplayer_spawner.h
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -71,12 +71,13 @@ private:
ObjectID spawn_node;
HashMap<ObjectID, SpawnInfo> tracked_nodes;
uint32_t spawn_limit = 0;
+ Callable spawn_function;
void _update_spawn_node();
void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
void _node_added(Node *p_node);
void _node_exit(ObjectID p_id);
- void _node_ready(ObjectID p_id);
+ void _spawn_notify(ObjectID p_id);
Vector<String> _get_spawnable_scenes() const;
void _set_spawnable_scenes(const Vector<String> &p_scenes);
@@ -106,6 +107,8 @@ public:
void set_spawn_path(const NodePath &p_path);
uint32_t get_spawn_limit() const { return spawn_limit; }
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
+ void set_spawn_function(Callable p_spawn_function) { spawn_function = p_spawn_function; }
+ Callable get_spawn_function() const { return spawn_function; }
const Variant get_spawn_argument(const ObjectID &p_id) const;
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
@@ -114,8 +117,6 @@ public:
Node *instantiate_custom(const Variant &p_data);
Node *instantiate_scene(int p_idx);
- GDVIRTUAL1R(Node *, _spawn_custom, const Variant &);
-
MultiplayerSpawner() {}
};
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
index 0a580f782f..01fc1b5275 100644
--- a/modules/multiplayer/scene_multiplayer.cpp
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -610,7 +610,6 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con
}
void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) {
- ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active.");
server_relay = p_enabled;
}
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index e1b7b0c346..233ff76c7d 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -125,6 +125,20 @@ void SceneReplicationInterface::on_reset() {
}
void SceneReplicationInterface::on_network_process() {
+ // Prevent endless stalling in case of unforseen spawn errors.
+ if (spawn_queue.size()) {
+ ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node.");
+ for (const ObjectID &oid : spawn_queue) {
+ Node *node = get_id_as<Node>(oid);
+ ERR_CONTINUE(!node);
+ if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) {
+ node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready));
+ }
+ }
+ spawn_queue.clear();
+ }
+
+ // Process timed syncs.
uint64_t msec = OS::get_singleton()->get_ticks_msec();
for (KeyValue<int, PeerInfo> &E : peers_info) {
const HashSet<ObjectID> to_sync = E.value.sync_nodes;
@@ -144,17 +158,39 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
// Track node.
const ObjectID oid = node->get_instance_id();
TrackedNode &tobj = _track(oid);
+
+ // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree".
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
tobj.spawner = spawner->get_instance_id();
- spawned_nodes.insert(oid);
+ spawn_queue.insert(oid);
+ node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT);
+ return OK;
+}
+
+void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) {
+ ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug.
- if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
- if (tobj.net_id == 0) {
- tobj.net_id = ++last_net_id;
+ // If we are a nested spawn, we need to wait until the parent is ready.
+ if (p_oid != *(spawn_queue.begin())) {
+ return;
+ }
+
+ for (const ObjectID &oid : spawn_queue) {
+ ERR_CONTINUE(!tracked_nodes.has(oid));
+
+ TrackedNode &tobj = tracked_nodes[oid];
+ MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner);
+ ERR_CONTINUE(!spawner);
+
+ spawned_nodes.insert(oid);
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ if (tobj.net_id == 0) {
+ tobj.net_id = ++last_net_id;
+ }
+ _update_spawn_visibility(0, oid);
}
- _update_spawn_visibility(0, oid);
}
- return OK;
+ spawn_queue.clear();
}
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h
index a5e610cff6..cf45db2138 100644
--- a/modules/multiplayer/scene_replication_interface.h
+++ b/modules/multiplayer/scene_replication_interface.h
@@ -51,7 +51,6 @@ private:
bool operator==(const ObjectID &p_other) { return id == p_other; }
- _FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
TrackedNode() {}
TrackedNode(const ObjectID &p_id) { id = p_id; }
TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
@@ -75,7 +74,10 @@ private:
HashSet<ObjectID> spawned_nodes;
HashSet<ObjectID> sync_nodes;
- // Pending spawn information.
+ // Pending local spawn information (handles spawning nested nodes during ready).
+ HashSet<ObjectID> spawn_queue;
+
+ // Pending remote spawn information.
ObjectID pending_spawn;
int pending_spawn_remote = 0;
const uint8_t *pending_buffer = nullptr;
@@ -89,6 +91,7 @@ private:
TrackedNode &_track(const ObjectID &p_id);
void _untrack(const ObjectID &p_id);
+ void _node_ready(const ObjectID &p_oid);
void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len);
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index 4532ea9391..9b5d78d465 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -52,7 +52,7 @@ using namespace NavigationUtilities;
server->MERGE(_cmd_, F_NAME)(d_0); \
} \
}; \
- void GodotNavigationServer::F_NAME(T_0 D_0) const { \
+ void GodotNavigationServer::F_NAME(T_0 D_0) { \
auto cmd = memnew(MERGE(F_NAME, _command)( \
D_0)); \
add_command(cmd); \
@@ -73,7 +73,7 @@ using namespace NavigationUtilities;
server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
} \
}; \
- void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) { \
auto cmd = memnew(MERGE(F_NAME, _command)( \
D_0, \
D_1)); \
@@ -81,34 +81,34 @@ using namespace NavigationUtilities;
} \
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
-#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
- struct MERGE(F_NAME, _command) : public SetCommand { \
- T_0 d_0; \
- T_1 d_1; \
- T_2 d_2; \
- T_3 d_3; \
- MERGE(F_NAME, _command) \
- ( \
- T_0 p_d_0, \
- T_1 p_d_1, \
- T_2 p_d_2, \
- T_3 p_d_3) : \
- d_0(p_d_0), \
- d_1(p_d_1), \
- d_2(p_d_2), \
- d_3(p_d_3) {} \
- virtual void exec(GodotNavigationServer *server) override { \
- server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
- } \
- }; \
- void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
- auto cmd = memnew(MERGE(F_NAME, _command)( \
- D_0, \
- D_1, \
- D_2, \
- D_3)); \
- add_command(cmd); \
- } \
+#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ T_2 d_2; \
+ T_3 d_3; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1, \
+ T_2 p_d_2, \
+ T_3 p_d_3) : \
+ d_0(p_d_0), \
+ d_1(p_d_1), \
+ d_2(p_d_2), \
+ d_3(p_d_3) {} \
+ virtual void exec(GodotNavigationServer *server) override { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
+ } \
+ }; \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1, \
+ D_2, \
+ D_3)); \
+ add_command(cmd); \
+ } \
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
GodotNavigationServer::GodotNavigationServer() {}
@@ -117,12 +117,10 @@ GodotNavigationServer::~GodotNavigationServer() {
flush_queries();
}
-void GodotNavigationServer::add_command(SetCommand *command) const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- {
- MutexLock lock(commands_mutex);
- mut_this->commands.push_back(command);
- }
+void GodotNavigationServer::add_command(SetCommand *command) {
+ MutexLock lock(commands_mutex);
+
+ commands.push_back(command);
}
TypedArray<RID> GodotNavigationServer::get_maps() const {
@@ -137,12 +135,12 @@ TypedArray<RID> GodotNavigationServer::get_maps() const {
return all_map_rids;
}
-RID GodotNavigationServer::map_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::map_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = map_owner.make_rid();
- NavMap *space = map_owner.get_or_null(rid);
- space->set_self(rid);
+ NavMap *map = map_owner.get_or_null(rid);
+ map->set_self(rid);
return rid;
}
@@ -279,8 +277,10 @@ TypedArray<RID> GodotNavigationServer::map_get_regions(RID p_map) const {
TypedArray<RID> regions_rids;
const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, regions_rids);
+
const LocalVector<NavRegion *> regions = map->get_regions();
regions_rids.resize(regions.size());
+
for (uint32_t i = 0; i < regions.size(); i++) {
regions_rids[i] = regions[i]->get_self();
}
@@ -291,8 +291,10 @@ TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const {
TypedArray<RID> agents_rids;
const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, agents_rids);
+
const LocalVector<RvoAgent *> agents = map->get_agents();
agents_rids.resize(agents.size());
+
for (uint32_t i = 0; i < agents.size(); i++) {
agents_rids[i] = agents[i]->get_self();
}
@@ -302,6 +304,7 @@ TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const {
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());
+
if (region->get_map()) {
return region->get_map()->get_self();
}
@@ -311,15 +314,16 @@ RID GodotNavigationServer::region_get_map(RID p_region) const {
RID GodotNavigationServer::agent_get_map(RID p_agent) const {
RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND_V(agent == nullptr, RID());
+
if (agent->get_map()) {
return agent->get_map()->get_self();
}
return RID();
}
-RID GodotNavigationServer::region_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::region_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = region_owner.make_rid();
NavRegion *reg = region_owner.get_or_null(rid);
reg->set_self(rid);
@@ -402,6 +406,7 @@ ObjectID GodotNavigationServer::region_get_owner_id(RID p_region) const {
bool GodotNavigationServer::region_owns_point(RID p_region, const Vector3 &p_point) const {
const NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, false);
+
if (region->get_map()) {
RID closest_point_owner = map_get_closest_point_owner(region->get_map()->get_self(), p_point);
return closest_point_owner == region->get_self();
@@ -430,7 +435,7 @@ COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navi
region->set_mesh(p_navigation_mesh);
}
-void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const {
+void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) {
ERR_FAIL_COND(p_navigation_mesh.is_null());
ERR_FAIL_COND(p_root_node == nullptr);
@@ -461,9 +466,9 @@ Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, i
return region->get_connection_pathway_end(p_connection_id);
}
-RID GodotNavigationServer::link_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::link_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = link_owner.make_rid();
NavLink *link = link_owner.get_or_null(rid);
link->set_self(rid);
@@ -600,9 +605,9 @@ ObjectID GodotNavigationServer::link_get_owner_id(RID p_link) const {
return link->get_owner_id();
}
-RID GodotNavigationServer::agent_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::agent_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = agent_owner.make_rid();
RvoAgent *agent = agent_owner.get_or_null(rid);
agent->set_self(rid);
@@ -789,10 +794,10 @@ COMMAND_1(free, RID, p_object) {
}
}
-void GodotNavigationServer::set_active(bool p_active) const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
- mut_this->active = p_active;
+void GodotNavigationServer::set_active(bool p_active) {
+ MutexLock lock(operations_mutex);
+
+ active = p_active;
}
void GodotNavigationServer::flush_queries() {
@@ -800,6 +805,7 @@ void GodotNavigationServer::flush_queries() {
// even with mutable functions.
MutexLock lock(commands_mutex);
MutexLock lock2(operations_mutex);
+
for (size_t i(0); i < commands.size(); i++) {
commands[i]->exec(this);
memdelete(commands[i]);
@@ -823,6 +829,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return;
}
+ int _new_pm_region_count = 0;
+ int _new_pm_agent_count = 0;
+ int _new_pm_link_count = 0;
+ int _new_pm_polygon_count = 0;
+ int _new_pm_edge_count = 0;
+ int _new_pm_edge_merge_count = 0;
+ int _new_pm_edge_connection_count = 0;
+ int _new_pm_edge_free_count = 0;
+
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
MutexLock lock(operations_mutex);
@@ -831,6 +846,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps[i]->step(p_delta_time);
active_maps[i]->dispatch_callbacks();
+ _new_pm_region_count += active_maps[i]->get_pm_region_count();
+ _new_pm_agent_count += active_maps[i]->get_pm_agent_count();
+ _new_pm_link_count += active_maps[i]->get_pm_link_count();
+ _new_pm_polygon_count += active_maps[i]->get_pm_polygon_count();
+ _new_pm_edge_count += active_maps[i]->get_pm_edge_count();
+ _new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
+ _new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
+ _new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
+
// Emit a signal if a map changed.
const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
if (new_map_update_id != active_maps_update_id[i]) {
@@ -838,6 +862,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps_update_id[i] = new_map_update_id;
}
}
+
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
}
PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const {
@@ -880,6 +913,40 @@ PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_
return r_query_result;
}
+int GodotNavigationServer::get_process_info(ProcessInfo p_info) const {
+ switch (p_info) {
+ case INFO_ACTIVE_MAPS: {
+ return active_maps.size();
+ } break;
+ case INFO_REGION_COUNT: {
+ return pm_region_count;
+ } break;
+ case INFO_AGENT_COUNT: {
+ return pm_agent_count;
+ } break;
+ case INFO_LINK_COUNT: {
+ return pm_link_count;
+ } break;
+ case INFO_POLYGON_COUNT: {
+ return pm_polygon_count;
+ } break;
+ case INFO_EDGE_COUNT: {
+ return pm_edge_count;
+ } break;
+ case INFO_EDGE_MERGE_COUNT: {
+ return pm_edge_merge_count;
+ } break;
+ case INFO_EDGE_CONNECTION_COUNT: {
+ return pm_edge_connection_count;
+ } break;
+ case INFO_EDGE_FREE_COUNT: {
+ return pm_edge_free_count;
+ } break;
+ }
+
+ return 0;
+}
+
#undef COMMAND_1
#undef COMMAND_2
#undef COMMAND_4
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 519034119d..a87a88d3bc 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -46,16 +46,16 @@
#define MERGE_INTERNAL(A, B) A##B
#define MERGE(A, B) MERGE_INTERNAL(A, B)
-#define COMMAND_1(F_NAME, T_0, D_0) \
- virtual void F_NAME(T_0 D_0) const override; \
+#define COMMAND_1(F_NAME, T_0, D_0) \
+ virtual void F_NAME(T_0 D_0) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0)
-#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
- virtual void F_NAME(T_0 D_0, T_1 D_1) const override; \
+#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
-#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
- virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) const override; \
+#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
class GodotNavigationServer;
@@ -81,15 +81,25 @@ class GodotNavigationServer : public NavigationServer3D {
LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
GodotNavigationServer();
virtual ~GodotNavigationServer();
- void add_command(SetCommand *command) const;
+ void add_command(SetCommand *command);
virtual TypedArray<RID> get_maps() const override;
- virtual RID map_create() const override;
+ virtual RID map_create() override;
COMMAND_2(map_set_active, RID, p_map, bool, p_active);
virtual bool map_is_active(RID p_map) const override;
@@ -118,7 +128,7 @@ public:
virtual void map_force_update(RID p_map) override;
- virtual RID region_create() const override;
+ virtual RID region_create() 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;
@@ -136,12 +146,12 @@ public:
virtual uint32_t region_get_navigation_layers(RID p_region) const override;
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh);
- virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const override;
+ virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override;
virtual int region_get_connections_count(RID p_region) const override;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
- virtual RID link_create() const override;
+ virtual RID link_create() override;
COMMAND_2(link_set_map, RID, p_link, RID, p_map);
virtual RID link_get_map(RID p_link) const override;
COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional);
@@ -159,7 +169,7 @@ public:
COMMAND_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id);
virtual ObjectID link_get_owner_id(RID p_link) const override;
- virtual RID agent_create() const override;
+ virtual RID agent_create() 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);
@@ -176,12 +186,14 @@ public:
COMMAND_1(free, RID, p_object);
- virtual void set_active(bool p_active) const override;
+ virtual void set_active(bool p_active) override;
void flush_queries();
virtual void process(real_t p_delta_time) override;
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;
+
+ int get_process_info(ProcessInfo p_info) const override;
};
#undef COMMAND_1
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 2a2f8aa1b7..fd735f8793 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -611,6 +611,16 @@ void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
}
void NavMap::sync() {
+ // Performance Monitor
+ int _new_pm_region_count = regions.size();
+ int _new_pm_agent_count = agents.size();
+ int _new_pm_link_count = links.size();
+ int _new_pm_polygon_count = pm_polygon_count;
+ int _new_pm_edge_count = pm_edge_count;
+ int _new_pm_edge_merge_count = pm_edge_merge_count;
+ int _new_pm_edge_connection_count = pm_edge_connection_count;
+ int _new_pm_edge_free_count = pm_edge_free_count;
+
// Check if we need to update the links.
if (regenerate_polygons) {
for (uint32_t r = 0; r < regions.size(); r++) {
@@ -632,6 +642,12 @@ void NavMap::sync() {
}
if (regenerate_links) {
+ _new_pm_polygon_count = 0;
+ _new_pm_edge_count = 0;
+ _new_pm_edge_merge_count = 0;
+ _new_pm_edge_connection_count = 0;
+ _new_pm_edge_free_count = 0;
+
// Remove regions connections.
for (uint32_t r = 0; r < regions.size(); r++) {
regions[r]->get_connections().clear();
@@ -654,6 +670,8 @@ void NavMap::sync() {
count += regions[r]->get_polygons().size();
}
+ _new_pm_polygon_count = polygons.size();
+
// Group all edges per key.
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
for (uint32_t poly_id = 0; poly_id < polygons.size(); poly_id++) {
@@ -666,6 +684,7 @@ void NavMap::sync() {
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
if (!connection) {
connections[ek] = Vector<gd::Edge::Connection>();
+ _new_pm_edge_count += 1;
}
if (connections[ek].size() <= 1) {
// Add the polygon/edge tuple to this key.
@@ -691,6 +710,7 @@ void NavMap::sync() {
c1.polygon->edges[c1.edge].connections.push_back(c2);
c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
+ _new_pm_edge_merge_count += 1;
} else {
CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
free_edges.push_back(E.value[0]);
@@ -704,6 +724,8 @@ void NavMap::sync() {
// to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during
// connection, integration and path finding.
+ _new_pm_edge_free_count = free_edges.size();
+
for (int i = 0; i < free_edges.size(); i++) {
const gd::Edge::Connection &free_edge = free_edges[i];
Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
@@ -757,6 +779,7 @@ void NavMap::sync() {
// Add the connection to the region_connection map.
((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection);
+ _new_pm_edge_connection_count += 1;
}
}
@@ -892,6 +915,16 @@ void NavMap::sync() {
regenerate_polygons = false;
regenerate_links = false;
agents_dirty = false;
+
+ // Performance Monitor
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
}
void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index 321d5560f0..fce7aff3ba 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -89,6 +89,16 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id = 0;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
NavMap();
~NavMap();
@@ -152,6 +162,16 @@ public:
void step(real_t p_deltatime);
void dispatch_callbacks();
+ // Performance Monitor
+ int get_pm_region_count() const { return pm_region_count; }
+ int get_pm_agent_count() const { return pm_agent_count; }
+ int get_pm_link_count() const { return pm_link_count; }
+ int get_pm_polygon_count() const { return pm_polygon_count; }
+ int get_pm_edge_count() const { return pm_edge_count; }
+ int get_pm_edge_merge_count() const { return pm_edge_merge_count; }
+ int get_pm_edge_connection_count() const { return pm_edge_connection_count; }
+ int get_pm_edge_free_count() const { return pm_edge_free_count; }
+
private:
void compute_single_step(uint32_t index, RvoAgent **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;
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 568e8b9b26..fff7a02fc4 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -207,11 +207,11 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
List<uint32_t> shape_owners;
static_body->get_shape_owners(&shape_owners);
for (uint32_t shape_owner : shape_owners) {
+ if (static_body->is_shape_owner_disabled(shape_owner)) {
+ continue;
+ }
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
for (int i = 0; i < shape_count; i++) {
- if (static_body->is_shape_owner_disabled(i)) {
- continue;
- }
Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i);
if (s.is_null()) {
continue;
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index fefee9bb24..3b39967ba4 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -101,6 +101,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_ext
env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp")
env.modules_sources += module_obj
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index e3ff1b4382..669c395b3e 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -307,6 +307,31 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
+ // Create our Pico 4 / Neo 3 controller profile
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/pico/neo3_controller");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ 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(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available
+ profile->add_new_binding(menu_button, "/user/hand/left/input/back/click,/user/hand/right/input/back/click"); // right hand back click may not be available
+ 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(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
+ 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(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
+ profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
+ profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
+ profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
+ // primary on our pico controller is our thumbstick
+ profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
+ profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
+ profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
+ // pico controller has no secondary input
+ profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
+ add_interaction_profile(profile);
+
// Create our Valve index controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
index e3fe06c6f7..586b0b0697 100644
--- a/modules/openxr/editor/openxr_action_editor.cpp
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_action_editor.h"
-#include "editor/editor_node.h"
void OpenXRActionEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name);
@@ -125,7 +124,7 @@ void OpenXRActionEditor::_on_remove_action() {
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action = p_action;
set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h
index 8af5448aed..765b3ef378 100644
--- a/modules/openxr/editor/openxr_action_editor.h
+++ b/modules/openxr/editor/openxr_action_editor.h
@@ -43,7 +43,7 @@ class OpenXRActionEditor : public HBoxContainer {
GDCLASS(OpenXRActionEditor, HBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 12c95f8978..ad5a515a01 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -387,7 +387,7 @@ void OpenXRActionMapEditor::_clear_action_map() {
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
set_custom_minimum_size(Size2(0.0, 300.0));
top_hb = memnew(HBoxContainer);
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index 47bfda3425..a04bae4a6e 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -48,7 +48,7 @@ class OpenXRActionMapEditor : public VBoxContainer {
GDCLASS(OpenXRActionMapEditor, VBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
String edited_path;
Ref<OpenXRActionMap> action_map;
diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp
index 078d83f5f2..bcb0f5f8b1 100644
--- a/modules/openxr/editor/openxr_action_set_editor.cpp
+++ b/modules/openxr/editor/openxr_action_set_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_action_set_editor.h"
-#include "editor/editor_node.h"
#include "openxr_action_editor.h"
void OpenXRActionSetEditor::_bind_methods() {
@@ -212,7 +211,7 @@ void OpenXRActionSetEditor::set_focus_on_entry() {
}
OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
action_set = p_action_set;
diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h
index 6040f7fb4e..129f800abe 100644
--- a/modules/openxr/editor/openxr_action_set_editor.h
+++ b/modules/openxr/editor/openxr_action_set_editor.h
@@ -44,7 +44,7 @@ class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRActionSet> action_set;
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
index 76b0ae5a3c..6a848dd430 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
@@ -29,7 +29,6 @@
/**************************************************************************/
#include "openxr_interaction_profile_editor.h"
-#include "editor/editor_node.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
@@ -141,7 +140,7 @@ void OpenXRInteractionProfileEditorBase::remove_all_bindings_for_action(Ref<Open
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
- undo_redo = EditorNode::get_undo_redo();
+ undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
interaction_profile = p_interaction_profile;
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h
index 73dba71cbd..fa25a000a9 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.h
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.h
@@ -43,7 +43,7 @@ class OpenXRInteractionProfileEditorBase : public ScrollContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer);
protected:
- Ref<EditorUndoRedoManager> undo_redo;
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp
index aae284f6bd..4465daf22a 100644
--- a/modules/openxr/extensions/openxr_android_extension.cpp
+++ b/modules/openxr/extensions/openxr_android_extension.cpp
@@ -51,18 +51,17 @@ OpenXRAndroidExtension::OpenXRAndroidExtension() {
HashMap<String, bool *> OpenXRAndroidExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
- request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = &loader_init_extension_available;
request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
return request_extensions;
}
void OpenXRAndroidExtension::on_before_instance_created() {
- if (!loader_init_extension_available) {
- print_line("OpenXR: XR_KHR_loader_init_android is not reported as available - trying to initialize anyway...");
+ if (XR_FAILED(EXT_TRY_INIT_XR_FUNC(xrInitializeLoaderKHR))) {
+ // XR_KHR_loader_init not supported on this platform
+ return;
}
-
- EXT_INIT_XR_FUNC(xrInitializeLoaderKHR);
+ loader_init_extension_available = true;
JNIEnv *env = get_jni_env();
JavaVM *vm;
@@ -85,6 +84,9 @@ static XrInstanceCreateInfoAndroidKHR instance_create_info;
void *OpenXRAndroidExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
if (!create_instance_extension_available) {
+ if (!loader_init_extension_available) {
+ WARN_PRINT("No Android extensions available, couldn't pass JVM and Activity to OpenXR");
+ }
return nullptr;
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index 51eb06c9df..84279635b5 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -73,7 +73,7 @@ public:
virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance.
virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance.
virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
- virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successsfully created our OpenXR session.
+ virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session.
// `on_process` is called as part of our OpenXR process handling,
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index cd371b9ed9..6ce8f0805f 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -157,7 +157,6 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
}
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
- p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
p_usable_swap_chains.push_back(GL_RGBA8);
}
diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.cpp b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
new file mode 100644
index 0000000000..f2fcf22ce2
--- /dev/null
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* openxr_pico_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_pico_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+// Pico controllers are not part of the OpenXR spec at the time of writing this
+// code. We'll hardcode the extension name that is used internally, verified by
+// tests on the Pico 4. Note that later versions of the Pico 4 and OpenXR
+// runtime on Pico might use a different, standardized extension name.
+#ifndef XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME
+#define XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_PICO_controller_interaction"
+#endif
+
+HashMap<String, bool *> OpenXRPicoControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRPicoControllerExtension::is_available() {
+ return available;
+}
+
+void OpenXRPicoControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Pico controller (Pico 4 and Pico Neo 3 controllers)
+ metadata->register_interaction_profile("Pico controller", "/interaction_profiles/pico/neo3_controller", XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Screenshot click", "/user/hand/right", "/user/hand/right/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/modules/openxr/extensions/openxr_pico_controller_extension.h
index da39044f97..a2a1e2f3d3 100644
--- a/scene/resources/skeleton_modification_3d_stackholder.h
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* skeleton_modification_3d_stackholder.h */
+/* openxr_pico_controller_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,32 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef SKELETON_MODIFICATION_3D_STACKHOLDER_H
-#define SKELETON_MODIFICATION_3D_STACKHOLDER_H
+#ifndef OPENXR_PICO_CONTROLLER_EXTENSION_H
+#define OPENXR_PICO_CONTROLLER_EXTENSION_H
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DStackHolder : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D);
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
+#include "openxr_extension_wrapper.h"
+class OpenXRPicoControllerExtension : public OpenXRExtensionWrapper {
public:
- Ref<SkeletonModificationStack3D> held_modification_stack;
+ virtual HashMap<String, bool *> get_requested_extensions() override;
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+ bool is_available();
- void set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack);
- Ref<SkeletonModificationStack3D> get_held_modification_stack() const;
+ virtual void on_register_metadata() override;
- SkeletonModification3DStackHolder();
- ~SkeletonModification3DStackHolder();
+private:
+ bool available = false;
};
-#endif // SKELETON_MODIFICATION_3D_STACKHOLDER_H
+#endif // OPENXR_PICO_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 550ed8052e..d556f475d2 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -217,7 +217,7 @@ bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) {
String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_extension(p_toplevel_path);
// If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
- ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_toplevel_path);
+ ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path);
if (required_extension == "") {
// no extension needed, core top level are always "supported", they just won't be used if not really supported
@@ -1224,8 +1224,12 @@ bool OpenXRAPI::resolve_instance_openxr_symbols() {
return true;
}
+XrResult OpenXRAPI::try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
+ return xrGetInstanceProcAddr(instance, p_name, p_addr);
+}
+
XrResult OpenXRAPI::get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
- XrResult result = xrGetInstanceProcAddr(instance, p_name, p_addr);
+ XrResult result = try_get_instance_proc_addr(p_name, p_addr);
if (result != XR_SUCCESS) {
String error_message = String("Symbol ") + p_name + " not found in OpenXR instance.";
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index a697a5f90a..5fb8de660e 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -305,6 +305,7 @@ public:
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
_FORCE_INLINE_ static OpenXRAPI *get_singleton() { return singleton; }
+ XrResult try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result);
String get_swapchain_format_name(int64_t p_swapchain_format) const;
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 56c31883e6..4e2fe3dab5 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -53,6 +53,7 @@
#include "extensions/openxr_htc_vive_tracker_extension.h"
#include "extensions/openxr_huawei_controller_extension.h"
#include "extensions/openxr_palm_pose_extension.h"
+#include "extensions/openxr_pico_controller_extension.h"
#include "extensions/openxr_wmr_controller_extension.h"
static OpenXRAPI *openxr_api = nullptr;
@@ -92,6 +93,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
// register our other extensions
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPalmPoseExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension));
diff --git a/modules/openxr/util.h b/modules/openxr/util.h
index f3fa187faa..6665d45007 100644
--- a/modules/openxr/util.h
+++ b/modules/openxr/util.h
@@ -53,6 +53,12 @@
#define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
#define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name)
+#define TRY_INIT_XR_FUNC(openxr_api, name) \
+ openxr_api->try_get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr)
+
+#define EXT_TRY_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
+#define OPENXR_TRY_API_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(this, name)
+
#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \
PFN_##func_name func_name##_ptr = nullptr; \
XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1) const { \
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 37c8a95905..209ebab388 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -96,7 +96,7 @@ if env["builtin_embree"]:
if not env.msvc:
# Flags synced with upstream gnu.cmake.
- if env["arch"] == "arm64" and env["platform"] == "linuxbsd":
+ if env["arch"] == "arm64" and env["platform"] == "linuxbsd" and not env["use_llvm"]:
env_thirdparty.Append(CXXFLAGS=["-flax-vector-conversions"])
env_thirdparty.Append(
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 44a2c76727..e52b87741e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -42,7 +42,7 @@
using namespace godot;
-#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
#else
// Headers for building as built-in module.
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 809bfbe41a..034f88e387 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -42,7 +42,7 @@
using namespace godot;
-#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
#else
// Headers for building as built-in module.
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index f5964eb4d1..ba1750386f 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -18,7 +18,7 @@
func _ready():
# We assume this node has a button as a child.
# This button is for the user to consent to entering immersive VR mode.
- $Button.pressed.connect(self._on_Button_pressed)
+ $Button.pressed.connect(self._on_button_pressed)
webxr_interface = XRServer.find_interface("WebXR")
if webxr_interface:
@@ -38,7 +38,7 @@
if session_mode == 'immersive-vr':
vr_supported = supported
- func _on_Button_pressed():
+ func _on_button_pressed():
if not vr_supported:
OS.alert("Your browser doesn't support VR")
return
diff --git a/platform/android/README.md b/platform/android/README.md
index f6aabab708..01eb9c03a6 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -5,7 +5,7 @@ using [Gradle](https://gradle.org/) as a build system.
## Documentation
-- [Compiling for Android](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_android.html)
+- [Compiling for Android](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_android.html)
- Instructions on building this platform port from source.
- [Exporting for Android](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_android.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index cfa1046bb3..e3f93086dc 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -225,7 +225,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
}
}
-void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
if (!mouse_event_info.valid) {
return;
}
@@ -242,7 +242,7 @@ void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask
hover_prev_pos = mouse_event_info.pos;
}
ev->set_pressed(p_pressed);
- MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+ BitField<MouseButtonMask> changed_button_mask = BitField<MouseButtonMask>(buttons_state.operator int64_t() ^ event_buttons_mask.operator int64_t());
buttons_state = event_buttons_mask;
@@ -253,12 +253,12 @@ void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask
}
void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
- _parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
+ _parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, p_source_mouse_relative);
mouse_event_info.valid = false;
}
void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
- MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
+ BitField<MouseButtonMask> event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
@@ -342,11 +342,11 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
}
}
-void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
+void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
Ref<InputEventMouseButton> evd = ev->duplicate();
_set_key_modifier_state(evd);
evd->set_button_index(wheel_button);
- evd->set_button_mask(MouseButton(event_buttons_mask ^ mouse_button_to_mask(wheel_button)));
+ evd->set_button_mask(BitField<MouseButtonMask>(event_buttons_mask.operator int64_t() ^ int64_t(mouse_button_to_mask(wheel_button))));
evd->set_factor(factor);
Input::get_singleton()->parse_input_event(evd);
Ref<InputEventMouseButton> evdd = evd->duplicate();
@@ -373,39 +373,39 @@ void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
Input::get_singleton()->parse_input_event(pan_event);
}
-MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
- switch (button_mask) {
- case MouseButton::MASK_LEFT:
+MouseButton AndroidInputHandler::_button_index_from_mask(BitField<MouseButtonMask> button_mask) {
+ switch (MouseButtonMask(button_mask.operator int64_t())) {
+ case MouseButtonMask::LEFT:
return MouseButton::LEFT;
- case MouseButton::MASK_RIGHT:
+ case MouseButtonMask::RIGHT:
return MouseButton::RIGHT;
- case MouseButton::MASK_MIDDLE:
+ case MouseButtonMask::MIDDLE:
return MouseButton::MIDDLE;
- case MouseButton::MASK_XBUTTON1:
+ case MouseButtonMask::MB_XBUTTON1:
return MouseButton::MB_XBUTTON1;
- case MouseButton::MASK_XBUTTON2:
+ case MouseButtonMask::MB_XBUTTON2:
return MouseButton::MB_XBUTTON2;
default:
return MouseButton::NONE;
}
}
-MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
- MouseButton godot_button_mask = MouseButton::NONE;
+BitField<MouseButtonMask> AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
+ BitField<MouseButtonMask> godot_button_mask;
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
- godot_button_mask |= MouseButton::MASK_LEFT;
+ godot_button_mask.set_flag(MouseButtonMask::LEFT);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
- godot_button_mask |= MouseButton::MASK_RIGHT;
+ godot_button_mask.set_flag(MouseButtonMask::RIGHT);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
- godot_button_mask |= MouseButton::MASK_MIDDLE;
+ godot_button_mask.set_flag(MouseButtonMask::MIDDLE);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
- godot_button_mask |= MouseButton::MASK_XBUTTON1;
+ godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON1);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
- godot_button_mask |= MouseButton::MASK_XBUTTON2;
+ godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON2);
}
return godot_button_mask;
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 1b89096057..34259efd81 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -61,7 +61,7 @@ public:
int index = 0; // Can be either JoyAxis or JoyButton.
bool pressed = false;
float value = 0;
- HatMask hat = HatMask::CENTER;
+ BitField<HatMask> hat;
};
private:
@@ -70,7 +70,7 @@ private:
bool control_mem = false;
bool meta_mem = false;
- MouseButton buttons_state = MouseButton::NONE;
+ BitField<MouseButtonMask> buttons_state;
Vector<TouchPos> touch;
MouseEventInfo mouse_event_info;
@@ -78,12 +78,12 @@ private:
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
- static MouseButton _button_index_from_mask(MouseButton button_mask);
- static MouseButton _android_button_mask_to_godot_button_mask(int android_button_mask);
+ static MouseButton _button_index_from_mask(BitField<MouseButtonMask> button_mask);
+ static BitField<MouseButtonMask> _android_button_mask_to_godot_button_mask(int android_button_mask);
- void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
+ void _wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
- void _parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
+ void _parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
void _release_mouse_event_info(bool p_source_mouse_relative = false);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 70370764e2..6b3bdb7fe6 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -466,14 +466,15 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error));
if (r_error != OK) {
- OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
if (p_rendering_driver == "vulkan") {
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try exporting your game using the gl_compatibility renderer.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required Vulkan version.\n\n"
+ "Please try exporting your game using the 'gl_compatibility' renderer.",
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support OpenGL ES 3.0.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required OpenGL ES 3.0 version.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
@@ -624,8 +625,8 @@ Point2i DisplayServerAndroid::mouse_get_position() const {
return Input::get_singleton()->get_mouse_position();
}
-MouseButton DisplayServerAndroid::mouse_get_button_state() const {
- return (MouseButton)Input::get_singleton()->get_mouse_button_mask();
+BitField<MouseButtonMask> DisplayServerAndroid::mouse_get_button_state() const {
+ return Input::get_singleton()->get_mouse_button_mask();
}
void DisplayServerAndroid::_cursor_set_shape_helper(CursorShape p_shape, bool force) {
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index faaa3dbd9c..b2400d81dc 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -207,7 +207,7 @@ public:
void notify_surface_changed(int p_width, int p_height);
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
void reset_swap_buffers_flag();
bool should_swap_buffers() const;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 0bb7b57a35..bb1ad3d83b 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -43,10 +43,16 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "main/splash.gen.h"
-#include "platform/android/logo.gen.h"
-#include "platform/android/run_icon.gen.h"
+#include "platform/android/logo_svg.gen.h"
+#include "platform/android/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
#include <string.h>
@@ -1025,7 +1031,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
string_table.write[attr_value] = "com.oculus.handtracking.version";
}
- if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_value") {
+ if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_version_value") {
string_table.write[attr_value] = "V2.0";
}
}
@@ -3238,8 +3244,17 @@ void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<
}
EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
- logo = ImageTexture::create_from_image(memnew(Image(_android_logo)));
- run_icon = ImageTexture::create_from_image(memnew(Image(_android_run_icon)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
devices_changed.set();
plugins_changed.set();
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index b502e587a7..e6cdd7932a 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -353,19 +353,19 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = AndroidInputHandler::JOY_EVENT_HAT;
- HatMask hat = HatMask::CENTER;
+ BitField<HatMask> hat;
if (p_hat_x != 0) {
if (p_hat_x < 0) {
- hat |= HatMask::LEFT;
+ hat.set_flag(HatMask::LEFT);
} else {
- hat |= HatMask::RIGHT;
+ hat.set_flag(HatMask::RIGHT);
}
}
if (p_hat_y != 0) {
if (p_hat_y < 0) {
- hat |= HatMask::UP;
+ hat.set_flag(HatMask::UP);
} else {
- hat |= HatMask::DOWN;
+ hat.set_flag(HatMask::DOWN);
}
}
jevent.hat = hat;
diff --git a/platform/android/logo.png b/platform/android/logo.png
deleted file mode 100644
index 9c8be93646..0000000000
--- a/platform/android/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/logo.svg b/platform/android/logo.svg
new file mode 100644
index 0000000000..f154e55d11
--- /dev/null
+++ b/platform/android/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M22.904 20.192a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m-13.808 0a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m14.256-7.525 2.496-4.323a.52.52 0 1 0-.899-.52l-2.528 4.378a15.69 15.69 0 0 0-12.842 0L7.051 7.823a.52.52 0 1 0-.9.521l2.497 4.323C4.361 15 1.43 19.34 1 24.467h30c-.43-5.128-3.361-9.468-7.648-11.8" fill="#56d881"/></svg>
diff --git a/platform/android/run_icon.png b/platform/android/run_icon.png
deleted file mode 100644
index b687c9ac31..0000000000
--- a/platform/android/run_icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/run_icon.svg b/platform/android/run_icon.svg
new file mode 100644
index 0000000000..24d930fece
--- /dev/null
+++ b/platform/android/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M11.187 9.936a.577.577 0 1 1 .578-.578.577.577 0 0 1-.578.578m-6.374 0a.577.577 0 1 1 .577-.578.577.577 0 0 1-.577.578m6.581-3.475 1.153-1.996a.24.24 0 1 0-.415-.24l-1.167 2.021a7.244 7.244 0 0 0-5.93 0L3.868 4.225a.24.24 0 1 0-.415.24l1.153 1.996a6.806 6.806 0 0 0-3.532 5.448h13.852a6.807 6.807 0 0 0-3.532-5.448" fill="#56d881" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/ios/README.md b/platform/ios/README.md
index 82c275ad31..0e1d6802cb 100644
--- a/platform/ios/README.md
+++ b/platform/ios/README.md
@@ -8,7 +8,7 @@ project template used for packaging the iOS export templates.
## Documentation
-- [Compiling for iOS](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_ios.html)
+- [Compiling for iOS](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_ios.html)
- Instructions on building this platform port from source.
- [Exporting for iOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_ios.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/ios/device_metrics.m b/platform/ios/device_metrics.m
index b51f45c602..d6f5ec8e42 100644
--- a/platform/ios/device_metrics.m
+++ b/platform/ios/device_metrics.m
@@ -35,117 +35,159 @@
+ (NSDictionary *)dpiList {
return @{
@[
- @"iPad1,1",
- @"iPad2,1",
- @"iPad2,2",
- @"iPad2,3",
- @"iPad2,4",
+ @"iPad1,1", // iPad 1th Gen
+ @"iPad1,2", // iPad 1th Gen (3G)
+ @"iPad2,1", // iPad 2nd Gen
+ @"iPad2,2", // iPad 2nd Gen (GSM)
+ @"iPad2,3", // iPad 2nd Gen (CDMA)
+ @"iPad2,4", // iPad 2nd Gen
] : @132,
@[
- @"iPhone1,1",
- @"iPhone1,2",
- @"iPhone2,1",
- @"iPad2,5",
- @"iPad2,6",
- @"iPad2,7",
- @"iPod1,1",
- @"iPod2,1",
- @"iPod3,1",
+ @"iPhone1,1", // iPhone 1st Gen
+ @"iPhone1,2", // iPhone 3G
+ @"iPhone2,1", // iPhone 3GS
+ @"iPad2,5", // iPad mini
+ @"iPad2,6", // iPad mini (GSM+LTE)
+ @"iPad2,7", // iPad mini (CDMA+LTE)
+ @"iPod1,1", // iPod 1st Gen
+ @"iPod2,1", // iPod 2nd Gen
+ @"iPod3,1", // iPod 3rd Gen
] : @163,
@[
- @"iPad3,1",
- @"iPad3,2",
- @"iPad3,3",
- @"iPad3,4",
- @"iPad3,5",
- @"iPad3,6",
- @"iPad4,1",
- @"iPad4,2",
- @"iPad4,3",
- @"iPad5,3",
- @"iPad5,4",
- @"iPad6,3",
- @"iPad6,4",
- @"iPad6,7",
- @"iPad6,8",
- @"iPad6,11",
- @"iPad6,12",
- @"iPad7,1",
- @"iPad7,2",
- @"iPad7,3",
- @"iPad7,4",
- @"iPad7,5",
- @"iPad7,6",
- @"iPad7,11",
- @"iPad7,12",
- @"iPad8,1",
- @"iPad8,2",
- @"iPad8,3",
- @"iPad8,4",
- @"iPad8,5",
- @"iPad8,6",
- @"iPad8,7",
- @"iPad8,8",
- @"iPad8,9",
- @"iPad8,10",
- @"iPad8,11",
- @"iPad8,12",
- @"iPad11,3",
- @"iPad11,4",
+ @"iPad3,1", // iPad 3rd Gen
+ @"iPad3,2", // iPad 3rd Gen (CDMA)
+ @"iPad3,3", // iPad 3rd Gen (GSM)
+ @"iPad3,4", // iPad 4th Gen
+ @"iPad3,5", // iPad 4th Gen (GSM+LTE)
+ @"iPad3,6", // iPad 4th Gen (CDMA+LTE)
+ @"iPad4,1", // iPad Air (WiFi)
+ @"iPad4,2", // iPad Air (GSM+CDMA)
+ @"iPad4,3", // iPad Air (China)
+ @"iPad4,7", // iPad mini 3 (WiFi)
+ @"iPad4,8", // iPad mini 3 (GSM+CDMA)
+ @"iPad4,9", // iPad Mini 3 (China)
+ @"iPad6,3", // iPad Pro (9.7 inch, WiFi)
+ @"iPad6,4", // iPad Pro (9.7 inch, WiFi+LTE)
+ @"iPad6,7", // iPad Pro (12.9 inch, WiFi)
+ @"iPad6,8", // iPad Pro (12.9 inch, WiFi+LTE)
+ @"iPad6,11", // iPad 5th Gen (2017)
+ @"iPad6,12", // iPad 5th Gen (2017)
+ @"iPad7,1", // iPad Pro 2nd Gen (WiFi)
+ @"iPad7,2", // iPad Pro 2nd Gen (WiFi+Cellular)
+ @"iPad7,3", // iPad Pro 10.5-inch 2nd Gen
+ @"iPad7,4", // iPad Pro 10.5-inch 2nd Gen
+ @"iPad7,5", // iPad 6th Gen (WiFi)
+ @"iPad7,6", // iPad 6th Gen (WiFi+Cellular)
+ @"iPad7,11", // iPad 7th Gen 10.2-inch (WiFi)
+ @"iPad7,12", // iPad 7th Gen 10.2-inch (WiFi+Cellular)
+ @"iPad8,1", // iPad Pro 11 inch 3rd Gen (WiFi)
+ @"iPad8,2", // iPad Pro 11 inch 3rd Gen (1TB, WiFi)
+ @"iPad8,3", // iPad Pro 11 inch 3rd Gen (WiFi+Cellular)
+ @"iPad8,4", // iPad Pro 11 inch 3rd Gen (1TB, WiFi+Cellular)
+ @"iPad8,5", // iPad Pro 12.9 inch 3rd Gen (WiFi)
+ @"iPad8,6", // iPad Pro 12.9 inch 3rd Gen (1TB, WiFi)
+ @"iPad8,7", // iPad Pro 12.9 inch 3rd Gen (WiFi+Cellular)
+ @"iPad8,8", // iPad Pro 12.9 inch 3rd Gen (1TB, WiFi+Cellular)
+ @"iPad8,9", // iPad Pro 11 inch 4th Gen (WiFi)
+ @"iPad8,10", // iPad Pro 11 inch 4th Gen (WiFi+Cellular)
+ @"iPad8,11", // iPad Pro 12.9 inch 4th Gen (WiFi)
+ @"iPad8,12", // iPad Pro 12.9 inch 4th Gen (WiFi+Cellular)
+ @"iPad11,3", // iPad Air 3rd Gen (WiFi)
+ @"iPad11,4", // iPad Air 3rd Gen
+ @"iPad11,6", // iPad 8th Gen (WiFi)
+ @"iPad11,7", // iPad 8th Gen (WiFi+Cellular)
+ @"iPad12,1", // iPad 9th Gen (WiFi)
+ @"iPad12,2", // iPad 9th Gen (WiFi+Cellular)
+ @"iPad13,1", // iPad Air 4th Gen (WiFi)
+ @"iPad13,2", // iPad Air 4th Gen (WiFi+Cellular)
+ @"iPad13,4", // iPad Pro 11 inch 5th Gen
+ @"iPad13,5", // iPad Pro 11 inch 5th Gen
+ @"iPad13,6", // iPad Pro 11 inch 5th Gen
+ @"iPad13,7", // iPad Pro 11 inch 5th Gen
+ @"iPad13,8", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,9", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,10", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,11", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,16", // iPad Air 5th Gen (WiFi)
+ @"iPad13,17", // iPad Air 5th Gen (WiFi+Cellular)
+ @"iPad13,18", // iPad 10th Gen
+ @"iPad13,19", // iPad 10th Gen
+ @"iPad14,3", // iPad Pro 11 inch 6th Gen
+ @"iPad14,4", // iPad Pro 11 inch 6th Gen
+ @"iPad14,5", // iPad Pro 12.9 inch 6th Gen
+ @"iPad14,6", // iPad Pro 12.9 inch 6th Gen
] : @264,
@[
- @"iPhone3,1",
- @"iPhone3,2",
- @"iPhone3,3",
- @"iPhone4,1",
- @"iPhone5,1",
- @"iPhone5,2",
- @"iPhone5,3",
- @"iPhone5,4",
- @"iPhone6,1",
- @"iPhone6,2",
- @"iPhone7,2",
- @"iPhone8,1",
- @"iPhone8,4",
- @"iPhone9,1",
- @"iPhone9,3",
- @"iPhone10,1",
- @"iPhone10,4",
- @"iPhone11,8",
- @"iPhone12,1",
- @"iPhone12,8",
- @"iPad4,4",
- @"iPad4,5",
- @"iPad4,6",
- @"iPad4,7",
- @"iPad4,8",
- @"iPad4,9",
- @"iPad5,1",
- @"iPad5,2",
- @"iPad11,1",
- @"iPad11,2",
- @"iPod4,1",
- @"iPod5,1",
- @"iPod7,1",
- @"iPod9,1",
+ @"iPhone3,1", // iPhone 4
+ @"iPhone3,2", // iPhone 4 (GSM)
+ @"iPhone3,3", // iPhone 4 (CDMA)
+ @"iPhone4,1", // iPhone 4S
+ @"iPhone5,1", // iPhone 5 (GSM)
+ @"iPhone5,2", // iPhone 5 (GSM+CDMA)
+ @"iPhone5,3", // iPhone 5C (GSM)
+ @"iPhone5,4", // iPhone 5C (Global)
+ @"iPhone6,1", // iPhone 5S (GSM)
+ @"iPhone6,2", // iPhone 5S (Global)
+ @"iPhone7,2", // iPhone 6
+ @"iPhone8,1", // iPhone 6s
+ @"iPhone8,4", // iPhone SE (GSM)
+ @"iPhone9,1", // iPhone 7
+ @"iPhone9,3", // iPhone 7
+ @"iPhone10,1", // iPhone 8
+ @"iPhone10,4", // iPhone 8
+ @"iPhone11,8", // iPhone XR
+ @"iPhone12,1", // iPhone 11
+ @"iPhone12,8", // iPhone SE 2nd gen
+ @"iPhone14,6", // iPhone SE 3rd gen
+ @"iPad4,4", // iPad mini Retina (WiFi)
+ @"iPad4,5", // iPad mini Retina (GSM+CDMA)
+ @"iPad4,6", // iPad mini Retina (China)
+ @"iPad5,1", // iPad mini 4th Gen (WiFi)
+ @"iPad5,2", // iPad mini 4th Gen
+ @"iPad5,3", // iPad Air 2 (WiFi)
+ @"iPad5,4", // iPad Air 2
+ @"iPad11,1", // iPad mini 5th Gen (WiFi)
+ @"iPad11,2", // iPad mini 5th Gen
+ @"iPad14,1", // iPad mini 6th Gen (WiFi)
+ @"iPad14,2", // iPad mini 6th Gen
+ @"iPod4,1", // iPod 4th Gen
+ @"iPod5,1", // iPod 5th Gen
+ @"iPod7,1", // iPod 6th Gen
+ @"iPod9,1", // iPod 7th Gen
] : @326,
@[
- @"iPhone7,1",
- @"iPhone8,2",
- @"iPhone9,2",
- @"iPhone9,4",
- @"iPhone10,2",
- @"iPhone10,5",
+ @"iPhone7,1", // iPhone 6 Plus
+ @"iPhone8,2", // iPhone 6s Plus
+ @"iPhone9,2", // iPhone 7 Plus
+ @"iPhone9,4", // iPhone 7 Plus
+ @"iPhone10,2", // iPhone 8 Plus
+ @"iPhone10,5", // iPhone 8 Plus
] : @401,
@[
- @"iPhone10,3",
- @"iPhone10,6",
- @"iPhone11,2",
- @"iPhone11,4",
- @"iPhone11,6",
- @"iPhone12,3",
- @"iPhone12,5",
+ @"iPhone10,3", // iPhone X Global
+ @"iPhone10,6", // iPhone X GSM
+ @"iPhone11,2", // iPhone XS
+ @"iPhone11,4", // iPhone XS Max
+ @"iPhone11,6", // iPhone XS Max Global
+ @"iPhone12,3", // iPhone 11 Pro
+ @"iPhone12,5", // iPhone 11 Pro Max
+ @"iPhone13,4", // iPhone 12 Pro Max
+ @"iPhone14,3", // iPhone 13 Pro Max
+ @"iPhone14,8", // iPhone 14 Plus
] : @458,
+ @[
+ @"iPhone13,2", // iPhone 12
+ @"iPhone13,3", // iPhone 12 Pro
+ @"iPhone14,2", // iPhone 13 Pro
+ @"iPhone14,5", // iPhone 13
+ @"iPhone14,7", // iPhone 14
+ @"iPhone15,2", // iPhone 14 Pro
+ @"iPhone15,3", // iPhone 14 Pro Max
+ ] : @460,
+ @[
+ @"iPhone13,1", // iPhone 12 Mini
+ @"iPhone14,4", // iPhone 13 Mini
+ ] : @476
};
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index bea88a7f9b..86ea602a98 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -56,16 +56,9 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
tts = [[TTS_IOS alloc] init];
#if defined(GLES3_ENABLED)
- // FIXME: Add support for both OpenGL and Vulkan when OpenGL is implemented
- // again,
- // Note that we should be checking "opengl3" as the driver, might never enable this seeing OpenGL is deprecated on iOS
- // We are hardcoding the rendering_driver to "vulkan" down below
-
if (rendering_driver == "opengl3") {
bool gl_initialization_error = false;
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
if (RasterizerGLES3::is_viable() == OK) {
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
@@ -74,22 +67,10 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
}
if (gl_initialization_error) {
- OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.", "Unable to initialize video driver");
- // return ERR_UNAVAILABLE;
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required OpenGL ES 3.0 version.\n\n",
+ "Unable to initialize OpenGL video driver");
}
-
- // rendering_server = memnew(RenderingServerDefault);
- // // FIXME: Reimplement threaded rendering
- // if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- // rendering_server = memnew(RenderingServerWrapMT(rendering_server,
- // false));
- // }
- // rendering_server->init();
- // rendering_server->cursor_set_visible(false, 0);
-
- // reset this to what it should be, it will have been set to 0 after
- // rendering_server->init() is called
- // RasterizerStorageGLES3system_fbo = gl_view_base_fb;
}
#endif
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 49b4852cbf..87b599bc81 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -32,6 +32,13 @@
#include "core/string/translation.h"
#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "platform/ios/logo_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
// Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
@@ -1565,7 +1572,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
int ret = unzGoToFirstFile(src_pkg_zip);
Vector<uint8_t> project_file_data;
while (ret == UNZ_OK) {
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
bool is_execute = false;
#endif
@@ -1598,7 +1605,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
continue; //ignore!
}
found_library = true;
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
is_execute = true;
#endif
file = file.replace(library_to_use, binary_name + ".xcframework");
@@ -1641,7 +1648,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
f->store_buffer(data.ptr(), data.size());
}
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
if (is_execute) {
// we need execute rights on this file
chmod(file.utf8().get_data(), 0755);
@@ -1799,7 +1806,10 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
CodesignData codesign_data(p_preset, p_debug);
err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
- ERR_FAIL_COND_V(err, err);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Code signing failed, see editor log for details."));
+ return err;
+ }
}
if (ep.step("Making .xcarchive", 3)) {
@@ -1825,6 +1835,10 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true);
ERR_FAIL_COND_V(err, err);
print_line("xcodebuild (.xcarchive):\n" + archive_str);
+ if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details."));
+ return FAILED;
+ }
if (ep.step("Making .ipa", 4)) {
return ERR_SKIP;
@@ -1841,9 +1855,14 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String export_str;
err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
ERR_FAIL_COND_V(err, err);
+
print_line("xcodebuild (.ipa):\n" + export_str);
+ if (!export_str.contains("** EXPORT SUCCEEDED **")) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
+ return FAILED;
+ }
#else
- print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package.");
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Xcode Build"), TTR(".ipa can only be built on macOS. Leaving Xcode project without building the package."));
#endif
return OK;
@@ -1914,7 +1933,15 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
}
EditorExportPlatformIOS::EditorExportPlatformIOS() {
- logo = ImageTexture::create_from_image(memnew(Image(_ios_logo)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+#endif
+
plugins_changed.set();
#ifndef ANDROID_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index deadec4b43..628dae2e6f 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -43,7 +43,6 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "main/splash.gen.h"
-#include "platform/ios/logo.gen.h"
#include "string.h"
#include "godot_plugin_config.h"
diff --git a/platform/ios/logo.png b/platform/ios/logo.png
deleted file mode 100644
index 966d8aa70a..0000000000
--- a/platform/ios/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/logo.svg b/platform/ios/logo.svg
new file mode 100644
index 0000000000..47a72bcf49
--- /dev/null
+++ b/platform/ios/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M1 23.27h2.504V12.61H1zm1.247-12.057c.784 0 1.398-.603 1.398-1.358 0-.764-.614-1.367-1.398-1.367-.774 0-1.388.603-1.388 1.367 0 .755.614 1.358 1.388 1.358zm9.594-2.695c-4.233 0-6.888 2.886-6.888 7.502s2.654 7.492 6.888 7.492c4.224 0 6.88-2.876 6.88-7.492s-2.656-7.502-6.88-7.502zm0 2.212c2.585 0 4.234 2.052 4.234 5.29 0 3.228-1.649 5.28-4.234 5.28-2.594 0-4.233-2.052-4.233-5.28 0-3.238 1.639-5.29 4.233-5.29zm7.936 8.458c.11 2.675 2.303 4.324 5.641 4.324 3.51 0 5.723-1.73 5.723-4.485 0-2.162-1.247-3.379-4.194-4.053l-1.67-.382c-1.78-.422-2.513-.985-2.513-1.95 0-1.208 1.106-2.012 2.745-2.012 1.66 0 2.796.814 2.916 2.172H30.9c-.06-2.554-2.172-4.284-5.37-4.284-3.158 0-5.4 1.74-5.4 4.314 0 2.072 1.267 3.36 3.942 3.973l1.88.442c1.83.433 2.575 1.036 2.575 2.082 0 1.207-1.217 2.072-2.967 2.072-1.77 0-3.107-.875-3.268-2.213h-2.514z" fill="#bfbfbf"/></svg>
diff --git a/platform/linuxbsd/README.md b/platform/linuxbsd/README.md
index efa8682062..c4f287cc6c 100644
--- a/platform/linuxbsd/README.md
+++ b/platform/linuxbsd/README.md
@@ -7,7 +7,7 @@ used by this platform.
## Documentation
-- [Compiling for Linux/*BSD](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_linuxbsd.html)
+- [Compiling for Linux/*BSD](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_linuxbsd.html)
- Instructions on building this platform port from source.
- [Exporting for Linux/*BSD](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_linux.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index add69c436f..8d03e3d31c 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -44,6 +44,7 @@
#include <cxxabi.h>
#include <dlfcn.h>
#include <execinfo.h>
+#include <link.h>
#include <signal.h>
#include <stdlib.h>
@@ -79,7 +80,27 @@ static void handle_crash(int sig) {
}
print_error(vformat("Dumping the backtrace. %s", msg));
char **strings = backtrace_symbols(bt_buffer, size);
+ // PIE executable relocation, zero for non-PIE executables
+ uintptr_t relocation = _r_debug.r_map->l_addr;
if (strings) {
+ List<String> args;
+ for (size_t i = 0; i < size; i++) {
+ char str[1024];
+ snprintf(str, 1024, "%p", (void *)((uintptr_t)bt_buffer[i] - relocation));
+ args.push_back(str);
+ }
+ args.push_back("-e");
+ args.push_back(_execpath);
+
+ // Try to get the file/line number using addr2line
+ int ret;
+ String output = "";
+ Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
+ Vector<String> addr2line_results;
+ if (err == OK) {
+ addr2line_results = output.substr(0, output.length() - 1).split("\n", false);
+ }
+
for (size_t i = 1; i < size; i++) {
char fname[1024];
Dl_info info;
@@ -102,24 +123,7 @@ static void handle_crash(int sig) {
}
}
- List<String> args;
-
- char str[1024];
- snprintf(str, 1024, "%p", bt_buffer[i]);
- args.push_back(str);
- args.push_back("-e");
- args.push_back(_execpath);
-
- String output = "";
-
- // Try to get the file/line number using addr2line
- int ret;
- Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
- if (err == OK) {
- output = output.substr(0, output.length() - 1);
- }
-
- print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, output));
+ print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i] : ""));
}
free(strings);
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 747dcbd76c..f4d36f1a87 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -338,12 +338,17 @@ def configure(env: "Environment"):
env.Prepend(CPPPATH=["#platform/linuxbsd"])
+ env.Append(
+ CPPDEFINES=[
+ "LINUXBSD_ENABLED",
+ "UNIX_ENABLED",
+ ("_FILE_OFFSET_BITS", 64),
+ ]
+ )
+
if env["x11"]:
env.Append(CPPDEFINES=["X11_ENABLED"])
- env.Append(CPPDEFINES=["UNIX_ENABLED"])
- env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
-
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index e6925a2011..2c5a945b6c 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -36,7 +36,6 @@
void register_linuxbsd_exporter() {
Ref<EditorExportPlatformLinuxBSD> platform;
platform.instantiate();
- platform->set_logo(ImageTexture::create_from_image(memnew(Image(_linuxbsd_logo))));
platform->set_name("Linux/X11");
platform->set_os_name("Linux");
platform->set_chmod_flags(0755);
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index f6e59bf465..c900cad007 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -32,6 +32,15 @@
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/linuxbsd/logo_svg.gen.h"
+#include "platform/linuxbsd/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
@@ -49,26 +58,47 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
}
Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
-
- if (err != OK) {
- return err;
- }
+ bool export_as_zip = p_path.ends_with("zip");
- String app_name;
+ String pkg_name;
if (String(GLOBAL_GET("application/config/name")) != "") {
- app_name = String(GLOBAL_GET("application/config/name"));
+ pkg_name = String(GLOBAL_GET("application/config/name"));
} else {
- app_name = "Unnamed";
+ pkg_name = "Unnamed";
+ }
+
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ // Setup temp folder.
+ String path = p_path;
+ String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
+
+ Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
+ if (export_as_zip) {
+ if (tmp_app_dir.is_null()) {
+ return ERR_CANT_CREATE;
+ }
+ if (DirAccess::exists(tmp_dir_path)) {
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ }
+ }
+ tmp_app_dir->make_dir_recursive(tmp_dir_path);
+ path = tmp_dir_path.path_join(p_path.get_file().get_basename());
+ }
+
+ // Export project.
+ Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
+ if (err != OK) {
+ return err;
}
- app_name = OS::get_singleton()->get_safe_dir_name(app_name);
// Save console script.
if (err == OK) {
int con_scr = p_preset->get("debug/export_console_script");
if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
- String scr_path = p_path.get_basename() + ".sh";
- err = _export_debug_script(p_preset, app_name, p_path.get_file(), scr_path);
+ String scr_path = path.get_basename() + ".sh";
+ err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
FileAccess::set_unix_permissions(scr_path, 0755);
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), TTR("Could not create console script."));
@@ -76,6 +106,27 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
}
}
+ // ZIP project.
+ if (export_as_zip) {
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
+ }
+
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
+
+ zipClose(zip, nullptr);
+
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(pkg_name);
+ }
+ }
+
return err;
}
@@ -86,12 +137,51 @@ String EditorExportPlatformLinuxBSD::get_template_file_name(const String &p_targ
List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
List<String> list;
list.push_back(p_preset->get("binary_format/architecture"));
+ list.push_back("zip");
+
return list;
}
void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64,arm32,rv64,ppc64,ppc32"), "x86_64"));
+
+ String run_script = "#!/usr/bin/env bash\n"
+ "export DISPLAY=:0\n"
+ "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
+ "\"{temp_dir}/{exe_name}\" {cmd_args}";
+
+ String cleanup_script = "#!/usr/bin/env bash\n"
+ "kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\n"
+ "rm -rf \"{temp_dir}\"";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
+}
+
+bool EditorExportPlatformLinuxBSD::is_elf(const String &p_path) const {
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
+ uint32_t magic = fb->get_32();
+ return (magic == 0x464c457f);
+}
+
+bool EditorExportPlatformLinuxBSD::is_shebang(const String &p_path) const {
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
+ uint16_t magic = fb->get_16();
+ return (magic == 0x2123);
+}
+
+bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const {
+ return is_elf(p_path) || is_shebang(p_path);
}
Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
@@ -200,3 +290,235 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
}
return OK;
}
+
+Ref<Texture2D> EditorExportPlatformLinuxBSD::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformLinuxBSD::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformLinuxBSD::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformLinuxBSD::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformLinuxBSD::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Linux/BSD system");
+}
+
+String EditorExportPlatformLinuxBSD::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Linux/BSD system");
+}
+
+void EditorExportPlatformLinuxBSD::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("linuxbsd");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_linuxbsd_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.sh")) { \
+ da->remove(basepath + "_start.sh"); \
+ } \
+ if (da->file_exists(basepath + "_clean.sh")) { \
+ da->remove(basepath + "_clean.sh"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", basepath.get_file());
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", basepath.get_file());
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
+}
+
+EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
+ set_logo(ImageTexture::create_from_image(img));
+
+ img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
+}
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index 71cf18ff3e..4f860c3fd0 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -34,19 +34,58 @@
#include "core/io/file_access.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "platform/linuxbsd/logo.gen.h"
#include "scene/resources/texture.h"
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
+ HashMap<String, String> extensions;
+
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
+ bool is_elf(const String &p_path) const;
+ bool is_shebang(const String &p_path) const;
+
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
public:
- void set_extension(const String &p_extension, const String &p_feature_key = "default");
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
+ virtual bool is_executable(const String &p_path) const override;
+
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
+ EditorExportPlatformLinuxBSD();
};
#endif // LINUXBSD_EXPORT_PLUGIN_H
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 672c48addc..b77f989677 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -59,7 +59,7 @@ JoypadLinux::Joypad::~Joypad() {
}
void JoypadLinux::Joypad::reset() {
- dpad = HatMask::CENTER;
+ dpad = 0;
fd = -1;
for (int i = 0; i < MAX_ABS; i++) {
abs_map[i] = -1;
@@ -485,27 +485,33 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (joypad_event.value != 0) {
if (joypad_event.value < 0) {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::LEFT) & ~HatMask::RIGHT);
+ joypad.dpad.set_flag(HatMask::LEFT);
+ joypad.dpad.clear_flag(HatMask::RIGHT);
} else {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::RIGHT) & ~HatMask::LEFT);
+ joypad.dpad.set_flag(HatMask::RIGHT);
+ joypad.dpad.clear_flag(HatMask::LEFT);
}
} else {
- joypad.dpad &= ~(HatMask::LEFT | HatMask::RIGHT);
+ joypad.dpad.clear_flag(HatMask::LEFT);
+ joypad.dpad.clear_flag(HatMask::RIGHT);
}
- input->joy_hat(i, (HatMask)joypad.dpad);
+ input->joy_hat(i, joypad.dpad);
break;
case ABS_HAT0Y:
if (joypad_event.value != 0) {
if (joypad_event.value < 0) {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::UP) & ~HatMask::DOWN);
+ joypad.dpad.set_flag(HatMask::UP);
+ joypad.dpad.clear_flag(HatMask::DOWN);
} else {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::DOWN) & ~HatMask::UP);
+ joypad.dpad.set_flag(HatMask::DOWN);
+ joypad.dpad.clear_flag(HatMask::UP);
}
} else {
- joypad.dpad &= ~(HatMask::UP | HatMask::DOWN);
+ joypad.dpad.clear_flag(HatMask::UP);
+ joypad.dpad.clear_flag(HatMask::DOWN);
}
- input->joy_hat(i, (HatMask)joypad.dpad);
+ input->joy_hat(i, joypad.dpad);
break;
default:
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index 7926756cbe..6661abdb37 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -62,7 +62,7 @@ private:
float curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
- HatMask dpad = HatMask::CENTER;
+ BitField<HatMask> dpad;
int fd = -1;
String devpath;
diff --git a/platform/linuxbsd/logo.png b/platform/linuxbsd/logo.png
deleted file mode 100644
index 078654b757..0000000000
--- a/platform/linuxbsd/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/linuxbsd/logo.svg b/platform/linuxbsd/logo.svg
new file mode 100644
index 0000000000..e5f9f03e0c
--- /dev/null
+++ b/platform/linuxbsd/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32"><path d="M13 31h6s3 0 6-6c2.864-5.727-6-17-6-17h-6S3.775 18.55 7 25c3 6 6 6 6 6z" fill="#fff"/><path d="M15.876 28.636c-.05.322-.116.637-.204.941-.142.496-.35.993-.659 1.416.32.02.649.023.985.007a9.1 9.1 0 0 0 .985-.007c-.309-.423-.516-.92-.659-1.416a7.666 7.666 0 0 1-.203-.94l-.123.003c-.04 0-.081-.003-.122-.004z" fill="#333"/><path d="M21.693 21.916c-.629.01-.934.633-1.497.7-.694.08-1.128-.722-2.11-.123-.98.6-1.826 7.473.45 8.409 2.274.935 6.506-4.545 6.23-5.662-.275-1.116-1.146-.853-1.582-1.399-.436-.545.003-1.41-.995-1.82a1.246 1.246 0 0 0-.496-.105zm-11.461 0a1.315 1.315 0 0 0-.421.105c-.998.41-.56 1.275-.995 1.82-.436.546-1.31.283-1.586 1.4-.275 1.116 3.956 6.596 6.232 5.66 2.275-.935 1.429-7.808.448-8.408-.981-.6-1.415.204-2.11.122-.584-.068-.888-.739-1.568-.7z" fill="#f4bb37"/><path d="M15.998.99c-2.934 0-4.657 1.79-4.982 4.204-.324 2.414.198 2.856-.614 5.328-.813 2.472-4.456 6.71-4.37 10.62.026 1.217.166 2.27.41 3.192.3-.496.743-.846 1.066-.995.253-.117.375-.173.432-.194.008-.062.04-.205.098-.485.08-.386.387-.99.91-1.386-.005-.12-.01-.239-.013-.363-.06-3.033 3.073-6.318 3.65-8.236.577-1.917.326-2.114.421-2.59.096-.477.463-1.032.992-1.475a.23.23 0 0 1 .15-.06c.482-.005.965 1.75 1.898 1.752.933 0 1.419-2.141 1.956-1.692.529.443.896.998.992 1.474.095.477-.156.674.42 2.591.578 1.918 3.708 5.203 3.648 8.236-.003.123-.008.24-.014.36.526.396.834 1.002.914 1.389.058.28.09.423.098.485.057.021.18.08.432.197.323.15.764.499 1.063.995.244-.922.387-1.976.414-3.195.085-3.91-3.562-8.148-4.374-10.62-.813-2.472-.287-2.914-.611-5.328C20.659 2.78 18.933.99 15.998.99z" fill="#333"/></svg>
diff --git a/platform/linuxbsd/run_icon.svg b/platform/linuxbsd/run_icon.svg
new file mode 100644
index 0000000000..56465a0df3
--- /dev/null
+++ b/platform/linuxbsd/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444l-.058.001-.058-.001z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696a.11.11 0 0 1 .07-.028c.228-.003.456.826.897.827.44 0 .67-1.01.923-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888-.001.058-.004.114-.007.17a1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914Z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index ea109a8816..d4f82cc81e 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -458,7 +458,7 @@ Point2i DisplayServerX11::mouse_get_position() const {
return Vector2i();
}
-MouseButton DisplayServerX11::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
@@ -1420,31 +1420,35 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- const WindowData &wd = windows[p_window];
+ windows[p_window].mpath = p_region;
+ _update_window_mouse_passthrough(p_window);
+}
+
+void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+
+ const Vector<Vector2> region_path = windows[p_window].mpath;
int event_base, error_base;
const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base);
if (ext_okay) {
- Region region;
- if (p_region.size() == 0) {
- region = XCreateRegion();
- XRectangle rect;
- rect.x = 0;
- rect.y = 0;
- rect.width = window_get_size_with_decorations(p_window).x;
- rect.height = window_get_size_with_decorations(p_window).y;
- XUnionRectWithRegion(&rect, region, region);
+ if (windows[p_window].mpass) {
+ Region region = XCreateRegion();
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ } else if (region_path.size() == 0) {
+ XShapeCombineMask(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, None, ShapeSet);
} else {
- XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size());
- for (int i = 0; i < p_region.size(); i++) {
- points[i].x = p_region[i].x;
- points[i].y = p_region[i].y;
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * region_path.size());
+ for (int i = 0; i < region_path.size(); i++) {
+ points[i].x = region_path[i].x;
+ points[i].y = region_path[i].y;
}
- region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ Region region = XPolygonRegion(points, region_path.size(), EvenOddRule);
memfree(points);
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
}
- XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
}
}
@@ -2312,6 +2316,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
window_set_size(window_get_size(p_window), p_window);
wd.borderless = p_enabled;
+ _update_window_mouse_passthrough(p_window);
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
@@ -2345,6 +2350,10 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ _update_window_mouse_passthrough(p_window);
+ } break;
case WINDOW_FLAG_POPUP: {
XWindowAttributes xwa;
XSync(x11_display, False);
@@ -2398,6 +2407,9 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -2843,13 +2855,13 @@ void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<Inp
state->set_meta_pressed((p_x11_state & Mod4Mask));
}
-MouseButton DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
- MouseButton mask = mouse_button_to_mask(p_x11_button);
+BitField<MouseButtonMask> DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
+ MouseButtonMask mask = mouse_button_to_mask(p_x11_button);
if (p_x11_type == ButtonPress) {
- last_button_state |= mask;
+ last_button_state.set_flag(mask);
} else {
- last_button_state &= ~mask;
+ last_button_state.clear_flag(mask);
}
return last_button_state;
@@ -4171,13 +4183,13 @@ void DisplayServerX11::process_events() {
if (xi.pressure_supported) {
mm->set_pressure(xi.pressure);
} else {
- mm->set_pressure(bool(mouse_get_button_state() & MouseButton::MASK_LEFT) ? 1.0f : 0.0f);
+ mm->set_pressure(bool(mouse_get_button_state().has_flag(MouseButtonMask::LEFT)) ? 1.0f : 0.0f);
}
mm->set_tilt(xi.tilt);
mm->set_pen_inverted(xi.pen_inverted);
_get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask((MouseButton)mouse_get_button_state());
+ mm->set_button_mask(mouse_get_button_state());
mm->set_position(pos);
mm->set_global_position(pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
@@ -4564,18 +4576,20 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n'./" +
- executable_name + " --rendering-driver opengl3'.\n "
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s --rendering-driver opengl3'\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 2fac446fcf..437766d953 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -149,6 +149,8 @@ class DisplayServerX11 : public DisplayServer {
Callable input_text_callback;
Callable drop_files_callback;
+ Vector<Vector2> mpath;
+
WindowID transient_parent = INVALID_WINDOW_ID;
HashSet<WindowID> transient_children;
@@ -169,6 +171,7 @@ class DisplayServerX11 : public DisplayServer {
bool maximized = false;
bool is_popup = false;
bool layered_window = false;
+ bool mpass = false;
Rect2i parent_safe_rect;
@@ -205,7 +208,7 @@ class DisplayServerX11 : public DisplayServer {
Point2i last_click_pos = Point2i(-100, -100);
uint64_t last_click_ms = 0;
MouseButton last_click_button_index = MouseButton::NONE;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool app_focused = false;
uint64_t time_since_no_focus = 0;
@@ -234,7 +237,7 @@ class DisplayServerX11 : public DisplayServer {
Rect2i _screen_get_rect(int p_screen) const;
- MouseButton _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
+ BitField<MouseButtonMask> _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
void _flush_mouse_motion();
@@ -245,6 +248,7 @@ class DisplayServerX11 : public DisplayServer {
Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const;
void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
+ void _update_window_mouse_passthrough(WindowID p_window);
String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
String _clipboard_get(Atom p_source, Window x11_window) const;
@@ -344,7 +348,7 @@ public:
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/macos/README.md b/platform/macos/README.md
index feead80736..205c59e05d 100644
--- a/platform/macos/README.md
+++ b/platform/macos/README.md
@@ -11,7 +11,7 @@ packaging macOS export templates.
## Documentation
-- [Compiling for macOS](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_macos.html)
+- [Compiling for macOS](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_macos.html)
- Instructions on building this platform port from source.
- [Exporting for macOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_macos.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 2927c67890..4873db4b64 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -114,6 +114,7 @@ public:
bool resize_disabled = false;
bool no_focus = false;
bool is_popup = false;
+ bool mpass = false;
Rect2i parent_safe_rect;
};
@@ -154,7 +155,7 @@ private:
CGEventSourceRef event_source;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool drop_events = false;
bool in_dispatch_input_event = false;
@@ -317,8 +318,8 @@ public:
bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- void mouse_set_button_state(MouseButton p_state);
- virtual MouseButton mouse_get_button_state() const override;
+ void mouse_set_button_state(BitField<MouseButtonMask> p_state);
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 3bbb3e35fb..d992467042 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -166,6 +166,17 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
layer.contentsScale = scale;
}
+ NSColor *bg_color = [NSColor windowBackgroundColor];
+ Color _bg_color;
+ if (_get_window_early_clear_override(_bg_color)) {
+ bg_color = [NSColor colorWithCalibratedRed:_bg_color.r green:_bg_color.g blue:_bg_color.b alpha:1.f];
+ }
+
+ [wd.window_object setBackgroundColor:bg_color];
+ if (layer) {
+ [layer setBackgroundColor:bg_color.CGColor];
+ }
+
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
@@ -273,12 +284,17 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl
#endif
wd.layered_window = true;
} else {
- [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ NSColor *bg_color = [NSColor windowBackgroundColor];
+ Color _bg_color;
+ if (_get_window_early_clear_override(_bg_color)) {
+ bg_color = [NSColor colorWithCalibratedRed:_bg_color.r green:_bg_color.g blue:_bg_color.b alpha:1.f];
+ }
+ [wd.window_object setBackgroundColor:bg_color];
[wd.window_object setOpaque:YES];
[wd.window_object setHasShadow:YES];
CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
- [layer setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1].CGColor];
+ [layer setBackgroundColor:bg_color.CGColor];
[layer setOpaque:YES];
}
#if defined(GLES3_ENABLED)
@@ -2034,11 +2050,11 @@ Point2i DisplayServerMacOS::mouse_get_position() const {
return Vector2i();
}
-void DisplayServerMacOS::mouse_set_button_state(MouseButton p_state) {
+void DisplayServerMacOS::mouse_set_button_state(BitField<MouseButtonMask> p_state) {
last_button_state = p_state;
}
-MouseButton DisplayServerMacOS::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerMacOS::mouse_get_button_state() const {
return last_button_state;
}
@@ -2958,6 +2974,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ } break;
case WINDOW_FLAG_POPUP: {
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
@@ -2997,6 +3016,9 @@ bool DisplayServerMacOS::window_get_flag(WindowFlags p_flag, WindowID p_window)
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -3471,7 +3493,11 @@ void DisplayServerMacOS::process_events() {
for (KeyValue<WindowID, WindowData> &E : windows) {
WindowData &wd = E.value;
- if (wd.mpath.size() > 0) {
+ if (wd.mpass) {
+ if (![wd.window_object ignoresMouseEvents]) {
+ [wd.window_object setIgnoresMouseEvents:YES];
+ }
+ } else if (wd.mpath.size() > 0) {
update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
if (Geometry2D::is_point_in_polygon(wd.mouse_pos, wd.mpath)) {
if ([wd.window_object ignoresMouseEvents]) {
@@ -3581,18 +3607,18 @@ DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver,
} else {
executable_command = vformat("open %s --args --rendering-driver opengl3", OS::get_singleton()->get_bundle_resource_dir().path_join("../..").simplify_path());
}
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command: '" +
- executable_command + "'.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your macOS version or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s'",
+ executable_command),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your macOS version.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 46485da783..73fbd45400 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -38,8 +38,14 @@
#include "core/string/translation.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/macos/logo_svg.gen.h"
+#include "platform/macos/run_icon_svg.gen.h"
-#include "modules/modules_enabled.gen.h" // For regex.
+#include "modules/modules_enabled.gen.h" // For svg and regex.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
if (p_preset->get("texture_format/s3tc")) {
@@ -207,6 +213,23 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
+
+ String run_script = "#!/usr/bin/env bash\n"
+ "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
+ "open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}";
+
+ String cleanup_script = "#!/usr/bin/env bash\n"
+ "kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\")\n"
+ "rm -rf \"{temp_dir}\"";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
}
void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
@@ -993,7 +1016,7 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str
return OK;
}
-bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
+bool EditorExportPlatformMacOS::is_shebang(const String &p_path) const {
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
uint16_t magic = fb->get_16();
@@ -1001,7 +1024,7 @@ bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
}
bool EditorExportPlatformMacOS::is_executable(const String &p_path) const {
- return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path);
+ return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shebang(p_path);
}
Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
@@ -1082,7 +1105,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
} else {
pkg_name = "Unnamed";
}
-
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
String export_format;
@@ -1684,7 +1706,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
- _zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
+ zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
zipClose(zip, nullptr);
}
@@ -1723,119 +1745,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
return err;
}
-void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
- String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);
-
- Ref<DirAccess> da = DirAccess::open(dir);
- da->list_dir_begin();
- String f = da->get_next();
- while (!f.is_empty()) {
- if (f == "." || f == "..") {
- f = da->get_next();
- continue;
- }
- if (da->is_link(f)) {
- OS::DateTime dt = OS::get_singleton()->get_datetime();
-
- zip_fileinfo zipfi;
- zipfi.tmz_date.tm_year = dt.year;
- zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_mday = dt.day;
- zipfi.tmz_date.tm_hour = dt.hour;
- zipfi.tmz_date.tm_min = dt.minute;
- zipfi.tmz_date.tm_sec = dt.second;
- zipfi.dosDate = 0;
- // 0120000: symbolic link type
- // 0000755: permissions rwxr-xr-x
- // 0000644: permissions rw-r--r--
- uint32_t _mode = 0120644;
- zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
- zipfi.internal_fa = 0;
-
- zipOpenNewFileInZip4(p_zip,
- p_folder.path_join(f).utf8().get_data(),
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION,
- 0,
- -MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY,
- nullptr,
- 0,
- 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
- 0);
-
- String target = da->read_link(f);
- zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
- zipCloseFileInZip(p_zip);
- } else if (da->current_is_dir()) {
- _zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
- } else {
- OS::DateTime dt = OS::get_singleton()->get_datetime();
-
- zip_fileinfo zipfi;
- zipfi.tmz_date.tm_year = dt.year;
- zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_mday = dt.day;
- zipfi.tmz_date.tm_hour = dt.hour;
- zipfi.tmz_date.tm_min = dt.minute;
- zipfi.tmz_date.tm_sec = dt.second;
- zipfi.dosDate = 0;
- // 0100000: regular file type
- // 0000755: permissions rwxr-xr-x
- // 0000644: permissions rw-r--r--
- uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644);
- zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
- zipfi.internal_fa = 0;
-
- zipOpenNewFileInZip4(p_zip,
- p_folder.path_join(f).utf8().get_data(),
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION,
- 0,
- -MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY,
- nullptr,
- 0,
- 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
- 0);
-
- Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
- if (fa.is_null()) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
- return;
- }
- const int bufsize = 16384;
- uint8_t buf[bufsize];
-
- while (true) {
- uint64_t got = fa->get_buffer(buf, bufsize);
- if (got == 0) {
- break;
- }
- zipWriteInFileInZip(p_zip, buf, got);
- }
-
- zipCloseFileInZip(p_zip);
- }
- f = da->get_next();
- }
- da->list_dir_end();
-}
-
bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -1937,6 +1846,10 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
}
}
}
+ if (notary_tool == 2 && p_preset->get("notarization/apple_team_id") == "") {
+ err += TTR("Notarization: Apple Team ID not specified.") + "\n";
+ valid = false;
+ }
} else if (notary_tool == 1) {
if (p_preset->get("notarization/api_uuid") == "") {
err += TTR("Notarization: App Store Connect issuer ID name not specified.") + "\n";
@@ -2008,9 +1921,242 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
return valid;
}
-EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
- logo = ImageTexture::create_from_image(memnew(Image(_macos_logo)));
+Ref<Texture2D> EditorExportPlatformMacOS::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformMacOS::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformMacOS::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformMacOS::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformMacOS::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote macOS system");
+}
+
+String EditorExportPlatformMacOS::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote macOS system");
+}
+
+void EditorExportPlatformMacOS::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("macos");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String pkg_name;
+ if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_macos_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.sh")) { \
+ da->remove(basepath + "_start.sh"); \
+ } \
+ if (da->file_exists(basepath + "_clean.sh")) { \
+ da->remove(basepath + "_clean.sh"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", pkg_name);
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", pkg_name);
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
}
-EditorExportPlatformMacOS::~EditorExportPlatformMacOS() {
+EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
}
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index 7de6ddf304..c10192949c 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -36,12 +36,10 @@
#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
-#include "core/io/zip_io.h"
#include "core/os/os.h"
#include "core/version.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
-#include "platform/macos/logo.gen.h"
#include <sys/stat.h>
@@ -52,6 +50,30 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
Ref<ImageTexture> logo;
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
@@ -65,14 +87,17 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
- void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
bool use_codesign() const { return true; }
#ifdef MACOS_ENABLED
- bool use_dmg() const { return true; }
+ bool use_dmg() const {
+ return true;
+ }
#else
- bool use_dmg() const { return false; }
+ bool use_dmg() const {
+ return false;
+ }
#endif
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
@@ -97,7 +122,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
return true;
}
- bool is_shbang(const String &p_path) const;
+ bool is_shebang(const String &p_path) const;
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@@ -105,9 +130,15 @@ protected:
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
public:
- virtual String get_name() const override { return "macOS"; }
- virtual String get_os_name() const override { return "macOS"; }
- virtual Ref<Texture2D> get_logo() const override { return logo; }
+ virtual String get_name() const override {
+ return "macOS";
+ }
+ virtual String get_os_name() const override {
+ return "macOS";
+ }
+ virtual Ref<Texture2D> get_logo() const override {
+ return logo;
+ }
virtual bool is_executable(const String &p_path) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
@@ -133,8 +164,16 @@ public:
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
}
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
EditorExportPlatformMacOS();
- ~EditorExportPlatformMacOS();
};
#endif // MACOS_EXPORT_PLUGIN_H
diff --git a/platform/macos/godot_content_view.h b/platform/macos/godot_content_view.h
index c0fde4d765..ba7f15c32b 100644
--- a/platform/macos/godot_content_view.h
+++ b/platform/macos/godot_content_view.h
@@ -66,7 +66,7 @@
- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor;
- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy;
-- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed;
+- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(bool)pressed;
- (void)setWindowID:(DisplayServer::WindowID)wid;
- (void)updateLayerDelegate;
- (void)cancelComposition;
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 50befc997d..337aa59a5e 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -371,19 +371,21 @@
ds->cursor_update_shape();
}
-- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed {
+- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(bool)pressed {
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
- MouseButton last_button_state = ds->mouse_get_button_state();
+ BitField<MouseButtonMask> last_button_state = ds->mouse_get_button_state();
+
+ MouseButtonMask mask = mouse_button_to_mask(index);
if (pressed) {
- last_button_state |= mask;
+ last_button_state.set_flag(mask);
} else {
- last_button_state &= (MouseButton)~mask;
+ last_button_state.clear_flag(mask);
}
ds->mouse_set_button_state(last_button_state);
@@ -407,10 +409,10 @@
- (void)mouseDown:(NSEvent *)event {
if (([event modifierFlags] & NSEventModifierFlagControl)) {
mouse_down_control = true;
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:true];
} else {
mouse_down_control = false;
- [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:true];
+ [self processMouseEvent:event index:MouseButton::LEFT pressed:true];
}
}
@@ -420,9 +422,9 @@
- (void)mouseUp:(NSEvent *)event {
if (mouse_down_control) {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:false];
} else {
- [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:false];
+ [self processMouseEvent:event index:MouseButton::LEFT pressed:false];
}
}
@@ -469,7 +471,7 @@
}
- (void)rightMouseDown:(NSEvent *)event {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:true];
}
- (void)rightMouseDragged:(NSEvent *)event {
@@ -477,16 +479,16 @@
}
- (void)rightMouseUp:(NSEvent *)event {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:false];
}
- (void)otherMouseDown:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:true];
+ [self processMouseEvent:event index:MouseButton::MIDDLE pressed:true];
} else if ((int)[event buttonNumber] == 3) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:true];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:true];
} else if ((int)[event buttonNumber] == 4) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:true];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:true];
} else {
return;
}
@@ -498,11 +500,11 @@
- (void)otherMouseUp:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:false];
+ [self processMouseEvent:event index:MouseButton::MIDDLE pressed:false];
} else if ((int)[event buttonNumber] == 3) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:false];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:false];
} else if ((int)[event buttonNumber] == 4) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:false];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:false];
} else {
return;
}
@@ -751,7 +753,8 @@
}
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
- MouseButton mask = mouse_button_to_mask(button);
+ MouseButtonMask mask = mouse_button_to_mask(button);
+ BitField<MouseButtonMask> last_button_state = ds->mouse_get_button_state();
Ref<InputEventMouseButton> sc;
sc.instantiate();
@@ -763,7 +766,7 @@
sc->set_pressed(true);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- MouseButton last_button_state = ds->mouse_get_button_state() | (MouseButton)mask;
+ last_button_state.set_flag(mask);
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
@@ -776,7 +779,7 @@
sc->set_pressed(false);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- last_button_state &= (MouseButton)~mask;
+ last_button_state.clear_flag(mask);
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
diff --git a/platform/macos/joypad_macos.cpp b/platform/macos/joypad_macos.cpp
index 4ea18916fe..b7b8e0604c 100644
--- a/platform/macos/joypad_macos.cpp
+++ b/platform/macos/joypad_macos.cpp
@@ -402,10 +402,10 @@ bool joypad::check_ff_features() {
return false;
}
-static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
+static BitField<HatMask> process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- HatMask hat_value = HatMask::CENTER;
+ BitField<HatMask> hat_value;
if (range == 4) {
value *= 2;
}
@@ -415,31 +415,34 @@ static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offse
switch (value) {
case 0:
- hat_value = HatMask::UP;
+ hat_value.set_flag(HatMask::UP);
break;
case 1:
- hat_value = (HatMask::UP | HatMask::RIGHT);
+ hat_value.set_flag(HatMask::UP);
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 2:
- hat_value = HatMask::RIGHT;
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 3:
- hat_value = (HatMask::DOWN | HatMask::RIGHT);
+ hat_value.set_flag(HatMask::DOWN);
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 4:
- hat_value = HatMask::DOWN;
+ hat_value.set_flag(HatMask::DOWN);
break;
case 5:
- hat_value = (HatMask::DOWN | HatMask::LEFT);
+ hat_value.set_flag(HatMask::DOWN);
+ hat_value.set_flag(HatMask::LEFT);
break;
case 6:
- hat_value = HatMask::LEFT;
+ hat_value.set_flag(HatMask::LEFT);
break;
case 7:
- hat_value = (HatMask::UP | HatMask::LEFT);
+ hat_value.set_flag(HatMask::UP);
+ hat_value.set_flag(HatMask::LEFT);
break;
default:
- hat_value = HatMask::CENTER;
break;
}
return hat_value;
@@ -474,7 +477,7 @@ void JoypadMacOS::process_joypads() {
for (int j = 0; j < joy.hat_elements.size(); j++) {
rec_element &elem = joy.hat_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- HatMask hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
+ BitField<HatMask> hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
input->joy_hat(joy.id, hat_value);
}
diff --git a/platform/macos/logo.png b/platform/macos/logo.png
deleted file mode 100644
index b5a660b165..0000000000
--- a/platform/macos/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/macos/logo.svg b/platform/macos/logo.svg
new file mode 100644
index 0000000000..759583d76b
--- /dev/null
+++ b/platform/macos/logo.svg
@@ -0,0 +1 @@
+<svg height="32" viewBox="0 0 25.6 25.6" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M.948 19.474V6.126c0-2.767 2.42-5.178 5.178-5.178h6.904s-2.992 7.506-2.992 13.233c0 .346.337.69.69.69h2.877c0 6.648.906 8.436 1.266 9.781H6.126c-2.75 0-5.178-2.407-5.178-5.178z" fill="#1c9ef8"/><path d="M7.162 8.312v1.496" fill="none" stroke="#323232" stroke-linecap="round" stroke-width="1.036"/><path d="M10.729 14.871c-.352 0-.69-.344-.69-.69C10.038 8.414 13.03.948 13.03.948h6.444c2.77 0 5.178 2.416 5.178 5.178v13.348c0 2.754-2.407 5.178-5.178 5.178h-4.603c-.357-1.333-1.266-2.887-1.266-9.78z" fill="#e1e6ed"/><g fill="none" stroke="#323232" stroke-linecap="round"><path d="M17.575 8.255V9.75" stroke-width="1.036"/><path d="M5.55 16.943c1.037 1.794 3.907 2.876 6.79 2.876 2.877 0 5.745-1.068 6.789-2.876" stroke-width=".69"/></g></svg>
diff --git a/platform/macos/run_icon.svg b/platform/macos/run_icon.svg
new file mode 100644
index 0000000000..c7067bb4b6
--- /dev/null
+++ b/platform/macos/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-width:.93168;-inkscape-stroke:none" d="M4.418 1.055c-1.82 0-3.365 1.537-3.365 3.363v7.164c0 1.829 1.548 3.363 3.365 3.363h7.164c1.828 0 3.363-1.544 3.363-3.363V4.418c0-1.822-1.537-3.363-3.363-3.363H7.729Zm3.875 1.164h3.291c1.149 0 2.2 1.053 2.2 2.199v7.164c0 1.14-1.052 2.2-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a3.947 3.947 0 0 1-.049.004l-.008-.002a7.345 7.345 0 0 1-1.205-.004 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.558.558 0 0 1-.059-.424.564.564 0 0 1 .881-.299.56.56 0 0 1 .145.164c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM4.973 5.03h.002a.562.562 0 0 1 .558.559v.805a.556.556 0 0 1-.558.558.56.56 0 0 1-.397-.162.565.565 0 0 1-.164-.396V5.59a.561.561 0 0 1 .559-.559Z"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-linecap:round;-inkscape-stroke:none" d="M26.117 11.467c.008.11.014.225.022.328.022.283.052.565.088.846l.012.053c1.238-.252 2.448-.829 3.011-1.803a.6.6 0 1 0-1.039-.6c-.28.486-1.161.936-2.094 1.176z" transform="translate(-15.37 .357) scale(.93168)"/><g style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-opacity:1"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 5.585v.862" transform="translate(-15.37 .357) scale(.93168)"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 4.984a.6.6 0 0 0-.6.6v.863a.6.6 0 0 0 .6.6.6.6 0 0 0 .6-.6v-.863a.6.6 0 0 0-.6-.6Z" transform="translate(-15.37 .357) scale(.93168)"/></g></svg>
diff --git a/platform/uwp/README.md b/platform/uwp/README.md
index 575f90e3c7..d69a8a8850 100644
--- a/platform/uwp/README.md
+++ b/platform/uwp/README.md
@@ -14,7 +14,7 @@ project template used for packaging the UWP export templates.
## Documentation
-- [Compiling for Universal Windows Platform](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_uwp.html)
+- [Compiling for Universal Windows Platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_uwp.html)
- Instructions on building this platform port from source.
- [Exporting for Universal Windows Platform](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_uwp.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 6eb74fd90a..f810cb0ca9 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -30,8 +30,14 @@
#include "export_plugin.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-#include "platform/uwp/logo.gen.h"
+#include "platform/uwp/logo_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg and regex.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
String EditorExportPlatformUWP::get_name() const {
return "UWP";
@@ -504,5 +510,13 @@ void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<Edit
}
EditorExportPlatformUWP::EditorExportPlatformUWP() {
- logo = ImageTexture::create_from_image(memnew(Image(_uwp_logo)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false);
+
+ logo = ImageTexture::create_from_image(img);
+#endif
}
diff --git a/platform/uwp/logo.png b/platform/uwp/logo.png
deleted file mode 100644
index 9017a30636..0000000000
--- a/platform/uwp/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/uwp/logo.svg b/platform/uwp/logo.svg
new file mode 100644
index 0000000000..5bcbdcfcd4
--- /dev/null
+++ b/platform/uwp/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(8.981 1.816)"><circle style="fill:#2f75bb;fill-opacity:1;stroke:none;stroke-width:.815427" cx="7.019" cy="14.184" r="13.825"/><path d="m-1.192 8.234 6.73-.927v6.503h-6.73Zm0 11.899 6.73.927v-6.422h-6.73Zm7.47 1.026 8.952 1.236v-7.757H6.278Zm0-13.951v6.602h8.952V5.973Z" fill="#00abed" style="fill:#fff;fill-opacity:1"/></g></svg>
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 40c19056b3..38df68c764 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -680,7 +680,7 @@ bool OS_UWP::set_environment(const String &p_var, const String &p_value) const {
return false;
}
-String OS_UWP::get_stdin_string(bool p_block) {
+String OS_UWP::get_stdin_string() {
return String();
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 02a81f1a47..153656add7 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -162,7 +162,7 @@ public:
HANDLE mouse_mode_changed;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- String get_stdin_string(bool p_block);
+ String get_stdin_string();
void set_mouse_mode(MouseMode p_mode);
MouseMode get_mouse_mode() const;
diff --git a/platform/web/README.md b/platform/web/README.md
index 1265ca09df..906eb508fe 100644
--- a/platform/web/README.md
+++ b/platform/web/README.md
@@ -10,7 +10,7 @@ this platform such as the html shell (web page).
## Documentation
-- [Compiling for the Web](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_web.html)
+- [Compiling for the Web](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_web.html)
- Instructions on building this platform port from source.
- [Exporting for the Web](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_web.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp
index eb3a199ae1..a630e3d866 100644
--- a/platform/web/api/api.cpp
+++ b/platform/web/api/api.cpp
@@ -73,6 +73,7 @@ void JavaScriptBridge::_bind_methods() {
ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScriptBridge::download_buffer, DEFVAL("application/octet-stream"));
ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScriptBridge::pwa_needs_update);
ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScriptBridge::pwa_update);
+ ClassDB::bind_method(D_METHOD("force_fs_sync"), &JavaScriptBridge::force_fs_sync);
ADD_SIGNAL(MethodInfo("pwa_update_available"));
}
@@ -111,6 +112,8 @@ bool JavaScriptBridge::pwa_needs_update() const {
Error JavaScriptBridge::pwa_update() {
return ERR_UNAVAILABLE;
}
+void JavaScriptBridge::force_fs_sync() {
+}
void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
}
#endif
diff --git a/platform/web/api/javascript_bridge_singleton.h b/platform/web/api/javascript_bridge_singleton.h
index bcf1ed653a..456fa6b313 100644
--- a/platform/web/api/javascript_bridge_singleton.h
+++ b/platform/web/api/javascript_bridge_singleton.h
@@ -61,6 +61,7 @@ public:
void download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime = "application/octet-stream");
bool pwa_needs_update() const;
Error pwa_update();
+ void force_fs_sync();
static JavaScriptBridge *get_singleton();
JavaScriptBridge();
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 2c69c0873d..fdb9d107a7 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -193,12 +193,12 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double
}
}
- MouseButton mask = Input::get_singleton()->get_mouse_button_mask();
- MouseButton button_flag = mouse_button_to_mask(ev->get_button_index());
+ BitField<MouseButtonMask> mask = Input::get_singleton()->get_mouse_button_mask();
+ MouseButtonMask button_flag = mouse_button_to_mask(ev->get_button_index());
if (ev->is_pressed()) {
- mask |= button_flag;
- } else if ((mask & button_flag) != MouseButton::NONE) {
- mask &= ~button_flag;
+ mask.set_flag(button_flag);
+ } else if (mask.has_flag(button_flag)) {
+ mask.clear_flag(button_flag);
} else {
// Received release event, but press was outside the canvas, so ignore.
return false;
@@ -218,10 +218,10 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double
}
void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
- MouseButton input_mask = Input::get_singleton()->get_mouse_button_mask();
+ BitField<MouseButtonMask> input_mask = Input::get_singleton()->get_mouse_button_mask();
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behavior.
- if (!get_singleton()->cursor_inside_canvas && input_mask == MouseButton::NONE) {
+ if (!get_singleton()->cursor_inside_canvas && input_mask.is_empty()) {
return;
}
@@ -525,15 +525,17 @@ int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
- MouseButton button_flag = mouse_button_to_mask(ev->get_button_index());
+ MouseButtonMask button_flag = mouse_button_to_mask(ev->get_button_index());
+ BitField<MouseButtonMask> button_mask = input->get_mouse_button_mask();
+ button_mask.set_flag(button_flag);
ev->set_pressed(true);
- ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
+ ev->set_button_mask(button_mask);
input->parse_input_event(ev);
Ref<InputEventMouseButton> release = ev->duplicate();
release->set_pressed(false);
- release->set_button_mask(MouseButton(input->get_mouse_button_mask() & ~button_flag));
+ release->set_button_mask(input->get_mouse_button_mask());
input->parse_input_event(release);
return true;
@@ -785,8 +787,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
RasterizerGLES3::make_current();
} else {
- OS::get_singleton()->alert("Your browser does not seem to support WebGL2. Please update your browser version.",
- "Unable to initialize video driver");
+ OS::get_singleton()->alert(
+ "Your browser seems not to support WebGL 2.\n\n"
+ "If possible, consider updating your browser version and video card drivers.",
+ "Unable to initialize WebGL 2 video driver");
RasterizerDummy::make_current();
}
#else
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 3e11db6887..d8e04904c7 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -31,7 +31,15 @@
#include "export_plugin.h"
#include "core/config/project_settings.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "platform/web/logo_svg.gen.h"
+#include "platform/web/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformWeb::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
Ref<FileAccess> io_fa;
@@ -153,7 +161,7 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
const String custom_head_include = p_preset->get("html/head_include");
HashMap<String, String> replaces;
replaces["$GODOT_URL"] = p_name + ".js";
- replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
replaces["$GODOT_CONFIG"] = str_config;
_replace_strings(replaces, p_html);
@@ -193,7 +201,7 @@ Error EditorExportPlatformWeb::_add_manifest_icon(const String &p_path, const St
}
Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
- String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ String proj_name = GLOBAL_GET("application/config/name");
if (proj_name.is_empty()) {
proj_name = "Godot Game";
}
@@ -651,8 +659,17 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() {
server.instantiate();
server_thread.start(_server_thread_poll, this);
- logo = ImageTexture::create_from_image(memnew(Image(_web_logo)));
- run_icon = ImageTexture::create_from_image(memnew(Image(_web_run_icon)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
if (theme.is_valid()) {
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index b85492b66f..e74c945837 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -38,11 +38,8 @@
#include "core/io/zip_io.h"
#include "editor/editor_node.h"
#include "editor/export/editor_export_platform.h"
-#include "main/splash.gen.h"
-#include "platform/web/logo.gen.h"
-#include "platform/web/run_icon.gen.h"
-
#include "editor_http_server.h"
+#include "main/splash.gen.h"
class EditorExportPlatformWeb : public EditorExportPlatform {
GDCLASS(EditorExportPlatformWeb, EditorExportPlatform);
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index 308ca2d5de..dba630404f 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -361,6 +361,11 @@ void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_na
bool JavaScriptBridge::pwa_needs_update() const {
return OS_Web::get_singleton()->pwa_needs_update();
}
+
Error JavaScriptBridge::pwa_update() {
return OS_Web::get_singleton()->pwa_update();
}
+
+void JavaScriptBridge::force_fs_sync() {
+ OS_Web::get_singleton()->force_fs_sync();
+}
diff --git a/platform/web/logo.png b/platform/web/logo.png
deleted file mode 100644
index c046d87dc4..0000000000
--- a/platform/web/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/web/logo.svg b/platform/web/logo.svg
new file mode 100644
index 0000000000..567b6f3c77
--- /dev/null
+++ b/platform/web/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M7 5h18v21H7z" fill="#fff"/><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1zM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145z" fill="#eb6428"/></svg>
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index cb304ce7ac..e12f62f4ad 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -196,6 +196,12 @@ void OS_Web::update_pwa_state_callback() {
}
}
+void OS_Web::force_fs_sync() {
+ if (is_userfs_persistent()) {
+ idb_needs_sync = true;
+ }
+}
+
Error OS_Web::pwa_update() {
return godot_js_pwa_update() ? FAILED : OK;
}
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index c8fdea2ee0..70d8af9db9 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -69,6 +69,7 @@ public:
bool pwa_needs_update() const { return pwa_is_waiting; }
Error pwa_update();
+ void force_fs_sync();
void initialize_joypads() override;
diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json
index e1428546c6..4399e8243c 100644
--- a/platform/web/package-lock.json
+++ b/platform/web/package-lock.json
@@ -1529,9 +1529,9 @@
"dev": true
},
"node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -3662,9 +3662,9 @@
"dev": true
},
"json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
diff --git a/platform/web/run_icon.png b/platform/web/run_icon.png
deleted file mode 100644
index 574abb0150..0000000000
--- a/platform/web/run_icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/web/run_icon.svg b/platform/web/run_icon.svg
new file mode 100644
index 0000000000..494f53cb90
--- /dev/null
+++ b/platform/web/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1ZM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145Z" fill="#eb6428" style="fill:#e0e0e0;fill-opacity:1" transform="translate(.586 .586) scale(.46337)"/></svg>
diff --git a/platform/windows/README.md b/platform/windows/README.md
index c04032ae1d..4c775576fe 100644
--- a/platform/windows/README.md
+++ b/platform/windows/README.md
@@ -7,7 +7,7 @@ used by this platform.
## Documentation
-- [Compiling for Windows](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_windows.html)
+- [Compiling for Windows](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html)
- Instructions on building this platform port from source.
- [Exporting for Windows](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_windows.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 114bfcb6f8..fa81c81b20 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -257,7 +257,7 @@ Point2i DisplayServerWindows::mouse_get_position() const {
return Point2i(p.x, p.y) - _get_screens_origin();
}
-MouseButton DisplayServerWindows::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {
return last_button_state;
}
@@ -348,11 +348,9 @@ typedef struct {
static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumScreenData *data = (EnumScreenData *)dwData;
- if (data->monitor == hMonitor) {
- if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {
- data->screen = data->count;
- return FALSE;
- }
+ if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {
+ data->screen = data->count;
+ return FALSE;
}
data->count++;
@@ -737,6 +735,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
wd.no_focus = true;
}
+ if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {
+ wd.mpass = true;
+ }
if (p_flags & WINDOW_FLAG_POPUP_BIT) {
wd.is_popup = true;
}
@@ -920,7 +921,7 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p
void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
- if (windows[p_window].mpath.size() == 0) {
+ if (windows[p_window].mpass || windows[p_window].mpath.size() == 0) {
SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE);
} else {
POINT *points = (POINT *)memalloc(sizeof(POINT) * windows[p_window].mpath.size());
@@ -1501,6 +1502,10 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
wd.no_focus = p_enabled;
_update_window_style(p_window);
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ _update_window_mouse_passthrough(p_window);
+ } break;
case WINDOW_FLAG_POPUP: {
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
@@ -1532,6 +1537,9 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -2466,6 +2474,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Process window messages.
switch (uMsg) {
+ case WM_NCHITTEST: {
+ if (windows[window_id].mpass) {
+ return HTTRANSPARENT;
+ }
+ } break;
case WM_MOUSEACTIVATE: {
if (windows[window_id].no_focus) {
return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages.
@@ -2545,6 +2558,25 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return 0;
}
} break;
+ case WM_ERASEBKGND: {
+ Color early_color;
+ if (!_get_window_early_clear_override(early_color)) {
+ break;
+ }
+ bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();
+ if (must_recreate_brush) {
+ if (window_bkg_brush) {
+ DeleteObject(window_bkg_brush);
+ }
+ window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));
+ }
+ HDC hdc = (HDC)wParam;
+ RECT rect = {};
+ if (GetUpdateRect(hWnd, &rect, true)) {
+ FillRect(hdc, &rect, window_bkg_brush);
+ }
+ return 1;
+ } break;
case WM_PAINT: {
Main::force_redraw();
} break;
@@ -3164,9 +3196,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mb->set_alt_pressed(alt_mem);
// mb->is_alt_pressed()=(wParam&MK_MENU)!=0;
if (mb->is_pressed()) {
- last_button_state |= mouse_button_to_mask(mb->get_button_index());
+ last_button_state.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- last_button_state &= ~mouse_button_to_mask(mb->get_button_index());
+ last_button_state.clear_flag(mouse_button_to_mask(mb->get_button_index()));
}
mb->set_button_mask(last_button_state);
@@ -3207,7 +3239,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Send release for mouse wheel.
Ref<InputEventMouseButton> mbd = mb->duplicate();
mbd->set_window_id(window_id);
- last_button_state &= ~mouse_button_to_mask(mbd->get_button_index());
+ last_button_state.clear_flag(mouse_button_to_mask(mbd->get_button_index()));
mbd->set_button_mask(last_button_state);
mbd->set_pressed(false);
Input::get_singleton()->parse_input_event(mbd);
@@ -3639,7 +3671,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
DWORD dwExStyle;
DWORD dwStyle;
- _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
+ _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
RECT WindowRect;
@@ -3984,7 +4016,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
memset(&wc, 0, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+ wc.style = CS_OWNDC | CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
@@ -4113,18 +4145,20 @@ DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_drive
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n'./" +
- executable_name + " --rendering-driver opengl3'.\n "
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s --rendering-driver opengl3'\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index feed9f35ed..ce4b94af59 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -370,6 +370,7 @@ class DisplayServerWindows : public DisplayServer {
bool window_has_focus = false;
bool exclusive = false;
bool context_created = false;
+ bool mpass = false;
// Used to transfer data between events using timer.
WPARAM saved_wparam;
@@ -446,12 +447,14 @@ class DisplayServerWindows : public DisplayServer {
bool shift_mem = false;
bool control_mem = false;
bool meta_mem = false;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool use_raw_input = false;
bool drop_events = false;
bool in_dispatch_input_event = false;
WNDCLASSEXW wc;
+ HBRUSH window_bkg_brush = nullptr;
+ uint32_t window_bkg_brush_color = 0;
HCURSOR cursors[CURSOR_MAX] = { nullptr };
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
@@ -506,7 +509,7 @@ public:
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index b0e2f5a05b..4112bb84b5 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -51,7 +51,6 @@ void register_windows_exporter() {
Ref<EditorExportPlatformWindows> platform;
platform.instantiate();
- platform->set_logo(ImageTexture::create_from_image(memnew(Image(_windows_logo))));
platform->set_name("Windows Desktop");
platform->set_os_name("Windows");
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 7c61a79fc8..bf32b16018 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -34,6 +34,14 @@
#include "core/io/image_loader.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/windows/logo_svg.gen.h"
+#include "platform/windows/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) {
static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ };
@@ -168,9 +176,39 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset>
}
Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- String pck_path = p_path;
- if (p_preset->get("binary_format/embed_pck")) {
- pck_path = p_path.get_basename() + ".tmp";
+ bool export_as_zip = p_path.ends_with("zip");
+ bool embedded = p_preset->get("binary_format/embed_pck");
+
+ String pkg_name;
+ if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ // Setup temp folder.
+ String path = p_path;
+ String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
+ Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
+ if (export_as_zip) {
+ if (tmp_app_dir.is_null()) {
+ return ERR_CANT_CREATE;
+ }
+ if (DirAccess::exists(tmp_dir_path)) {
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ }
+ }
+ tmp_app_dir->make_dir_recursive(tmp_dir_path);
+ path = tmp_dir_path.path_join(p_path.get_file().get_basename() + ".exe");
+ }
+
+ // Export project.
+ String pck_path = path;
+ if (embedded) {
+ pck_path = pck_path.get_basename() + ".tmp";
}
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
if (p_preset->get("codesign/enable") && err == OK) {
@@ -181,7 +219,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
}
}
- if (p_preset->get("binary_format/embed_pck") && err == OK) {
+ if (embedded && err == OK) {
Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->rename(pck_path, p_path);
if (err != OK) {
@@ -189,6 +227,27 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
}
}
+ // ZIP project.
+ if (export_as_zip) {
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
+ }
+
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
+
+ zipClose(zip, nullptr);
+
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(pkg_name);
+ }
+ }
+
return err;
}
@@ -199,6 +258,7 @@ String EditorExportPlatformWindows::get_template_file_name(const String &p_targe
List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
List<String> list;
list.push_back("exe");
+ list.push_back("zip");
return list;
}
@@ -212,6 +272,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
+
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));
@@ -235,6 +296,29 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
+
+ String run_script = "Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'\n"
+ "$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'\n"
+ "$trigger = New-ScheduledTaskTrigger -Once -At 00:00\n"
+ "$settings = New-ScheduledTaskSettingsSet\n"
+ "$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings\n"
+ "Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true\n"
+ "Start-ScheduledTask -TaskName godot_remote_debug\n"
+ "while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }\n"
+ "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue";
+
+ String cleanup_script = "Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue\n"
+ "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue\n"
+ "Remove-Item -Recurse -Force '{temp_dir}'";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
}
Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon) {
@@ -659,3 +743,227 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6
}
return OK;
}
+
+Ref<Texture2D> EditorExportPlatformWindows::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformWindows::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformWindows::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformWindows::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformWindows::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Windows system");
+}
+
+String EditorExportPlatformWindows::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Windows system");
+}
+
+void EditorExportPlatformWindows::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("windows");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_windows_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.ps1")) { \
+ da->remove(basepath + "_start.ps1"); \
+ } \
+ if (da->file_exists(basepath + "_clean.ps1")) { \
+ da->remove(basepath + "_clean.ps1"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "powershell -command \\\"\\$tmp = Join-Path \\$Env:Temp \\$(New-Guid); New-Item -Type Directory -Path \\$tmp | Out-Null; Write-Output \\$tmp\\\"", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", basepath.get_file() + ".exe");
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.ps1", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", basepath.get_file() + ".exe");
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.ps1", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.ps1", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.ps1", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_start.ps1"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_clean.ps1")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
+}
+
+EditorExportPlatformWindows::EditorExportPlatformWindows() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false);
+ set_logo(ImageTexture::create_from_image(img));
+
+ img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
+}
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 96608728d3..fa75a17a1f 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -35,9 +35,32 @@
#include "core/os/os.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "platform/windows/logo.gen.h"
class EditorExportPlatformWindows : public EditorExportPlatformPC {
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
@@ -53,6 +76,17 @@ public:
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
+
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
+ EditorExportPlatformWindows();
};
#endif // WINDOWS_EXPORT_PLUGIN_H
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index aed5f5b8dc..183958cf40 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -420,38 +420,43 @@ void JoypadWindows::process_joypads() {
}
void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
- HatMask dpad_val = (HatMask)0;
+ BitField<HatMask> dpad_val;
// Should be -1 when centered, but according to docs:
// "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
- dpad_val = (HatMask)HatMask::CENTER;
+ // Do nothing.
+ // dpad_val.set_flag(HatMask::CENTER);
}
if (p_dpad == 0) {
- dpad_val = (HatMask)HatMask::UP;
+ dpad_val.set_flag(HatMask::UP);
} else if (p_dpad == 4500) {
- dpad_val = (HatMask)(HatMask::UP | HatMask::RIGHT);
+ dpad_val.set_flag(HatMask::UP);
+ dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 9000) {
- dpad_val = (HatMask)HatMask::RIGHT;
+ dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 13500) {
- dpad_val = (HatMask)(HatMask::RIGHT | HatMask::DOWN);
+ dpad_val.set_flag(HatMask::RIGHT);
+ dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 18000) {
- dpad_val = (HatMask)HatMask::DOWN;
+ dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 22500) {
- dpad_val = (HatMask)(HatMask::DOWN | HatMask::LEFT);
+ dpad_val.set_flag(HatMask::DOWN);
+ dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 27000) {
- dpad_val = (HatMask)HatMask::LEFT;
+ dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 31500) {
- dpad_val = (HatMask)(HatMask::LEFT | HatMask::UP);
+ dpad_val.set_flag(HatMask::LEFT);
+ dpad_val.set_flag(HatMask::UP);
}
input->joy_hat(p_device, dpad_val);
}
diff --git a/platform/windows/logo.png b/platform/windows/logo.png
deleted file mode 100644
index f06b463850..0000000000
--- a/platform/windows/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/windows/logo.svg b/platform/windows/logo.svg
new file mode 100644
index 0000000000..77a0b20766
--- /dev/null
+++ b/platform/windows/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m1 5.132 12.295-1.694v11.879H1zm0 21.736 12.295 1.695V16.83H1zm13.647 1.875L31 31V16.83H14.647zm0-25.486v12.06H31V1z" fill="#00abed"/></svg>
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 2a44a19085..130c5f7b97 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -718,15 +718,23 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
}
Error OS_Windows::kill(const ProcessID &p_pid) {
- ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
+ int ret = 0;
+ if (process_map->has(p_pid)) {
+ const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
+ process_map->erase(p_pid);
- const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
- process_map->erase(p_pid);
+ ret = TerminateProcess(pi.hProcess, 0);
- const int ret = TerminateProcess(pi.hProcess, 0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ } else {
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, (DWORD)p_pid);
+ if (hProcess != NULL) {
+ ret = TerminateProcess(hProcess, 0);
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
+ CloseHandle(hProcess);
+ }
+ }
return ret != 0 ? OK : FAILED;
}
@@ -1158,17 +1166,24 @@ String OS_Windows::get_environment(const String &p_var) const {
return "";
}
-bool OS_Windows::set_environment(const String &p_var, const String &p_value) const {
- return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data()));
+void OS_Windows::set_environment(const String &p_var, const String &p_value) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ Char16String var = p_var.utf16();
+ Char16String value = p_value.utf16();
+ ERR_FAIL_COND_MSG(var.length() + value.length() + 2 > 32767, vformat("Invalid definition for environment variable '%s', cannot exceed 32767 characters.", p_var));
+ SetEnvironmentVariableW((LPCWSTR)(var.get_data()), (LPCWSTR)(value.get_data()));
}
-String OS_Windows::get_stdin_string(bool p_block) {
- if (p_block) {
- WCHAR buff[1024];
- DWORD count = 0;
- if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
- return String::utf16((const char16_t *)buff, count);
- }
+void OS_Windows::unset_environment(const String &p_var) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), nullptr); // Null to delete.
+}
+
+String OS_Windows::get_stdin_string() {
+ WCHAR buff[1024];
+ DWORD count = 0;
+ if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
+ return String::utf16((const char16_t *)buff, count);
}
return String();
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 9cb3977030..05110c2614 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -140,7 +140,7 @@ protected:
virtual void finalize() override;
virtual void finalize_core() override;
- virtual String get_stdin_string(bool p_block) override;
+ virtual String get_stdin_string() override;
String _quote_command_line_argument(const String &p_text) const;
@@ -186,7 +186,8 @@ public:
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
- virtual bool set_environment(const String &p_var, const String &p_value) const override;
+ virtual void set_environment(const String &p_var, const String &p_value) const override;
+ virtual void unset_environment(const String &p_var) const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
diff --git a/platform/windows/run_icon.svg b/platform/windows/run_icon.svg
new file mode 100644
index 0000000000..0897276ef7
--- /dev/null
+++ b/platform/windows/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z" fill="#00abed" style="stroke-width:.460341;fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 39be51879d..afd4763d74 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1430,8 +1430,8 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index f69b3728f7..4fc375ff8d 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -49,14 +49,10 @@ void MeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MeshInstance2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &MeshInstance2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
-
ADD_SIGNAL(MethodInfo("texture_changed"));
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
}
void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) {
@@ -77,15 +73,6 @@ void MeshInstance2D::set_texture(const Ref<Texture2D> &p_texture) {
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
-void MeshInstance2D::set_normal_map(const Ref<Texture2D> &p_texture) {
- normal_map = p_texture;
- queue_redraw();
-}
-
-Ref<Texture2D> MeshInstance2D::get_normal_map() const {
- return normal_map;
-}
-
Ref<Texture2D> MeshInstance2D::get_texture() const {
return texture;
}
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index 795b3758e3..c914f13ade 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -39,7 +39,6 @@ class MeshInstance2D : public Node2D {
Ref<Mesh> mesh;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
protected:
void _notification(int p_what);
@@ -57,9 +56,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_normal_map() const;
-
MeshInstance2D();
};
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
index ac7c350751..f347eb6520 100644
--- a/scene/2d/multimesh_instance_2d.cpp
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -50,14 +50,10 @@ void MultiMeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MultiMeshInstance2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &MultiMeshInstance2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MultiMeshInstance2D::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &MultiMeshInstance2D::get_normal_map);
-
ADD_SIGNAL(MethodInfo("texture_changed"));
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
}
void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
@@ -91,15 +87,6 @@ Ref<Texture2D> MultiMeshInstance2D::get_texture() const {
return texture;
}
-void MultiMeshInstance2D::set_normal_map(const Ref<Texture2D> &p_texture) {
- normal_map = p_texture;
- queue_redraw();
-}
-
-Ref<Texture2D> MultiMeshInstance2D::get_normal_map() const {
- return normal_map;
-}
-
#ifdef TOOLS_ENABLED
Rect2 MultiMeshInstance2D::_edit_get_rect() const {
if (multimesh.is_valid()) {
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
index 64ecae6d6c..0647412294 100644
--- a/scene/2d/multimesh_instance_2d.h
+++ b/scene/2d/multimesh_instance_2d.h
@@ -40,7 +40,6 @@ class MultiMeshInstance2D : public Node2D {
Ref<MultiMesh> multimesh;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
protected:
void _notification(int p_what);
@@ -57,9 +56,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_normal_map() const;
-
MultiMeshInstance2D();
~MultiMeshInstance2D();
};
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 4f6ba093cb..6b842e6e6b 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -94,9 +94,9 @@ void NavigationAgent2D::_bind_methods() {
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_location", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_location", "get_target_location");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:px"), "set_path_desired_distance", "get_path_desired_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1,suffix:px"), "set_path_max_distance", "get_path_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_path_desired_distance", "get_path_desired_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,1000,1,suffix:px"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
@@ -105,8 +105,8 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01,suffix:px"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_distance", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px"), "set_neighbor_distance", "get_neighbor_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:s"), "set_time_horizon", "get_time_horizon");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10,0.01,suffix:s"), "set_time_horizon", "get_time_horizon");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -182,11 +182,11 @@ void NavigationAgent2D::_notification(int p_what) {
NavigationAgent2D::NavigationAgent2D() {
agent = NavigationServer2D::get_singleton()->agent_create();
- set_neighbor_distance(500.0);
- set_max_neighbors(10);
- set_time_horizon(20.0);
- set_radius(10.0);
- set_max_speed(200.0);
+ set_neighbor_distance(neighbor_distance);
+ set_max_neighbors(max_neighbors);
+ set_time_horizon(time_horizon);
+ set_radius(radius);
+ set_max_speed(max_speed);
// Preallocate query and result objects to improve performance.
navigation_query = Ref<NavigationPathQueryParameters2D>();
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 113b1e9623..190a2fcbda 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -50,15 +50,15 @@ class NavigationAgent2D : public Node {
uint32_t navigation_layers = 1;
BitField<NavigationPathQueryParameters2D::PathMetadataFlags> path_metadata_flags = NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_ALL;
- real_t path_desired_distance = 1.0;
- real_t target_desired_distance = 1.0;
- real_t radius = 0.0;
- real_t neighbor_distance = 0.0;
- int max_neighbors = 0;
- real_t time_horizon = 0.0;
- real_t max_speed = 0.0;
-
- real_t path_max_distance = 3.0;
+ real_t path_desired_distance = 20.0;
+ real_t target_desired_distance = 10.0;
+ real_t radius = 10.0;
+ real_t neighbor_distance = 500.0;
+ int max_neighbors = 10;
+ real_t time_horizon = 1.0;
+ real_t max_speed = 100.0;
+
+ real_t path_max_distance = 100.0;
Vector2 target_location;
bool target_position_submitted = false;
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index a205e704ba..fe6af8dad2 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -48,10 +48,10 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
if (!enabled) {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
- NavigationServer2D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
} else {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
#ifdef DEBUG_ENABLED
@@ -150,7 +150,7 @@ void NavigationRegion2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled) {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
} break;
@@ -161,7 +161,7 @@ void NavigationRegion2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
if (enabled) {
- NavigationServer2D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer2D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
}
} break;
@@ -337,8 +337,8 @@ NavigationRegion2D::NavigationRegion2D() {
NavigationServer2D::get_singleton()->region_set_travel_cost(region, get_travel_cost());
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
- NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ NavigationServer3D::get_singleton()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
#endif // DEBUG_ENABLED
}
@@ -347,7 +347,7 @@ NavigationRegion2D::~NavigationRegion2D() {
NavigationServer2D::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
- NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion2D::_map_changed));
+ 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
}
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 09f4406ffe..5036dd30b1 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -31,6 +31,7 @@
#include "path_2d.h"
#include "core/math/geometry_2d.h"
+#include "scene/main/timer.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
@@ -171,6 +172,12 @@ void Path2D::_curve_changed() {
}
queue_redraw();
+ for (int i = 0; i < get_child_count(); i++) {
+ PathFollow2D *follow = Object::cast_to<PathFollow2D>(get_child(i));
+ if (follow) {
+ follow->path_changed();
+ }
+ }
}
void Path2D::set_curve(const Ref<Curve2D> &p_curve) {
@@ -200,6 +207,14 @@ void Path2D::_bind_methods() {
/////////////////////////////////////////////////////////////////////////////////
+void PathFollow2D::path_changed() {
+ if (update_timer && !update_timer->is_stopped()) {
+ update_timer->start();
+ } else {
+ _update_transform();
+ }
+}
+
void PathFollow2D::_update_transform() {
if (!path) {
return;
@@ -230,6 +245,16 @@ void PathFollow2D::_update_transform() {
void PathFollow2D::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_READY: {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ update_timer = memnew(Timer);
+ update_timer->set_wait_time(0.2);
+ update_timer->set_one_shot(true);
+ update_timer->connect("timeout", callable_mp(this, &PathFollow2D::_update_transform));
+ add_child(update_timer, false, Node::INTERNAL_MODE_BACK);
+ }
+ } break;
+
case NOTIFICATION_ENTER_TREE: {
path = Object::cast_to<Path2D>(get_parent());
if (path) {
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 884743dd2a..89c77c49eb 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -34,6 +34,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/curve.h"
+class Timer;
+
class Path2D : public Node2D {
GDCLASS(Path2D, Node2D);
@@ -65,6 +67,7 @@ public:
private:
Path2D *path = nullptr;
real_t progress = 0.0;
+ Timer *update_timer = nullptr;
real_t h_offset = 0.0;
real_t v_offset = 0.0;
real_t lookahead = 4.0;
@@ -81,6 +84,8 @@ protected:
static void _bind_methods();
public:
+ void path_changed();
+
void set_progress(real_t p_progress);
real_t get_progress() const;
diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp
index 9a3fe73be1..237eb3d987 100644
--- a/scene/2d/visible_on_screen_notifier_2d.cpp
+++ b/scene/2d/visible_on_screen_notifier_2d.cpp
@@ -137,7 +137,7 @@ void VisibleOnScreenEnabler2D::set_enable_node_path(NodePath p_path) {
return;
}
enable_node_path = p_path;
- if (is_inside_tree()) {
+ if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
node_id = ObjectID();
Node *node = get_node(enable_node_path);
if (node) {
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index a44e66ce2a..fe7f6837f0 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -61,11 +61,7 @@ void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const {
}
bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) {
- if (p_path == SNAME("override_pose")) {
- set_override_pose(p_value);
- } else if (p_path == SNAME("override_mode")) {
- set_override_mode(p_value);
- } else if (p_path == SNAME("use_external_skeleton")) {
+ if (p_path == SNAME("use_external_skeleton")) {
set_use_external_skeleton(p_value);
} else if (p_path == SNAME("external_skeleton")) {
set_external_skeleton(p_value);
@@ -75,11 +71,7 @@ bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) {
}
bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const {
- if (p_path == SNAME("override_pose")) {
- r_ret = get_override_pose();
- } else if (p_path == SNAME("override_mode")) {
- r_ret = get_override_mode();
- } else if (p_path == SNAME("use_external_skeleton")) {
+ if (p_path == SNAME("use_external_skeleton")) {
r_ret = get_use_external_skeleton();
} else if (p_path == SNAME("external_skeleton")) {
r_ret = get_external_skeleton();
@@ -208,14 +200,10 @@ void BoneAttachment3D::_transform_changed() {
Transform3D our_trans = get_transform();
if (use_external_skeleton) {
- our_trans = sk->world_transform_to_global_pose(get_global_transform());
+ our_trans = sk->get_global_transform().affine_inverse() * get_global_transform();
}
- if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
- sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true);
- } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
- sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true);
- }
+ sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true);
}
}
@@ -267,11 +255,7 @@ void BoneAttachment3D::set_override_pose(bool p_override) {
if (!override_pose) {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
- if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
- sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
- } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
- sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
- }
+ sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
}
_transform_changed();
}
@@ -282,27 +266,6 @@ bool BoneAttachment3D::get_override_pose() const {
return override_pose;
}
-void BoneAttachment3D::set_override_mode(int p_mode) {
- if (override_pose) {
- Skeleton3D *sk = _get_skeleton3d();
- if (sk) {
- if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) {
- sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
- } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
- sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
- }
- }
- override_mode = p_mode;
- _transform_changed();
- return;
- }
- override_mode = p_mode;
-}
-
-int BoneAttachment3D::get_override_mode() const {
- return override_mode;
-}
-
void BoneAttachment3D::set_use_external_skeleton(bool p_use_external) {
use_external_skeleton = p_use_external;
@@ -361,7 +324,7 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
if (sk) {
if (!override_pose) {
if (use_external_skeleton) {
- set_global_transform(sk->global_pose_to_world_transform(sk->get_bone_global_pose(bone_idx)));
+ set_global_transform(sk->get_global_transform() * sk->get_bone_global_pose(bone_idx));
} else {
set_transform(sk->get_bone_global_pose(bone_idx));
}
@@ -407,8 +370,6 @@ void BoneAttachment3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose);
ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose);
- ClassDB::bind_method(D_METHOD("set_override_mode", "override_mode"), &BoneAttachment3D::set_override_mode);
- ClassDB::bind_method(D_METHOD("get_override_mode"), &BoneAttachment3D::get_override_mode);
ClassDB::bind_method(D_METHOD("set_use_external_skeleton", "use_external_skeleton"), &BoneAttachment3D::set_use_external_skeleton);
ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton);
@@ -420,4 +381,5 @@ void BoneAttachment3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_pose"), "set_override_pose", "get_override_pose");
}
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 2065271bbe..327cbaa0ab 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -44,14 +44,8 @@ class BoneAttachment3D : public Node3D {
int bone_idx = -1;
bool override_pose = false;
- int override_mode = 0;
bool _override_dirty = false;
- enum OVERRIDE_MODES {
- MODE_GLOBAL_POSE,
- MODE_LOCAL_POSE,
- };
-
bool use_external_skeleton = false;
NodePath external_skeleton_node;
ObjectID external_skeleton_node_cache;
@@ -86,8 +80,6 @@ public:
void set_override_pose(bool p_override);
bool get_override_pose() const;
- void set_override_mode(int p_mode);
- int get_override_mode() const;
void set_use_external_skeleton(bool p_external_skeleton);
bool get_use_external_skeleton() const;
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index f24ed805c3..b9a161e476 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -1616,8 +1616,8 @@ void CPUParticles3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_less"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET);
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index a6d63619df..6940bad4a6 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -763,7 +763,15 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
MeshesFound &mf = meshes_found.write[m_i];
- Size2i lightmap_size = mf.mesh->get_lightmap_size_hint() * mf.lightmap_scale;
+ Size2i lightmap_size = mf.mesh->get_lightmap_size_hint();
+
+ if (lightmap_size == Size2i(0, 0)) {
+ // TODO we should compute a size if no lightmap hint is set, as we did in 3.x.
+ // For now set to basic size to avoid crash.
+ lightmap_size = Size2i(64, 64);
+ }
+
+ lightmap_size *= mf.lightmap_scale;
TypedArray<RID> overrides;
overrides.resize(mf.overrides.size());
for (int i = 0; i < mf.overrides.size(); i++) {
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index b64cf11c05..e058ef62d0 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -133,8 +133,8 @@ void NavigationLink3D::_update_debug_mesh() {
RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario());
RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree());
- Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material();
- Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material();
+ Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton()->get_debug_navigation_link_connections_material();
+ Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton()->get_debug_navigation_link_connections_disabled_material();
if (enabled) {
RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid());
@@ -262,10 +262,10 @@ void NavigationLink3D::set_enabled(bool p_enabled) {
#ifdef DEBUG_ENABLED
if (debug_instance.is_valid() && debug_mesh.is_valid()) {
if (enabled) {
- Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material();
+ Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton()->get_debug_navigation_link_connections_material();
RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid());
} else {
- Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material();
+ Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton()->get_debug_navigation_link_connections_disabled_material();
RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid());
}
}
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index d5b79690dd..22a6ec3517 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -55,10 +55,10 @@ void NavigationRegion3D::set_enabled(bool p_enabled) {
if (!is_enabled()) {
if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
- RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
}
if (debug_mesh->get_surface_count() > 1) {
- RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
}
}
} else {
@@ -365,9 +365,9 @@ NavigationRegion3D::NavigationRegion3D() {
NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost());
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
- NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
- NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
+ NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
+ NavigationServer3D::get_singleton()->connect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
#endif // DEBUG_ENABLED
}
@@ -379,9 +379,9 @@ NavigationRegion3D::~NavigationRegion3D() {
NavigationServer3D::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
- NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
- NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
- NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
+ NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
+ NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_instance.is_valid()) {
@@ -459,8 +459,8 @@ void NavigationRegion3D::_update_debug_mesh() {
Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
- Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material();
- Ref<StandardMaterial3D> line_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
+ Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_material();
+ Ref<StandardMaterial3D> line_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_material();
RandomPCG rand;
Color polygon_color = debug_navigation_geometry_face_color;
@@ -518,10 +518,10 @@ void NavigationRegion3D::_update_debug_mesh() {
if (!is_enabled()) {
if (debug_mesh.is_valid()) {
if (debug_mesh->get_surface_count() > 0) {
- RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
}
if (debug_mesh->get_surface_count() > 1) {
- RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
+ RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
}
}
} else {
@@ -610,7 +610,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
return;
}
- Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_edge_connections_material();
+ Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connections_material();
Array mesh_array;
mesh_array.resize(Mesh::ARRAY_MAX);
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index 358da2b6d0..87361d6b38 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -30,8 +30,9 @@
#include "shape_cast_3d.h"
-#include "collision_object_3d.h"
-#include "mesh_instance_3d.h"
+#include "core/core_string_names.h"
+#include "scene/3d/collision_object_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/concave_polygon_shape_3d.h"
void ShapeCast3D::_notification(int p_what) {
@@ -318,25 +319,35 @@ void ShapeCast3D::resource_changed(Ref<Resource> p_res) {
update_gizmos();
}
+void ShapeCast3D::_shape_changed() {
+ update_gizmos();
+ bool is_editor = Engine::get_singleton()->is_editor_hint();
+ if (is_inside_tree() && (is_editor || get_tree()->is_debugging_collisions_hint())) {
+ _update_debug_shape();
+ }
+}
+
void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) {
if (p_shape == shape) {
return;
}
if (!shape.is_null()) {
+ shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
shape->unregister_owner(this);
}
shape = p_shape;
if (!shape.is_null()) {
shape->register_owner(this);
+ shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
}
if (p_shape.is_valid()) {
shape_rid = shape->get_rid();
}
- if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
+ bool is_editor = Engine::get_singleton()->is_editor_hint();
+ if (is_inside_tree() && (is_editor || get_tree()->is_debugging_collisions_hint())) {
_update_debug_shape();
}
-
update_gizmos();
update_configuration_warnings();
}
diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h
index 483364472f..344f1d3b8a 100644
--- a/scene/3d/shape_cast_3d.h
+++ b/scene/3d/shape_cast_3d.h
@@ -77,6 +77,7 @@ class ShapeCast3D : public Node3D {
protected:
void _notification(int p_what);
void _update_shapecast_state();
+ void _shape_changed();
static void _bind_methods();
public:
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 4eadaa603f..1b46879079 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -33,7 +33,6 @@
#include "core/object/message_queue.h"
#include "core/variant/type_info.h"
#include "scene/3d/physics_body_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
@@ -70,13 +69,6 @@ SkinReference::~SkinReference() {
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
-#ifndef _3D_DISABLED
- if (path.begins_with("modification_stack")) {
- set_modification_stack(p_value);
- return true;
- }
-#endif //_3D_DISABLED
-
if (!path.begins_with("bones/")) {
return false;
}
@@ -113,13 +105,6 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
String path = p_path;
-#ifndef _3D_DISABLED
- if (path.begins_with("modification_stack")) {
- r_ret = modification_stack;
- return true;
- }
-#endif //_3D_DISABLED
-
if (!path.begins_with("bones/")) {
return false;
}
@@ -162,14 +147,6 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
-#ifndef _3D_DISABLED
- p_list->push_back(
- PropertyInfo(Variant::OBJECT, "modification_stack",
- PROPERTY_HINT_RESOURCE_TYPE,
- "SkeletonModificationStack3D",
- PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
-#endif //_3D_DISABLED
-
for (PropertyInfo &E : *p_list) {
_validate_property(E);
}
@@ -330,24 +307,10 @@ void Skeleton3D::_notification(int p_what) {
}
}
}
-
- if (modification_stack.is_valid()) {
- execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process);
- }
- } break;
-
- case NOTIFICATION_INTERNAL_PROCESS: {
- if (modification_stack.is_valid()) {
- execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process);
- }
} break;
-
case NOTIFICATION_READY: {
- set_physics_process_internal(true);
- set_process_internal(true);
-
- if (modification_stack.is_valid()) {
- set_modification_stack(modification_stack);
+ if (Engine::get_singleton()->is_editor_hint()) {
+ set_physics_process_internal(true);
}
} break;
#endif // _3D_DISABLED
@@ -395,99 +358,6 @@ Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
return bones[p_bone].pose_global_no_override;
}
-void Skeleton3D::clear_bones_local_pose_override() {
- for (int i = 0; i < bones.size(); i += 1) {
- bones.write[i].local_pose_override_amount = 0;
- }
- _make_dirty();
-}
-
-void Skeleton3D::set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].local_pose_override_amount = p_amount;
- bones.write[p_bone].local_pose_override = p_pose;
- bones.write[p_bone].local_pose_override_reset = !p_persistent;
- _make_dirty();
-}
-
-Transform3D Skeleton3D::get_bone_local_pose_override(int p_bone) const {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].local_pose_override;
-}
-
-void Skeleton3D::update_bone_rest_forward_vector(int p_bone, bool p_force_update) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
-
- if (bones[p_bone].rest_bone_forward_vector.length_squared() > 0 && p_force_update == false) {
- update_bone_rest_forward_axis(p_bone, p_force_update);
- }
-
- // If it is a child/leaf bone...
- if (get_bone_parent(p_bone) > 0) {
- bones.write[p_bone].rest_bone_forward_vector = bones[p_bone].rest.origin.normalized();
- } else {
- // If it has children...
- Vector<int> child_bones = get_bone_children(p_bone);
- if (child_bones.size() > 0) {
- Vector3 combined_child_dir = Vector3(0, 0, 0);
- for (int i = 0; i < child_bones.size(); i++) {
- combined_child_dir += bones[child_bones[i]].rest.origin.normalized();
- }
- combined_child_dir = combined_child_dir / child_bones.size();
- bones.write[p_bone].rest_bone_forward_vector = combined_child_dir.normalized();
- } else {
- WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone));
- WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone");
- bones.write[p_bone].rest_bone_forward_vector = Vector3(0, 1, 0);
- }
- }
- update_bone_rest_forward_axis(p_bone, p_force_update);
-}
-
-void Skeleton3D::update_bone_rest_forward_axis(int p_bone, bool p_force_update) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
- if (bones[p_bone].rest_bone_forward_axis > -1 && p_force_update == false) {
- return;
- }
-
- Vector3 forward_axis_absolute = bones[p_bone].rest_bone_forward_vector.abs();
- if (forward_axis_absolute.x > forward_axis_absolute.y && forward_axis_absolute.x > forward_axis_absolute.z) {
- if (bones[p_bone].rest_bone_forward_vector.x > 0) {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_X_FORWARD;
- } else {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_X_FORWARD;
- }
- } else if (forward_axis_absolute.y > forward_axis_absolute.x && forward_axis_absolute.y > forward_axis_absolute.z) {
- if (bones[p_bone].rest_bone_forward_vector.y > 0) {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Y_FORWARD;
- } else {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Y_FORWARD;
- }
- } else {
- if (bones[p_bone].rest_bone_forward_vector.z > 0) {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Z_FORWARD;
- } else {
- bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Z_FORWARD;
- }
- }
-}
-
-Vector3 Skeleton3D::get_bone_axis_forward_vector(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3(0, 0, 0));
- return bones[p_bone].rest_bone_forward_vector;
-}
-
-int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, -1);
- return bones[p_bone].rest_bone_forward_axis;
-}
-
void Skeleton3D::set_motion_scale(float p_motion_scale) {
if (p_motion_scale <= 0) {
motion_scale = 1;
@@ -503,6 +373,10 @@ float Skeleton3D::get_motion_scale() const {
// Skeleton creation api
+uint64_t Skeleton3D::get_version() const {
+ return version;
+}
+
void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":") || p_name.contains("/"));
@@ -546,6 +420,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
}
bones.write[p_bone].name = p_name;
+ version++;
}
bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
@@ -1068,23 +943,10 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest;
}
- if (b.local_pose_override_amount >= CMP_EPSILON) {
- Transform3D override_local_pose;
- if (b.parent >= 0) {
- override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override;
- } else {
- override_local_pose = b.local_pose_override;
- }
- b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount);
- }
-
if (b.global_pose_override_amount >= CMP_EPSILON) {
b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
}
- if (b.local_pose_override_reset) {
- b.local_pose_override_amount = 0.0;
- }
if (b.global_pose_override_reset) {
b.global_pose_override_amount = 0.0;
}
@@ -1100,100 +962,6 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
rest_dirty = false;
}
-// Helper functions
-
-Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) {
- return get_global_transform() * p_global_pose;
-}
-
-Transform3D Skeleton3D::world_transform_to_global_pose(Transform3D p_world_transform) {
- return get_global_transform().affine_inverse() * p_world_transform;
-}
-
-Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
- if (bones[p_bone_idx].parent >= 0) {
- int parent_bone_idx = bones[p_bone_idx].parent;
- Transform3D conversion_transform = get_bone_global_pose(parent_bone_idx).affine_inverse();
- return conversion_transform * p_global_pose;
- } else {
- return p_global_pose;
- }
-}
-
-Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
- if (bones[p_bone_idx].parent >= 0) {
- int parent_bone_idx = bones[p_bone_idx].parent;
- return bones[parent_bone_idx].pose_global * p_local_pose;
- } else {
- return p_local_pose;
- }
-}
-
-Basis Skeleton3D::global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Basis());
- Basis return_basis = p_basis;
-
- if (bones[p_bone_idx].rest_bone_forward_axis < 0) {
- update_bone_rest_forward_vector(p_bone_idx, true);
- }
-
- if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_X_FORWARD) {
- return_basis.rotate_local(Vector3(0, 1, 0), (Math_PI / 2.0));
- } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_X_FORWARD) {
- return_basis.rotate_local(Vector3(0, 1, 0), -(Math_PI / 2.0));
- } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Y_FORWARD) {
- return_basis.rotate_local(Vector3(1, 0, 0), -(Math_PI / 2.0));
- } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Y_FORWARD) {
- return_basis.rotate_local(Vector3(1, 0, 0), (Math_PI / 2.0));
- } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Z_FORWARD) {
- // Do nothing!
- } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Z_FORWARD) {
- return_basis.rotate_local(Vector3(0, 0, 1), Math_PI);
- }
-
- return return_basis;
-}
-
-// Modifications
-
-#ifndef _3D_DISABLED
-
-void Skeleton3D::set_modification_stack(Ref<SkeletonModificationStack3D> p_stack) {
- if (modification_stack.is_valid()) {
- modification_stack->is_setup = false;
- modification_stack->set_skeleton(nullptr);
- }
-
- modification_stack = p_stack;
- if (modification_stack.is_valid()) {
- modification_stack->set_skeleton(this);
- modification_stack->setup();
- }
-}
-Ref<SkeletonModificationStack3D> Skeleton3D::get_modification_stack() {
- return modification_stack;
-}
-
-void Skeleton3D::execute_modifications(real_t p_delta, int p_execution_mode) {
- if (!modification_stack.is_valid()) {
- return;
- }
-
- // Needed to avoid the issue where the stack looses reference to the skeleton when the scene is saved.
- if (modification_stack->skeleton != this) {
- modification_stack->set_skeleton(this);
- }
-
- modification_stack->execute(p_delta, p_execution_mode);
-}
-
-#endif // _3D_DISABLED
-
void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone);
@@ -1204,6 +972,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton3D::set_bone_parent);
ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton3D::get_bone_count);
+ ClassDB::bind_method(D_METHOD("get_version"), &Skeleton3D::get_version);
ClassDB::bind_method(D_METHOD("unparent_bone_and_rest", "bone_idx"), &Skeleton3D::unparent_bone_and_rest);
@@ -1243,23 +1012,12 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
- ClassDB::bind_method(D_METHOD("clear_bones_local_pose_override"), &Skeleton3D::clear_bones_local_pose_override);
- ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override);
-
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
ClassDB::bind_method(D_METHOD("set_motion_scale", "motion_scale"), &Skeleton3D::set_motion_scale);
ClassDB::bind_method(D_METHOD("get_motion_scale"), &Skeleton3D::get_motion_scale);
- // Helper functions
- ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform);
- ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose);
- ClassDB::bind_method(D_METHOD("global_pose_to_local_pose", "bone_idx", "global_pose"), &Skeleton3D::global_pose_to_local_pose);
- ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose);
- ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward);
-
ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only);
ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only);
@@ -1271,11 +1029,6 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
- // Modifications
- ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton3D::set_modification_stack);
- ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack);
- ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
#ifndef _3D_DISABLED
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index f66cc2f9ed..3df909d936 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -32,7 +32,6 @@
#define SKELETON_3D_H
#include "scene/3d/node_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/skin.h"
typedef int BoneId;
@@ -64,8 +63,6 @@ public:
~SkinReference();
};
-class SkeletonModificationStack3D;
-
class Skeleton3D : public Node3D {
GDCLASS(Skeleton3D, Node3D);
@@ -104,17 +101,8 @@ private:
PhysicalBone3D *physical_bone = nullptr;
PhysicalBone3D *cache_parent_physical_bone = nullptr;
- real_t local_pose_override_amount;
- bool local_pose_override_reset;
- Transform3D local_pose_override;
-
Vector<int> child_bones;
- // The forward direction vector and rest bone forward axis are cached because they do not change
- // 99% of the time, but recalculating them can be expensive on models with many bones.
- Vector3 rest_bone_forward_vector;
- int rest_bone_forward_axis = -1;
-
Bone() {
parent = -1;
enabled = true;
@@ -124,12 +112,7 @@ private:
physical_bone = nullptr;
cache_parent_physical_bone = nullptr;
#endif // _3D_DISABLED
- local_pose_override_amount = 0;
- local_pose_override_reset = false;
child_bones = Vector<int>();
-
- rest_bone_forward_vector = Vector3(0, 0, 0);
- rest_bone_forward_axis = -1;
}
};
@@ -162,25 +145,13 @@ protected:
void _notification(int p_what);
static void _bind_methods();
-#ifndef _3D_DISABLED
- Ref<SkeletonModificationStack3D> modification_stack;
-#endif // _3D_DISABLED
-
public:
- enum Bone_Forward_Axis {
- BONE_AXIS_X_FORWARD = 0,
- BONE_AXIS_Y_FORWARD = 1,
- BONE_AXIS_Z_FORWARD = 2,
- BONE_AXIS_NEGATIVE_X_FORWARD = 3,
- BONE_AXIS_NEGATIVE_Y_FORWARD = 4,
- BONE_AXIS_NEGATIVE_Z_FORWARD = 5,
- };
-
enum {
NOTIFICATION_UPDATE_SKELETON = 50
};
// skeleton creation api
+ uint64_t get_version() const;
void add_bone(const String &p_name);
int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
@@ -233,10 +204,6 @@ public:
Transform3D get_bone_global_pose_override(int p_bone) const;
void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
- void clear_bones_local_pose_override();
- Transform3D get_bone_local_pose_override(int p_bone) const;
- void set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
-
void localize_rests(); // used for loaders and tools
Ref<Skin> create_skin_from_rest_transforms();
@@ -247,26 +214,6 @@ public:
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
- void update_bone_rest_forward_vector(int p_bone, bool p_force_update = false);
- void update_bone_rest_forward_axis(int p_bone, bool p_force_update = false);
- Vector3 get_bone_axis_forward_vector(int p_bone);
- int get_bone_axis_forward_enum(int p_bone);
-
- // Helper functions
- Transform3D global_pose_to_world_transform(Transform3D p_global_pose);
- Transform3D world_transform_to_global_pose(Transform3D p_transform);
- Transform3D global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose);
- Transform3D local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose);
-
- Basis global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis);
-
- // Modifications
-#ifndef _3D_DISABLED
- Ref<SkeletonModificationStack3D> get_modification_stack();
- void set_modification_stack(Ref<SkeletonModificationStack3D> p_stack);
- void execute_modifications(real_t p_delta, int p_execution_mode);
-#endif // _3D_DISABLED
-
// Physical bone API
void set_animate_physical_bones(bool p_enabled);
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index f35cd3fbc5..75f54a925b 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -249,6 +249,26 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_invers
}
}
+static Vector3 get_bone_axis_forward_vector(Skeleton3D *skeleton, int p_bone) {
+ // If it is a child/leaf bone...
+ if (skeleton->get_bone_parent(p_bone) > 0) {
+ return skeleton->get_bone_rest(p_bone).origin.normalized();
+ }
+ // If it has children...
+ Vector<int> child_bones = skeleton->get_bone_children(p_bone);
+ if (child_bones.size() == 0) {
+ WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone));
+ WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone");
+ return Vector3(0, 1, 0);
+ }
+ Vector3 combined_child_dir = Vector3(0, 0, 0);
+ for (int i = 0; i < child_bones.size(); i++) {
+ combined_child_dir += skeleton->get_bone_rest(child_bones[i]).origin.normalized();
+ }
+ combined_child_dir = combined_child_dir / child_bones.size();
+ return combined_child_dir.normalized();
+}
+
void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) {
if (blending_delta <= 0.01f) {
// Before skipping, make sure we undo the global pose overrides
@@ -287,8 +307,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
new_bone_pose.origin = ci->current_pos;
if (!ci->children.is_empty()) {
- p_task->skeleton->update_bone_rest_forward_vector(ci->bone);
- Vector3 forward_vector = p_task->skeleton->get_bone_axis_forward_vector(ci->bone);
+ Vector3 forward_vector = get_bone_axis_forward_vector(p_task->skeleton, ci->bone);
// Rotate the bone towards the next bone in the chain:
new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos));
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index e73bd460ed..e8b51ceb92 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -368,7 +368,7 @@ void SoftBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_parent_collision_ignore", "get_parent_collision_ignore");
ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness");
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index 60a0b03c0d..afddfdb749 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -128,7 +128,7 @@ void VisibleOnScreenEnabler3D::set_enable_node_path(NodePath p_path) {
return;
}
enable_node_path = p_path;
- if (is_inside_tree()) {
+ if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
node_id = ObjectID();
Node *node = get_node(enable_node_path);
if (node) {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 7f42c8fac3..0771c7ef06 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -745,7 +745,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} break;
case Animation::TYPE_METHOD: {
- if (!nc->node) {
+ if (!nc->node || is_stopping) {
continue;
}
if (!p_is_current) {
@@ -808,7 +808,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} break;
case Animation::TYPE_AUDIO: {
- if (!nc->node) {
+ if (!nc->node || is_stopping) {
continue;
}
@@ -915,6 +915,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} break;
case Animation::TYPE_ANIMATION: {
+ if (is_stopping) {
+ continue;
+ }
+
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node);
if (!player) {
continue;
@@ -1658,7 +1662,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
}
if (get_current_animation() != p_name) {
- _stop_playing_caches();
+ _stop_playing_caches(false);
}
c.current.from = &animation_set[name];
@@ -1731,18 +1735,12 @@ String AnimationPlayer::get_assigned_animation() const {
return playback.assigned;
}
-void AnimationPlayer::stop(bool p_reset) {
- _stop_playing_caches();
- Playback &c = playback;
- c.blend.clear();
- if (p_reset) {
- c.current.from = nullptr;
- c.current.speed_scale = 1;
- c.current.pos = 0;
- }
- _set_process(false);
- queued.clear();
- playing = false;
+void AnimationPlayer::pause() {
+ _stop_internal(false);
+}
+
+void AnimationPlayer::stop() {
+ _stop_internal(true);
}
void AnimationPlayer::set_speed_scale(float p_speed) {
@@ -1814,7 +1812,7 @@ void AnimationPlayer::_animation_changed(const StringName &p_name) {
}
}
-void AnimationPlayer::_stop_playing_caches() {
+void AnimationPlayer::_stop_playing_caches(bool p_reset) {
for (TrackNodeCache *E : playing_caches) {
if (E->node && E->audio_playing) {
E->node->call(SNAME("stop"));
@@ -1824,7 +1822,12 @@ void AnimationPlayer::_stop_playing_caches() {
if (!player) {
continue;
}
- player->stop();
+
+ if (p_reset) {
+ player->stop();
+ } else {
+ player->pause();
+ }
}
}
@@ -1836,7 +1839,7 @@ void AnimationPlayer::_node_removed(Node *p_node) {
}
void AnimationPlayer::clear_caches() {
- _stop_playing_caches();
+ _stop_playing_caches(true);
node_cache_map.clear();
@@ -1957,6 +1960,22 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
processing = p_process;
}
+void AnimationPlayer::_stop_internal(bool p_reset) {
+ _stop_playing_caches(p_reset);
+ Playback &c = playback;
+ c.blend.clear();
+ if (p_reset) {
+ is_stopping = true;
+ seek(0, true);
+ is_stopping = false;
+ c.current.from = nullptr;
+ c.current.speed_scale = 1;
+ }
+ _set_process(false);
+ queued.clear();
+ playing = false;
+}
+
void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) {
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
animation_set[p_animation].next = p_next;
@@ -2081,7 +2100,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values();
old_values->restore();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Animation Apply Reset"));
ur->add_do_method(new_values.ptr(), "restore");
ur->add_undo_method(old_values.ptr(), "restore");
@@ -2119,7 +2138,8 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(""), DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("stop", "reset"), &AnimationPlayer::stop, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
+ ClassDB::bind_method(D_METHOD("stop"), &AnimationPlayer::stop);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);
ClassDB::bind_method(D_METHOD("set_current_animation", "anim"), &AnimationPlayer::set_current_animation);
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index f431253876..8dfa7aed27 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -192,6 +192,7 @@ private:
uint64_t accum_pass = 1;
float speed_scale = 1.0;
double default_blend_time = 0.0;
+ bool is_stopping = false;
struct AnimationData {
String name;
@@ -277,7 +278,7 @@ private:
void _animation_process(double p_delta);
void _node_removed(Node *p_node);
- void _stop_playing_caches();
+ void _stop_playing_caches(bool p_reset);
// bind helpers
Vector<String> _get_animation_list() const {
@@ -294,6 +295,7 @@ private:
void _animation_changed(const StringName &p_name);
void _set_process(bool p_process, bool p_force = false);
+ void _stop_internal(bool p_reset);
bool playing = false;
@@ -346,7 +348,8 @@ public:
void queue(const StringName &p_name);
Vector<String> get_queue();
void clear_queue();
- void stop(bool p_reset = true);
+ void pause();
+ void stop();
bool is_playing() const;
String get_current_animation() const;
void set_current_animation(const String &p_anim);
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index b88695427e..39d1793368 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -60,7 +60,7 @@ void Tweener::_bind_methods() {
ADD_SIGNAL(MethodInfo("finished"));
}
-void Tween::start_tweeners() {
+void Tween::_start_tweeners() {
if (tweeners.is_empty()) {
dead = true;
ERR_FAIL_MSG("Tween without commands, aborting.");
@@ -71,6 +71,15 @@ void Tween::start_tweeners() {
}
}
+void Tween::_stop_internal(bool p_reset) {
+ running = false;
+ if (p_reset) {
+ started = false;
+ dead = false;
+ total_time = 0;
+ }
+}
+
Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration) {
ERR_FAIL_NULL_V(p_target, nullptr);
ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree.");
@@ -135,14 +144,11 @@ void Tween::append(Ref<Tweener> p_tweener) {
}
void Tween::stop() {
- started = false;
- running = false;
- dead = false;
- total_time = 0;
+ _stop_internal(true);
}
void Tween::pause() {
- running = false;
+ _stop_internal(false);
}
void Tween::play() {
@@ -274,11 +280,20 @@ bool Tween::step(double p_delta) {
}
if (!started) {
- ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
+ if (tweeners.is_empty()) {
+ String tween_id;
+ Node *node = get_bound_node();
+ if (node) {
+ tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name());
+ } else {
+ tween_id = to_string();
+ }
+ ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners.");
+ }
current_step = 0;
loops_done = 0;
total_time = 0;
- start_tweeners();
+ _start_tweeners();
started = true;
}
@@ -319,7 +334,7 @@ bool Tween::step(double p_delta) {
} else {
emit_signal(SNAME("loop_finished"), loops_done);
current_step = 0;
- start_tweeners();
+ _start_tweeners();
#ifdef DEBUG_ENABLED
if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
if (!potential_infinite) {
@@ -332,7 +347,7 @@ bool Tween::step(double p_delta) {
#endif
}
} else {
- start_tweeners();
+ _start_tweeners();
}
}
}
@@ -387,6 +402,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, d
return ret;
}
+String Tween::to_string() {
+ String ret = Object::to_string();
+ Node *node = get_bound_node();
+ if (node) {
+ ret += vformat(" (bound to %s)", node->get_name());
+ }
+ return ret;
+}
+
void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property);
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 8f65416e71..58217db535 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -123,12 +123,15 @@ private:
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
- void start_tweeners();
+ void _start_tweeners();
+ void _stop_internal(bool p_reset);
protected:
static void _bind_methods();
public:
+ virtual String to_string() override;
+
Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration);
Ref<IntervalTweener> tween_interval(double p_time);
Ref<CallbackTweener> tween_callback(Callable p_callback);
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 4c27cf4b21..d0326290ac 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -30,6 +30,7 @@
#include "base_button.h"
+#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
@@ -62,7 +63,7 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mouse_button = p_event;
bool ui_accept = p_event->is_action("ui_accept", true) && !p_event->is_echo();
- bool button_masked = mouse_button.is_valid() && (mouse_button_to_mask(mouse_button->get_button_index()) & button_mask) != MouseButton::NONE;
+ bool button_masked = mouse_button.is_valid() && button_mask.has_flag(mouse_button_to_mask(mouse_button->get_button_index()));
if (button_masked || ui_accept) {
was_mouse_pressed = button_masked;
on_action_event(p_event);
@@ -127,7 +128,6 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
status.press_attempt = false;
status.pressing_inside = false;
- status.shortcut_press = false;
} break;
}
}
@@ -154,14 +154,10 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
if (status.press_attempt && status.pressing_inside) {
if (toggle_mode) {
bool is_pressed = p_event->is_pressed();
- if (Object::cast_to<InputEventShortcut>(*p_event)) {
- is_pressed = false;
- }
if ((is_pressed && action_mode == ACTION_MODE_BUTTON_PRESS) || (!is_pressed && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
if (action_mode == ACTION_MODE_BUTTON_PRESS) {
status.press_attempt = false;
status.pressing_inside = false;
- status.shortcut_press = false;
}
status.pressed = !status.pressed;
_unpress_group();
@@ -187,7 +183,6 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
}
status.press_attempt = false;
status.pressing_inside = false;
- status.shortcut_press = false;
emit_signal(SNAME("button_up"));
}
@@ -212,7 +207,6 @@ void BaseButton::set_disabled(bool p_disabled) {
}
status.press_attempt = false;
status.pressing_inside = false;
- status.shortcut_press = false;
}
queue_redraw();
}
@@ -267,6 +261,10 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
return DRAW_DISABLED;
}
+ if (in_shortcut_feedback) {
+ return DRAW_HOVER_PRESSED;
+ }
+
if (!status.press_attempt && status.hovering) {
if (status.pressed) {
return DRAW_HOVER_PRESSED;
@@ -285,7 +283,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
pressing = status.pressed;
}
- if ((shortcut_feedback || !status.shortcut_press) && pressing) {
+ if (pressing) {
return DRAW_PRESSED;
} else {
return DRAW_NORMAL;
@@ -323,11 +321,11 @@ BaseButton::ActionMode BaseButton::get_action_mode() const {
return action_mode;
}
-void BaseButton::set_button_mask(MouseButton p_mask) {
+void BaseButton::set_button_mask(BitField<MouseButtonMask> p_mask) {
button_mask = p_mask;
}
-MouseButton BaseButton::get_button_mask() const {
+BitField<MouseButtonMask> BaseButton::get_button_mask() const {
return button_mask;
}
@@ -339,6 +337,14 @@ bool BaseButton::is_keep_pressed_outside() const {
return keep_pressed_outside;
}
+void BaseButton::set_shortcut_feedback(bool p_enable) {
+ shortcut_feedback = p_enable;
+}
+
+bool BaseButton::is_shortcut_feedback() const {
+ return shortcut_feedback;
+}
+
void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) {
shortcut = p_shortcut;
set_process_shortcut_input(shortcut.is_valid());
@@ -348,13 +354,45 @@ Ref<Shortcut> BaseButton::get_shortcut() const {
return shortcut;
}
+void BaseButton::_shortcut_feedback_timeout() {
+ in_shortcut_feedback = false;
+ queue_redraw();
+}
+
void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) {
- status.shortcut_press = true;
- on_action_event(p_event);
+ if (!is_disabled() && p_event->is_pressed() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) {
+ if (toggle_mode) {
+ status.pressed = !status.pressed;
+
+ if (status.pressed) {
+ _unpress_group();
+ if (button_group.is_valid()) {
+ button_group->emit_signal(SNAME("pressed"), this);
+ }
+ }
+
+ _toggled(status.pressed);
+ _pressed();
+
+ } else {
+ _pressed();
+ }
+ queue_redraw();
accept_event();
+
+ if (shortcut_feedback) {
+ if (shortcut_feedback_timer == nullptr) {
+ shortcut_feedback_timer = memnew(Timer);
+ add_child(shortcut_feedback_timer);
+ shortcut_feedback_timer->set_wait_time(GLOBAL_GET("gui/timers/button_shortcut_feedback_highlight_time"));
+ shortcut_feedback_timer->connect("timeout", callable_mp(this, &BaseButton::_shortcut_feedback_timeout));
+ }
+
+ in_shortcut_feedback = true;
+ shortcut_feedback_timer->start();
+ }
}
}
@@ -393,14 +431,6 @@ bool BaseButton::_was_pressed_by_mouse() const {
return was_mouse_pressed;
}
-void BaseButton::set_shortcut_feedback(bool p_feedback) {
- shortcut_feedback = p_feedback;
-}
-
-bool BaseButton::is_shortcut_feedback() const {
- return shortcut_feedback;
-}
-
PackedStringArray BaseButton::get_configuration_warnings() const {
PackedStringArray warnings = Control::get_configuration_warnings();
@@ -429,6 +459,8 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode);
ClassDB::bind_method(D_METHOD("set_keep_pressed_outside", "enabled"), &BaseButton::set_keep_pressed_outside);
ClassDB::bind_method(D_METHOD("is_keep_pressed_outside"), &BaseButton::is_keep_pressed_outside);
+ ClassDB::bind_method(D_METHOD("set_shortcut_feedback", "enabled"), &BaseButton::set_shortcut_feedback);
+ ClassDB::bind_method(D_METHOD("is_shortcut_feedback"), &BaseButton::is_shortcut_feedback);
ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &BaseButton::set_shortcut);
ClassDB::bind_method(D_METHOD("get_shortcut"), &BaseButton::get_shortcut);
@@ -436,9 +468,6 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_button_group", "button_group"), &BaseButton::set_button_group);
ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group);
- ClassDB::bind_method(D_METHOD("set_shortcut_feedback", "enabled"), &BaseButton::set_shortcut_feedback);
- ClassDB::bind_method(D_METHOD("is_shortcut_feedback"), &BaseButton::is_shortcut_feedback);
-
GDVIRTUAL_BIND(_pressed);
GDVIRTUAL_BIND(_toggled, "button_pressed");
@@ -466,6 +495,8 @@ void BaseButton::_bind_methods() {
BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_PRESS);
BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_RELEASE);
+
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/button_shortcut_feedback_highlight_time", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), 0.2);
}
BaseButton::BaseButton() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index d7e6b68517..962a16c453 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -46,14 +46,14 @@ public:
};
private:
- MouseButton button_mask = MouseButton::MASK_LEFT;
+ BitField<MouseButtonMask> button_mask = MouseButtonMask::LEFT;
bool toggle_mode = false;
bool shortcut_in_tooltip = true;
bool was_mouse_pressed = false;
bool keep_pressed_outside = false;
+ bool shortcut_feedback = true;
Ref<Shortcut> shortcut;
ObjectID shortcut_context;
- bool shortcut_feedback = true;
ActionMode action_mode = ACTION_MODE_BUTTON_RELEASE;
struct Status {
@@ -61,7 +61,6 @@ private:
bool hovering = false;
bool press_attempt = false;
bool pressing_inside = false;
- bool shortcut_press = false;
bool disabled = false;
@@ -75,6 +74,10 @@ private:
void on_action_event(Ref<InputEvent> p_event);
+ Timer *shortcut_feedback_timer = nullptr;
+ bool in_shortcut_feedback = false;
+ void _shortcut_feedback_timeout();
+
protected:
virtual void pressed();
virtual void toggled(bool p_pressed);
@@ -122,8 +125,11 @@ public:
void set_keep_pressed_outside(bool p_on);
bool is_keep_pressed_outside() const;
- void set_button_mask(MouseButton p_mask);
- MouseButton get_button_mask() const;
+ void set_shortcut_feedback(bool p_enable);
+ bool is_shortcut_feedback() const;
+
+ void set_button_mask(BitField<MouseButtonMask> p_mask);
+ BitField<MouseButtonMask> get_button_mask() const;
void set_shortcut(const Ref<Shortcut> &p_shortcut);
Ref<Shortcut> get_shortcut() const;
@@ -133,9 +139,6 @@ public:
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
- void set_shortcut_feedback(bool p_feedback);
- bool is_shortcut_feedback() const;
-
PackedStringArray get_configuration_warnings() const override;
BaseButton();
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index 1e75659a8c..97729b1ac5 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -68,12 +68,12 @@ void BoxContainer::_resort() {
if (vertical) { /* VERTICAL */
stretch_min += size.height;
msc.min_size = size.height;
- msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND;
+ msc.will_stretch = c->get_v_size_flags().has_flag(SIZE_EXPAND);
} else { /* HORIZONTAL */
stretch_min += size.width;
msc.min_size = size.width;
- msc.will_stretch = c->get_h_size_flags() & SIZE_EXPAND;
+ msc.will_stretch = c->get_h_size_flags().has_flag(SIZE_EXPAND);
}
if (msc.will_stretch) {
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 46764b64b2..1e07a53642 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -572,7 +572,7 @@ void Button::_bind_methods() {
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);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index a68dfa80ba..4356e91e41 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -380,13 +380,13 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (symbol_lookup_on_click_enabled) {
- if (mm->is_command_or_control_pressed() && mm->get_button_mask() == MouseButton::NONE) {
+ if (mm->is_command_or_control_pressed() && mm->get_button_mask().is_empty()) {
symbol_lookup_pos = get_line_column_at_pos(mpos);
symbol_lookup_new_word = get_word_at_pos(mpos);
if (symbol_lookup_new_word != symbol_lookup_word) {
emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word);
}
- } else if (!mm->is_command_or_control_pressed() || (mm->get_button_mask() != MouseButton::NONE && symbol_lookup_pos != get_line_column_at_pos(mpos))) {
+ } else if (!mm->is_command_or_control_pressed() || (!mm->get_button_mask().is_empty() && symbol_lookup_pos != get_line_column_at_pos(mpos))) {
set_symbol_lookup_word_as_valid(false);
}
}
@@ -2086,9 +2086,11 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
int post_brace_pair = get_caret_column(i) < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i)) : -1;
// Strings do not nest like brackets, so ensure we don't add an additional closing pair.
- if (has_string_delimiter(String::chr(last_completion_char)) && post_brace_pair != -1 && last_char_matches) {
- remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
- adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+ if (has_string_delimiter(String::chr(last_completion_char))) {
+ if (post_brace_pair != -1 && last_char_matches) {
+ remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+ adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
+ }
} else {
if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && last_char_matches) {
remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1);
@@ -2899,6 +2901,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
const int caret_line = get_caret_line();
const int caret_column = get_caret_column();
const String line = get_line(caret_line);
+ ERR_FAIL_INDEX_MSG(caret_column - 1, line.length(), "Caret column exceeds line length.");
if (caret_column > 0 && line[caret_column - 1] == '(' && !code_completion_forced) {
cancel_code_completion();
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 944363bcb0..b5846cb692 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -642,7 +642,7 @@ inline int ColorPicker::_get_preset_size() {
void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1)));
- btn_preset_new->set_drag_forwarding(this);
+ btn_preset_new->set_drag_forwarding_compat(this);
btn_preset_new->set_button_group(preset_group);
preset_container->add_child(btn_preset_new);
btn_preset_new->set_pressed(true);
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 8d25195199..145074a626 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -104,22 +104,22 @@ void Container::fit_child_in_rect(Control *p_child, const Rect2 &p_rect) {
Size2 minsize = p_child->get_combined_minimum_size();
Rect2 r = p_rect;
- if (!(p_child->get_h_size_flags() & SIZE_FILL)) {
+ if (!(p_child->get_h_size_flags().has_flag(SIZE_FILL))) {
r.size.x = minsize.width;
- if (p_child->get_h_size_flags() & SIZE_SHRINK_END) {
+ if (p_child->get_h_size_flags().has_flag(SIZE_SHRINK_END)) {
r.position.x += rtl ? 0 : (p_rect.size.width - minsize.width);
- } else if (p_child->get_h_size_flags() & SIZE_SHRINK_CENTER) {
+ } else if (p_child->get_h_size_flags().has_flag(SIZE_SHRINK_CENTER)) {
r.position.x += Math::floor((p_rect.size.x - minsize.width) / 2);
} else {
r.position.x += rtl ? (p_rect.size.width - minsize.width) : 0;
}
}
- if (!(p_child->get_v_size_flags() & SIZE_FILL)) {
+ if (!(p_child->get_v_size_flags().has_flag(SIZE_FILL))) {
r.size.y = minsize.y;
- if (p_child->get_v_size_flags() & SIZE_SHRINK_END) {
+ if (p_child->get_v_size_flags().has_flag(SIZE_SHRINK_END)) {
r.position.y += p_rect.size.height - minsize.height;
- } else if (p_child->get_v_size_flags() & SIZE_SHRINK_CENTER) {
+ } else if (p_child->get_v_size_flags().has_flag(SIZE_SHRINK_CENTER)) {
r.position.y += Math::floor((p_rect.size.y - minsize.height) / 2);
} else {
r.position.y += 0;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 7daeb6eab6..10dbad232a 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -105,7 +105,7 @@ void Control::_edit_set_state(const Dictionary &p_state) {
}
_set_layout_mode(_layout);
- if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS) {
+ if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS || _layout == LayoutMode::LAYOUT_MODE_UNCONTROLLED) {
_set_anchors_layout_preset((int)state["anchors_layout_preset"]);
}
@@ -125,7 +125,7 @@ void Control::_edit_set_state(const Dictionary &p_state) {
void Control::_edit_set_position(const Point2 &p_position) {
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
- set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
+ set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && get_parent_control());
};
Point2 Control::_edit_get_position() const {
@@ -553,7 +553,8 @@ void Control::_validate_property(PropertyInfo &p_property) const {
}
// Use the layout mode to display or hide advanced anchoring properties.
- bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS;
+ LayoutMode _layout = _get_layout_mode();
+ bool use_anchors = (_layout == LayoutMode::LAYOUT_MODE_ANCHORS || _layout == LayoutMode::LAYOUT_MODE_UNCONTROLLED);
if (!use_anchors && p_property.name == "anchors_preset") {
p_property.usage ^= PROPERTY_USAGE_EDITOR;
}
@@ -606,7 +607,7 @@ bool Control::is_top_level_control() const {
}
Control *Control::get_parent_control() const {
- return data.parent;
+ return data.parent_control;
}
Window *Control::get_parent_window() const {
@@ -757,6 +758,7 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos,
}
void Control::set_begin(const Size2 &p_point) {
+ ERR_FAIL_COND(!isfinite(p_point.x) || !isfinite(p_point.y));
if (data.offset[0] == p_point.x && data.offset[1] == p_point.y) {
return;
}
@@ -863,6 +865,14 @@ void Control::_set_layout_mode(LayoutMode p_mode) {
}
}
+void Control::_update_layout_mode() {
+ LayoutMode computed_layout = _get_layout_mode();
+ if (data.stored_layout_mode != computed_layout) {
+ data.stored_layout_mode = computed_layout;
+ notify_property_list_changed();
+ }
+}
+
Control::LayoutMode Control::_get_layout_mode() const {
Node *parent_node = get_parent_control();
// In these modes the property is read-only.
@@ -895,11 +905,9 @@ Control::LayoutMode Control::_get_default_layout_mode() const {
}
void Control::_set_anchors_layout_preset(int p_preset) {
- bool list_changed = false;
-
- if (data.stored_layout_mode != LayoutMode::LAYOUT_MODE_ANCHORS) {
- list_changed = true;
- data.stored_layout_mode = LayoutMode::LAYOUT_MODE_ANCHORS;
+ if (data.stored_layout_mode != LayoutMode::LAYOUT_MODE_UNCONTROLLED && data.stored_layout_mode != LayoutMode::LAYOUT_MODE_ANCHORS) {
+ // In other modes the anchor preset is non-operational and shouldn't be set to anything.
+ return;
}
if (p_preset == -1) {
@@ -910,6 +918,8 @@ void Control::_set_anchors_layout_preset(int p_preset) {
return; // Keep settings as is.
}
+ bool list_changed = false;
+
if (data.stored_use_custom_anchors) {
list_changed = true;
data.stored_use_custom_anchors = false;
@@ -952,6 +962,11 @@ void Control::_set_anchors_layout_preset(int p_preset) {
}
int Control::_get_anchors_layout_preset() const {
+ // If this is a layout mode that doesn't rely on anchors, avoid excessive checks.
+ if (data.stored_layout_mode != LayoutMode::LAYOUT_MODE_UNCONTROLLED && data.stored_layout_mode != LayoutMode::LAYOUT_MODE_ANCHORS) {
+ return LayoutPreset::PRESET_TOP_LEFT;
+ }
+
// If the custom preset was selected by user, use it.
if (data.stored_use_custom_anchors) {
return -1;
@@ -1391,6 +1406,7 @@ void Control::_set_size(const Size2 &p_size) {
}
void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
+ ERR_FAIL_COND(!isfinite(p_size.x) || !isfinite(p_size.y));
Size2 new_size = p_size;
Size2 min = get_combined_minimum_size();
if (new_size.x < min.x) {
@@ -1533,19 +1549,20 @@ void Control::update_minimum_size() {
Control *invalidate = this;
- //invalidate cache upwards
+ // Invalidate cache upwards.
while (invalidate && invalidate->data.minimum_size_valid) {
invalidate->data.minimum_size_valid = false;
if (invalidate->is_set_as_top_level()) {
- break; // do not go further up
+ break; // Do not go further up.
}
- if (!invalidate->data.parent && get_parent()) {
- Window *parent_window = Object::cast_to<Window>(get_parent());
- if (parent_window && parent_window->is_wrapping_controls()) {
- parent_window->child_controls_changed();
- }
+
+ Window *parent_window = invalidate->get_parent_window();
+ if (parent_window && parent_window->is_wrapping_controls()) {
+ parent_window->child_controls_changed();
+ break; // Stop on a window as well.
}
- invalidate = invalidate->data.parent;
+
+ invalidate = invalidate->get_parent_control();
}
if (!is_visible_in_tree()) {
@@ -1580,7 +1597,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) {
return;
}
- if (isnan(p_custom.x) || isnan(p_custom.y)) {
+ if (!isfinite(p_custom.x) || !isfinite(p_custom.y)) {
// Prevent infinite loop.
return;
}
@@ -1684,27 +1701,27 @@ void Control::_clear_size_warning() {
// Container sizing.
-void Control::set_h_size_flags(int p_flags) {
- if (data.h_size_flags == p_flags) {
+void Control::set_h_size_flags(BitField<SizeFlags> p_flags) {
+ if ((int)data.h_size_flags == (int)p_flags) {
return;
}
data.h_size_flags = p_flags;
emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-int Control::get_h_size_flags() const {
+BitField<Control::SizeFlags> Control::get_h_size_flags() const {
return data.h_size_flags;
}
-void Control::set_v_size_flags(int p_flags) {
- if (data.v_size_flags == p_flags) {
+void Control::set_v_size_flags(BitField<SizeFlags> p_flags) {
+ if ((int)data.v_size_flags == (int)p_flags) {
return;
}
data.v_size_flags = p_flags;
emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-int Control::get_v_size_flags() const {
+BitField<Control::SizeFlags> Control::get_v_size_flags() const {
return data.v_size_flags;
}
@@ -1806,33 +1823,53 @@ bool Control::is_focus_owner_in_shortcut_context() const {
// Drag and drop handling.
-void Control::set_drag_forwarding(Object *p_target) {
- if (p_target) {
- data.drag_owner = p_target->get_instance_id();
+void Control::set_drag_forwarding_compat(Object *p_base) {
+ if (p_base != nullptr) {
+ data.forward_drag = Callable(p_base, "_get_drag_data_fw").bind(this);
+ data.forward_can_drop = Callable(p_base, "_can_drop_data_fw").bind(this);
+ data.forward_drop = Callable(p_base, "_drop_data_fw").bind(this);
+
} else {
- data.drag_owner = ObjectID();
+ data.forward_drag = Callable();
+ data.forward_can_drop = Callable();
+ data.forward_drop = Callable();
}
}
+void Control::set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop) {
+ data.forward_drag = p_drag;
+ data.forward_can_drop = p_can_drop;
+ data.forward_drop = p_drop;
+}
+
Variant Control::get_drag_data(const Point2 &p_point) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_get_drag_data_fw", p_point, this);
+ Variant ret;
+ if (data.forward_drag.is_valid()) {
+ Variant p = p_point;
+ const Variant *vp[1] = { &p };
+ Callable::CallError ce;
+ data.forward_drag.callp((const Variant **)vp, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Variant(), "Error calling forwarded method from 'get_drag_data': " + Variant::get_callable_error_text(data.forward_drag, (const Variant **)vp, 1, ce) + ".");
}
+ return ret;
}
- Variant dd;
- GDVIRTUAL_CALL(_get_drag_data, p_point, dd);
- return dd;
+ GDVIRTUAL_CALL(_get_drag_data, p_point, ret);
+ return ret;
}
bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_can_drop_data_fw", p_point, p_data, this);
+ if (data.forward_can_drop.is_valid()) {
+ Variant ret;
+ Variant p = p_point;
+ const Variant *vp[2] = { &p, &p_data };
+ Callable::CallError ce;
+ data.forward_can_drop.callp((const Variant **)vp, 2, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Variant(), "Error calling forwarded method from 'can_drop_data': " + Variant::get_callable_error_text(data.forward_can_drop, (const Variant **)vp, 2, ce) + ".");
}
+ return ret;
}
bool ret = false;
@@ -1841,12 +1878,16 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const
}
void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- obj->call("_drop_data_fw", p_point, p_data, this);
- return;
+ if (data.forward_drop.is_valid()) {
+ Variant ret;
+ Variant p = p_point;
+ const Variant *vp[2] = { &p, &p_data };
+ Callable::CallError ce;
+ data.forward_drop.callp((const Variant **)vp, 2, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_MSG("Error calling forwarded method from 'drop_data': " + Variant::get_callable_error_text(data.forward_drop, (const Variant **)vp, 2, ce) + ".");
}
+ return;
}
GDVIRTUAL_CALL(_drop_data, p_point, p_data);
@@ -2858,10 +2899,19 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_PARENTED: {
+ Node *parent_node = get_parent();
+ data.parent_control = Object::cast_to<Control>(parent_node);
+ data.parent_window = Object::cast_to<Window>(parent_node);
+
data.theme_owner->assign_theme_on_parented(this);
+
+ _update_layout_mode();
} break;
case NOTIFICATION_UNPARENTED: {
+ data.parent_control = nullptr;
+ data.parent_window = nullptr;
+
data.theme_owner->clear_theme_on_unparented(this);
} break;
@@ -2887,8 +2937,6 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_ENTER_CANVAS: {
- data.parent = Object::cast_to<Control>(get_parent());
- data.parent_window = Object::cast_to<Window>(get_parent());
data.is_rtl_dirty = true;
CanvasItem *node = this;
@@ -2946,17 +2994,16 @@ void Control::_notification(int p_notification) {
data.RI = nullptr;
}
- data.parent = nullptr;
data.parent_canvas_item = nullptr;
- data.parent_window = nullptr;
data.is_rtl_dirty = true;
} break;
case NOTIFICATION_MOVED_IN_PARENT: {
- // some parents need to know the order of the children to draw (like TabContainer)
- // update if necessary
- if (data.parent) {
- data.parent->queue_redraw();
+ // Some parents need to know the order of the children to draw (like TabContainer),
+ // so we update them just in case.
+ Control *parent_control = get_parent_control();
+ if (parent_control) {
+ parent_control->queue_redraw();
}
queue_redraw();
@@ -3178,7 +3225,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("grab_click_focus"), &Control::grab_click_focus);
- ClassDB::bind_method(D_METHOD("set_drag_forwarding", "target"), &Control::set_drag_forwarding);
+ ClassDB::bind_method(D_METHOD("set_drag_forwarding", "drag_func", "can_drop_func", "drop_func"), &Control::set_drag_forwarding);
ClassDB::bind_method(D_METHOD("set_drag_preview", "control"), &Control::set_drag_preview);
ClassDB::bind_method(D_METHOD("is_drag_successful"), &Control::is_drag_successful);
@@ -3326,12 +3373,12 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT);
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE);
- BIND_ENUM_CONSTANT(SIZE_SHRINK_BEGIN);
- BIND_ENUM_CONSTANT(SIZE_FILL);
- BIND_ENUM_CONSTANT(SIZE_EXPAND);
- BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL);
- BIND_ENUM_CONSTANT(SIZE_SHRINK_CENTER);
- BIND_ENUM_CONSTANT(SIZE_SHRINK_END);
+ BIND_BITFIELD_FLAG(SIZE_SHRINK_BEGIN);
+ BIND_BITFIELD_FLAG(SIZE_FILL);
+ BIND_BITFIELD_FLAG(SIZE_EXPAND);
+ BIND_BITFIELD_FLAG(SIZE_EXPAND_FILL);
+ BIND_BITFIELD_FLAG(SIZE_SHRINK_CENTER);
+ BIND_BITFIELD_FLAG(SIZE_SHRINK_END);
BIND_ENUM_CONSTANT(MOUSE_FILTER_STOP);
BIND_ENUM_CONSTANT(MOUSE_FILTER_PASS);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index a11f7da00f..22a37dd89e 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -165,10 +165,12 @@ private:
List<Control *>::Element *RI = nullptr;
- Control *parent = nullptr;
+ Control *parent_control = nullptr;
Window *parent_window = nullptr;
CanvasItem *parent_canvas_item = nullptr;
- ObjectID drag_owner;
+ Callable forward_drag;
+ Callable forward_can_drop;
+ Callable forward_drop;
// Positioning and sizing.
@@ -198,8 +200,8 @@ private:
// Container sizing.
- int h_size_flags = SIZE_FILL;
- int v_size_flags = SIZE_FILL;
+ BitField<SizeFlags> h_size_flags = SIZE_FILL;
+ BitField<SizeFlags> v_size_flags = SIZE_FILL;
real_t expand = 1.0;
Point2 custom_minimum_size;
@@ -280,6 +282,7 @@ private:
void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
void _set_layout_mode(LayoutMode p_mode);
+ void _update_layout_mode();
LayoutMode _get_layout_mode() const;
LayoutMode _get_default_layout_mode() const;
void _set_anchors_layout_preset(int p_preset);
@@ -471,10 +474,10 @@ public:
// Container sizing.
- void set_h_size_flags(int p_flags);
- int get_h_size_flags() const;
- void set_v_size_flags(int p_flags);
- int get_v_size_flags() const;
+ void set_h_size_flags(BitField<SizeFlags> p_flags);
+ BitField<SizeFlags> get_h_size_flags() const;
+ void set_v_size_flags(BitField<SizeFlags> p_flags);
+ BitField<SizeFlags> get_v_size_flags() const;
void set_stretch_ratio(real_t p_ratio);
real_t get_stretch_ratio() const;
@@ -499,7 +502,8 @@ public:
// Drag and drop handling.
- virtual void set_drag_forwarding(Object *p_target);
+ virtual void set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop);
+ virtual void set_drag_forwarding_compat(Object *p_base);
virtual Variant get_drag_data(const Point2 &p_point);
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
virtual void drop_data(const Point2 &p_point, const Variant &p_data);
@@ -619,7 +623,7 @@ public:
};
VARIANT_ENUM_CAST(Control::FocusMode);
-VARIANT_ENUM_CAST(Control::SizeFlags);
+VARIANT_BITFIELD_CAST(Control::SizeFlags);
VARIANT_ENUM_CAST(Control::CursorShape);
VARIANT_ENUM_CAST(Control::LayoutPreset);
VARIANT_ENUM_CAST(Control::LayoutPresetMode);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 1d5e6320dd..4365db2ea2 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -378,7 +378,7 @@ void AcceptDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text"), "set_ok_button_text", "get_ok_button_text");
ADD_GROUP("Dialog", "dialog_");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
index e0a303651a..12ce6e17cb 100644
--- a/scene/gui/flow_container.cpp
+++ b/scene/gui/flow_container.cpp
@@ -86,7 +86,7 @@ void FlowContainer::_resort() {
}
line_height = MAX(line_height, child_msc.x);
- if (child->get_v_size_flags() & SIZE_EXPAND) {
+ if (child->get_v_size_flags().has_flag(SIZE_EXPAND)) {
line_stretch_ratio_total += child->get_stretch_ratio();
}
ofs.y += child_msc.y;
@@ -108,7 +108,7 @@ void FlowContainer::_resort() {
}
line_height = MAX(line_height, child_msc.y);
- if (child->get_h_size_flags() & SIZE_EXPAND) {
+ if (child->get_h_size_flags().has_flag(SIZE_EXPAND)) {
line_stretch_ratio_total += child->get_stretch_ratio();
}
ofs.x += child_msc.x;
@@ -175,21 +175,21 @@ void FlowContainer::_resort() {
}
if (vertical) { /* VERTICAL */
- if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ if (child->get_h_size_flags().has_flag(SIZE_FILL) || child->get_h_size_flags().has_flag(SIZE_SHRINK_CENTER) || child->get_h_size_flags().has_flag(SIZE_SHRINK_END)) {
child_size.width = line_data.min_line_height;
}
- if (child->get_v_size_flags() & SIZE_EXPAND) {
+ if (child->get_v_size_flags().has_flag(SIZE_EXPAND)) {
int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
child_size.height += stretch;
}
} else { /* HORIZONTAL */
- if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ if (child->get_v_size_flags().has_flag(SIZE_FILL) || child->get_v_size_flags().has_flag(SIZE_SHRINK_CENTER) || child->get_v_size_flags().has_flag(SIZE_SHRINK_END)) {
child_size.height = line_data.min_line_height;
}
- if (child->get_h_size_flags() & SIZE_EXPAND) {
+ if (child->get_h_size_flags().has_flag(SIZE_EXPAND)) {
int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
child_size.width += stretch;
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index a7b01e00ae..6c495ab2c9 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -404,8 +404,8 @@ void GraphEdit::add_child_notify(Node *p_child) {
if (gn) {
gn->set_scale(Vector2(zoom, zoom));
gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(gn));
- gn->connect("selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(gn));
- gn->connect("deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(gn));
+ gn->connect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(gn));
+ gn->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(gn));
gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(gn));
gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised).bind(gn));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
@@ -432,8 +432,8 @@ void GraphEdit::remove_child_notify(Node *p_child) {
GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) {
gn->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
- gn->disconnect("selected", callable_mp(this, &GraphEdit::_graph_node_selected));
- gn->disconnect("deselected", callable_mp(this, &GraphEdit::_graph_node_deselected));
+ gn->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected));
+ gn->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected));
gn->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised));
@@ -1496,6 +1496,7 @@ float GraphEdit::get_zoom() const {
void GraphEdit::set_zoom_step(float p_zoom_step) {
p_zoom_step = abs(p_zoom_step);
+ ERR_FAIL_COND(!isfinite(p_zoom_step));
if (zoom_step == p_zoom_step) {
return;
}
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index b0318b3e8f..f8d2ff0d2c 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -174,7 +174,7 @@ void GraphNode::_resort() {
stretch_min += size.height;
msc.min_size = size.height;
- msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND;
+ msc.will_stretch = c->get_v_size_flags().has_flag(SIZE_EXPAND);
if (msc.will_stretch) {
stretch_avail += msc.min_size;
@@ -749,7 +749,7 @@ void GraphNode::set_selected(bool p_selected) {
}
selected = p_selected;
- emit_signal(p_selected ? SNAME("selected") : SNAME("deselected"));
+ emit_signal(p_selected ? SNAME("node_selected") : SNAME("node_deselected"));
queue_redraw();
}
@@ -1161,8 +1161,8 @@ void GraphNode::_bind_methods() {
ADD_GROUP("", "");
ADD_SIGNAL(MethodInfo("position_offset_changed"));
- ADD_SIGNAL(MethodInfo("selected"));
- ADD_SIGNAL(MethodInfo("deselected"));
+ ADD_SIGNAL(MethodInfo("node_selected"));
+ ADD_SIGNAL(MethodInfo("node_deselected"));
ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
ADD_SIGNAL(MethodInfo("raise_request"));
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index e11afdf00d..28f86369a2 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -73,10 +73,10 @@ void GridContainer::_notification(int p_what) {
row_minh[row] = ms.height;
}
- if (c->get_h_size_flags() & SIZE_EXPAND) {
+ if (c->get_h_size_flags().has_flag(SIZE_EXPAND)) {
col_expanded.insert(col);
}
- if (c->get_v_size_flags() & SIZE_EXPAND) {
+ if (c->get_v_size_flags().has_flag(SIZE_EXPAND)) {
row_expanded.insert(row);
}
}
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 81e5de1886..f59702835c 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -222,6 +222,7 @@ void Label::_shape() {
}
}
lines_dirty = false;
+ lines_shaped_last_width = get_size().width;
}
_update_visible();
@@ -596,7 +597,13 @@ void Label::_notification(int p_what) {
} break;
case NOTIFICATION_RESIZED: {
- lines_dirty = true;
+ // It may happen that the reshaping due to this size change triggers a cascade of re-layout
+ // across the hierarchy where this label belongs to in a way that its size changes multiple
+ // times, but ending up with the original size it was already shaped for.
+ // This check prevents the catastrophic, freezing infinite cascade of re-layout.
+ if (lines_shaped_last_width != get_size().width) {
+ lines_dirty = true;
+ }
} break;
}
}
@@ -949,7 +956,7 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 2350525236..b80646810b 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -49,6 +49,8 @@ private:
bool uppercase = false;
bool lines_dirty = true;
+ int lines_shaped_last_width = -1;
+
bool dirty = true;
bool font_dirty = true;
RID text_rid;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index ab96e2c557..e5bb64b225 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -394,7 +394,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (m->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
if (selection.creating) {
set_caret_at_pixel_pos(m->get_position().x);
selection_fill_at_caret();
diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp
index d262959e8a..224e405d41 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -842,27 +842,6 @@ String MenuBar::get_tooltip(const Point2 &p_pos) const {
}
}
-void MenuBar::get_translatable_strings(List<String> *p_strings) const {
- Vector<PopupMenu *> popups = _get_popups();
- for (int i = 0; i < popups.size(); i++) {
- PopupMenu *pm = popups[i];
-
- if (!pm->has_meta("_menu_name") && !pm->has_meta("_menu_tooltip")) {
- continue;
- }
-
- String name = pm->get_meta("_menu_name");
- if (!name.is_empty()) {
- p_strings->push_back(name);
- }
-
- String tooltip = pm->get_meta("_menu_tooltip");
- if (!tooltip.is_empty()) {
- p_strings->push_back(tooltip);
- }
- }
-}
-
MenuBar::MenuBar() {
set_process_shortcut_input(true);
}
diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h
index 306fcc01ab..3436978a0e 100644
--- a/scene/gui/menu_bar.h
+++ b/scene/gui/menu_bar.h
@@ -170,7 +170,6 @@ public:
PopupMenu *get_menu_popup(int p_menu) const;
- virtual void get_translatable_strings(List<String> *p_strings) const override;
virtual String get_tooltip(const Point2 &p_pos) const override;
MenuBar();
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index f21b5f43c6..027c97b383 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -519,10 +519,6 @@ void OptionButton::show_popup() {
popup->popup();
}
-void OptionButton::get_translatable_strings(List<String> *p_strings) const {
- popup->get_translatable_strings(p_strings);
-}
-
void OptionButton::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "text" || p_property.name == "icon") {
p_property.usage = PROPERTY_USAGE_NONE;
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 9e409356e2..d8e2f3209d 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -125,8 +125,6 @@ public:
PopupMenu *get_popup() const;
void show_popup();
- virtual void get_translatable_strings(List<String> *p_strings) const override;
-
OptionButton(const String &p_text = String());
~OptionButton();
};
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 942e6fac7f..4e8a44dd63 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -395,10 +395,10 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Activate the item on release of either the left mouse button or
// any mouse button held down when the popup was opened.
// This allows for opening the popup and triggering an action in a single mouse click.
- if (button_idx == MouseButton::LEFT || (initial_button_mask & mouse_button_to_mask(button_idx)) != MouseButton::NONE) {
+ if (button_idx == MouseButton::LEFT || initial_button_mask.has_flag(mouse_button_to_mask(button_idx))) {
bool was_during_grabbed_click = during_grabbed_click;
during_grabbed_click = false;
- initial_button_mask = MouseButton::NONE;
+ initial_button_mask.clear();
// Disable clicks under a time threshold to avoid selection right when opening the popup.
uint64_t now = OS::get_singleton()->get_ticks_msec();
@@ -1841,14 +1841,6 @@ void PopupMenu::set_parent_rect(const Rect2 &p_rect) {
parent_rect = p_rect;
}
-void PopupMenu::get_translatable_strings(List<String> *p_strings) const {
- for (int i = 0; i < items.size(); i++) {
- if (!items[i].xl_text.is_empty()) {
- p_strings->push_back(items[i].xl_text);
- }
- }
-}
-
void PopupMenu::add_autohide_area(const Rect2 &p_area) {
autohide_areas.push_back(p_area);
}
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 1e56f8c192..bcc02a890f 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -92,7 +92,7 @@ class PopupMenu : public Popup {
Timer *submenu_timer = nullptr;
List<Rect2> autohide_areas;
Vector<Item> items;
- MouseButton initial_button_mask = MouseButton::NONE;
+ BitField<MouseButtonMask> initial_button_mask;
bool during_grabbed_click = false;
int mouse_over = -1;
int submenu_over = -1;
@@ -284,8 +284,6 @@ public:
virtual String get_tooltip(const Point2 &p_pos) const;
- virtual void get_translatable_strings(List<String> *p_strings) const override;
-
void add_autohide_area(const Rect2 &p_area);
void clear_autohide_areas();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index dea61fcf66..5ab64b35fd 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -3336,6 +3336,7 @@ void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment, int p
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_columns < 1);
ItemTable *item = memnew(ItemTable);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index ed24f30197..b678f46091 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -327,10 +327,10 @@ void ScrollContainer::_reposition_children() {
Size2 minsize = c->get_combined_minimum_size();
Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize);
- if (c->get_h_size_flags() & SIZE_EXPAND) {
+ if (c->get_h_size_flags().has_flag(SIZE_EXPAND)) {
r.size.width = MAX(size.width, minsize.width);
}
- if (c->get_v_size_flags() & SIZE_EXPAND) {
+ if (c->get_v_size_flags().has_flag(SIZE_EXPAND)) {
r.size.height = MAX(size.height, minsize.height);
}
r.position += ofs;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 29458ea16c..dfc03c666b 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -176,7 +176,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
if (drag.enabled) {
drag.diff_y += mm->get_relative().y;
double diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8) * SIGN(drag.diff_y);
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index ce6ccc3fa4..eca6cb3eef 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -1577,6 +1577,7 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_scroll_to_selected"), &TabBar::get_scroll_to_selected);
ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb);
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb);
+ ClassDB::bind_method(D_METHOD("clear_tabs"), &TabBar::clear_tabs);
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 0d7b055ce8..3457cfa94f 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -803,22 +803,6 @@ Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
return tab_bar->get_tab_button_icon(p_tab);
}
-void TabContainer::get_translatable_strings(List<String> *p_strings) const {
- Vector<Control *> controls = _get_tab_controls();
- for (int i = 0; i < controls.size(); i++) {
- Control *c = controls[i];
-
- if (!c->has_meta("_tab_name")) {
- continue;
- }
-
- String name = c->get_meta("_tab_name");
- if (!name.is_empty()) {
- p_strings->push_back(name);
- }
- }
-}
-
Size2 TabContainer::get_minimum_size() const {
Size2 ms;
@@ -991,7 +975,7 @@ void TabContainer::_bind_methods() {
TabContainer::TabContainer() {
tab_bar = memnew(TabBar);
- tab_bar->set_drag_forwarding(this);
+ tab_bar->set_drag_forwarding_compat(this);
add_child(tab_bar, false, INTERNAL_MODE_FRONT);
tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed));
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4a0205c2f4..3020e1fada 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -147,8 +147,6 @@ public:
virtual Size2 get_minimum_size() const override;
- virtual void get_translatable_strings(List<String> *p_strings) const override;
-
void set_popup(Node *p_popup);
Popup *get_popup() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 898c91a03c..108a533a74 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1906,7 +1906,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
mpos.x = get_size().x - mpos.x;
}
- if ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
+ if (mm->get_button_mask().has_flag(MouseButtonMask::LEFT) && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
_reset_caret_blink_timer();
if (draw_minimap && !dragging_selection) {
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index ac6d0cd453..20472ab46e 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -110,22 +110,45 @@ void TextureRect::_notification(int p_what) {
draw_texture_rect(texture, Rect2(offset, size), tile);
}
} break;
+ case NOTIFICATION_RESIZED: {
+ update_minimum_size();
+ } break;
}
}
Size2 TextureRect::get_minimum_size() const {
- if (!ignore_texture_size && !texture.is_null()) {
- return texture->get_size();
- } else {
- return Size2();
+ if (!texture.is_null()) {
+ switch (expand_mode) {
+ case EXPAND_KEEP_SIZE: {
+ return texture->get_size();
+ } break;
+ case EXPAND_IGNORE_SIZE: {
+ return Size2();
+ } break;
+ case EXPAND_FIT_WIDTH: {
+ return Size2(get_size().y, 0);
+ } break;
+ case EXPAND_FIT_WIDTH_PROPORTIONAL: {
+ real_t ratio = real_t(texture->get_width()) / texture->get_height();
+ return Size2(get_size().y * ratio, 0);
+ } break;
+ case EXPAND_FIT_HEIGHT: {
+ return Size2(0, get_size().x);
+ } break;
+ case EXPAND_FIT_HEIGHT_PROPORTIONAL: {
+ real_t ratio = real_t(texture->get_height()) / texture->get_width();
+ return Size2(0, get_size().x * ratio);
+ } break;
+ }
}
+ return Size2();
}
void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TextureRect::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &TextureRect::get_texture);
- ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureRect::set_ignore_texture_size);
- ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureRect::get_ignore_texture_size);
+ ClassDB::bind_method(D_METHOD("set_expand_mode", "expand_mode"), &TextureRect::set_expand_mode);
+ ClassDB::bind_method(D_METHOD("get_expand_mode"), &TextureRect::get_expand_mode);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureRect::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureRect::is_flipped_h);
ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureRect::set_flip_v);
@@ -134,11 +157,18 @@ void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size"), "set_ignore_texture_size", "get_ignore_texture_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "expand_mode", PROPERTY_HINT_ENUM, "Keep Size,Ignore Size,Fit Width,Fit Width Proportional,Fit Height,Fit Height Proportional"), "set_expand_mode", "get_expand_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
+ BIND_ENUM_CONSTANT(EXPAND_KEEP_SIZE);
+ BIND_ENUM_CONSTANT(EXPAND_IGNORE_SIZE);
+ BIND_ENUM_CONSTANT(EXPAND_FIT_WIDTH);
+ BIND_ENUM_CONSTANT(EXPAND_FIT_WIDTH_PROPORTIONAL);
+ BIND_ENUM_CONSTANT(EXPAND_FIT_HEIGHT);
+ BIND_ENUM_CONSTANT(EXPAND_FIT_HEIGHT_PROPORTIONAL);
+
BIND_ENUM_CONSTANT(STRETCH_SCALE);
BIND_ENUM_CONSTANT(STRETCH_TILE);
BIND_ENUM_CONSTANT(STRETCH_KEEP);
@@ -148,6 +178,16 @@ void TextureRect::_bind_methods() {
BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT_COVERED);
}
+#ifndef DISABLE_DEPRECATED
+bool TextureRect::_set(const StringName &p_name, const Variant &p_value) {
+ if ((p_name == SNAME("expand") || p_name == SNAME("ignore_texture_size")) && p_value.operator bool()) {
+ expand_mode = EXPAND_IGNORE_SIZE;
+ return true;
+ }
+ return false;
+}
+#endif
+
void TextureRect::_texture_changed() {
if (texture.is_valid()) {
queue_redraw();
@@ -178,18 +218,18 @@ Ref<Texture2D> TextureRect::get_texture() const {
return texture;
}
-void TextureRect::set_ignore_texture_size(bool p_ignore) {
- if (ignore_texture_size == p_ignore) {
+void TextureRect::set_expand_mode(ExpandMode p_mode) {
+ if (expand_mode == p_mode) {
return;
}
- ignore_texture_size = p_ignore;
+ expand_mode = p_mode;
queue_redraw();
update_minimum_size();
}
-bool TextureRect::get_ignore_texture_size() const {
- return ignore_texture_size;
+TextureRect::ExpandMode TextureRect::get_expand_mode() const {
+ return expand_mode;
}
void TextureRect::set_stretch_mode(StretchMode p_mode) {
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index c56fee91e1..2425c6094b 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -37,6 +37,15 @@ class TextureRect : public Control {
GDCLASS(TextureRect, Control);
public:
+ enum ExpandMode {
+ EXPAND_KEEP_SIZE,
+ EXPAND_IGNORE_SIZE,
+ EXPAND_FIT_WIDTH,
+ EXPAND_FIT_WIDTH_PROPORTIONAL,
+ EXPAND_FIT_HEIGHT,
+ EXPAND_FIT_HEIGHT_PROPORTIONAL,
+ };
+
enum StretchMode {
STRETCH_SCALE,
STRETCH_TILE,
@@ -48,10 +57,10 @@ public:
};
private:
- bool ignore_texture_size = false;
bool hflip = false;
bool vflip = false;
Ref<Texture2D> texture;
+ ExpandMode expand_mode = EXPAND_KEEP_SIZE;
StretchMode stretch_mode = STRETCH_SCALE;
void _texture_changed();
@@ -60,13 +69,16 @@ protected:
void _notification(int p_what);
virtual Size2 get_minimum_size() const override;
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ bool _set(const StringName &p_name, const Variant &p_value);
+#endif
public:
void set_texture(const Ref<Texture2D> &p_tex);
Ref<Texture2D> get_texture() const;
- void set_ignore_texture_size(bool p_ignore);
- bool get_ignore_texture_size() const;
+ void set_expand_mode(ExpandMode p_mode);
+ ExpandMode get_expand_mode() const;
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const;
@@ -81,6 +93,7 @@ public:
~TextureRect();
};
+VARIANT_ENUM_CAST(TextureRect::ExpandMode);
VARIANT_ENUM_CAST(TextureRect::StretchMode);
#endif // TEXTURE_RECT_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index ace3edfcb0..2138f10ad0 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -657,7 +657,7 @@ int TreeItem::get_custom_minimum_height() const {
/* Item manipulation */
-TreeItem *TreeItem::create_child(int p_idx) {
+TreeItem *TreeItem::create_child(int p_index) {
TreeItem *ti = memnew(TreeItem(tree));
if (tree) {
ti->cells.resize(tree->columns.size());
@@ -669,7 +669,7 @@ TreeItem *TreeItem::create_child(int p_idx) {
int idx = 0;
while (c) {
- if (idx++ == p_idx) {
+ if (idx++ == p_index) {
c->prev = ti;
ti->next = c;
break;
@@ -683,7 +683,7 @@ TreeItem *TreeItem::create_child(int p_idx) {
ti->prev = l_prev;
if (!children_cache.is_empty()) {
if (ti->next) {
- children_cache.insert(p_idx, ti);
+ children_cache.insert(p_index, ti);
} else {
children_cache.append(ti);
}
@@ -826,15 +826,15 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) {
return next_item;
}
-TreeItem *TreeItem::get_child(int p_idx) {
+TreeItem *TreeItem::get_child(int p_index) {
_create_children_cache();
- if (p_idx < 0) {
- p_idx += children_cache.size();
+ if (p_index < 0) {
+ p_index += children_cache.size();
}
- ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr);
+ ERR_FAIL_INDEX_V(p_index, children_cache.size(), nullptr);
- return children_cache.get(p_idx);
+ return children_cache.get(p_index);
}
int TreeItem::get_visible_child_count() {
@@ -1058,28 +1058,28 @@ int TreeItem::get_button_count(int p_column) const {
return cells[p_column].buttons.size();
}
-Ref<Texture2D> TreeItem::get_button(int p_column, int p_idx) const {
+Ref<Texture2D> TreeItem::get_button(int p_column, int p_index) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Texture2D>());
- ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), Ref<Texture2D>());
- return cells[p_column].buttons[p_idx].texture;
+ ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), Ref<Texture2D>());
+ return cells[p_column].buttons[p_index].texture;
}
-String TreeItem::get_button_tooltip_text(int p_column, int p_idx) const {
+String TreeItem::get_button_tooltip_text(int p_column, int p_index) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), String());
- ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), String());
- return cells[p_column].buttons[p_idx].tooltip;
+ ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), String());
+ return cells[p_column].buttons[p_index].tooltip;
}
-int TreeItem::get_button_id(int p_column, int p_idx) const {
+int TreeItem::get_button_id(int p_column, int p_index) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
- ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1);
- return cells[p_column].buttons[p_idx].id;
+ ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), -1);
+ return cells[p_column].buttons[p_index].id;
}
-void TreeItem::erase_button(int p_column, int p_idx) {
+void TreeItem::erase_button(int p_column, int p_index) {
ERR_FAIL_INDEX(p_column, cells.size());
- ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
- cells.write[p_column].buttons.remove_at(p_idx);
+ ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
+ cells.write[p_column].buttons.remove_at(p_index);
_changed_notify(p_column);
}
@@ -1094,52 +1094,52 @@ int TreeItem::get_button_by_id(int p_column, int p_id) const {
return -1;
}
-void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button) {
+void TreeItem::set_button(int p_column, int p_index, const Ref<Texture2D> &p_button) {
ERR_FAIL_COND(p_button.is_null());
ERR_FAIL_INDEX(p_column, cells.size());
- ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
+ ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
- if (cells[p_column].buttons[p_idx].texture == p_button) {
+ if (cells[p_column].buttons[p_index].texture == p_button) {
return;
}
- cells.write[p_column].buttons.write[p_idx].texture = p_button;
+ cells.write[p_column].buttons.write[p_index].texture = p_button;
cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
}
-void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) {
+void TreeItem::set_button_color(int p_column, int p_index, const Color &p_color) {
ERR_FAIL_INDEX(p_column, cells.size());
- ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
+ ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
- if (cells[p_column].buttons[p_idx].color == p_color) {
+ if (cells[p_column].buttons[p_index].color == p_color) {
return;
}
- cells.write[p_column].buttons.write[p_idx].color = p_color;
+ cells.write[p_column].buttons.write[p_index].color = p_color;
_changed_notify(p_column);
}
-void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) {
+void TreeItem::set_button_disabled(int p_column, int p_index, bool p_disabled) {
ERR_FAIL_INDEX(p_column, cells.size());
- ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
+ ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
- if (cells[p_column].buttons[p_idx].disabled == p_disabled) {
+ if (cells[p_column].buttons[p_index].disabled == p_disabled) {
return;
}
- cells.write[p_column].buttons.write[p_idx].disabled = p_disabled;
+ cells.write[p_column].buttons.write[p_index].disabled = p_disabled;
cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
}
-bool TreeItem::is_button_disabled(int p_column, int p_idx) const {
+bool TreeItem::is_button_disabled(int p_column, int p_index) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), false);
- ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), false);
+ ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), false);
- return cells[p_column].buttons[p_idx].disabled;
+ return cells[p_column].buttons[p_index].disabled;
}
void TreeItem::set_editable(int p_column, bool p_editable) {
@@ -1497,15 +1497,15 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
- ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_idx"), &TreeItem::get_button_tooltip_text);
- ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id);
+ ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_index"), &TreeItem::get_button_tooltip_text);
+ ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_index"), &TreeItem::get_button_id);
ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id);
- ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button);
- ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button);
- ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button);
- ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled);
- ClassDB::bind_method(D_METHOD("set_button_color", "column", "button_idx", "color"), &TreeItem::set_button_color);
- ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled);
+ ClassDB::bind_method(D_METHOD("get_button", "column", "button_index"), &TreeItem::get_button);
+ ClassDB::bind_method(D_METHOD("set_button", "column", "button_index", "button"), &TreeItem::set_button);
+ ClassDB::bind_method(D_METHOD("erase_button", "column", "button_index"), &TreeItem::erase_button);
+ ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_index", "disabled"), &TreeItem::set_button_disabled);
+ ClassDB::bind_method(D_METHOD("set_button_color", "column", "button_index", "color"), &TreeItem::set_button_color);
+ ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_index"), &TreeItem::is_button_disabled);
ClassDB::bind_method(D_METHOD("set_tooltip_text", "column", "tooltip"), &TreeItem::set_tooltip_text);
ClassDB::bind_method(D_METHOD("get_tooltip_text", "column"), &TreeItem::get_tooltip_text);
@@ -1518,7 +1518,7 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding);
ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled);
- ClassDB::bind_method(D_METHOD("create_child", "idx"), &TreeItem::create_child, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("create_child", "index"), &TreeItem::create_child, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree);
ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next);
@@ -1529,7 +1529,7 @@ void TreeItem::_bind_methods() {
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));
- ClassDB::bind_method(D_METHOD("get_child", "idx"), &TreeItem::get_child);
+ ClassDB::bind_method(D_METHOD("get_child", "index"), &TreeItem::get_child);
ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count);
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
@@ -4108,14 +4108,14 @@ Size2 Tree::get_minimum_size() const {
}
}
-TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) {
+TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) {
ERR_FAIL_COND_V(blocked > 0, nullptr);
TreeItem *ti = nullptr;
if (p_parent) {
ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent");
- ti = p_parent->create_child(p_idx);
+ ti = p_parent->create_child(p_index);
} else {
if (!root) {
// No root exists, make the given item the new root.
@@ -4126,7 +4126,7 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) {
root = ti;
} else {
// Root exists, append or insert to root.
- ti = create_item(root, p_idx);
+ ti = create_item(root, p_index);
}
}
@@ -4850,7 +4850,11 @@ void Tree::_do_incr_search(const String &p_add) {
return;
}
- item->select(col);
+ if (select_mode == SELECT_MULTI) {
+ item->set_as_cursor(col);
+ } else {
+ item->select(col);
+ }
ensure_cursor_is_visible();
}
@@ -5158,7 +5162,7 @@ bool Tree::get_allow_reselect() const {
void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Tree::clear);
- ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("create_item", "parent", "index"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
@@ -5180,6 +5184,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode);
ClassDB::bind_method(D_METHOD("get_select_mode"), &Tree::get_select_mode);
+ ClassDB::bind_method(D_METHOD("deselect_all"), &Tree::deselect_all);
ClassDB::bind_method(D_METHOD("set_columns", "amount"), &Tree::set_columns);
ClassDB::bind_method(D_METHOD("get_columns"), &Tree::get_columns);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index e0ed5fdfb5..ec639ce439 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -247,15 +247,15 @@ public:
void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = "");
int get_button_count(int p_column) const;
- String get_button_tooltip_text(int p_column, int p_idx) const;
- Ref<Texture2D> get_button(int p_column, int p_idx) const;
- int get_button_id(int p_column, int p_idx) const;
- void erase_button(int p_column, int p_idx);
+ String get_button_tooltip_text(int p_column, int p_index) const;
+ Ref<Texture2D> get_button(int p_column, int p_index) const;
+ int get_button_id(int p_column, int p_index) const;
+ void erase_button(int p_column, int p_index);
int get_button_by_id(int p_column, int p_id) const;
- void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button);
- void set_button_color(int p_column, int p_idx, const Color &p_color);
- void set_button_disabled(int p_column, int p_idx, bool p_disabled);
- bool is_button_disabled(int p_column, int p_idx) const;
+ void set_button(int p_column, int p_index, const Ref<Texture2D> &p_button);
+ void set_button_color(int p_column, int p_index, const Color &p_color);
+ void set_button_disabled(int p_column, int p_index, bool p_disabled);
+ bool is_button_disabled(int p_column, int p_index) const;
/* range works for mode number or mode combo */
@@ -329,7 +329,7 @@ public:
/* Item manipulation */
- TreeItem *create_child(int p_idx = -1);
+ TreeItem *create_child(int p_index = -1);
Tree *get_tree() const;
@@ -341,7 +341,7 @@ public:
TreeItem *get_prev_visible(bool p_wrap = false);
TreeItem *get_next_visible(bool p_wrap = false);
- TreeItem *get_child(int p_idx);
+ TreeItem *get_child(int p_index);
int get_visible_child_count();
int get_child_count();
TypedArray<TreeItem> get_children();
@@ -647,7 +647,7 @@ public:
void clear();
- TreeItem *create_item(TreeItem *p_parent = nullptr, int p_idx = -1);
+ TreeItem *create_item(TreeItem *p_parent = nullptr, int p_index = -1);
TreeItem *get_root() const;
TreeItem *get_last_item() const;
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index 9a0c93a1df..e8d54e6937 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -114,7 +114,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
if (k.is_valid()) {
if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) {
pan_key_pressed = k->is_pressed();
- if (simple_panning_enabled || (Input::get_singleton()->get_mouse_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
+ if (simple_panning_enabled || Input::get_singleton()->get_mouse_button_mask().has_flag(MouseButtonMask::LEFT)) {
is_dragging = pan_key_pressed;
}
return true;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index a04c299705..9be1a6431b 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -195,7 +195,15 @@ void CanvasItem::_top_level_raise_self() {
}
void CanvasItem::_enter_canvas() {
- if ((!Object::cast_to<CanvasItem>(get_parent())) || top_level) {
+ // Resolves to nullptr if the node is toplevel.
+ CanvasItem *parent_item = get_parent_item();
+
+ if (parent_item) {
+ canvas_layer = parent_item->canvas_layer;
+ RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent_item->get_canvas_item());
+ RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index());
+ RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer);
+ } else {
Node *n = this;
canvas_layer = nullptr;
@@ -231,13 +239,6 @@ void CanvasItem::_enter_canvas() {
}
get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self"));
-
- } else {
- CanvasItem *parent = get_parent_item();
- canvas_layer = parent->canvas_layer;
- RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item());
- RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index());
- RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer);
}
pending_update = false;
@@ -320,8 +321,7 @@ void CanvasItem::_notification(int p_what) {
if (canvas_group != StringName()) {
get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self");
} else {
- CanvasItem *p = get_parent_item();
- ERR_FAIL_COND(!p);
+ ERR_FAIL_COND_MSG(!get_parent_item(), "Moved child is in incorrect state (no canvas group, no canvas item parent).");
RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index());
}
} break;
@@ -483,7 +483,7 @@ bool CanvasItem::is_y_sort_enabled() const {
return y_sort_enabled;
}
-void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash) {
+void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
float length = (p_to - p_from).length();
@@ -492,14 +492,20 @@ void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, cons
return;
}
- Point2 off = p_from;
Vector2 step = p_dash * (p_to - p_from).normalized();
- int steps = length / p_dash / 2;
- for (int i = 0; i < steps; i++) {
- RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, off, (off + step), p_color, p_width);
- off += 2 * step;
+ int steps = (p_aligned) ? Math::ceil(length / p_dash) : Math::floor(length / p_dash);
+ if (steps % 2 == 0) {
+ steps--;
+ }
+
+ Point2 off = p_from;
+ if (p_aligned) {
+ off += (p_to - p_from).normalized() * (length - steps * p_dash) / 2.0;
+ }
+ for (int i = 0; i < steps; i += 2) {
+ RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, (i == 0) ? p_from : off, (p_aligned && i == steps - 1) ? p_to : (off + step), p_color, p_width);
+ off += step * 2;
}
- RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, off, p_to, p_color, p_width);
}
void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, bool p_antialiased) {
@@ -552,7 +558,7 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
if (p_filled) {
- if (p_width != 1.0) {
+ if (p_width != -1.0) {
WARN_PRINT("The draw_rect() \"width\" argument has no effect when \"filled\" is \"true\".");
}
@@ -561,7 +567,7 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil
// Thick lines are offset depending on their width to avoid partial overlapping.
// Thin lines don't require an offset, so don't apply one in this case
real_t offset;
- if (p_width >= 2) {
+ if (p_width >= 0) {
offset = p_width / 2.0;
} else {
offset = 0.0;
@@ -641,11 +647,11 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p
p_style_box->draw(canvas_item, p_rect);
}
-void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, real_t p_width) {
+void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, 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.");
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width);
+ RenderingServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid);
}
void CanvasItem::draw_set_transform(const Point2 &p_offset, real_t p_rot, const Size2 &p_scale) {
@@ -962,14 +968,14 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent);
ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled);
- ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash"), &CanvasItem::draw_dashed_line, DEFVAL(1.0), DEFVAL(2.0));
+ ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(-1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash", "aligned"), &CanvasItem::draw_dashed_line, DEFVAL(-1.0), DEFVAL(2.0), DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(1.0));
- ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0));
- ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(-1.0));
+ ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(-1.0));
+ ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(-1.0));
ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
@@ -977,7 +983,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range", "scale"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0), DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("draw_lcd_texture_rect_region", "texture", "rect", "src_rect", "modulate"), &CanvasItem::draw_lcd_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box);
- ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 47b40c9b46..b2c12b2ea2 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -247,14 +247,14 @@ public:
/* DRAWING API */
- void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0);
- void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
+ void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, real_t p_dash = 2.0, bool p_aligned = true);
+ void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0, bool p_antialiased = false);
void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false);
- void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0);
- void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0);
- void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = 1.0);
+ void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0);
+ void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0);
+ void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = -1.0);
void draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color);
void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
@@ -262,7 +262,7 @@ public:
void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0, double p_scale = 1.0);
void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1));
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
- void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1);
+ void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>());
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 3544ec4c83..eb57ccfef1 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -39,6 +39,7 @@
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/main/multiplayer_api.h"
+#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -1467,6 +1468,14 @@ Node *Node::find_parent(const String &p_pattern) const {
return nullptr;
}
+Window *Node::get_window() const {
+ Viewport *vp = get_viewport();
+ if (vp) {
+ return vp->get_base_window();
+ }
+ return nullptr;
+}
+
bool Node::is_ancestor_of(const Node *p_node) const {
ERR_FAIL_NULL_V(p_node, false);
Node *p = p_node->data.parent;
@@ -2858,6 +2867,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_process_internal", "enable"), &Node::set_physics_process_internal);
ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal);
+ ClassDB::bind_method(D_METHOD("get_window"), &Node::get_window);
ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree);
ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween);
diff --git a/scene/main/node.h b/scene/main/node.h
index 398465c3cd..dbdcca6170 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -37,6 +37,7 @@
#include "scene/main/scene_tree.h"
class Viewport;
+class Window;
class SceneState;
class Tween;
class PropertyTweener;
@@ -321,6 +322,8 @@ public:
Node *get_parent() const;
Node *find_parent(const String &p_pattern) const;
+ Window *get_window() const;
+
_FORCE_INLINE_ SceneTree *get_tree() const {
ERR_FAIL_COND_V(!data.tree, nullptr);
return data.tree;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 73d85a0660..fbe11c94d1 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1131,11 +1131,10 @@ Error SceneTree::change_scene_to_file(const String &p_path) {
}
Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) {
- Node *new_scene = nullptr;
- if (p_scene.is_valid()) {
- new_scene = p_scene->instantiate();
- ERR_FAIL_COND_V(!new_scene, ERR_CANT_CREATE);
- }
+ ERR_FAIL_COND_V_MSG(p_scene.is_null(), ERR_INVALID_PARAMETER, "Can't change to a null scene. Use unload_current_scene() if you wish to unload it.");
+
+ Node *new_scene = p_scene->instantiate();
+ ERR_FAIL_COND_V(!new_scene, ERR_CANT_CREATE);
call_deferred(SNAME("_change_scene"), new_scene);
return OK;
@@ -1147,6 +1146,13 @@ Error SceneTree::reload_current_scene() {
return change_scene_to_file(fname);
}
+void SceneTree::unload_current_scene() {
+ if (current_scene) {
+ memdelete(current_scene);
+ current_scene = nullptr;
+ }
+}
+
void SceneTree::add_current_scene(Node *p_current) {
current_scene = p_current;
root->add_child(p_current);
@@ -1297,6 +1303,7 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("change_scene_to_packed", "packed_scene"), &SceneTree::change_scene_to_packed);
ClassDB::bind_method(D_METHOD("reload_current_scene"), &SceneTree::reload_current_scene);
+ ClassDB::bind_method(D_METHOD("unload_current_scene"), &SceneTree::unload_current_scene);
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
@@ -1396,6 +1403,7 @@ SceneTree::SceneTree() {
// Create with mainloop.
root = memnew(Window);
+ root->set_min_size(Size2i(64, 64)); // Define a very small minimum window size to prevent bugs such as GH-37242.
root->set_process_mode(Node::PROCESS_MODE_PAUSABLE);
root->set_name("root");
root->set_title(GLOBAL_GET("application/config/name"));
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 77cfe0a04a..fc185b4f37 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -361,6 +361,7 @@ public:
Error change_scene_to_file(const String &p_path);
Error change_scene_to_packed(const Ref<PackedScene> &p_scene);
Error reload_current_scene();
+ void unload_current_scene();
Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false);
Ref<Tween> create_tween();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 1287f69bef..ef58386b45 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -603,9 +603,9 @@ void Viewport::_process_picking() {
physics_last_mouse_state.meta = mb->is_meta_pressed();
if (mb->is_pressed()) {
- physics_last_mouse_state.mouse_mask |= mouse_button_to_mask(mb->get_button_index());
+ physics_last_mouse_state.mouse_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- physics_last_mouse_state.mouse_mask &= ~mouse_button_to_mask(mb->get_button_index());
+ physics_last_mouse_state.mouse_mask.clear_flag(mouse_button_to_mask(mb->get_button_index()));
// If touch mouse raised, assume we don't know last mouse pos until new events come
if (mb->get_device() == InputEvent::DEVICE_ID_TOUCH_MOUSE) {
@@ -1256,6 +1256,7 @@ void Viewport::_gui_show_tooltip() {
panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
panel->set_flag(Window::FLAG_POPUP, false);
+ panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
panel->set_wrap_controls(true);
panel->add_child(base_tooltip);
panel->gui_parent = this;
@@ -1401,7 +1402,7 @@ Control *Viewport::gui_find_control(const Point2 &p_global) {
xform = sw->get_canvas_transform();
}
- Control *ret = _gui_find_control_at_pos(sw, p_global, xform, gui.focus_inv_xform);
+ Control *ret = _gui_find_control_at_pos(sw, p_global, xform);
if (ret) {
return ret;
}
@@ -1410,7 +1411,7 @@ Control *Viewport::gui_find_control(const Point2 &p_global) {
return nullptr;
}
-Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform) {
+Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform) {
if (!p_node->is_visible()) {
return nullptr; // Canvas item hidden, discard.
}
@@ -1430,7 +1431,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
continue;
}
- Control *ret = _gui_find_control_at_pos(ci, p_global, matrix, r_inv_xform);
+ Control *ret = _gui_find_control_at_pos(ci, p_global, matrix);
if (ret) {
return ret;
}
@@ -1448,7 +1449,6 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
Control *drag_preview = _gui_get_drag_preview();
if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) {
- r_inv_xform = matrix;
return c;
}
@@ -1495,20 +1495,20 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Point2 mpos = mb->get_position();
if (mb->is_pressed()) {
- Size2 pos = mpos;
- if (gui.mouse_focus_mask != MouseButton::NONE) {
+ if (!gui.mouse_focus_mask.is_empty()) {
// Do not steal mouse focus and stuff while a focus mask exists.
- gui.mouse_focus_mask |= mouse_button_to_mask(mb->get_button_index());
+ gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- gui.mouse_focus = gui_find_control(pos);
+ gui.mouse_focus = gui_find_control(mpos);
gui.last_mouse_focus = gui.mouse_focus;
if (!gui.mouse_focus) {
- gui.mouse_focus_mask = MouseButton::NONE;
+ gui.mouse_focus_mask.clear();
return;
}
- gui.mouse_focus_mask = mouse_button_to_mask(mb->get_button_index());
+ gui.mouse_focus_mask.clear();
+ gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
if (mb->get_button_index() == MouseButton::LEFT) {
gui.drag_accum = Vector2();
@@ -1518,10 +1518,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
mb = mb->xformed_by(Transform2D()); // Make a copy of the event.
- mb->set_global_position(pos);
-
- pos = gui.focus_inv_xform.xform(pos);
-
+ Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
mb->set_position(pos);
#ifdef DEBUG_ENABLED
@@ -1578,18 +1575,15 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos);
}
- gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
+ gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
if (!gui.mouse_focus) {
// Release event is only sent if a mouse focus (previously pressed button) exists.
return;
}
- Size2 pos = mpos;
-
mb = mb->xformed_by(Transform2D()); // Make a copy.
- mb->set_global_position(pos);
- pos = gui.focus_inv_xform.xform(pos);
+ Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
mb->set_position(pos);
Control *mouse_focus = gui.mouse_focus;
@@ -1597,7 +1591,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
// Disable mouse focus if needed before calling input,
// this makes popups on mouse press event work better,
// as the release will never be received otherwise.
- if (gui.mouse_focus_mask == MouseButton::NONE) {
+ if (gui.mouse_focus_mask.is_empty()) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
}
@@ -1619,7 +1613,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Point2 mpos = mm->get_position();
// Drag & drop.
- if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
gui.drag_accum += mm->get_relative();
float len = gui.drag_accum.length();
if (len > 10) {
@@ -1633,7 +1627,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = MouseButton::NONE;
+ gui.mouse_focus_mask.clear();
break;
} else {
Control *drag_preview = _gui_get_drag_preview();
@@ -1701,7 +1695,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
mm->set_velocity(velocity);
mm->set_relative(rel);
- if (mm->get_button_mask() == MouseButton::NONE) {
+ if (mm->get_button_mask().is_empty()) {
// Nothing pressed.
bool is_tooltip_shown = false;
@@ -1744,7 +1738,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *c = over;
Vector2 cpos = pos;
while (c) {
- if (gui.mouse_focus_mask != MouseButton::NONE || c->has_point(cpos)) {
+ if (!gui.mouse_focus_mask.is_empty() || c->has_point(cpos)) {
cursor_shape = c->get_cursor_shape(cpos);
} else {
cursor_shape = Control::CURSOR_ARROW;
@@ -1892,11 +1886,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
bool stopped = false;
if (over->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
- if (over == gui.mouse_focus) {
- pos = gui.focus_inv_xform.xform(pos);
- } else {
- pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
- }
+ pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
touch_event->set_position(pos);
stopped = _gui_call_input(over, touch_event);
}
@@ -1911,11 +1901,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
if (over && over->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
- if (over == gui.last_mouse_focus) {
- pos = gui.focus_inv_xform.xform(pos);
- } else {
- pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
- }
+ pos = gui.last_mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(pos);
touch_event->set_position(pos);
stopped = _gui_call_input(over, touch_event);
@@ -1941,11 +1927,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
bool stopped = false;
if (over->can_process()) {
gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy.
- if (over == gui.mouse_focus) {
- pos = gui.focus_inv_xform.xform(pos);
- } else {
- pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
- }
+ pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
gesture_event->set_position(pos);
stopped = _gui_call_input(over, gesture_event);
}
@@ -2103,7 +2085,7 @@ void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
if (!mb->is_pressed()) {
- gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
+ gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
}
}
}
@@ -2194,7 +2176,7 @@ void Viewport::_gui_remove_control(Control *p_control) {
if (gui.mouse_focus == p_control) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = MouseButton::NONE;
+ gui.mouse_focus_mask.clear();
}
if (gui.last_mouse_focus == p_control) {
gui.last_mouse_focus = nullptr;
@@ -2263,10 +2245,10 @@ void Viewport::_drop_mouse_over() {
void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
- MouseButton mask = gui.mouse_focus_mask;
+ BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = MouseButton::NONE;
+ gui.mouse_focus_mask.clear();
for (int i = 0; i < 3; i++) {
if ((int)mask & (1 << i)) {
@@ -2374,7 +2356,7 @@ void Viewport::_post_gui_grab_click_focus() {
return;
}
- MouseButton mask = gui.mouse_focus_mask;
+ BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
@@ -2392,7 +2374,6 @@ void Viewport::_post_gui_grab_click_focus() {
}
gui.mouse_focus = focus_grabber;
- gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse();
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
for (int i = 0; i < 3; i++) {
@@ -3239,7 +3220,7 @@ void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
- gui.mouse_focus_mask = MouseButton::NONE;
+ gui.mouse_focus_mask.clear();
}
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index ca6f3163df..603e92b071 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -259,7 +259,7 @@ private:
bool control = false;
bool shift = false;
bool meta = false;
- MouseButton mouse_mask = MouseButton::NONE;
+ BitField<MouseButtonMask> mouse_mask;
} physics_last_mouse_state;
@@ -358,7 +358,7 @@ private:
Control *mouse_focus = nullptr;
Control *last_mouse_focus = nullptr;
Control *mouse_click_grabber = nullptr;
- MouseButton mouse_focus_mask = MouseButton::NONE;
+ BitField<MouseButtonMask> mouse_focus_mask;
Control *key_focus = nullptr;
Control *mouse_over = nullptr;
Control *drag_mouse_over = nullptr;
@@ -374,7 +374,6 @@ private:
ObjectID drag_preview_id;
Ref<SceneTreeTimer> tooltip_timer;
double tooltip_delay = 0.0;
- Transform2D focus_inv_xform;
bool roots_order_dirty = false;
List<Control *> roots;
int canvas_sort_index = 0; //for sorting items with canvas as root
@@ -403,7 +402,7 @@ private:
void _gui_call_notification(Control *p_control, int p_what);
void _gui_sort_roots();
- Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform);
+ Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform);
void _gui_input_event(Ref<InputEvent> p_event);
void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2());
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index c5dbfffd7b..4baedc8c14 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -489,6 +489,7 @@ void Window::_make_window() {
ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID);
DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id);
DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
+ DisplayServer::get_singleton()->window_set_mouse_passthrough(mpath, window_id);
String tr_title = atr(title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
@@ -1235,6 +1236,18 @@ DisplayServer::WindowID Window::get_window_id() const {
return window_id;
}
+void Window::set_mouse_passthrough_polygon(const Vector<Vector2> &p_region) {
+ mpath = p_region;
+ if (window_id == DisplayServer::INVALID_WINDOW_ID) {
+ return;
+ }
+ DisplayServer::get_singleton()->window_set_mouse_passthrough(mpath, window_id);
+}
+
+Vector<Vector2> Window::get_mouse_passthrough_polygon() const {
+ return mpath;
+}
+
void Window::set_wrap_controls(bool p_enable) {
wrap_controls = p_enable;
if (wrap_controls) {
@@ -2163,6 +2176,9 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &Window::set_use_font_oversampling);
ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &Window::is_using_font_oversampling);
+ ClassDB::bind_method(D_METHOD("set_mouse_passthrough_polygon", "polygon"), &Window::set_mouse_passthrough_polygon);
+ ClassDB::bind_method(D_METHOD("get_mouse_passthrough_polygon"), &Window::get_mouse_passthrough_polygon);
+
ClassDB::bind_method(D_METHOD("set_wrap_controls", "enable"), &Window::set_wrap_controls);
ClassDB::bind_method(D_METHOD("is_wrapping_controls"), &Window::is_wrapping_controls);
ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed);
@@ -2243,6 +2259,8 @@ void Window::_bind_methods() {
}
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen", PROPERTY_HINT_ENUM, screen_hints), "set_current_screen", "get_current_screen");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "mouse_passthrough_polygon"), "set_mouse_passthrough_polygon", "get_mouse_passthrough_polygon");
+
ADD_GROUP("Flags", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_controls"), "set_wrap_controls", "is_wrapping_controls");
@@ -2255,6 +2273,7 @@ void Window::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "extend_to_title"), "set_flag", "get_flag", FLAG_EXTEND_TO_TITLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "mouse_passthrough"), "set_flag", "get_flag", FLAG_MOUSE_PASSTHROUGH);
ADD_GROUP("Limits", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size", PROPERTY_HINT_NONE, "suffix:px"), "set_min_size", "get_min_size");
@@ -2302,6 +2321,7 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_NO_FOCUS);
BIND_ENUM_CONSTANT(FLAG_POPUP);
BIND_ENUM_CONSTANT(FLAG_EXTEND_TO_TITLE);
+ BIND_ENUM_CONSTANT(FLAG_MOUSE_PASSTHROUGH);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED);
diff --git a/scene/main/window.h b/scene/main/window.h
index 97f8450628..d819ca7c60 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -59,6 +59,7 @@ public:
FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS,
FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP,
FLAG_EXTEND_TO_TITLE = DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE,
+ FLAG_MOUSE_PASSTHROUGH = DisplayServer::WINDOW_FLAG_MOUSE_PASSTHROUGH,
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
};
@@ -101,6 +102,7 @@ private:
mutable Size2i size = Size2i(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE);
mutable Size2i min_size;
mutable Size2i max_size;
+ mutable Vector<Vector2> mpath;
mutable Mode mode = MODE_WINDOWED;
mutable bool flags[FLAG_MAX] = {};
bool visible = true;
@@ -281,10 +283,14 @@ public:
void set_use_font_oversampling(bool p_oversampling);
bool is_using_font_oversampling() const;
+ void set_mouse_passthrough_polygon(const Vector<Vector2> &p_region);
+ Vector<Vector2> get_mouse_passthrough_polygon() const;
+
void set_wrap_controls(bool p_enable);
bool is_wrapping_controls() const;
void child_controls_changed();
+ Window *get_exclusive_child() const { return exclusive_child; };
Window *get_parent_visible_window() const;
Viewport *get_parent_viewport() const;
void popup(const Rect2i &p_rect = Rect2i());
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 00c538976f..d56b94b6fe 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -184,15 +184,7 @@
#include "scene/resources/skeleton_modification_2d_physicalbones.h"
#include "scene/resources/skeleton_modification_2d_stackholder.h"
#include "scene/resources/skeleton_modification_2d_twoboneik.h"
-#include "scene/resources/skeleton_modification_3d.h"
-#include "scene/resources/skeleton_modification_3d_ccdik.h"
-#include "scene/resources/skeleton_modification_3d_fabrik.h"
-#include "scene/resources/skeleton_modification_3d_jiggle.h"
-#include "scene/resources/skeleton_modification_3d_lookat.h"
-#include "scene/resources/skeleton_modification_3d_stackholder.h"
-#include "scene/resources/skeleton_modification_3d_twoboneik.h"
#include "scene/resources/skeleton_modification_stack_2d.h"
-#include "scene/resources/skeleton_modification_stack_3d.h"
#include "scene/resources/skeleton_profile.h"
#include "scene/resources/sky.h"
#include "scene/resources/sky_material.h"
@@ -833,15 +825,6 @@ void register_scene_types() {
GDREGISTER_CLASS(ConvexPolygonShape3D);
GDREGISTER_CLASS(ConcavePolygonShape3D);
- GDREGISTER_CLASS(SkeletonModificationStack3D);
- GDREGISTER_CLASS(SkeletonModification3D);
- GDREGISTER_CLASS(SkeletonModification3DLookAt);
- GDREGISTER_CLASS(SkeletonModification3DCCDIK);
- GDREGISTER_CLASS(SkeletonModification3DFABRIK);
- GDREGISTER_CLASS(SkeletonModification3DJiggle);
- GDREGISTER_CLASS(SkeletonModification3DTwoBoneIK);
- GDREGISTER_CLASS(SkeletonModification3DStackHolder);
-
OS::get_singleton()->yield(); // may take time to init
#endif // _3D_DISABLED
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index d92d4fa4a4..e51b48a4b1 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -82,7 +82,7 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) {
if (is_collision_outline_enabled()) {
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
// Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0, true);
+ RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0);
}
}
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index 2254c19327..f4a0db3930 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -194,25 +194,20 @@ void ImmediateMesh::surface_end() {
if (uses_normals) {
uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset];
- Vector3 n = normals[i] * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+ Vector2 n = normals[i].octahedron_encode();
uint32_t value = 0;
- value |= CLAMP(int(n.x * 1023.0), 0, 1023);
- value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10;
- value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20;
+ value |= (uint16_t)CLAMP(n.x * 65535, 0, 65535);
+ value |= (uint16_t)CLAMP(n.y * 65535, 0, 65535) << 16;
*normal = value;
}
if (uses_tangents) {
uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset];
- Plane t = tangents[i];
+ Vector2 t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d);
uint32_t value = 0;
- value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023);
- value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10;
- value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20;
- if (t.d > 0) {
- value |= 3UL << 30;
- }
+ value |= (uint16_t)CLAMP(t.x * 65535, 0, 65535);
+ value |= (uint16_t)CLAMP(t.y * 65535, 0, 65535) << 16;
*tangent = value;
}
@@ -346,7 +341,7 @@ TypedArray<Array> ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) c
Dictionary ImmediateMesh::surface_get_lods(int p_surface) const {
return Dictionary();
}
-uint32_t ImmediateMesh::surface_get_format(int p_idx) const {
+BitField<Mesh::ArrayFormat> ImmediateMesh::surface_get_format(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), 0);
return surfaces[p_idx].format;
}
diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h
index 9407ecd98c..bf07c82a0c 100644
--- a/scene/resources/immediate_mesh.h
+++ b/scene/resources/immediate_mesh.h
@@ -99,7 +99,7 @@ public:
virtual Array surface_get_arrays(int p_surface) const override;
virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override;
virtual Dictionary surface_get_lods(int p_surface) const override;
- virtual uint32_t surface_get_format(int p_idx) const override;
+ virtual BitField<ArrayFormat> surface_get_format(int p_idx) const override;
virtual PrimitiveType surface_get_primitive_type(int p_idx) const override;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
virtual Ref<Material> surface_get_material(int p_idx) const override;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 07baa0ad96..5e18b5df37 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -75,7 +75,7 @@ Dictionary Mesh::surface_get_lods(int p_surface) const {
return ret;
}
-uint32_t Mesh::surface_get_format(int p_idx) const {
+BitField<Mesh::ArrayFormat> Mesh::surface_get_format(int p_idx) const {
uint32_t ret = 0;
GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret);
return ret;
@@ -658,35 +658,35 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_FLOAT);
BIND_ENUM_CONSTANT(ARRAY_CUSTOM_MAX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BITS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3_SHIFT);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_MASK);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_FLAGS_BASE);
-
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_VERTEX);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_NORMAL);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TANGENT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_COLOR);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TEX_UV);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TEX_UV2);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM0);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM1);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM2);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM3);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_BONES);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_INDEX);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_BLEND_SHAPE_MASK);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_BASE);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_BITS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM0_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM1_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM2_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM3_SHIFT);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_MASK);
+ BIND_BITFIELD_FLAG(ARRAY_COMPRESS_FLAGS_BASE);
+
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
@@ -1554,7 +1554,7 @@ void ArrayMesh::_recompute_aabb() {
}
// TODO: Need to add binding to add_surface using future MeshSurfaceData object.
-void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
+void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
_create_if_empty();
Surface s;
@@ -1589,7 +1589,7 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
emit_changed();
}
-void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, uint32_t p_flags) {
+void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, BitField<ArrayFormat> p_flags) {
ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX);
RS::SurfaceData surface;
@@ -1705,7 +1705,7 @@ int ArrayMesh::surface_get_array_index_len(int p_idx) const {
return surfaces[p_idx].index_array_length;
}
-uint32_t ArrayMesh::surface_get_format(int p_idx) const {
+BitField<Mesh::ArrayFormat> ArrayMesh::surface_get_format(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, surfaces.size(), 0);
return surfaces[p_idx].format;
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index f62f060682..1baa466312 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -156,7 +156,7 @@ public:
virtual Array surface_get_arrays(int p_surface) const;
virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const;
virtual Dictionary surface_get_lods(int p_surface) const;
- virtual uint32_t surface_get_format(int p_idx) const;
+ virtual BitField<ArrayFormat> surface_get_format(int p_idx) const;
virtual PrimitiveType surface_get_primitive_type(int p_idx) const;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material);
virtual Ref<Material> surface_get_material(int p_idx) const;
@@ -269,9 +269,9 @@ protected:
static void _bind_methods();
public:
- void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = TypedArray<Array>(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = 0);
+ void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = TypedArray<Array>(), const Dictionary &p_lods = Dictionary(), BitField<ArrayFormat> p_flags = 0);
- void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
+ void add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
Array surface_get_arrays(int p_surface) const override;
TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override;
@@ -298,7 +298,7 @@ public:
int surface_get_array_len(int p_idx) const override;
int surface_get_array_index_len(int p_idx) const override;
- uint32_t surface_get_format(int p_idx) const override;
+ BitField<ArrayFormat> surface_get_format(int p_idx) const override;
PrimitiveType surface_get_primitive_type(int p_idx) const override;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
@@ -330,7 +330,7 @@ public:
};
VARIANT_ENUM_CAST(Mesh::ArrayType);
-VARIANT_ENUM_CAST(Mesh::ArrayFormat);
+VARIANT_BITFIELD_CAST(Mesh::ArrayFormat);
VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat);
VARIANT_ENUM_CAST(Mesh::PrimitiveType);
VARIANT_ENUM_CAST(Mesh::BlendShapeMode);
@@ -351,7 +351,7 @@ public:
virtual Array surface_get_arrays(int p_surface) const override { return Array(); }
virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override { return TypedArray<Array>(); }
virtual Dictionary surface_get_lods(int p_surface) const override { return Dictionary(); }
- virtual uint32_t surface_get_format(int p_idx) const override { return 0; }
+ virtual BitField<ArrayFormat> surface_get_format(int p_idx) const override { return 0; }
virtual PrimitiveType surface_get_primitive_type(int p_idx) const override { return PRIMITIVE_TRIANGLES; }
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override {}
virtual Ref<Material> surface_get_material(int p_idx) const override { return Ref<Material>(); }
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 287597de32..7e1b42c80b 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -401,7 +401,7 @@ Ref<ArrayMesh> NavigationMesh::get_debug_mesh() {
}
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
- Ref<StandardMaterial3D> debug_geometry_face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material();
+ Ref<StandardMaterial3D> debug_geometry_face_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_material();
debug_mesh->surface_set_material(0, debug_geometry_face_material);
// if enabled build geometry edge line surface
@@ -426,7 +426,7 @@ Ref<ArrayMesh> NavigationMesh::get_debug_mesh() {
line_mesh_array.resize(Mesh::ARRAY_MAX);
line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
- Ref<StandardMaterial3D> debug_geometry_edge_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
+ Ref<StandardMaterial3D> debug_geometry_edge_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_material();
debug_mesh->surface_set_material(1, debug_geometry_edge_material);
}
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index a7f48b92fe..7ae154ea1d 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -1756,8 +1756,8 @@ void ParticleProcessMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
ADD_GROUP("Sub Emitter", "sub_emitter_");
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index b6e953dd56..5ef66a22b6 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -180,7 +180,7 @@ TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) c
return TypedArray<Array>(); //not really supported
}
-uint32_t PrimitiveMesh::surface_get_format(int p_idx) const {
+BitField<Mesh::ArrayFormat> PrimitiveMesh::surface_get_format(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, 1, 0);
uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index c1c51b350b..22cd12b004 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -84,7 +84,7 @@ public:
virtual Array surface_get_arrays(int p_surface) const override;
virtual TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override;
virtual Dictionary surface_get_lods(int p_surface) const override;
- virtual uint32_t surface_get_format(int p_idx) const override;
+ virtual BitField<ArrayFormat> surface_get_format(int p_idx) const override;
virtual Mesh::PrimitiveType surface_get_primitive_type(int p_idx) const override;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
virtual Ref<Material> surface_get_material(int p_idx) const override;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index ade8875935..80b9ff3f38 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -449,10 +449,10 @@ Error ResourceLoaderText::load() {
#ifdef TOOLS_ENABLED
// Silence a warning that can happen during the initial filesystem scan due to cache being regenerated.
if (ResourceLoader::get_resource_uid(path) != uid) {
- WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data());
+ WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UID: " + uidt + " - using text path instead: " + path).utf8().get_data());
}
#else
- WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UUID: " + uidt + " - using text path instead: " + path).utf8().get_data());
+ WARN_PRINT(String(res_path + ":" + itos(lines) + " - ext_resource, invalid UID: " + uidt + " - using text path instead: " + path).utf8().get_data());
#endif
}
}
@@ -601,14 +601,14 @@ Error ResourceLoaderText::load() {
resource_current++;
+ if (progress && resources_total > 0) {
+ *progress = resource_current / float(resources_total);
+ }
+
int_resources[id] = res; //always assign int resources
- if (do_assign) {
- if (cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) {
- res->set_path(path);
- } else {
- res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
- res->set_scene_unique_id(id);
- }
+ if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ res->set_scene_unique_id(id);
}
Dictionary missing_resource_properties;
@@ -663,10 +663,6 @@ Error ResourceLoaderText::load() {
if (!missing_resource_properties.is_empty()) {
res->set_meta(META_MISSING_RESOURCES, missing_resource_properties);
}
-
- if (progress && resources_total > 0) {
- *progress = resource_current / float(resources_total);
- }
}
while (true) {
@@ -716,8 +712,6 @@ Error ResourceLoaderText::load() {
resource = Ref<Resource>(r);
}
- resource_current++;
-
Dictionary missing_resource_properties;
while (true) {
@@ -770,6 +764,12 @@ Error ResourceLoaderText::load() {
}
}
+ resource_current++;
+
+ if (progress && resources_total > 0) {
+ *progress = resource_current / float(resources_total);
+ }
+
if (missing_resource) {
missing_resource->set_recording_properties(false);
}
@@ -779,9 +779,6 @@ Error ResourceLoaderText::load() {
}
error = OK;
- if (progress && resources_total > 0) {
- *progress = resource_current / float(resources_total);
- }
return error;
}
@@ -2237,6 +2234,35 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso
return OK;
}
+Error ResourceLoaderText::set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid) {
+ open(p_f, true);
+ ERR_FAIL_COND_V(error != OK, error);
+ ignore_resource_parsing = true;
+
+ Ref<FileAccess> fw;
+
+ fw = FileAccess::open(local_path + ".uidren", FileAccess::WRITE);
+ if (is_scene) {
+ fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]");
+ } else {
+ fw->store_string("[gd_resource type=\"" + res_type + "\" load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]");
+ }
+
+ uint8_t c = f->get_8();
+ while (!f->eof_reached()) {
+ fw->store_8(c);
+ c = f->get_8();
+ }
+
+ bool all_ok = fw->get_error() == OK;
+
+ if (!all_ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
Error ResourceFormatSaverText::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) {
return ERR_FILE_UNRECOGNIZED;
@@ -2246,6 +2272,35 @@ Error ResourceFormatSaverText::save(const Ref<Resource> &p_resource, const Strin
return saver.save(p_path, p_resource, p_flags);
}
+Error ResourceFormatSaverText::set_uid(const String &p_path, ResourceUID::ID p_uid) {
+ String lc = p_path.to_lower();
+ if (!lc.ends_with(".tscn") && !lc.ends_with(".tres")) {
+ return ERR_FILE_UNRECOGNIZED;
+ }
+
+ String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ Error err = OK;
+ {
+ Ref<FileAccess> fo = FileAccess::open(p_path, FileAccess::READ);
+ if (fo.is_null()) {
+ ERR_FAIL_V(ERR_CANT_OPEN);
+ }
+
+ ResourceLoaderText loader;
+ loader.local_path = local_path;
+ loader.res_path = loader.local_path;
+ err = loader.set_uid(fo, p_uid);
+ }
+
+ if (err == OK) {
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(local_path);
+ da->rename(local_path + ".uidren", local_path);
+ }
+
+ return err;
+}
+
bool ResourceFormatSaverText::recognize(const Ref<Resource> &p_resource) const {
return true; // All resources recognized!
}
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index f96511fb74..0f95e2fbfd 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -106,6 +106,7 @@ class ResourceLoaderText {
VariantParser::ResourceParser rp;
friend class ResourceFormatLoaderText;
+ friend class ResourceFormatSaverText;
Error error = OK;
@@ -117,6 +118,7 @@ public:
void set_local_path(const String &p_local_path);
Ref<Resource> get_resource();
Error load();
+ Error set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid);
int get_stage() const;
int get_stage_count() const;
void set_translation_remapped(bool p_remapped);
@@ -195,6 +197,7 @@ class ResourceFormatSaverText : public ResourceFormatSaver {
public:
static ResourceFormatSaverText *singleton;
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid);
virtual bool recognize(const Ref<Resource> &p_resource) const;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
deleted file mode 100644
index 92fb4bfd78..0000000000
--- a/scene/resources/skeleton_modification_3d.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_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 "skeleton_modification_3d.h"
-#include "scene/3d/skeleton_3d.h"
-
-void SkeletonModification3D::_execute(real_t p_delta) {
- GDVIRTUAL_CALL(_execute, p_delta);
-
- if (!enabled) {
- return;
- }
-}
-
-void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
- if (stack) {
- is_setup = true;
- } else {
- WARN_PRINT("Could not setup modification with name " + this->get_name());
- }
-
- GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack3D>(p_stack));
-}
-
-void SkeletonModification3D::set_enabled(bool p_enabled) {
- enabled = p_enabled;
-}
-
-bool SkeletonModification3D::get_enabled() {
- return enabled;
-}
-
-// Helper function. Needed for CCDIK.
-real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert) {
- // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range.
- if (p_angle < 0) {
- p_angle = Math_TAU + p_angle;
- }
-
- // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order
- if (p_min_bound < 0) {
- p_min_bound = Math_TAU + p_min_bound;
- }
- if (p_max_bound < 0) {
- p_max_bound = Math_TAU + p_max_bound;
- }
- if (p_min_bound > p_max_bound) {
- SWAP(p_min_bound, p_max_bound);
- }
-
- bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
- bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
-
- // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
-
- return p_angle;
-}
-
-bool SkeletonModification3D::_print_execution_error(bool p_condition, String p_message) {
- // If the modification is not setup, don't bother printing the error
- if (!is_setup) {
- return p_condition;
- }
-
- if (p_condition && !execution_error_found) {
- ERR_PRINT(p_message);
- execution_error_found = true;
- }
- return p_condition;
-}
-
-Ref<SkeletonModificationStack3D> SkeletonModification3D::get_modification_stack() {
- return stack;
-}
-
-void SkeletonModification3D::set_is_setup(bool p_is_setup) {
- is_setup = p_is_setup;
-}
-
-bool SkeletonModification3D::get_is_setup() const {
- return is_setup;
-}
-
-void SkeletonModification3D::set_execution_mode(int p_mode) {
- execution_mode = p_mode;
-}
-
-int SkeletonModification3D::get_execution_mode() const {
- return execution_mode;
-}
-
-void SkeletonModification3D::_bind_methods() {
- GDVIRTUAL_BIND(_execute, "delta");
- GDVIRTUAL_BIND(_setup_modification, "modification_stack")
-
- ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled);
- ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled);
- ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification3D::get_modification_stack);
- ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification3D::set_is_setup);
- ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification3D::get_is_setup);
- ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification3D::set_execution_mode);
- ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification3D::get_execution_mode);
- ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification3D::clamp_angle);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process,physics_process"), "set_execution_mode", "get_execution_mode");
-}
-
-SkeletonModification3D::SkeletonModification3D() {
- stack = nullptr;
- is_setup = false;
-}
diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h
deleted file mode 100644
index 367adbd977..0000000000
--- a/scene/resources/skeleton_modification_3d.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_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 SKELETON_MODIFICATION_3D_H
-#define SKELETON_MODIFICATION_3D_H
-
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_stack_3d.h"
-
-class SkeletonModificationStack3D;
-
-class SkeletonModification3D : public Resource {
- GDCLASS(SkeletonModification3D, Resource);
- friend class Skeleton3D;
- friend class SkeletonModificationStack3D;
-
-protected:
- static void _bind_methods();
-
- SkeletonModificationStack3D *stack = nullptr;
- int execution_mode = 0; // 0 = process
-
- bool enabled = true;
- bool is_setup = false;
- bool execution_error_found = false;
-
- bool _print_execution_error(bool p_condition, String p_message);
-
- GDVIRTUAL1(_execute, double)
- GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack3D>)
-
-public:
- virtual void _execute(real_t p_delta);
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack);
-
- real_t clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert);
-
- void set_enabled(bool p_enabled);
- bool get_enabled();
-
- void set_execution_mode(int p_mode);
- int get_execution_mode() const;
-
- Ref<SkeletonModificationStack3D> get_modification_stack();
-
- void set_is_setup(bool p_setup);
- bool get_is_setup() const;
-
- SkeletonModification3D();
-};
-
-#endif // SKELETON_MODIFICATION_3D_H
diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp
deleted file mode 100644
index b5c930f970..0000000000
--- a/scene/resources/skeleton_modification_3d_ccdik.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_ccdik.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 "scene/resources/skeleton_modification_3d_ccdik.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- int ccdik_data_size = ccdik_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
-
- if (what == "bone_name") {
- set_ccdik_joint_bone_name(which, p_value);
- } else if (what == "bone_index") {
- set_ccdik_joint_bone_index(which, p_value);
- } else if (what == "ccdik_axis") {
- set_ccdik_joint_ccdik_axis(which, p_value);
- } else if (what == "enable_joint_constraint") {
- set_ccdik_joint_enable_constraint(which, p_value);
- } else if (what == "joint_constraint_angle_min") {
- set_ccdik_joint_constraint_angle_min(which, Math::deg_to_rad(real_t(p_value)));
- } else if (what == "joint_constraint_angle_max") {
- set_ccdik_joint_constraint_angle_max(which, Math::deg_to_rad(real_t(p_value)));
- } else if (what == "joint_constraint_angles_invert") {
- set_ccdik_joint_constraint_invert(which, p_value);
- }
- return true;
- }
- return true;
-}
-
-bool SkeletonModification3DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- const int ccdik_data_size = ccdik_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
-
- if (what == "bone_name") {
- r_ret = get_ccdik_joint_bone_name(which);
- } else if (what == "bone_index") {
- r_ret = get_ccdik_joint_bone_index(which);
- } else if (what == "ccdik_axis") {
- r_ret = get_ccdik_joint_ccdik_axis(which);
- } else if (what == "enable_joint_constraint") {
- r_ret = get_ccdik_joint_enable_constraint(which);
- } else if (what == "joint_constraint_angle_min") {
- r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_min(which));
- } else if (what == "joint_constraint_angle_max") {
- r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_max(which));
- } else if (what == "joint_constraint_angles_invert") {
- r_ret = get_ccdik_joint_constraint_invert(which);
- }
- return true;
- }
- return true;
-}
-
-void SkeletonModification3DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
- for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
- String base_string = "joint_data/" + itos(i) + "/";
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
-
- p_list->push_back(PropertyInfo(Variant::INT, base_string + "ccdik_axis",
- PROPERTY_HINT_ENUM, "X Axis,Y Axis,Z Axis", PROPERTY_USAGE_DEFAULT));
-
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_joint_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (ccdik_data_chain[i].enable_constraint) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "joint_constraint_angles_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- }
- }
-}
-
-void SkeletonModification3DCCDIK::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
- if (!enabled) {
- return;
- }
-
- if (target_node_cache.is_null()) {
- _print_execution_error(true, "Target cache is out of date. Attempting to update");
- update_target_cache();
- return;
- }
- if (tip_node_cache.is_null()) {
- _print_execution_error(true, "Tip cache is out of date. Attempting to update");
- update_tip_cache();
- return;
- }
-
- // Reset the local bone overrides for CCDIK affected nodes
- for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
- stack->skeleton->set_bone_local_pose_override(ccdik_data_chain[i].bone_idx,
- stack->skeleton->get_bone_local_pose_override(ccdik_data_chain[i].bone_idx),
- 0.0, false);
- }
-
- Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
- Node3D *node_tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
-
- if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
- if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
-
- if (use_high_quality_solve) {
- for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
- for (uint32_t j = i; j < ccdik_data_chain.size(); j++) {
- _execute_ccdik_joint(j, node_target, node_tip);
- }
- }
- } else {
- for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
- _execute_ccdik_joint(i, node_target, node_tip);
- }
- }
-
- execution_error_found = false;
-}
-
-void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip) {
- CCDIK_Joint_Data ccdik_data = ccdik_data_chain[p_joint_idx];
-
- if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(),
- "CCDIK joint: bone index for joint" + itos(p_joint_idx) + " not found. Cannot execute modification!")) {
- return;
- }
-
- Transform3D bone_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->get_bone_global_pose(ccdik_data.bone_idx));
- Transform3D tip_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_tip->get_global_transform()));
- Transform3D target_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()));
-
- if (tip_trans.origin.distance_to(target_trans.origin) <= 0.01) {
- return;
- }
-
- // Inspired (and very loosely based on) by the CCDIK algorithm made by Zalo on GitHub (https://github.com/zalo/MathUtilities)
- // Convert the 3D position to a 2D position so we can use Atan2 (via the angle function)
- // to know how much rotation we need on the given axis to place the tip at the target.
- Vector2 tip_pos_2d;
- Vector2 target_pos_2d;
- if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
- tip_pos_2d = Vector2(tip_trans.origin.y, tip_trans.origin.z);
- target_pos_2d = Vector2(target_trans.origin.y, target_trans.origin.z);
- bone_trans.basis.rotate_local(Vector3(1, 0, 0), target_pos_2d.angle() - tip_pos_2d.angle());
- } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
- tip_pos_2d = Vector2(tip_trans.origin.z, tip_trans.origin.x);
- target_pos_2d = Vector2(target_trans.origin.z, target_trans.origin.x);
- bone_trans.basis.rotate_local(Vector3(0, 1, 0), target_pos_2d.angle() - tip_pos_2d.angle());
- } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
- tip_pos_2d = Vector2(tip_trans.origin.x, tip_trans.origin.y);
- target_pos_2d = Vector2(target_trans.origin.x, target_trans.origin.y);
- bone_trans.basis.rotate_local(Vector3(0, 0, 1), target_pos_2d.angle() - tip_pos_2d.angle());
- } else {
- // Should never happen, but...
- ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
- }
-
- if (ccdik_data.enable_constraint) {
- Vector3 rotation_axis;
- real_t rotation_angle;
- bone_trans.basis.get_axis_angle(rotation_axis, rotation_angle);
-
- // Note: When the axis has a negative direction, the angle is OVER 180 degrees and therefore we need to account for this
- // when constraining.
- if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
- if (rotation_axis.x < 0) {
- rotation_angle += Math_PI;
- rotation_axis = Vector3(1, 0, 0);
- }
- } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
- if (rotation_axis.y < 0) {
- rotation_angle += Math_PI;
- rotation_axis = Vector3(0, 1, 0);
- }
- } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
- if (rotation_axis.z < 0) {
- rotation_angle += Math_PI;
- rotation_axis = Vector3(0, 0, 1);
- }
- } else {
- // Should never happen, but...
- ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
- }
- rotation_angle = clamp_angle(rotation_angle, ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angles_invert);
-
- bone_trans.basis.set_axis_angle(rotation_axis, rotation_angle);
- }
-
- stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, bone_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx);
-}
-
-void SkeletonModification3DCCDIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
- if (stack != nullptr) {
- is_setup = true;
- execution_error_found = false;
- update_target_cache();
- update_tip_cache();
- }
-}
-
-void SkeletonModification3DCCDIK::update_target_cache() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
- return;
- }
-
- target_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(target_node)) {
- Node *node = stack->skeleton->get_node(target_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update target cache: node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update target cache: node is not in scene tree!");
- target_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DCCDIK::update_tip_cache() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
- return;
- }
-
- tip_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(tip_node)) {
- Node *node = stack->skeleton->get_node(tip_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update tip cache: node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update tip cache: node is not in scene tree!");
- tip_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DCCDIK::set_target_node(const NodePath &p_target_node) {
- target_node = p_target_node;
- update_target_cache();
-}
-
-NodePath SkeletonModification3DCCDIK::get_target_node() const {
- return target_node;
-}
-
-void SkeletonModification3DCCDIK::set_tip_node(const NodePath &p_tip_node) {
- tip_node = p_tip_node;
- update_tip_cache();
-}
-
-NodePath SkeletonModification3DCCDIK::get_tip_node() const {
- return tip_node;
-}
-
-void SkeletonModification3DCCDIK::set_use_high_quality_solve(bool p_high_quality) {
- use_high_quality_solve = p_high_quality;
-}
-
-bool SkeletonModification3DCCDIK::get_use_high_quality_solve() const {
- return use_high_quality_solve;
-}
-
-// CCDIK joint data functions
-String SkeletonModification3DCCDIK::get_ccdik_joint_bone_name(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
- return ccdik_data_chain[p_joint_idx].bone_name;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ccdik_data_chain[p_joint_idx].bone_name = p_bone_name;
-
- if (stack) {
- if (stack->skeleton) {
- ccdik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return ccdik_data_chain[p_joint_idx].bone_idx;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
- ccdik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
-
- if (stack) {
- if (stack->skeleton) {
- ccdik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return ccdik_data_chain[p_joint_idx].ccdik_axis;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ERR_FAIL_COND_MSG(p_axis < 0, "CCDIK axis is out of range: The axis mode is too low!");
- ccdik_data_chain[p_joint_idx].ccdik_axis = p_axis;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return ccdik_data_chain[p_joint_idx].enable_constraint;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ccdik_data_chain[p_joint_idx].enable_constraint = p_enable;
- notify_property_list_changed();
-}
-
-real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return ccdik_data_chain[p_joint_idx].constraint_angle_min;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ccdik_data_chain[p_joint_idx].constraint_angle_min = p_angle_min;
-}
-
-real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return ccdik_data_chain[p_joint_idx].constraint_angle_max;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ccdik_data_chain[p_joint_idx].constraint_angle_max = p_angle_max;
-}
-
-bool SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert(int p_joint_idx) const {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return ccdik_data_chain[p_joint_idx].constraint_angles_invert;
-}
-
-void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert) {
- const int bone_chain_size = ccdik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ccdik_data_chain[p_joint_idx].constraint_angles_invert = p_invert;
-}
-
-int SkeletonModification3DCCDIK::get_ccdik_data_chain_length() {
- return ccdik_data_chain.size();
-}
-void SkeletonModification3DCCDIK::set_ccdik_data_chain_length(int p_length) {
- ERR_FAIL_COND(p_length < 0);
- ccdik_data_chain.resize(p_length);
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-void SkeletonModification3DCCDIK::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DCCDIK::set_target_node);
- ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DCCDIK::get_target_node);
-
- ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DCCDIK::set_tip_node);
- ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DCCDIK::get_tip_node);
-
- ClassDB::bind_method(D_METHOD("set_use_high_quality_solve", "high_quality_solve"), &SkeletonModification3DCCDIK::set_use_high_quality_solve);
- ClassDB::bind_method(D_METHOD("get_use_high_quality_solve"), &SkeletonModification3DCCDIK::get_use_high_quality_solve);
-
- // CCDIK joint data functions
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_name", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_name);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_name);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_index);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_index);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_ccdik_axis", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_ccdik_axis", "joint_idx", "axis"), &SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_joint_constraint", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_joint_constraint", "joint_idx", "enable"), &SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "min_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "max_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max);
- ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_invert", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert);
- ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_invert", "joint_idx", "invert"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert);
-
- ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification3DCCDIK::set_ccdik_data_chain_length);
- ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification3DCCDIK::get_ccdik_data_chain_length);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_tip_node", "get_tip_node");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "high_quality_solve", PROPERTY_HINT_NONE, ""), "set_use_high_quality_solve", "get_use_high_quality_solve");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_ccdik_data_chain_length", "get_ccdik_data_chain_length");
-}
-
-SkeletonModification3DCCDIK::SkeletonModification3DCCDIK() {
- stack = nullptr;
- is_setup = false;
- enabled = true;
-}
-
-SkeletonModification3DCCDIK::~SkeletonModification3DCCDIK() {
-}
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
deleted file mode 100644
index 30c29bb3aa..0000000000
--- a/scene/resources/skeleton_modification_3d_ccdik.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_ccdik.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 SKELETON_MODIFICATION_3D_CCDIK_H
-#define SKELETON_MODIFICATION_3D_CCDIK_H
-
-#include "core/templates/local_vector.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DCCDIK : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D);
-
-private:
- enum CCDIK_Axes {
- AXIS_X,
- AXIS_Y,
- AXIS_Z
- };
-
- struct CCDIK_Joint_Data {
- String bone_name = "";
- int bone_idx = -1;
- int ccdik_axis = 0;
-
- bool enable_constraint = false;
- real_t constraint_angle_min = 0;
- real_t constraint_angle_max = (2.0 * Math_PI);
- bool constraint_angles_invert = false;
- };
-
- LocalVector<CCDIK_Joint_Data> ccdik_data_chain;
- NodePath target_node;
- ObjectID target_node_cache;
-
- NodePath tip_node;
- ObjectID tip_node_cache;
-
- bool use_high_quality_solve = true;
-
- void update_target_cache();
- void update_tip_cache();
-
- void _execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip);
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
-
- void set_target_node(const NodePath &p_target_node);
- NodePath get_target_node() const;
-
- void set_tip_node(const NodePath &p_tip_node);
- NodePath get_tip_node() const;
-
- void set_use_high_quality_solve(bool p_solve);
- bool get_use_high_quality_solve() const;
-
- String get_ccdik_joint_bone_name(int p_joint_idx) const;
- void set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name);
- int get_ccdik_joint_bone_index(int p_joint_idx) const;
- void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
- int get_ccdik_joint_ccdik_axis(int p_joint_idx) const;
- void set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis);
- bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
- void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable);
- real_t get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
- void set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min);
- real_t get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
- void set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max);
- bool get_ccdik_joint_constraint_invert(int p_joint_idx) const;
- void set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert);
-
- int get_ccdik_data_chain_length();
- void set_ccdik_data_chain_length(int p_new_length);
-
- SkeletonModification3DCCDIK();
- ~SkeletonModification3DCCDIK();
-};
-
-#endif // SKELETON_MODIFICATION_3D_CCDIK_H
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
deleted file mode 100644
index 9fb7ade155..0000000000
--- a/scene/resources/skeleton_modification_3d_fabrik.cpp
+++ /dev/null
@@ -1,628 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_fabrik.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 "scene/resources/skeleton_modification_3d_fabrik.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- int fabrik_data_size = fabrik_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
-
- if (what == "bone_name") {
- set_fabrik_joint_bone_name(which, p_value);
- } else if (what == "bone_index") {
- set_fabrik_joint_bone_index(which, p_value);
- } else if (what == "length") {
- set_fabrik_joint_length(which, p_value);
- } else if (what == "magnet_position") {
- set_fabrik_joint_magnet(which, p_value);
- } else if (what == "auto_calculate_length") {
- set_fabrik_joint_auto_calculate_length(which, p_value);
- } else if (what == "use_tip_node") {
- set_fabrik_joint_use_tip_node(which, p_value);
- } else if (what == "tip_node") {
- set_fabrik_joint_tip_node(which, p_value);
- } else if (what == "use_target_basis") {
- set_fabrik_joint_use_target_basis(which, p_value);
- } else if (what == "roll") {
- set_fabrik_joint_roll(which, Math::deg_to_rad(real_t(p_value)));
- }
- return true;
- }
- return true;
-}
-
-bool SkeletonModification3DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- const int fabrik_data_size = fabrik_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
-
- if (what == "bone_name") {
- r_ret = get_fabrik_joint_bone_name(which);
- } else if (what == "bone_index") {
- r_ret = get_fabrik_joint_bone_index(which);
- } else if (what == "length") {
- r_ret = get_fabrik_joint_length(which);
- } else if (what == "magnet_position") {
- r_ret = get_fabrik_joint_magnet(which);
- } else if (what == "auto_calculate_length") {
- r_ret = get_fabrik_joint_auto_calculate_length(which);
- } else if (what == "use_tip_node") {
- r_ret = get_fabrik_joint_use_tip_node(which);
- } else if (what == "tip_node") {
- r_ret = get_fabrik_joint_tip_node(which);
- } else if (what == "use_target_basis") {
- r_ret = get_fabrik_joint_use_target_basis(which);
- } else if (what == "roll") {
- r_ret = Math::rad_to_deg(get_fabrik_joint_roll(which));
- }
- return true;
- }
- return true;
-}
-
-void SkeletonModification3DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
- for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
- String base_string = "joint_data/" + itos(i) + "/";
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "auto_calculate_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
-
- if (!fabrik_data_chain[i].auto_calculate_length) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- } else {
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (fabrik_data_chain[i].use_tip_node) {
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
- }
- }
-
- // Cannot apply magnet to the origin of the chain, as it will not do anything.
- if (i > 0) {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- }
- // Only give the override basis option on the last bone in the chain, so only include it for the last bone.
- if (i == fabrik_data_chain.size() - 1) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- }
- }
-}
-
-void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
- if (!enabled) {
- return;
- }
-
- if (target_node_cache.is_null()) {
- _print_execution_error(true, "Target cache is out of date. Attempting to update...");
- update_target_cache();
- return;
- }
-
- if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) {
- return;
- }
-
- Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
- if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
-
- // Make sure the transform cache is the correct size
- if (fabrik_transforms.size() != fabrik_data_chain.size()) {
- fabrik_transforms.resize(fabrik_data_chain.size());
- }
-
- // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more
- // Also, while we are here, apply magnet positions.
- for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
- if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) {
- return;
- }
-
- if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) {
- fabrik_joint_auto_calculate_length(i);
- }
- if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) {
- return;
- }
- fabrik_transforms[i] = stack->skeleton->get_bone_global_pose(fabrik_data_chain[i].bone_idx);
-
- // Apply magnet positions:
- if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
- int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
- Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx));
- fabrik_transforms[i].origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
- } else {
- fabrik_transforms[i].origin += fabrik_data_chain[i].magnet_position;
- }
- }
- Transform3D origin_global_pose_trans = stack->skeleton->get_bone_global_pose_no_override(fabrik_data_chain[0].bone_idx);
-
- target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform());
- origin_global_pose = origin_global_pose_trans;
-
- final_joint_idx = fabrik_data_chain.size() - 1;
- real_t target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin);
- chain_iterations = 0;
-
- while (target_distance > chain_tolerance) {
- chain_backwards();
- chain_forwards();
-
- // update the target distance
- target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin);
-
- // update chain iterations
- chain_iterations += 1;
- if (chain_iterations >= chain_max_iterations) {
- break;
- }
- }
- chain_apply();
-
- execution_error_found = false;
-}
-
-void SkeletonModification3DFABRIK::chain_backwards() {
- int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx;
- Transform3D final_joint_trans = fabrik_transforms[final_joint_idx];
-
- // Get the direction the final bone is facing in.
- stack->skeleton->update_bone_rest_forward_vector(final_bone_idx);
- Transform3D final_bone_direction_trans = final_joint_trans.looking_at(target_global_pose.origin, Vector3(0, 1, 0));
- final_bone_direction_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis);
- Vector3 direction = final_bone_direction_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
-
- // If set to override, then use the target's Basis rather than the bone's
- if (fabrik_data_chain[final_joint_idx].use_target_basis) {
- direction = target_global_pose.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
- }
-
- // set the position of the final joint to the target position
- final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length);
- fabrik_transforms[final_joint_idx] = final_joint_trans;
-
- // for all other joints, move them towards the target
- int i = final_joint_idx;
- while (i >= 1) {
- Transform3D next_bone_trans = fabrik_transforms[i];
- i -= 1;
- Transform3D current_trans = fabrik_transforms[i];
-
- real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin));
- current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
-
- // Save the result
- fabrik_transforms[i] = current_trans;
- }
-}
-
-void SkeletonModification3DFABRIK::chain_forwards() {
- // Set root at the initial position.
- Transform3D root_transform = fabrik_transforms[0];
-
- root_transform.origin = origin_global_pose.origin;
- fabrik_transforms[0] = origin_global_pose;
-
- for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) {
- Transform3D current_trans = fabrik_transforms[i];
- Transform3D next_bone_trans = fabrik_transforms[i + 1];
-
- real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin));
- next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
-
- // Save the result
- fabrik_transforms[i + 1] = next_bone_trans;
- }
-}
-
-void SkeletonModification3DFABRIK::chain_apply() {
- for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
- int current_bone_idx = fabrik_data_chain[i].bone_idx;
- Transform3D current_trans = fabrik_transforms[i];
-
- // If this is the last bone in the chain...
- if (i == fabrik_data_chain.size() - 1) {
- if (fabrik_data_chain[i].use_target_basis == false) { // Point to target...
- // Get the forward direction that the basis is facing in right now.
- stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
- Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
- // Rotate the bone towards the target:
- current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(target_global_pose.origin));
- current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
- } else { // Use the target's Basis...
- current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale());
- }
- } else { // every other bone in the chain...
- Transform3D next_trans = fabrik_transforms[i + 1];
-
- // Get the forward direction that the basis is facing in right now.
- stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
- Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
- // Rotate the bone towards the next bone in the chain:
- current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin));
- current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
- }
- stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
- }
-
- // Update all the bones so the next modification has up-to-date data.
- stack->skeleton->force_update_all_bone_transforms();
-}
-
-void SkeletonModification3DFABRIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
- if (stack != nullptr) {
- is_setup = true;
- execution_error_found = false;
- update_target_cache();
-
- for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
- update_joint_tip_cache(i);
- }
- }
-}
-
-void SkeletonModification3DFABRIK::update_target_cache() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
- return;
- }
- target_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
- if (stack->skeleton->has_node(target_node)) {
- Node *node = stack->skeleton->get_node(target_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update target cache: node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update target cache: node is not in the scene tree!");
- target_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DFABRIK::update_joint_tip_cache(int p_joint_idx) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_MSG(p_joint_idx, bone_chain_size, "FABRIK joint not found");
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
- return;
- }
- fabrik_data_chain[p_joint_idx].tip_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) {
- if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].tip_node)) {
- Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].tip_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!");
- fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DFABRIK::set_target_node(const NodePath &p_target_node) {
- target_node = p_target_node;
- update_target_cache();
-}
-
-NodePath SkeletonModification3DFABRIK::get_target_node() const {
- return target_node;
-}
-
-int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() {
- return fabrik_data_chain.size();
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) {
- ERR_FAIL_COND(p_length < 0);
- fabrik_data_chain.resize(p_length);
- fabrik_transforms.resize(p_length);
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-real_t SkeletonModification3DFABRIK::get_chain_tolerance() {
- return chain_tolerance;
-}
-
-void SkeletonModification3DFABRIK::set_chain_tolerance(real_t p_tolerance) {
- ERR_FAIL_COND_MSG(p_tolerance <= 0, "FABRIK chain tolerance must be more than zero!");
- chain_tolerance = p_tolerance;
-}
-
-int SkeletonModification3DFABRIK::get_chain_max_iterations() {
- return chain_max_iterations;
-}
-void SkeletonModification3DFABRIK::set_chain_max_iterations(int p_iterations) {
- ERR_FAIL_COND_MSG(p_iterations <= 0, "FABRIK chain iterations must be at least one. Set enabled to false to disable the FABRIK chain.");
- chain_max_iterations = p_iterations;
-}
-
-// FABRIK joint data functions
-String SkeletonModification3DFABRIK::get_fabrik_joint_bone_name(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
- return fabrik_data_chain[p_joint_idx].bone_name;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].bone_name = p_bone_name;
-
- if (stack) {
- if (stack->skeleton) {
- fabrik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return fabrik_data_chain[p_joint_idx].bone_idx;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
- fabrik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
-
- if (stack) {
- if (stack->skeleton) {
- fabrik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-real_t SkeletonModification3DFABRIK::get_fabrik_joint_length(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return fabrik_data_chain[p_joint_idx].length;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ERR_FAIL_COND_MSG(p_bone_length < 0, "FABRIK joint length cannot be less than zero!");
-
- if (!is_setup) {
- fabrik_data_chain[p_joint_idx].length = p_bone_length;
- return;
- }
-
- if (fabrik_data_chain[p_joint_idx].auto_calculate_length) {
- WARN_PRINT("FABRIK Length not set: auto calculate length is enabled for this joint!");
- fabrik_joint_auto_calculate_length(p_joint_idx);
- } else {
- fabrik_data_chain[p_joint_idx].length = p_bone_length;
- }
-
- execution_error_found = false;
-}
-
-Vector3 SkeletonModification3DFABRIK::get_fabrik_joint_magnet(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3());
- return fabrik_data_chain[p_joint_idx].magnet_position;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].magnet_position = p_magnet;
-}
-
-bool SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return fabrik_data_chain[p_joint_idx].auto_calculate_length;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].auto_calculate_length = p_auto_calculate;
- fabrik_joint_auto_calculate_length(p_joint_idx);
- notify_property_list_changed();
-}
-
-void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_joint_idx) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- if (!fabrik_data_chain[p_joint_idx].auto_calculate_length) {
- return;
- }
-
- if (!stack || !stack->skeleton || !is_setup) {
- _print_execution_error(true, "Cannot auto calculate joint length: modification is not properly setup!");
- return;
- }
- ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_count(),
- "Bone for joint " + itos(p_joint_idx) + " is not set or points to an unknown bone!");
-
- if (fabrik_data_chain[p_joint_idx].use_tip_node) { // Use the tip node to update joint length.
-
- update_joint_tip_cache(p_joint_idx);
-
- Node3D *tip_node = Object::cast_to<Node3D>(ObjectDB::get_instance(fabrik_data_chain[p_joint_idx].tip_node_cache));
- ERR_FAIL_COND_MSG(!tip_node, "Tip node for joint " + itos(p_joint_idx) + "is not a Node3D-based node. Cannot calculate length...");
- ERR_FAIL_COND_MSG(!tip_node->is_inside_tree(), "Tip node for joint " + itos(p_joint_idx) + "is not in the scene tree. Cannot calculate length...");
-
- Transform3D node_trans = tip_node->get_global_transform();
- node_trans = stack->skeleton->world_transform_to_global_pose(node_trans);
- //node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
- //fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
-
- fabrik_data_chain[p_joint_idx].length = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx).origin.distance_to(node_trans.origin);
-
- } else { // Use child bone(s) to update joint length, if possible
- Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx);
- if (bone_children.size() <= 0) {
- ERR_FAIL_MSG("Cannot calculate length for joint " + itos(p_joint_idx) + "joint uses leaf bone. \nPlease manually set the bone length or use a tip node!");
- return;
- }
-
- Transform3D bone_trans = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx);
-
- real_t final_length = 0;
- for (int i = 0; i < bone_children.size(); i++) {
- Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]);
- final_length += bone_trans.origin.distance_to(child_transform.origin);
- //final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
- }
- fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size();
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return fabrik_data_chain[p_joint_idx].use_tip_node;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].use_tip_node = p_use_tip_node;
- notify_property_list_changed();
-}
-
-NodePath SkeletonModification3DFABRIK::get_fabrik_joint_tip_node(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, NodePath());
- return fabrik_data_chain[p_joint_idx].tip_node;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].tip_node = p_tip_node;
- update_joint_tip_cache(p_joint_idx);
-}
-
-bool SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return fabrik_data_chain[p_joint_idx].use_target_basis;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_target_basis) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].use_target_basis = p_use_target_basis;
-}
-
-real_t SkeletonModification3DFABRIK::get_fabrik_joint_roll(int p_joint_idx) const {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
- return fabrik_data_chain[p_joint_idx].roll;
-}
-
-void SkeletonModification3DFABRIK::set_fabrik_joint_roll(int p_joint_idx, real_t p_roll) {
- const int bone_chain_size = fabrik_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- fabrik_data_chain[p_joint_idx].roll = p_roll;
-}
-
-void SkeletonModification3DFABRIK::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DFABRIK::set_target_node);
- ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DFABRIK::get_target_node);
- ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification3DFABRIK::set_fabrik_data_chain_length);
- ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification3DFABRIK::get_fabrik_data_chain_length);
- ClassDB::bind_method(D_METHOD("set_chain_tolerance", "tolerance"), &SkeletonModification3DFABRIK::set_chain_tolerance);
- ClassDB::bind_method(D_METHOD("get_chain_tolerance"), &SkeletonModification3DFABRIK::get_chain_tolerance);
- ClassDB::bind_method(D_METHOD("set_chain_max_iterations", "max_iterations"), &SkeletonModification3DFABRIK::set_chain_max_iterations);
- ClassDB::bind_method(D_METHOD("get_chain_max_iterations"), &SkeletonModification3DFABRIK::get_chain_max_iterations);
-
- // FABRIK joint data functions
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_name", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_name);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_name);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_index);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_index);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_length);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_length", "joint_idx", "length"), &SkeletonModification3DFABRIK::set_fabrik_joint_length);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_magnet);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet", "joint_idx", "magnet_position"), &SkeletonModification3DFABRIK::set_fabrik_joint_magnet);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_auto_calculate_length", "joint_idx", "auto_calculate_length"), &SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length);
- ClassDB::bind_method(D_METHOD("fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_tip_node", "joint_idx", "use_tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_tip_node);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_tip_node", "joint_idx", "tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_tip_node);
- ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_basis", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis);
- ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_basis", "joint_idx", "use_target_basis"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "chain_tolerance", PROPERTY_HINT_RANGE, "0,100,0.001"), "set_chain_tolerance", "get_chain_tolerance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "chain_max_iterations", PROPERTY_HINT_RANGE, "1,50,1"), "set_chain_max_iterations", "get_chain_max_iterations");
-}
-
-SkeletonModification3DFABRIK::SkeletonModification3DFABRIK() {
- stack = nullptr;
- is_setup = false;
- enabled = true;
-}
-
-SkeletonModification3DFABRIK::~SkeletonModification3DFABRIK() {
-}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
deleted file mode 100644
index c834658093..0000000000
--- a/scene/resources/skeleton_modification_3d_fabrik.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_fabrik.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 SKELETON_MODIFICATION_3D_FABRIK_H
-#define SKELETON_MODIFICATION_3D_FABRIK_H
-
-#include "core/templates/local_vector.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DFABRIK : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D);
-
-private:
- struct FABRIK_Joint_Data {
- String bone_name = "";
- int bone_idx = -1;
- real_t length = -1;
- Vector3 magnet_position = Vector3(0, 0, 0);
-
- bool auto_calculate_length = true;
- bool use_tip_node = false;
- NodePath tip_node;
- ObjectID tip_node_cache;
-
- bool use_target_basis = false;
- real_t roll = 0;
- };
-
- LocalVector<FABRIK_Joint_Data> fabrik_data_chain;
- LocalVector<Transform3D> fabrik_transforms;
-
- NodePath target_node;
- ObjectID target_node_cache;
-
- real_t chain_tolerance = 0.01;
- int chain_max_iterations = 10;
- int chain_iterations = 0;
-
- void update_target_cache();
- void update_joint_tip_cache(int p_joint_idx);
-
- int final_joint_idx = 0;
- Transform3D target_global_pose;
- Transform3D origin_global_pose;
-
- void chain_backwards();
- void chain_forwards();
- void chain_apply();
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
-
- void set_target_node(const NodePath &p_target_node);
- NodePath get_target_node() const;
-
- int get_fabrik_data_chain_length();
- void set_fabrik_data_chain_length(int p_new_length);
-
- real_t get_chain_tolerance();
- void set_chain_tolerance(real_t p_tolerance);
-
- int get_chain_max_iterations();
- void set_chain_max_iterations(int p_iterations);
-
- String get_fabrik_joint_bone_name(int p_joint_idx) const;
- void set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name);
- int get_fabrik_joint_bone_index(int p_joint_idx) const;
- void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
- real_t get_fabrik_joint_length(int p_joint_idx) const;
- void set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length);
- Vector3 get_fabrik_joint_magnet(int p_joint_idx) const;
- void set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet);
- bool get_fabrik_joint_auto_calculate_length(int p_joint_idx) const;
- void set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate);
- void fabrik_joint_auto_calculate_length(int p_joint_idx);
- bool get_fabrik_joint_use_tip_node(int p_joint_idx) const;
- void set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node);
- NodePath get_fabrik_joint_tip_node(int p_joint_idx) const;
- void set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node);
- bool get_fabrik_joint_use_target_basis(int p_joint_idx) const;
- void set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_basis);
- real_t get_fabrik_joint_roll(int p_joint_idx) const;
- void set_fabrik_joint_roll(int p_joint_idx, real_t p_roll);
-
- SkeletonModification3DFABRIK();
- ~SkeletonModification3DFABRIK();
-};
-
-#endif // SKELETON_MODIFICATION_3D_FABRIK_H
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
deleted file mode 100644
index 2cfc7fb10f..0000000000
--- a/scene/resources/skeleton_modification_3d_jiggle.cpp
+++ /dev/null
@@ -1,582 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_jiggle.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 "scene/resources/skeleton_modification_3d_jiggle.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- const int jiggle_size = jiggle_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, jiggle_size, false);
-
- if (what == "bone_name") {
- set_jiggle_joint_bone_name(which, p_value);
- } else if (what == "bone_index") {
- set_jiggle_joint_bone_index(which, p_value);
- } else if (what == "override_defaults") {
- set_jiggle_joint_override(which, p_value);
- } else if (what == "stiffness") {
- set_jiggle_joint_stiffness(which, p_value);
- } else if (what == "mass") {
- set_jiggle_joint_mass(which, p_value);
- } else if (what == "damping") {
- set_jiggle_joint_damping(which, p_value);
- } else if (what == "use_gravity") {
- set_jiggle_joint_use_gravity(which, p_value);
- } else if (what == "gravity") {
- set_jiggle_joint_gravity(which, p_value);
- } else if (what == "roll") {
- set_jiggle_joint_roll(which, Math::deg_to_rad(real_t(p_value)));
- }
- return true;
- } else {
- if (path == "use_colliders") {
- set_use_colliders(p_value);
- } else if (path == "collision_mask") {
- set_collision_mask(p_value);
- }
- return true;
- }
- return true;
-}
-
-bool SkeletonModification3DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("joint_data/")) {
- const int jiggle_size = jiggle_data_chain.size();
- int which = path.get_slicec('/', 1).to_int();
- String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, jiggle_size, false);
-
- if (what == "bone_name") {
- r_ret = get_jiggle_joint_bone_name(which);
- } else if (what == "bone_index") {
- r_ret = get_jiggle_joint_bone_index(which);
- } else if (what == "override_defaults") {
- r_ret = get_jiggle_joint_override(which);
- } else if (what == "stiffness") {
- r_ret = get_jiggle_joint_stiffness(which);
- } else if (what == "mass") {
- r_ret = get_jiggle_joint_mass(which);
- } else if (what == "damping") {
- r_ret = get_jiggle_joint_damping(which);
- } else if (what == "use_gravity") {
- r_ret = get_jiggle_joint_use_gravity(which);
- } else if (what == "gravity") {
- r_ret = get_jiggle_joint_gravity(which);
- } else if (what == "roll") {
- r_ret = Math::rad_to_deg(get_jiggle_joint_roll(which));
- }
- return true;
- } else {
- if (path == "use_colliders") {
- r_ret = get_use_colliders();
- } else if (path == "collision_mask") {
- r_ret = get_collision_mask();
- }
- return true;
- }
- return true;
-}
-
-void SkeletonModification3DJiggle::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::BOOL, "use_colliders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (use_colliders) {
- p_list->push_back(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
- }
-
- for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
- String base_string = "joint_data/" + itos(i) + "/";
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "override_defaults", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
-
- if (jiggle_data_chain[i].override_defaults) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "stiffness", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "mass", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (jiggle_data_chain[i].use_gravity) {
- p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- }
- }
- }
-}
-
-void SkeletonModification3DJiggle::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
- if (!enabled) {
- return;
- }
- if (target_node_cache.is_null()) {
- _print_execution_error(true, "Target cache is out of date. Attempting to update...");
- update_cache();
- return;
- }
- Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
- _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!");
-
- for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
- _execute_jiggle_joint(i, target, p_delta);
- }
-
- execution_error_found = false;
-}
-
-void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta) {
- // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone
- // With modifications by TwistedTwigleg.
-
- if (jiggle_data_chain[p_joint_idx].bone_idx <= -2) {
- jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name);
- }
- if (_print_execution_error(
- jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(),
- "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification!")) {
- return;
- }
-
- Transform3D bone_local_pos = stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx);
- if (bone_local_pos == Transform3D()) {
- bone_local_pos = stack->skeleton->get_bone_pose(jiggle_data_chain[p_joint_idx].bone_idx);
- }
-
- Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, bone_local_pos);
- Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin;
-
- jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
-
- if (jiggle_data_chain[p_joint_idx].use_gravity) {
- Vector3 gravity_to_apply = new_bone_trans.basis.inverse().xform(jiggle_data_chain[p_joint_idx].gravity);
- jiggle_data_chain[p_joint_idx].force += gravity_to_apply * p_delta;
- }
-
- jiggle_data_chain[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
- jiggle_data_chain[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
-
- jiggle_data_chain[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
- jiggle_data_chain[p_joint_idx].dynamic_position += new_bone_trans.origin - jiggle_data_chain[p_joint_idx].last_position;
- jiggle_data_chain[p_joint_idx].last_position = new_bone_trans.origin;
-
- // Collision detection/response
- if (use_colliders) {
- if (execution_mode == SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process) {
- Ref<World3D> world_3d = stack->skeleton->get_world_3d();
- ERR_FAIL_COND(world_3d.is_null());
- PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
- PhysicsDirectSpaceState3D::RayResult ray_result;
-
- // Convert to world transforms, which is what the physics server needs
- Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
- Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
-
- PhysicsDirectSpaceState3D::RayParameters ray_params;
- ray_params.from = new_bone_trans_world.origin;
- ray_params.to = dynamic_position_world.get_origin();
- ray_params.collision_mask = collision_mask;
-
- bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
-
- if (ray_hit) {
- jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
- jiggle_data_chain[p_joint_idx].acceleration = Vector3(0, 0, 0);
- jiggle_data_chain[p_joint_idx].velocity = Vector3(0, 0, 0);
- } else {
- jiggle_data_chain[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position;
- }
-
- } else {
- WARN_PRINT_ONCE("Jiggle modifier: You cannot detect colliders without the stack mode being set to _physics_process!");
- }
- }
-
- // Get the forward direction that the basis is facing in right now.
- stack->skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
- Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
-
- // Rotate the bone using the dynamic position!
- new_bone_trans.basis.rotate_to_align(forward_vector, new_bone_trans.origin.direction_to(jiggle_data_chain[p_joint_idx].dynamic_position));
-
- // Roll
- new_bone_trans.basis.rotate_local(forward_vector, jiggle_data_chain[p_joint_idx].roll);
-
- new_bone_trans = stack->skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans);
- stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx);
-}
-
-void SkeletonModification3DJiggle::_update_jiggle_joint_data() {
- for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
- if (!jiggle_data_chain[i].override_defaults) {
- set_jiggle_joint_stiffness(i, stiffness);
- set_jiggle_joint_mass(i, mass);
- set_jiggle_joint_damping(i, damping);
- set_jiggle_joint_use_gravity(i, use_gravity);
- set_jiggle_joint_gravity(i, gravity);
- }
- }
-}
-
-void SkeletonModification3DJiggle::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
-
- if (stack) {
- is_setup = true;
- execution_error_found = false;
-
- if (stack->skeleton) {
- for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
- int bone_idx = jiggle_data_chain[i].bone_idx;
- if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
- jiggle_data_chain[i].dynamic_position = stack->skeleton->local_pose_to_global_pose(bone_idx, stack->skeleton->get_bone_local_pose_override(bone_idx)).origin;
- }
- }
- }
-
- update_cache();
- }
-}
-
-void SkeletonModification3DJiggle::update_cache() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
- return;
- }
-
- target_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(target_node)) {
- Node *node = stack->skeleton->get_node(target_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update target cache: node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update target cache: node is not in the scene tree!");
- target_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DJiggle::set_target_node(const NodePath &p_target_node) {
- target_node = p_target_node;
- update_cache();
-}
-
-NodePath SkeletonModification3DJiggle::get_target_node() const {
- return target_node;
-}
-
-void SkeletonModification3DJiggle::set_stiffness(real_t p_stiffness) {
- ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
- stiffness = p_stiffness;
- _update_jiggle_joint_data();
-}
-
-real_t SkeletonModification3DJiggle::get_stiffness() const {
- return stiffness;
-}
-
-void SkeletonModification3DJiggle::set_mass(real_t p_mass) {
- ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
- mass = p_mass;
- _update_jiggle_joint_data();
-}
-
-real_t SkeletonModification3DJiggle::get_mass() const {
- return mass;
-}
-
-void SkeletonModification3DJiggle::set_damping(real_t p_damping) {
- ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
- ERR_FAIL_COND_MSG(p_damping > 1, "Damping cannot be more than one!");
- damping = p_damping;
- _update_jiggle_joint_data();
-}
-
-real_t SkeletonModification3DJiggle::get_damping() const {
- return damping;
-}
-
-void SkeletonModification3DJiggle::set_use_gravity(bool p_use_gravity) {
- use_gravity = p_use_gravity;
- _update_jiggle_joint_data();
-}
-
-bool SkeletonModification3DJiggle::get_use_gravity() const {
- return use_gravity;
-}
-
-void SkeletonModification3DJiggle::set_gravity(Vector3 p_gravity) {
- gravity = p_gravity;
- _update_jiggle_joint_data();
-}
-
-Vector3 SkeletonModification3DJiggle::get_gravity() const {
- return gravity;
-}
-
-void SkeletonModification3DJiggle::set_use_colliders(bool p_use_collider) {
- use_colliders = p_use_collider;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DJiggle::get_use_colliders() const {
- return use_colliders;
-}
-
-void SkeletonModification3DJiggle::set_collision_mask(int p_mask) {
- collision_mask = p_mask;
-}
-
-int SkeletonModification3DJiggle::get_collision_mask() const {
- return collision_mask;
-}
-
-// Jiggle joint data functions
-int SkeletonModification3DJiggle::get_jiggle_data_chain_length() {
- return jiggle_data_chain.size();
-}
-
-void SkeletonModification3DJiggle::set_jiggle_data_chain_length(int p_length) {
- ERR_FAIL_COND(p_length < 0);
- jiggle_data_chain.resize(p_length);
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_bone_name(int p_joint_idx, String p_name) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
-
- jiggle_data_chain[p_joint_idx].bone_name = p_name;
- if (stack && stack->skeleton) {
- jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_name);
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-String SkeletonModification3DJiggle::get_jiggle_joint_bone_name(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, "");
- return jiggle_data_chain[p_joint_idx].bone_name;
-}
-
-int SkeletonModification3DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return jiggle_data_chain[p_joint_idx].bone_idx;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
- jiggle_data_chain[p_joint_idx].bone_idx = p_bone_idx;
-
- if (stack) {
- if (stack->skeleton) {
- jiggle_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].override_defaults = p_override;
- _update_jiggle_joint_data();
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return jiggle_data_chain[p_joint_idx].override_defaults;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].stiffness = p_stiffness;
-}
-
-real_t SkeletonModification3DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return jiggle_data_chain[p_joint_idx].stiffness;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_mass(int p_joint_idx, real_t p_mass) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].mass = p_mass;
-}
-
-real_t SkeletonModification3DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return jiggle_data_chain[p_joint_idx].mass;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_damping(int p_joint_idx, real_t p_damping) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].damping = p_damping;
-}
-
-real_t SkeletonModification3DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
- return jiggle_data_chain[p_joint_idx].damping;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].use_gravity = p_use_gravity;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
- return jiggle_data_chain[p_joint_idx].use_gravity;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].gravity = p_gravity;
-}
-
-Vector3 SkeletonModification3DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3(0, 0, 0));
- return jiggle_data_chain[p_joint_idx].gravity;
-}
-
-void SkeletonModification3DJiggle::set_jiggle_joint_roll(int p_joint_idx, real_t p_roll) {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
- jiggle_data_chain[p_joint_idx].roll = p_roll;
-}
-
-real_t SkeletonModification3DJiggle::get_jiggle_joint_roll(int p_joint_idx) const {
- const int bone_chain_size = jiggle_data_chain.size();
- ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
- return jiggle_data_chain[p_joint_idx].roll;
-}
-
-void SkeletonModification3DJiggle::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DJiggle::set_target_node);
- ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DJiggle::get_target_node);
-
- ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification3DJiggle::set_jiggle_data_chain_length);
- ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification3DJiggle::get_jiggle_data_chain_length);
-
- ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification3DJiggle::set_stiffness);
- ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification3DJiggle::get_stiffness);
- ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification3DJiggle::set_mass);
- ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification3DJiggle::get_mass);
- ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification3DJiggle::set_damping);
- ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification3DJiggle::get_damping);
- ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification3DJiggle::set_use_gravity);
- ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification3DJiggle::get_use_gravity);
- ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification3DJiggle::set_gravity);
- ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification3DJiggle::get_gravity);
-
- ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification3DJiggle::set_use_colliders);
- ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification3DJiggle::get_use_colliders);
- ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &SkeletonModification3DJiggle::set_collision_mask);
- ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification3DJiggle::get_collision_mask);
-
- // Jiggle joint data functions
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_name", "joint_idx", "name"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_name);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_name", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_name);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_index);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_index);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification3DJiggle::set_jiggle_joint_override);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_override);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification3DJiggle::set_jiggle_joint_stiffness);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_stiffness);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification3DJiggle::set_jiggle_joint_mass);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_mass);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification3DJiggle::set_jiggle_joint_damping);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_damping);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_use_gravity);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_use_gravity);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_gravity);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_gravity);
- ClassDB::bind_method(D_METHOD("set_jiggle_joint_roll", "joint_idx", "roll"), &SkeletonModification3DJiggle::set_jiggle_joint_roll);
- ClassDB::bind_method(D_METHOD("get_jiggle_joint_roll", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_roll);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "jiggle_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_jiggle_data_chain_length", "get_jiggle_data_chain_length");
- ADD_GROUP("Default Joint Settings", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness"), "set_stiffness", "get_stiffness");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01"), "set_damping", "get_damping");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_gravity"), "set_use_gravity", "get_use_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
- ADD_GROUP("", "");
-}
-
-SkeletonModification3DJiggle::SkeletonModification3DJiggle() {
- stack = nullptr;
- is_setup = false;
- jiggle_data_chain = Vector<Jiggle_Joint_Data>();
- stiffness = 3;
- mass = 0.75;
- damping = 0.75;
- use_gravity = false;
- gravity = Vector3(0, -6.0, 0);
- enabled = true;
-}
-
-SkeletonModification3DJiggle::~SkeletonModification3DJiggle() {
-}
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
deleted file mode 100644
index cca620a72d..0000000000
--- a/scene/resources/skeleton_modification_3d_jiggle.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_jiggle.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 SKELETON_MODIFICATION_3D_JIGGLE_H
-#define SKELETON_MODIFICATION_3D_JIGGLE_H
-
-#include "core/templates/local_vector.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DJiggle : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D);
-
-private:
- struct Jiggle_Joint_Data {
- String bone_name = "";
- int bone_idx = -1;
-
- bool override_defaults = false;
- real_t stiffness = 3;
- real_t mass = 0.75;
- real_t damping = 0.75;
- bool use_gravity = false;
- Vector3 gravity = Vector3(0, -6.0, 0);
- real_t roll = 0;
-
- Vector3 cached_rotation = Vector3(0, 0, 0);
- Vector3 force = Vector3(0, 0, 0);
- Vector3 acceleration = Vector3(0, 0, 0);
- Vector3 velocity = Vector3(0, 0, 0);
- Vector3 last_position = Vector3(0, 0, 0);
- Vector3 dynamic_position = Vector3(0, 0, 0);
-
- Vector3 last_noncollision_position = Vector3(0, 0, 0);
- };
-
- NodePath target_node;
- ObjectID target_node_cache;
- LocalVector<Jiggle_Joint_Data> jiggle_data_chain;
-
- real_t stiffness = 3;
- real_t mass = 0.75;
- real_t damping = 0.75;
- bool use_gravity = false;
- Vector3 gravity = Vector3(0, -6.0, 0);
-
- bool use_colliders = false;
- uint32_t collision_mask = 1;
-
- void update_cache();
- void _execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta);
- void _update_jiggle_joint_data();
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
-
- void set_target_node(const NodePath &p_target_node);
- NodePath get_target_node() const;
-
- void set_stiffness(real_t p_stiffness);
- real_t get_stiffness() const;
- void set_mass(real_t p_mass);
- real_t get_mass() const;
- void set_damping(real_t p_damping);
- real_t get_damping() const;
-
- void set_use_gravity(bool p_use_gravity);
- bool get_use_gravity() const;
- void set_gravity(Vector3 p_gravity);
- Vector3 get_gravity() const;
-
- void set_use_colliders(bool p_use_colliders);
- bool get_use_colliders() const;
- void set_collision_mask(int p_mask);
- int get_collision_mask() const;
-
- int get_jiggle_data_chain_length();
- void set_jiggle_data_chain_length(int p_new_length);
-
- void set_jiggle_joint_bone_name(int p_joint_idx, String p_name);
- String get_jiggle_joint_bone_name(int p_joint_idx) const;
- void set_jiggle_joint_bone_index(int p_joint_idx, int p_idx);
- int get_jiggle_joint_bone_index(int p_joint_idx) const;
-
- void set_jiggle_joint_override(int p_joint_idx, bool p_override);
- bool get_jiggle_joint_override(int p_joint_idx) const;
- void set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness);
- real_t get_jiggle_joint_stiffness(int p_joint_idx) const;
- void set_jiggle_joint_mass(int p_joint_idx, real_t p_mass);
- real_t get_jiggle_joint_mass(int p_joint_idx) const;
- void set_jiggle_joint_damping(int p_joint_idx, real_t p_damping);
- real_t get_jiggle_joint_damping(int p_joint_idx) const;
- void set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity);
- bool get_jiggle_joint_use_gravity(int p_joint_idx) const;
- void set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity);
- Vector3 get_jiggle_joint_gravity(int p_joint_idx) const;
- void set_jiggle_joint_roll(int p_joint_idx, real_t p_roll);
- real_t get_jiggle_joint_roll(int p_joint_idx) const;
-
- SkeletonModification3DJiggle();
- ~SkeletonModification3DJiggle();
-};
-
-#endif // SKELETON_MODIFICATION_3D_JIGGLE_H
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
deleted file mode 100644
index 57eedeb64e..0000000000
--- a/scene/resources/skeleton_modification_3d_lookat.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_lookat.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 "scene/resources/skeleton_modification_3d_lookat.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant &p_value) {
- if (p_path == "lock_rotation_to_plane") {
- set_lock_rotation_to_plane(p_value);
- } else if (p_path == "lock_rotation_plane") {
- set_lock_rotation_plane(p_value);
- } else if (p_path == "additional_rotation") {
- Vector3 tmp = p_value;
- tmp.x = Math::deg_to_rad(tmp.x);
- tmp.y = Math::deg_to_rad(tmp.y);
- tmp.z = Math::deg_to_rad(tmp.z);
- set_additional_rotation(tmp);
- }
-
- return true;
-}
-
-bool SkeletonModification3DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
- if (p_path == "lock_rotation_to_plane") {
- r_ret = get_lock_rotation_to_plane();
- } else if (p_path == "lock_rotation_plane") {
- r_ret = get_lock_rotation_plane();
- } else if (p_path == "additional_rotation") {
- Vector3 tmp = get_additional_rotation();
- tmp.x = Math::rad_to_deg(tmp.x);
- tmp.y = Math::rad_to_deg(tmp.y);
- tmp.z = Math::rad_to_deg(tmp.z);
- r_ret = tmp;
- }
-
- return true;
-}
-
-void SkeletonModification3DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::BOOL, "lock_rotation_to_plane", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (lock_rotation_to_plane) {
- p_list->push_back(PropertyInfo(Variant::INT, "lock_rotation_plane", PROPERTY_HINT_ENUM, "X plane,Y plane,Z plane", PROPERTY_USAGE_DEFAULT));
- }
- p_list->push_back(PropertyInfo(Variant::VECTOR3, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
-}
-
-void SkeletonModification3DLookAt::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
- if (!enabled) {
- return;
- }
-
- if (target_node_cache.is_null()) {
- _print_execution_error(true, "Target cache is out of date. Attempting to update...");
- update_cache();
- return;
- }
-
- if (bone_idx <= -2) {
- bone_idx = stack->skeleton->find_bone(bone_name);
- }
-
- Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
- if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
- if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) {
- return;
- }
- Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx);
- if (new_bone_trans == Transform3D()) {
- new_bone_trans = stack->skeleton->get_bone_pose(bone_idx);
- }
- Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin;
-
- // Lock the rotation to a plane relative to the bone by changing the target position
- if (lock_rotation_to_plane) {
- if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) {
- target_pos.x = new_bone_trans.origin.x;
- } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) {
- target_pos.y = new_bone_trans.origin.y;
- } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) {
- target_pos.z = new_bone_trans.origin.z;
- }
- }
-
- // Look at the target!
- new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0));
- // Convert from Z-forward to whatever direction the bone faces.
- stack->skeleton->update_bone_rest_forward_vector(bone_idx);
- new_bone_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis);
-
- // Apply additional rotation
- new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x);
- new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y);
- new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z);
-
- stack->skeleton->set_bone_local_pose_override(bone_idx, new_bone_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(bone_idx);
-
- // If we completed it successfully, then we can set execution_error_found to false
- execution_error_found = false;
-}
-
-void SkeletonModification3DLookAt::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
-
- if (stack != nullptr) {
- is_setup = true;
- execution_error_found = false;
- update_cache();
- }
-}
-
-void SkeletonModification3DLookAt::set_bone_name(String p_name) {
- bone_name = p_name;
- if (stack) {
- if (stack->skeleton) {
- bone_idx = stack->skeleton->find_bone(bone_name);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-String SkeletonModification3DLookAt::get_bone_name() const {
- return bone_name;
-}
-
-int SkeletonModification3DLookAt::get_bone_index() const {
- return bone_idx;
-}
-
-void SkeletonModification3DLookAt::set_bone_index(int p_bone_idx) {
- ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
- bone_idx = p_bone_idx;
-
- if (stack) {
- if (stack->skeleton) {
- bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-void SkeletonModification3DLookAt::update_cache() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
- return;
- }
-
- target_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(target_node)) {
- Node *node = stack->skeleton->get_node(target_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update target cache: Node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update target cache: Node is not in the scene tree!");
- target_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DLookAt::set_target_node(const NodePath &p_target_node) {
- target_node = p_target_node;
- update_cache();
-}
-
-NodePath SkeletonModification3DLookAt::get_target_node() const {
- return target_node;
-}
-
-Vector3 SkeletonModification3DLookAt::get_additional_rotation() const {
- return additional_rotation;
-}
-
-void SkeletonModification3DLookAt::set_additional_rotation(Vector3 p_offset) {
- additional_rotation = p_offset;
-}
-
-bool SkeletonModification3DLookAt::get_lock_rotation_to_plane() const {
- return lock_rotation_plane;
-}
-
-void SkeletonModification3DLookAt::set_lock_rotation_to_plane(bool p_lock_rotation) {
- lock_rotation_to_plane = p_lock_rotation;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DLookAt::get_lock_rotation_plane() const {
- return lock_rotation_plane;
-}
-
-void SkeletonModification3DLookAt::set_lock_rotation_plane(int p_plane) {
- lock_rotation_plane = p_plane;
-}
-
-void SkeletonModification3DLookAt::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_bone_name", "name"), &SkeletonModification3DLookAt::set_bone_name);
- ClassDB::bind_method(D_METHOD("get_bone_name"), &SkeletonModification3DLookAt::get_bone_name);
-
- ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification3DLookAt::set_bone_index);
- ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification3DLookAt::get_bone_index);
-
- ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DLookAt::set_target_node);
- ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DLookAt::get_target_node);
-
- ClassDB::bind_method(D_METHOD("set_additional_rotation", "additional_rotation"), &SkeletonModification3DLookAt::set_additional_rotation);
- ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification3DLookAt::get_additional_rotation);
-
- ClassDB::bind_method(D_METHOD("set_lock_rotation_to_plane", "lock_to_plane"), &SkeletonModification3DLookAt::set_lock_rotation_to_plane);
- ClassDB::bind_method(D_METHOD("get_lock_rotation_to_plane"), &SkeletonModification3DLookAt::get_lock_rotation_to_plane);
- ClassDB::bind_method(D_METHOD("set_lock_rotation_plane", "plane"), &SkeletonModification3DLookAt::set_lock_rotation_plane);
- ClassDB::bind_method(D_METHOD("get_lock_rotation_plane"), &SkeletonModification3DLookAt::get_lock_rotation_plane);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
-}
-
-SkeletonModification3DLookAt::SkeletonModification3DLookAt() {
- stack = nullptr;
- is_setup = false;
- bone_name = "";
- bone_idx = -2;
- additional_rotation = Vector3();
- lock_rotation_to_plane = false;
- enabled = true;
-}
-
-SkeletonModification3DLookAt::~SkeletonModification3DLookAt() {
-}
diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h
deleted file mode 100644
index 9a25ac530d..0000000000
--- a/scene/resources/skeleton_modification_3d_lookat.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_lookat.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 SKELETON_MODIFICATION_3D_LOOKAT_H
-#define SKELETON_MODIFICATION_3D_LOOKAT_H
-
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DLookAt : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D);
-
-private:
- String bone_name = "";
- int bone_idx = -1;
- NodePath target_node;
- ObjectID target_node_cache;
-
- Vector3 additional_rotation = Vector3(1, 0, 0);
- bool lock_rotation_to_plane = false;
- int lock_rotation_plane = ROTATION_PLANE_X;
-
- void update_cache();
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- enum ROTATION_PLANE {
- ROTATION_PLANE_X,
- ROTATION_PLANE_Y,
- ROTATION_PLANE_Z
- };
-
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
-
- void set_bone_name(String p_name);
- String get_bone_name() const;
-
- void set_bone_index(int p_idx);
- int get_bone_index() const;
-
- void set_target_node(const NodePath &p_target_node);
- NodePath get_target_node() const;
-
- void set_additional_rotation(Vector3 p_offset);
- Vector3 get_additional_rotation() const;
-
- void set_lock_rotation_to_plane(bool p_lock_to_plane);
- bool get_lock_rotation_to_plane() const;
- void set_lock_rotation_plane(int p_plane);
- int get_lock_rotation_plane() const;
-
- SkeletonModification3DLookAt();
- ~SkeletonModification3DLookAt();
-};
-
-#endif // SKELETON_MODIFICATION_3D_LOOKAT_H
diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp
deleted file mode 100644
index 1598badb30..0000000000
--- a/scene/resources/skeleton_modification_3d_stackholder.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_stackholder.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 "scene/resources/skeleton_modification_3d_stackholder.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DStackHolder::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path == "held_modification_stack") {
- set_held_modification_stack(p_value);
- }
- return true;
-}
-
-bool SkeletonModification3DStackHolder::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path == "held_modification_stack") {
- r_ret = get_held_modification_stack();
- }
- return true;
-}
-
-void SkeletonModification3DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
-}
-
-void SkeletonModification3DStackHolder::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
-
- if (held_modification_stack.is_valid()) {
- held_modification_stack->execute(p_delta, execution_mode);
- }
-}
-
-void SkeletonModification3DStackHolder::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
-
- if (stack != nullptr) {
- is_setup = true;
-
- if (held_modification_stack.is_valid()) {
- held_modification_stack->set_skeleton(stack->get_skeleton());
- held_modification_stack->setup();
- }
- }
-}
-
-void SkeletonModification3DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack) {
- held_modification_stack = p_held_stack;
-
- if (is_setup && held_modification_stack.is_valid()) {
- held_modification_stack->set_skeleton(stack->get_skeleton());
- held_modification_stack->setup();
- }
-}
-
-Ref<SkeletonModificationStack3D> SkeletonModification3DStackHolder::get_held_modification_stack() const {
- return held_modification_stack;
-}
-
-void SkeletonModification3DStackHolder::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification3DStackHolder::set_held_modification_stack);
- ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification3DStackHolder::get_held_modification_stack);
-}
-
-SkeletonModification3DStackHolder::SkeletonModification3DStackHolder() {
- stack = nullptr;
- is_setup = false;
- enabled = true;
-}
-
-SkeletonModification3DStackHolder::~SkeletonModification3DStackHolder() {
-}
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
deleted file mode 100644
index 7d5b4d2b55..0000000000
--- a/scene/resources/skeleton_modification_3d_twoboneik.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_twoboneik.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 "scene/resources/skeleton_modification_3d_twoboneik.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path == "use_tip_node") {
- set_use_tip_node(p_value);
- } else if (path == "tip_node") {
- set_tip_node(p_value);
- } else if (path == "auto_calculate_joint_length") {
- set_auto_calculate_joint_length(p_value);
- } else if (path == "use_pole_node") {
- set_use_pole_node(p_value);
- } else if (path == "pole_node") {
- set_pole_node(p_value);
- } else if (path == "joint_one_length") {
- set_joint_one_length(p_value);
- } else if (path == "joint_two_length") {
- set_joint_two_length(p_value);
- } else if (path == "joint_one/bone_name") {
- set_joint_one_bone_name(p_value);
- } else if (path == "joint_one/bone_idx") {
- set_joint_one_bone_idx(p_value);
- } else if (path == "joint_one/roll") {
- set_joint_one_roll(Math::deg_to_rad(real_t(p_value)));
- } else if (path == "joint_two/bone_name") {
- set_joint_two_bone_name(p_value);
- } else if (path == "joint_two/bone_idx") {
- set_joint_two_bone_idx(p_value);
- } else if (path == "joint_two/roll") {
- set_joint_two_roll(Math::deg_to_rad(real_t(p_value)));
- }
-
- return true;
-}
-
-bool SkeletonModification3DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path == "use_tip_node") {
- r_ret = get_use_tip_node();
- } else if (path == "tip_node") {
- r_ret = get_tip_node();
- } else if (path == "auto_calculate_joint_length") {
- r_ret = get_auto_calculate_joint_length();
- } else if (path == "use_pole_node") {
- r_ret = get_use_pole_node();
- } else if (path == "pole_node") {
- r_ret = get_pole_node();
- } else if (path == "joint_one_length") {
- r_ret = get_joint_one_length();
- } else if (path == "joint_two_length") {
- r_ret = get_joint_two_length();
- } else if (path == "joint_one/bone_name") {
- r_ret = get_joint_one_bone_name();
- } else if (path == "joint_one/bone_idx") {
- r_ret = get_joint_one_bone_idx();
- } else if (path == "joint_one/roll") {
- r_ret = Math::rad_to_deg(get_joint_one_roll());
- } else if (path == "joint_two/bone_name") {
- r_ret = get_joint_two_bone_name();
- } else if (path == "joint_two/bone_idx") {
- r_ret = get_joint_two_bone_idx();
- } else if (path == "joint_two/roll") {
- r_ret = Math::rad_to_deg(get_joint_two_roll());
- }
-
- return true;
-}
-
-void SkeletonModification3DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::BOOL, "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (use_tip_node) {
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
- }
-
- p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_joint_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (!auto_calculate_joint_length) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
- }
-
- p_list->push_back(PropertyInfo(Variant::BOOL, "use_pole_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- if (use_pole_node) {
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, "pole_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
- }
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_one/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::INT, "joint_one/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_two/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::INT, "joint_two/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
- p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
-}
-
-void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
- ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot execute!");
-
- if (!enabled) {
- return;
- }
-
- if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0,
- "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) {
- return;
- }
-
- if (target_node_cache.is_null()) {
- _print_execution_error(true, "Target cache is out of date. Attempting to update...");
- update_cache_target();
- return;
- }
-
- // Update joint lengths (if needed)
- if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) {
- calculate_joint_lengths();
- }
-
- // Adopted from the links below:
- // http://theorangeduck.com/page/simple-two-joint
- // https://www.alanzucconi.com/2018/05/02/ik-2d-2/
- // With modifications by TwistedTwigleg
- Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
- if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
- Transform3D target_trans = stack->skeleton->world_transform_to_global_pose(target->get_global_transform());
-
- Transform3D bone_one_trans;
- Transform3D bone_two_trans;
-
- // Make the first joint look at the pole, and the second look at the target. That way, the
- // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole.
- if (use_pole_node) {
- if (pole_node_cache.is_null()) {
- _print_execution_error(true, "Pole cache is out of date. Attempting to update...");
- update_cache_pole();
- return;
- }
-
- Node3D *pole = Object::cast_to<Node3D>(ObjectDB::get_instance(pole_node_cache));
- if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
- Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform());
-
- Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx);
- if (bone_one_local_pos == Transform3D()) {
- bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx);
- }
- Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx);
- if (bone_two_local_pos == Transform3D()) {
- bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx);
- }
-
- bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos);
- bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0));
- bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis);
- stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
- bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
- stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
-
- bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos);
- bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0));
- bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis);
- stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
- bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
- stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
- } else {
- Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx);
- if (bone_one_local_pos == Transform3D()) {
- bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx);
- }
- Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx);
- if (bone_two_local_pos == Transform3D()) {
- bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx);
- }
-
- bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos);
- bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos);
- }
-
- Transform3D bone_two_tip_trans;
- if (use_tip_node) {
- if (tip_node_cache.is_null()) {
- _print_execution_error(true, "Tip cache is out of date. Attempting to update...");
- update_cache_tip();
- return;
- }
- Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
- if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
- return;
- }
- bone_two_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
- } else {
- stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
- bone_two_tip_trans = bone_two_trans;
- bone_two_tip_trans.origin += bone_two_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length;
- }
-
- real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin);
- if (joint_one_length + joint_two_length < joint_one_to_target_length) {
- // Set the target *just* out of reach to straighten the bones
- joint_one_to_target_length = joint_one_length + joint_two_length + 0.01;
- } else if (joint_one_to_target_length < joint_one_length) {
- // Place the target in reach so the solver doesn't do crazy things
- joint_one_to_target_length = joint_one_length;
- }
-
- // Get the square lengths for all three sides of the triangle we'll use to calculate the angles
- real_t sqr_one_length = joint_one_length * joint_one_length;
- real_t sqr_two_length = joint_two_length * joint_two_length;
- real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length;
-
- // Calculate the angles for the first joint using the law of cosigns
- real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1));
- real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1));
- real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1));
-
- // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target
- Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin));
- Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin));
-
- // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform.
- Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion();
- Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0));
- Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0);
- bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2));
-
- stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
- bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
-
- // Apply the rotation to the first joint
- bone_one_trans = stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans);
- bone_one_trans.origin = Vector3(0, 0, 0);
- stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, bone_one_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
-
- if (use_pole_node) {
- // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target.
- bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
- stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
- Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx);
- bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin));
-
- stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
- bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
-
- bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
- stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
- } else {
- // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into
- // alignment, and then apply it to the second joint.
- real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1));
- real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1));
- Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion();
- Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0));
- bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1);
-
- stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
- bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
-
- bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
- bone_two_trans.origin = Vector3(0, 0, 0);
- stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
- stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
- }
-}
-
-void SkeletonModification3DTwoBoneIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
- stack = p_stack;
-
- if (stack != nullptr) {
- is_setup = true;
- execution_error_found = false;
- update_cache_target();
- update_cache_tip();
- }
-}
-
-void SkeletonModification3DTwoBoneIK::update_cache_target() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
- return;
- }
-
- target_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
- if (stack->skeleton->has_node(target_node)) {
- Node *node = stack->skeleton->get_node(target_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update target cache: Target node is this modification's skeleton or cannot be found. Cannot execute modification");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update target cache: Target node is not in the scene tree. Cannot execute modification!");
- target_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DTwoBoneIK::update_cache_tip() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
- return;
- }
-
- tip_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(tip_node)) {
- Node *node = stack->skeleton->get_node(tip_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update tip cache: Tip node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update tip cache: Tip node is not in the scene tree. Cannot execute modification!");
- tip_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DTwoBoneIK::update_cache_pole() {
- if (!is_setup || !stack) {
- _print_execution_error(true, "Cannot update pole cache: modification is not properly setup!");
- return;
- }
-
- pole_node_cache = ObjectID();
- if (stack->skeleton) {
- if (stack->skeleton->is_inside_tree()) {
- if (stack->skeleton->has_node(pole_node)) {
- Node *node = stack->skeleton->get_node(pole_node);
- ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
- "Cannot update pole cache: Pole node is this modification's skeleton or cannot be found!");
- ERR_FAIL_COND_MSG(!node->is_inside_tree(),
- "Cannot update pole cache: Pole node is not in the scene tree. Cannot execute modification!");
- pole_node_cache = node->get_instance_id();
-
- execution_error_found = false;
- }
- }
- }
-}
-
-void SkeletonModification3DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
- target_node = p_target_node;
- update_cache_target();
-}
-
-NodePath SkeletonModification3DTwoBoneIK::get_target_node() const {
- return target_node;
-}
-
-void SkeletonModification3DTwoBoneIK::set_use_tip_node(const bool p_use_tip_node) {
- use_tip_node = p_use_tip_node;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DTwoBoneIK::get_use_tip_node() const {
- return use_tip_node;
-}
-
-void SkeletonModification3DTwoBoneIK::set_tip_node(const NodePath &p_tip_node) {
- tip_node = p_tip_node;
- update_cache_tip();
-}
-
-NodePath SkeletonModification3DTwoBoneIK::get_tip_node() const {
- return tip_node;
-}
-
-void SkeletonModification3DTwoBoneIK::set_use_pole_node(const bool p_use_pole_node) {
- use_pole_node = p_use_pole_node;
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DTwoBoneIK::get_use_pole_node() const {
- return use_pole_node;
-}
-
-void SkeletonModification3DTwoBoneIK::set_pole_node(const NodePath &p_pole_node) {
- pole_node = p_pole_node;
- update_cache_pole();
-}
-
-NodePath SkeletonModification3DTwoBoneIK::get_pole_node() const {
- return pole_node;
-}
-
-void SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length(bool p_calculate) {
- auto_calculate_joint_length = p_calculate;
- if (p_calculate) {
- calculate_joint_lengths();
- }
- notify_property_list_changed();
-}
-
-bool SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length() const {
- return auto_calculate_joint_length;
-}
-
-void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
- if (!is_setup) {
- return; // fail silently, as we likely just loaded the scene.
- }
- ERR_FAIL_COND_MSG(!stack || stack->skeleton == nullptr,
- "Modification is not setup and therefore cannot calculate joint lengths!");
- ERR_FAIL_COND_MSG(joint_one_bone_idx <= -1 || joint_two_bone_idx <= -1,
- "One of the bones in the TwoBoneIK modification are not set! Cannot calculate joint lengths!");
-
- Transform3D bone_one_rest_trans = stack->skeleton->get_bone_global_pose(joint_one_bone_idx);
- Transform3D bone_two_rest_trans = stack->skeleton->get_bone_global_pose(joint_two_bone_idx);
-
- joint_one_length = bone_one_rest_trans.origin.distance_to(bone_two_rest_trans.origin);
-
- if (use_tip_node) {
- if (tip_node_cache.is_null()) {
- update_cache_tip();
- WARN_PRINT("Tip cache is out of date. Updating...");
- }
-
- Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
- if (tip) {
- Transform3D bone_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
- joint_two_length = bone_two_rest_trans.origin.distance_to(bone_tip_trans.origin);
- }
- } else {
- // Attempt to use children bones to get the length
- Vector<int> bone_two_children = stack->skeleton->get_bone_children(joint_two_bone_idx);
- if (bone_two_children.size() > 0) {
- joint_two_length = 0;
- for (int i = 0; i < bone_two_children.size(); i++) {
- joint_two_length += bone_two_rest_trans.origin.distance_to(
- stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin);
- }
- joint_two_length = joint_two_length / bone_two_children.size();
- } else {
- WARN_PRINT("TwoBoneIK modification: Cannot auto calculate length for joint 2! Auto setting the length to 1...");
- joint_two_length = 1.0;
- }
- }
- execution_error_found = false;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_one_bone_name(String p_bone_name) {
- joint_one_bone_name = p_bone_name;
- if (stack && stack->skeleton) {
- joint_one_bone_idx = stack->skeleton->find_bone(p_bone_name);
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-String SkeletonModification3DTwoBoneIK::get_joint_one_bone_name() const {
- return joint_one_bone_name;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
- joint_one_bone_idx = p_bone_idx;
- if (stack && stack->skeleton) {
- joint_one_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx() const {
- return joint_one_bone_idx;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_one_length(real_t p_length) {
- joint_one_length = p_length;
-}
-
-real_t SkeletonModification3DTwoBoneIK::get_joint_one_length() const {
- return joint_one_length;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_two_bone_name(String p_bone_name) {
- joint_two_bone_name = p_bone_name;
- if (stack && stack->skeleton) {
- joint_two_bone_idx = stack->skeleton->find_bone(p_bone_name);
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-String SkeletonModification3DTwoBoneIK::get_joint_two_bone_name() const {
- return joint_two_bone_name;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
- joint_two_bone_idx = p_bone_idx;
- if (stack && stack->skeleton) {
- joint_two_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
- }
- execution_error_found = false;
- notify_property_list_changed();
-}
-
-int SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx() const {
- return joint_two_bone_idx;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_two_length(real_t p_length) {
- joint_two_length = p_length;
-}
-
-real_t SkeletonModification3DTwoBoneIK::get_joint_two_length() const {
- return joint_two_length;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_one_roll(real_t p_roll) {
- joint_one_roll = p_roll;
-}
-
-real_t SkeletonModification3DTwoBoneIK::get_joint_one_roll() const {
- return joint_one_roll;
-}
-
-void SkeletonModification3DTwoBoneIK::set_joint_two_roll(real_t p_roll) {
- joint_two_roll = p_roll;
-}
-
-real_t SkeletonModification3DTwoBoneIK::get_joint_two_roll() const {
- return joint_two_roll;
-}
-
-void SkeletonModification3DTwoBoneIK::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DTwoBoneIK::set_target_node);
- ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DTwoBoneIK::get_target_node);
-
- ClassDB::bind_method(D_METHOD("set_use_pole_node", "use_pole_node"), &SkeletonModification3DTwoBoneIK::set_use_pole_node);
- ClassDB::bind_method(D_METHOD("get_use_pole_node"), &SkeletonModification3DTwoBoneIK::get_use_pole_node);
- ClassDB::bind_method(D_METHOD("set_pole_node", "pole_nodepath"), &SkeletonModification3DTwoBoneIK::set_pole_node);
- ClassDB::bind_method(D_METHOD("get_pole_node"), &SkeletonModification3DTwoBoneIK::get_pole_node);
-
- ClassDB::bind_method(D_METHOD("set_use_tip_node", "use_tip_node"), &SkeletonModification3DTwoBoneIK::set_use_tip_node);
- ClassDB::bind_method(D_METHOD("get_use_tip_node"), &SkeletonModification3DTwoBoneIK::get_use_tip_node);
- ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DTwoBoneIK::set_tip_node);
- ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DTwoBoneIK::get_tip_node);
-
- ClassDB::bind_method(D_METHOD("set_auto_calculate_joint_length", "auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length);
- ClassDB::bind_method(D_METHOD("get_auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length);
-
- ClassDB::bind_method(D_METHOD("set_joint_one_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_name);
- ClassDB::bind_method(D_METHOD("get_joint_one_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_name);
- ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx);
- ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx);
- ClassDB::bind_method(D_METHOD("set_joint_one_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_one_length);
- ClassDB::bind_method(D_METHOD("get_joint_one_length"), &SkeletonModification3DTwoBoneIK::get_joint_one_length);
-
- ClassDB::bind_method(D_METHOD("set_joint_two_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_name);
- ClassDB::bind_method(D_METHOD("get_joint_two_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_name);
- ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx);
- ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx);
- ClassDB::bind_method(D_METHOD("set_joint_two_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_two_length);
- ClassDB::bind_method(D_METHOD("get_joint_two_length"), &SkeletonModification3DTwoBoneIK::get_joint_two_length);
-
- ClassDB::bind_method(D_METHOD("set_joint_one_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_one_roll);
- ClassDB::bind_method(D_METHOD("get_joint_one_roll"), &SkeletonModification3DTwoBoneIK::get_joint_one_roll);
- ClassDB::bind_method(D_METHOD("set_joint_two_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_two_roll);
- ClassDB::bind_method(D_METHOD("get_joint_two_roll"), &SkeletonModification3DTwoBoneIK::get_joint_two_roll);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
- ADD_GROUP("", "");
-}
-
-SkeletonModification3DTwoBoneIK::SkeletonModification3DTwoBoneIK() {
- stack = nullptr;
- is_setup = false;
-}
-
-SkeletonModification3DTwoBoneIK::~SkeletonModification3DTwoBoneIK() {
-}
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
deleted file mode 100644
index 57481e0181..0000000000
--- a/scene/resources/skeleton_modification_3d_twoboneik.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_3d_twoboneik.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 SKELETON_MODIFICATION_3D_TWOBONEIK_H
-#define SKELETON_MODIFICATION_3D_TWOBONEIK_H
-
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/skeleton_modification_3d.h"
-
-class SkeletonModification3DTwoBoneIK : public SkeletonModification3D {
- GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D);
-
-private:
- NodePath target_node;
- ObjectID target_node_cache;
-
- bool use_tip_node = false;
- NodePath tip_node;
- ObjectID tip_node_cache;
-
- bool use_pole_node = false;
- NodePath pole_node;
- ObjectID pole_node_cache;
-
- String joint_one_bone_name = "";
- int joint_one_bone_idx = -1;
- String joint_two_bone_name = "";
- int joint_two_bone_idx = -1;
-
- bool auto_calculate_joint_length = false;
- real_t joint_one_length = -1;
- real_t joint_two_length = -1;
-
- real_t joint_one_roll = 0;
- real_t joint_two_roll = 0;
-
- void update_cache_target();
- void update_cache_tip();
- void update_cache_pole();
-
-protected:
- static void _bind_methods();
- bool _get(const StringName &p_path, Variant &r_ret) const;
- bool _set(const StringName &p_path, const Variant &p_value);
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- virtual void _execute(real_t p_delta) override;
- virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
-
- void set_target_node(const NodePath &p_target_node);
- NodePath get_target_node() const;
-
- void set_use_tip_node(const bool p_use_tip_node);
- bool get_use_tip_node() const;
- void set_tip_node(const NodePath &p_tip_node);
- NodePath get_tip_node() const;
-
- void set_use_pole_node(const bool p_use_pole_node);
- bool get_use_pole_node() const;
- void set_pole_node(const NodePath &p_pole_node);
- NodePath get_pole_node() const;
-
- void set_auto_calculate_joint_length(bool p_calculate);
- bool get_auto_calculate_joint_length() const;
- void calculate_joint_lengths();
-
- void set_joint_one_bone_name(String p_bone_name);
- String get_joint_one_bone_name() const;
- void set_joint_one_bone_idx(int p_bone_idx);
- int get_joint_one_bone_idx() const;
- void set_joint_one_length(real_t p_length);
- real_t get_joint_one_length() const;
-
- void set_joint_two_bone_name(String p_bone_name);
- String get_joint_two_bone_name() const;
- void set_joint_two_bone_idx(int p_bone_idx);
- int get_joint_two_bone_idx() const;
- void set_joint_two_length(real_t p_length);
- real_t get_joint_two_length() const;
-
- void set_joint_one_roll(real_t p_roll);
- real_t get_joint_one_roll() const;
- void set_joint_two_roll(real_t p_roll);
- real_t get_joint_two_roll() const;
-
- SkeletonModification3DTwoBoneIK();
- ~SkeletonModification3DTwoBoneIK();
-};
-
-#endif // SKELETON_MODIFICATION_3D_TWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
deleted file mode 100644
index 32708e198a..0000000000
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_stack_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 "skeleton_modification_stack_3d.h"
-#include "scene/3d/skeleton_3d.h"
-
-///////////////////////////////////////
-// ModificationStack3D
-///////////////////////////////////////
-
-void SkeletonModificationStack3D::_get_property_list(List<PropertyInfo> *p_list) const {
- for (uint32_t i = 0; i < modifications.size(); i++) {
- p_list->push_back(
- PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
- PROPERTY_HINT_RESOURCE_TYPE,
- "SkeletonModification3D",
- PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
- }
-}
-
-bool SkeletonModificationStack3D::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("modifications/")) {
- int mod_idx = path.get_slicec('/', 1).to_int();
- set_modification(mod_idx, p_value);
- return true;
- }
- return true;
-}
-
-bool SkeletonModificationStack3D::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("modifications/")) {
- int mod_idx = path.get_slicec('/', 1).to_int();
- r_ret = get_modification(mod_idx);
- return true;
- }
- return true;
-}
-
-void SkeletonModificationStack3D::setup() {
- if (is_setup) {
- return;
- }
-
- if (skeleton != nullptr) {
- is_setup = true;
- for (uint32_t i = 0; i < modifications.size(); i++) {
- if (!modifications[i].is_valid()) {
- continue;
- }
- modifications[i]->_setup_modification(this);
- }
- } else {
- WARN_PRINT("Cannot setup SkeletonModificationStack3D: no skeleton set!");
- }
-}
-
-void SkeletonModificationStack3D::execute(real_t p_delta, int p_execution_mode) {
- ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(),
- "Modification stack is not properly setup and therefore cannot execute!");
-
- if (!skeleton->is_inside_tree()) {
- ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!");
- return;
- }
-
- if (!enabled) {
- return;
- }
-
- for (uint32_t i = 0; i < modifications.size(); i++) {
- if (!modifications[i].is_valid()) {
- continue;
- }
-
- if (modifications[i]->get_execution_mode() == p_execution_mode) {
- modifications[i]->_execute(p_delta);
- }
- }
-}
-
-void SkeletonModificationStack3D::enable_all_modifications(bool p_enabled) {
- for (uint32_t i = 0; i < modifications.size(); i++) {
- if (!modifications[i].is_valid()) {
- continue;
- }
- modifications[i]->set_enabled(p_enabled);
- }
-}
-
-Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_mod_idx) const {
- const int modifications_size = modifications.size();
- ERR_FAIL_INDEX_V(p_mod_idx, modifications_size, nullptr);
- return modifications[p_mod_idx];
-}
-
-void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
- ERR_FAIL_NULL(p_mod);
- p_mod->_setup_modification(this);
- modifications.push_back(p_mod);
-}
-
-void SkeletonModificationStack3D::delete_modification(int p_mod_idx) {
- const int modifications_size = modifications.size();
- ERR_FAIL_INDEX(p_mod_idx, modifications_size);
- modifications.remove_at(p_mod_idx);
-}
-
-void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod) {
- const int modifications_size = modifications.size();
- ERR_FAIL_INDEX(p_mod_idx, modifications_size);
-
- if (p_mod == nullptr) {
- modifications.remove_at(p_mod_idx);
- } else {
- p_mod->_setup_modification(this);
- modifications[p_mod_idx] = p_mod;
- }
-}
-
-void SkeletonModificationStack3D::set_modification_count(int p_count) {
- ERR_FAIL_COND_MSG(p_count < 0, "Modification count cannot be less than zero.");
- modifications.resize(p_count);
- notify_property_list_changed();
-}
-
-int SkeletonModificationStack3D::get_modification_count() const {
- return modifications.size();
-}
-
-void SkeletonModificationStack3D::set_skeleton(Skeleton3D *p_skeleton) {
- skeleton = p_skeleton;
-}
-
-Skeleton3D *SkeletonModificationStack3D::get_skeleton() const {
- return skeleton;
-}
-
-bool SkeletonModificationStack3D::get_is_setup() const {
- return is_setup;
-}
-
-void SkeletonModificationStack3D::set_enabled(bool p_enabled) {
- enabled = p_enabled;
-
- if (!enabled && is_setup && skeleton != nullptr) {
- skeleton->clear_bones_local_pose_override();
- }
-}
-
-bool SkeletonModificationStack3D::get_enabled() const {
- return enabled;
-}
-
-void SkeletonModificationStack3D::set_strength(real_t p_strength) {
- ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!");
- ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!");
- strength = p_strength;
-}
-
-real_t SkeletonModificationStack3D::get_strength() const {
- return strength;
-}
-
-void SkeletonModificationStack3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack3D::setup);
- ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack3D::execute);
-
- ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack3D::enable_all_modifications);
- ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack3D::get_modification);
- ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack3D::add_modification);
- ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
- ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
-
- ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count);
- ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
-
- ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
-
- ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack3D::set_enabled);
- ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack3D::get_enabled);
-
- ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack3D::set_strength);
- ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack3D::get_strength);
-
- ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack3D::get_skeleton);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
-}
-
-SkeletonModificationStack3D::SkeletonModificationStack3D() {
-}
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
deleted file mode 100644
index fd490c49e9..0000000000
--- a/scene/resources/skeleton_modification_stack_3d.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/**************************************************************************/
-/* skeleton_modification_stack_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 SKELETON_MODIFICATION_STACK_3D_H
-#define SKELETON_MODIFICATION_STACK_3D_H
-
-#include "core/templates/local_vector.h"
-#include "scene/3d/skeleton_3d.h"
-
-class Skeleton3D;
-class SkeletonModification3D;
-
-class SkeletonModificationStack3D : public Resource {
- GDCLASS(SkeletonModificationStack3D, Resource);
- friend class Skeleton3D;
- friend class SkeletonModification3D;
-
-protected:
- static void _bind_methods();
- virtual void _get_property_list(List<PropertyInfo> *p_list) const;
- virtual bool _set(const StringName &p_path, const Variant &p_value);
- virtual bool _get(const StringName &p_path, Variant &r_ret) const;
-
-public:
- Skeleton3D *skeleton = nullptr;
- bool is_setup = false;
- bool enabled = false;
- real_t strength = 1.0;
-
- enum EXECUTION_MODE {
- execution_mode_process,
- execution_mode_physics_process,
- };
-
- LocalVector<Ref<SkeletonModification3D>> modifications = LocalVector<Ref<SkeletonModification3D>>();
- int modifications_count = 0;
-
- virtual void setup();
- virtual void execute(real_t p_delta, int p_execution_mode);
-
- void enable_all_modifications(bool p_enable);
- Ref<SkeletonModification3D> get_modification(int p_mod_idx) const;
- void add_modification(Ref<SkeletonModification3D> p_mod);
- void delete_modification(int p_mod_idx);
- void set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod);
-
- void set_modification_count(int p_count);
- int get_modification_count() const;
-
- void set_skeleton(Skeleton3D *p_skeleton);
- Skeleton3D *get_skeleton() const;
-
- bool get_is_setup() const;
-
- void set_enabled(bool p_enabled);
- bool get_enabled() const;
-
- void set_strength(real_t p_strength);
- real_t get_strength() const;
-
- SkeletonModificationStack3D();
-};
-
-#endif // SKELETON_MODIFICATION_STACK_3D_H
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 4043b73b71..94e78fc3aa 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -473,6 +473,7 @@ void TileSet::_compute_next_source_id() {
int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source_id_override) {
ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), TileSet::INVALID_SOURCE);
ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), TileSet::INVALID_SOURCE, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override));
+ ERR_FAIL_COND_V_MSG(p_atlas_source_id_override < 0 && p_atlas_source_id_override != TileSet::INVALID_SOURCE, TileSet::INVALID_SOURCE, vformat("Provided source ID %d is not valid. Negative source IDs are not allowed.", p_atlas_source_id_override));
int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id;
sources[new_source_id] = p_tile_set_source;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 0008250f8f..4c99563a91 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -2508,14 +2508,6 @@ void VisualShader::_update_shader() const {
global_compute_code += " return __rand_from_seed(seed) * (to - from) + from;\n";
global_compute_code += "}\n\n";
- global_compute_code += "vec2 __randv2_range(inout uint seed, vec2 from, vec2 to) {\n";
- global_compute_code += " return vec2(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y));\n";
- global_compute_code += "}\n\n";
-
- global_compute_code += "vec3 __randv3_range(inout uint seed, vec3 from, vec3 to) {\n";
- global_compute_code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
- global_compute_code += "}\n\n";
-
global_compute_code += "uint __hash(uint x) {\n";
global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n";
global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n";
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 432d5a5b59..985bcb442e 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -6407,7 +6407,7 @@ VisualShaderNodeTexture2DParameter::VisualShaderNodeTexture2DParameter() {
////////////// Texture Parameter (Triplanar)
String VisualShaderNodeTextureParameterTriplanar::get_caption() const {
- return "TextureUniformTriplanar";
+ return "TextureParameterTriplanar";
}
int VisualShaderNodeTextureParameterTriplanar::get_input_port_count() const {
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 4ac663f7f2..9cf42b681c 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -911,11 +911,12 @@ void VisualShaderNodeParticleRandomness::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -939,6 +940,8 @@ VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
default:
break;
}
@@ -950,48 +953,69 @@ String VisualShaderNodeParticleRandomness::get_output_port_name(int p_port) cons
}
int VisualShaderNodeParticleRandomness::get_input_port_count() const {
- return 2;
+ return 3;
}
VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const {
- switch (op_type) {
- case OP_TYPE_VECTOR_2D:
- return PORT_TYPE_VECTOR_2D;
- case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR_3D;
- default:
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_SCALAR_UINT;
+ case 1:
+ case 2:
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ break;
+ }
break;
}
return PORT_TYPE_SCALAR;
}
String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "min";
- } else if (p_port == 1) {
- return "max";
+ switch (p_port) {
+ case 0:
+ return "seed";
+ case 1:
+ return "min";
+ case 2:
+ return "max";
}
return String();
}
-String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+bool VisualShaderNodeParticleRandomness::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return p_port == 0; // seed
+}
+
+String VisualShaderNodeParticleRandomness::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
- switch (op_type) {
- case OP_TYPE_SCALAR: {
- code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
- } break;
- case OP_TYPE_VECTOR_2D: {
- code += vformat(" %s = __randv2_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
- } break;
- case OP_TYPE_VECTOR_3D: {
- code += vformat(" %s = __randv3_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
- } break;
- default:
- break;
- }
+
+ code += "vec2 __randv2_range(inout uint seed, vec2 from, vec2 to) {\n";
+ code += " return vec2(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y));\n";
+ code += "}\n\n";
+
+ code += "vec3 __randv3_range(inout uint seed, vec3 from, vec3 to) {\n";
+ code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
+ code += "}\n\n";
+
+ code += "vec4 __randv4_range(inout uint seed, vec4 from, vec4 to) {\n";
+ code += " return vec4(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z), __randf_range(seed, from.w, to.w));\n";
+ code += "}\n\n";
+
return code;
}
+String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ static const char *func[(int)OP_TYPE_MAX] = { "__randf_range", "__randv2_range", "__randv3_range", "__randv4_range" };
+ return vformat(" %s = %s(%s, %s, %s);\n", p_output_vars[0], func[op_type], p_input_vars[0].is_empty() ? "__seed" : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1], p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]);
+}
+
void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
@@ -999,16 +1023,20 @@ void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_SCALAR: {
- set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
} break;
case OP_TYPE_VECTOR_2D: {
- set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
} break;
case OP_TYPE_VECTOR_3D: {
- set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2));
} break;
default:
break;
@@ -1026,8 +1054,8 @@ bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) con
}
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
- set_input_port_default_value(0, -1.0);
- set_input_port_default_value(1, 1.0);
+ set_input_port_default_value(1, -1.0);
+ set_input_port_default_value(2, 1.0);
}
// VisualShaderNodeParticleAccelerator
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index c0ab974693..08fb059534 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -216,6 +216,7 @@ public:
OP_TYPE_SCALAR,
OP_TYPE_VECTOR_2D,
OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_4D,
OP_TYPE_MAX,
};
@@ -232,12 +233,14 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual bool has_output_port_preview(int p_port) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
void set_op_type(OpType p_type);
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 07e0b8057e..39f14439f4 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -38,6 +38,9 @@ DisplayServer *DisplayServer::singleton = nullptr;
bool DisplayServer::hidpi_allowed = false;
+bool DisplayServer::window_early_clear_override_enabled = false;
+Color DisplayServer::window_early_clear_override_color = Color(0, 0, 0, 0);
+
DisplayServer::DisplayServerCreate DisplayServer::server_create_functions[DisplayServer::MAX_SERVERS] = {
{ "headless", &DisplayServerHeadless::create_func, &DisplayServerHeadless::get_rendering_drivers_func }
};
@@ -315,6 +318,23 @@ void DisplayServer::tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id
}
}
+bool DisplayServer::_get_window_early_clear_override(Color &r_color) {
+ if (window_early_clear_override_enabled) {
+ r_color = window_early_clear_override_color;
+ return true;
+ } else if (RenderingServer::get_singleton()) {
+ r_color = RenderingServer::get_singleton()->get_default_clear_color();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void DisplayServer::set_early_window_clear_color_override(bool p_enabled, Color p_color) {
+ window_early_clear_override_enabled = p_enabled;
+ window_early_clear_override_color = p_color;
+}
+
void DisplayServer::mouse_set_mode(MouseMode p_mode) {
WARN_PRINT("Mouse is not supported by this display server.");
}
@@ -331,8 +351,8 @@ Point2i DisplayServer::mouse_get_position() const {
ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server.");
}
-MouseButton DisplayServer::mouse_get_button_state() const {
- ERR_FAIL_V_MSG(MouseButton::NONE, "Mouse is not supported by this display server.");
+BitField<MouseButtonMask> DisplayServer::mouse_get_button_state() const {
+ ERR_FAIL_V_MSG(0, "Mouse is not supported by this display server.");
}
void DisplayServer::clipboard_set(const String &p_text) {
@@ -828,6 +848,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_FLAG_NO_FOCUS);
BIND_ENUM_CONSTANT(WINDOW_FLAG_POPUP);
BIND_ENUM_CONSTANT(WINDOW_FLAG_EXTEND_TO_TITLE);
+ BIND_ENUM_CONSTANT(WINDOW_FLAG_MOUSE_PASSTHROUGH);
BIND_ENUM_CONSTANT(WINDOW_FLAG_MAX);
BIND_ENUM_CONSTANT(WINDOW_EVENT_MOUSE_ENTER);
diff --git a/servers/display_server.h b/servers/display_server.h
index ee53ec1ca0..aa30b25b65 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -217,6 +217,16 @@ public:
virtual bool is_dark_mode() const { return false; };
virtual Color get_accent_color() const { return Color(0, 0, 0, 0); };
+private:
+ static bool window_early_clear_override_enabled;
+ static Color window_early_clear_override_color;
+
+protected:
+ static bool _get_window_early_clear_override(Color &r_color);
+
+public:
+ static void set_early_window_clear_color_override(bool p_enabled, Color p_color = Color(0, 0, 0, 0));
+
enum MouseMode {
MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN,
@@ -230,7 +240,7 @@ public:
virtual void warp_mouse(const Point2i &p_position);
virtual Point2i mouse_get_position() const;
- virtual MouseButton mouse_get_button_state() const;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const;
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
@@ -301,6 +311,7 @@ public:
WINDOW_FLAG_NO_FOCUS,
WINDOW_FLAG_POPUP,
WINDOW_FLAG_EXTEND_TO_TITLE,
+ WINDOW_FLAG_MOUSE_PASSTHROUGH,
WINDOW_FLAG_MAX,
};
@@ -313,6 +324,7 @@ public:
WINDOW_FLAG_NO_FOCUS_BIT = (1 << WINDOW_FLAG_NO_FOCUS),
WINDOW_FLAG_POPUP_BIT = (1 << WINDOW_FLAG_POPUP),
WINDOW_FLAG_EXTEND_TO_TITLE_BIT = (1 << WINDOW_FLAG_EXTEND_TO_TITLE),
+ WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT = (1 << WINDOW_FLAG_MOUSE_PASSTHROUGH),
};
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
diff --git a/servers/extensions/physics_server_2d_extension.cpp b/servers/extensions/physics_server_2d_extension.cpp
index a0c082ee44..2dbb59a323 100644
--- a/servers/extensions/physics_server_2d_extension.cpp
+++ b/servers/extensions/physics_server_2d_extension.cpp
@@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query(const RID &
thread_local const HashSet<RID> *PhysicsDirectSpaceState2DExtension::exclude = nullptr;
void PhysicsDirectSpaceState2DExtension::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState2DExtension::is_body_excluded_from_query);
+
GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "result");
GDVIRTUAL_BIND(_intersect_point, "position", "canvas_instance_id", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results");
GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result", "max_results");
@@ -101,6 +103,7 @@ void PhysicsDirectBodyState2DExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_contact_collider_object, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_shape, "contact_idx");
GDVIRTUAL_BIND(_get_contact_collider_velocity_at_position, "contact_idx");
+ GDVIRTUAL_BIND(_get_contact_impulse, "contact_idx");
GDVIRTUAL_BIND(_get_step);
GDVIRTUAL_BIND(_integrate_forces);
@@ -202,6 +205,9 @@ void PhysicsServer2DExtension::_bind_methods() {
/* BODY API */
+ ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer2DExtension::body_test_motion_is_excluding_body);
+ ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer2DExtension::body_test_motion_is_excluding_object);
+
GDVIRTUAL_BIND(_body_create);
GDVIRTUAL_BIND(_body_set_space, "body", "space");
diff --git a/servers/extensions/physics_server_2d_extension.h b/servers/extensions/physics_server_2d_extension.h
index c4970f6398..0008653f66 100644
--- a/servers/extensions/physics_server_2d_extension.h
+++ b/servers/extensions/physics_server_2d_extension.h
@@ -100,6 +100,7 @@ public:
EXBIND1RC(Object *, get_contact_collider_object, int)
EXBIND1RC(int, get_contact_collider_shape, int)
EXBIND1RC(Vector2, get_contact_collider_velocity_at_position, int)
+ EXBIND1RC(Vector2, get_contact_impulse, int)
EXBIND0RC(real_t, get_step)
EXBIND0(integrate_forces)
diff --git a/servers/extensions/physics_server_3d_extension.cpp b/servers/extensions/physics_server_3d_extension.cpp
index 788ece721b..0d8476fd68 100644
--- a/servers/extensions/physics_server_3d_extension.cpp
+++ b/servers/extensions/physics_server_3d_extension.cpp
@@ -37,6 +37,8 @@ bool PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query(const RID &
thread_local const HashSet<RID> *PhysicsDirectSpaceState3DExtension::exclude = nullptr;
void PhysicsDirectSpaceState3DExtension::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_body_excluded_from_query", "body"), &PhysicsDirectSpaceState3DExtension::is_body_excluded_from_query);
+
GDVIRTUAL_BIND(_intersect_ray, "from", "to", "collision_mask", "collide_with_bodies", "collide_with_areas", "hit_from_inside", "hit_back_faces", "result");
GDVIRTUAL_BIND(_intersect_point, "position", "collision_mask", "collide_with_bodies", "collide_with_areas", "results", "max_results");
GDVIRTUAL_BIND(_intersect_shape, "shape_rid", "transform", "motion", "margin", "collision_mask", "collide_with_bodies", "collide_with_areas", "result_count", "max_results");
@@ -206,6 +208,9 @@ void PhysicsServer3DExtension::_bind_methods() {
/* BODY API */
+ ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_body", "body"), &PhysicsServer3DExtension::body_test_motion_is_excluding_body);
+ ClassDB::bind_method(D_METHOD("body_test_motion_is_excluding_object", "object"), &PhysicsServer3DExtension::body_test_motion_is_excluding_object);
+
GDVIRTUAL_BIND(_body_create);
GDVIRTUAL_BIND(_body_set_space, "body", "space");
diff --git a/servers/extensions/physics_server_3d_extension.h b/servers/extensions/physics_server_3d_extension.h
index 9394ff3949..bec403c0ec 100644
--- a/servers/extensions/physics_server_3d_extension.h
+++ b/servers/extensions/physics_server_3d_extension.h
@@ -94,7 +94,7 @@ public:
EXBIND1RC(Vector3, get_contact_local_position, int)
EXBIND1RC(Vector3, get_contact_local_normal, int)
- EXBIND1RC(real_t, get_contact_impulse, int)
+ EXBIND1RC(Vector3, get_contact_impulse, int)
EXBIND1RC(int, get_contact_local_shape, int)
EXBIND1RC(RID, get_contact_collider, int)
EXBIND1RC(Vector3, get_contact_collider_position, int)
diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp
index 9b2755806b..943b2ae467 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -36,15 +36,20 @@
NavigationServer2D *NavigationServer2D::singleton = nullptr;
+#define FORWARD_0(FUNC_NAME) \
+ NavigationServer2D::FUNC_NAME() { \
+ return NavigationServer3D::get_singleton()->FUNC_NAME(); \
+ }
+
#define FORWARD_0_C(FUNC_NAME) \
NavigationServer2D::FUNC_NAME() \
const { \
return NavigationServer3D::get_singleton()->FUNC_NAME(); \
}
-#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \
- NavigationServer2D::FUNC_NAME(T_0 D_0) { \
- return NavigationServer3D::get_singleton_mut()->FUNC_NAME(CONV_0(D_0)); \
+#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \
+ NavigationServer2D::FUNC_NAME(T_0 D_0) { \
+ return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
}
#define FORWARD_1_C(FUNC_NAME, T_0, D_0, CONV_0) \
@@ -59,6 +64,11 @@ NavigationServer2D *NavigationServer2D::singleton = nullptr;
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0))); \
}
+#define FORWARD_2(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
+ NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) { \
+ return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
+ }
+
#define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \
const { \
@@ -71,6 +81,11 @@ NavigationServer2D *NavigationServer2D::singleton = nullptr;
return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \
}
+#define FORWARD_4(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
+ NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) { \
+ return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \
+ }
+
#define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
const { \
@@ -166,14 +181,14 @@ void NavigationServer2D::_emit_map_changed(RID p_map) {
#ifdef DEBUG_ENABLED
void NavigationServer2D::set_debug_enabled(bool p_enabled) {
- NavigationServer3D::get_singleton_mut()->set_debug_enabled(p_enabled);
+ NavigationServer3D::get_singleton()->set_debug_enabled(p_enabled);
}
bool NavigationServer2D::get_debug_enabled() const {
return NavigationServer3D::get_singleton()->get_debug_enabled();
}
void NavigationServer2D::set_debug_navigation_edge_connection_color(const Color &p_color) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_edge_connection_color(p_color);
+ NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_edge_connection_color() const {
@@ -181,7 +196,7 @@ Color NavigationServer2D::get_debug_navigation_edge_connection_color() const {
}
void NavigationServer2D::set_debug_navigation_geometry_face_color(const Color &p_color) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_face_color(p_color);
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_face_color() const {
@@ -189,7 +204,7 @@ Color NavigationServer2D::get_debug_navigation_geometry_face_color() const {
}
void NavigationServer2D::set_debug_navigation_geometry_face_disabled_color(const Color &p_color) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_geometry_face_disabled_color(p_color);
+ NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_disabled_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_geometry_face_disabled_color() const {
@@ -197,7 +212,7 @@ Color NavigationServer2D::get_debug_navigation_geometry_face_disabled_color() co
}
void NavigationServer2D::set_debug_navigation_link_connection_color(const Color &p_color) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_link_connection_color(p_color);
+ NavigationServer3D::get_singleton()->set_debug_navigation_link_connection_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_link_connection_color() const {
@@ -205,7 +220,7 @@ Color NavigationServer2D::get_debug_navigation_link_connection_color() const {
}
void NavigationServer2D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_link_connection_disabled_color(p_color);
+ NavigationServer3D::get_singleton()->set_debug_navigation_link_connection_disabled_color(p_color);
}
Color NavigationServer2D::get_debug_navigation_link_connection_disabled_color() const {
@@ -213,7 +228,7 @@ Color NavigationServer2D::get_debug_navigation_link_connection_disabled_color()
}
void NavigationServer2D::set_debug_navigation_enable_edge_connections(const bool p_value) {
- NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_connections(p_value);
+ NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections(p_value);
}
bool NavigationServer2D::get_debug_navigation_enable_edge_connections() const {
@@ -303,7 +318,7 @@ void NavigationServer2D::_bind_methods() {
NavigationServer2D::NavigationServer2D() {
singleton = this;
ERR_FAIL_COND_MSG(!NavigationServer3D::get_singleton(), "The Navigation3D singleton should be initialized before the 2D one.");
- NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed));
+ NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &NavigationServer2D::_emit_map_changed));
}
NavigationServer2D::~NavigationServer2D() {
@@ -322,23 +337,23 @@ 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);
-RID FORWARD_0_C(map_create);
+RID FORWARD_0(map_create);
-void FORWARD_2_C(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
+void FORWARD_2(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
void NavigationServer2D::map_force_update(RID p_map) {
- NavigationServer3D::get_singleton_mut()->map_force_update(p_map);
+ NavigationServer3D::get_singleton()->map_force_update(p_map);
}
-void FORWARD_2_C(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
+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_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
+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);
-void FORWARD_2_C(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real);
+void FORWARD_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_link_connection_radius, RID, p_map, rid_to_rid);
Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32);
@@ -346,22 +361,22 @@ Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2
Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
-RID FORWARD_0_C(region_create);
+RID FORWARD_0(region_create);
-void FORWARD_2_C(region_set_enter_cost, RID, p_region, real_t, p_enter_cost, rid_to_rid, real_to_real);
+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_C(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
+void FORWARD_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_travel_cost, RID, p_region, rid_to_rid);
-void FORWARD_2_C(region_set_owner_id, RID, p_region, ObjectID, p_owner_id, rid_to_rid, id_to_id);
+void FORWARD_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(region_get_owner_id, RID, p_region, rid_to_rid);
bool FORWARD_2_C(region_owns_point, RID, p_region, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
-void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
-void FORWARD_2_C(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
+void FORWARD_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid);
-void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
+void FORWARD_2(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
-void NavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) const {
+void NavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) {
NavigationServer3D::get_singleton()->region_set_navigation_mesh(p_region, poly_to_mesh(p_navigation_polygon));
}
@@ -369,56 +384,45 @@ int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
-RID FORWARD_0_C(link_create);
+RID FORWARD_0(link_create);
-void FORWARD_2_C(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
+void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(link_get_map, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool);
+void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_start_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3);
+void FORWARD_2(link_set_start_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_location, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_end_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3);
+void FORWARD_2(link_set_end_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3);
Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_location, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real);
+void FORWARD_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real);
+void FORWARD_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(link_get_travel_cost, RID, p_link, rid_to_rid);
-void FORWARD_2_C(link_set_owner_id, RID, p_link, ObjectID, p_owner_id, rid_to_rid, id_to_id);
+void FORWARD_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id, rid_to_rid, id_to_id);
ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
-RID NavigationServer2D::agent_create() const {
+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_C(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
-
-void FORWARD_2_C(agent_set_neighbor_distance, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
-
-void FORWARD_2_C(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
-
-void FORWARD_2_C(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
-
-void FORWARD_2_C(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
-
-void FORWARD_2_C(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
-
-void FORWARD_2_C(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
-
-void FORWARD_2_C(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
-
-void FORWARD_2_C(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
-
-void FORWARD_2_C(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
-
+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_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, 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_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
-void FORWARD_4_C(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
-
-void FORWARD_1_C(free, RID, p_object, rid_to_rid);
+void FORWARD_1(free, RID, p_object, rid_to_rid);
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 eabd506341..0adf736b2a 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -51,36 +51,33 @@ protected:
public:
/// Thread safe, can be used across many threads.
- static const NavigationServer2D *get_singleton() { return singleton; }
-
- /// MUST be used in single thread!
- static NavigationServer2D *get_singleton_mut() { return singleton; }
+ static NavigationServer2D *get_singleton() { return singleton; }
virtual TypedArray<RID> get_maps() const;
/// Create a new map.
- virtual RID map_create() const;
+ virtual RID map_create();
/// Set map active.
- virtual void map_set_active(RID p_map, bool p_active) const;
+ virtual void map_set_active(RID p_map, bool p_active);
/// Returns true if the map is active.
virtual bool map_is_active(RID p_map) const;
/// Set the map cell size used to weld the navigation mesh polygons.
- virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const;
+ virtual void map_set_cell_size(RID p_map, real_t p_cell_size);
/// Returns the map cell size.
virtual real_t map_get_cell_size(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) const;
+ virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin);
/// Returns the edge connection margin of this map.
virtual real_t map_get_edge_connection_margin(RID p_map) const;
/// Set the map link connection radius used to attach links to the nav mesh.
- virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) const;
+ virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius);
/// Returns the link connection radius of this map.
virtual real_t map_get_link_connection_radius(RID p_map) const;
@@ -98,35 +95,35 @@ public:
virtual void map_force_update(RID p_map);
/// Creates a new region.
- virtual RID region_create() const;
+ virtual RID region_create();
/// Set the enter_cost of a region
- virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) const;
+ 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;
/// Set the travel_cost of a region
- virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) const;
+ virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost);
virtual real_t region_get_travel_cost(RID p_region) const;
/// Set the node which manages this region.
- virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) const;
+ virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id);
virtual ObjectID region_get_owner_id(RID p_region) const;
virtual bool region_owns_point(RID p_region, const Vector2 &p_point) const;
/// Set the map of this region.
- virtual void region_set_map(RID p_region, RID p_map) const;
+ virtual void region_set_map(RID p_region, RID p_map);
virtual RID region_get_map(RID p_region) const;
/// Set the region's layers
- virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const;
+ virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers);
virtual uint32_t region_get_navigation_layers(RID p_region) const;
/// Set the global transformation of this region.
- virtual void region_set_transform(RID p_region, Transform2D p_transform) const;
+ virtual void region_set_transform(RID p_region, Transform2D p_transform);
/// Set the navigation poly of this region.
- virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) const;
+ virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon);
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const;
@@ -134,45 +131,45 @@ public:
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const;
/// Creates a new link between locations in the nav map.
- virtual RID link_create() const;
+ virtual RID link_create();
/// Set the map of this link.
- virtual void link_set_map(RID p_link, RID p_map) const;
+ virtual void link_set_map(RID p_link, RID p_map);
virtual RID link_get_map(RID p_link) const;
/// Set whether this link travels in both directions.
- virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) const;
+ virtual void link_set_bidirectional(RID p_link, bool p_bidirectional);
virtual bool link_is_bidirectional(RID p_link) const;
/// Set the link's layers.
- virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) const;
+ virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers);
virtual uint32_t link_get_navigation_layers(RID p_link) const;
/// Set the start location of the link.
- virtual void link_set_start_location(RID p_link, Vector2 p_location) const;
+ virtual void link_set_start_location(RID p_link, Vector2 p_location);
virtual Vector2 link_get_start_location(RID p_link) const;
/// Set the end location of the link.
- virtual void link_set_end_location(RID p_link, Vector2 p_location) const;
+ virtual void link_set_end_location(RID p_link, Vector2 p_location);
virtual Vector2 link_get_end_location(RID p_link) const;
/// Set the enter cost of the link.
- virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) const;
+ virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost);
virtual real_t link_get_enter_cost(RID p_link) const;
/// Set the travel cost of the link.
- virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) const;
+ virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost);
virtual real_t link_get_travel_cost(RID p_link) const;
/// Set the node which manages this link.
- virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) const;
+ virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id);
virtual ObjectID link_get_owner_id(RID p_link) const;
/// Creates the agent.
- virtual RID agent_create() const;
+ virtual RID agent_create();
/// Put the agent in the map.
- virtual void agent_set_map(RID p_agent, RID p_map) const;
+ virtual void agent_set_map(RID p_agent, RID p_map);
virtual RID agent_get_map(RID p_agent) const;
/// The maximum distance (center point to
@@ -182,7 +179,7 @@ public:
/// time of the simulation. If the number is too
/// low, the simulation will not be safe.
/// Must be non-negative.
- virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) const;
+ virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance);
/// The maximum number of other agents this
/// agent takes into account in the navigation.
@@ -190,7 +187,7 @@ public:
/// running time of the simulation. If the
/// number is too low, the simulation will not
/// be safe.
- virtual void agent_set_max_neighbors(RID p_agent, int p_count) const;
+ virtual void agent_set_max_neighbors(RID p_agent, int p_count);
/// The minimal amount of time for which this
/// agent's velocities that are computed by the
@@ -200,38 +197,38 @@ 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) const;
+ virtual void agent_set_time_horizon(RID p_agent, real_t p_time);
/// The radius of this agent.
/// Must be non-negative.
- virtual void agent_set_radius(RID p_agent, real_t p_radius) const;
+ virtual void agent_set_radius(RID p_agent, real_t p_radius);
/// The maximum speed of this agent.
/// Must be non-negative.
- virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) const;
+ 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) const;
+ virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
/// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) const;
+ virtual void agent_set_target_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) const;
+ 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) const;
+ 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, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) const;
+ virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant());
virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const;
/// Destroy the `RID`
- virtual void free(RID p_object) const;
+ virtual void free(RID p_object);
NavigationServer2D();
virtual ~NavigationServer2D();
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 35a965388d..253b28a623 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -123,13 +123,21 @@ void NavigationServer3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
-}
-const NavigationServer3D *NavigationServer3D::get_singleton() {
- return singleton;
+ ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &NavigationServer3D::get_process_info);
+
+ BIND_ENUM_CONSTANT(INFO_ACTIVE_MAPS);
+ BIND_ENUM_CONSTANT(INFO_REGION_COUNT);
+ BIND_ENUM_CONSTANT(INFO_AGENT_COUNT);
+ BIND_ENUM_CONSTANT(INFO_LINK_COUNT);
+ BIND_ENUM_CONSTANT(INFO_POLYGON_COUNT);
+ BIND_ENUM_CONSTANT(INFO_EDGE_COUNT);
+ BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT);
+ BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT);
+ BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT);
}
-NavigationServer3D *NavigationServer3D::get_singleton_mut() {
+NavigationServer3D *NavigationServer3D::get_singleton() {
return singleton;
}
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 0215ab3469..6c4bd2e151 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -55,42 +55,39 @@ protected:
public:
/// Thread safe, can be used across many threads.
- static const NavigationServer3D *get_singleton();
-
- /// MUST be used in single thread!
- static NavigationServer3D *get_singleton_mut();
+ static NavigationServer3D *get_singleton();
virtual TypedArray<RID> get_maps() const = 0;
/// Create a new map.
- virtual RID map_create() const = 0;
+ virtual RID map_create() = 0;
/// Set map active.
- virtual void map_set_active(RID p_map, bool p_active) const = 0;
+ virtual void map_set_active(RID p_map, bool p_active) = 0;
/// Returns true if the map is active.
virtual bool map_is_active(RID p_map) const = 0;
/// Set the map UP direction.
- virtual void map_set_up(RID p_map, Vector3 p_up) const = 0;
+ virtual void map_set_up(RID p_map, Vector3 p_up) = 0;
/// Returns the map UP direction.
virtual Vector3 map_get_up(RID p_map) const = 0;
/// Set the map cell size used to weld the navigation mesh polygons.
- virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const = 0;
+ virtual void map_set_cell_size(RID p_map, real_t p_cell_size) = 0;
/// Returns the map cell size.
virtual real_t map_get_cell_size(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) const = 0;
+ virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) = 0;
/// Returns the edge connection margin of this map.
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Set the map link connection radius used to attach links to the nav mesh.
- virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) const = 0;
+ virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) = 0;
/// Returns the link connection radius of this map.
virtual real_t map_get_link_connection_radius(RID p_map) const = 0;
@@ -110,38 +107,38 @@ public:
virtual void map_force_update(RID p_map) = 0;
/// Creates a new region.
- virtual RID region_create() const = 0;
+ virtual RID region_create() = 0;
/// Set the enter_cost of a region
- virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) const = 0;
+ 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;
/// Set the travel_cost of a region
- virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) const = 0;
+ virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) = 0;
virtual real_t region_get_travel_cost(RID p_region) const = 0;
/// Set the node which manages this region.
- virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) const = 0;
+ virtual void region_set_owner_id(RID p_region, ObjectID p_owner_id) = 0;
virtual ObjectID region_get_owner_id(RID p_region) const = 0;
virtual bool region_owns_point(RID p_region, const Vector3 &p_point) const = 0;
/// Set the map of this region.
- virtual void region_set_map(RID p_region, RID p_map) const = 0;
+ virtual void region_set_map(RID p_region, RID p_map) = 0;
virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers
- virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const = 0;
+ virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) = 0;
virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region.
- virtual void region_set_transform(RID p_region, Transform3D p_transform) const = 0;
+ virtual void region_set_transform(RID p_region, Transform3D p_transform) = 0;
/// Set the navigation mesh of this region.
- virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) const = 0;
+ virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0;
/// Bake the navigation mesh.
- virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) const = 0;
+ virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) = 0;
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const = 0;
@@ -149,45 +146,45 @@ public:
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0;
/// Creates a new link between locations in the nav map.
- virtual RID link_create() const = 0;
+ virtual RID link_create() = 0;
/// Set the map of this link.
- virtual void link_set_map(RID p_link, RID p_map) const = 0;
+ virtual void link_set_map(RID p_link, RID p_map) = 0;
virtual RID link_get_map(RID p_link) const = 0;
/// Set whether this link travels in both directions.
- virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) const = 0;
+ virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
/// Set the link's layers.
- virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) const = 0;
+ virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0;
virtual uint32_t link_get_navigation_layers(RID p_link) const = 0;
/// Set the start location of the link.
- virtual void link_set_start_location(RID p_link, Vector3 p_location) const = 0;
+ virtual void link_set_start_location(RID p_link, Vector3 p_location) = 0;
virtual Vector3 link_get_start_location(RID p_link) const = 0;
/// Set the end location of the link.
- virtual void link_set_end_location(RID p_link, Vector3 p_location) const = 0;
+ virtual void link_set_end_location(RID p_link, Vector3 p_location) = 0;
virtual Vector3 link_get_end_location(RID p_link) const = 0;
/// Set the enter cost of the link.
- virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) const = 0;
+ virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0;
virtual real_t link_get_enter_cost(RID p_link) const = 0;
/// Set the travel cost of the link.
- virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) const = 0;
+ virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) = 0;
virtual real_t link_get_travel_cost(RID p_link) const = 0;
/// Set the node which manages this link.
- virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) const = 0;
+ virtual void link_set_owner_id(RID p_link, ObjectID p_owner_id) = 0;
virtual ObjectID link_get_owner_id(RID p_link) const = 0;
/// Creates the agent.
- virtual RID agent_create() const = 0;
+ virtual RID agent_create() = 0;
/// Put the agent in the map.
- virtual void agent_set_map(RID p_agent, RID p_map) const = 0;
+ virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
/// The maximum distance (center point to
@@ -197,7 +194,7 @@ public:
/// time of the simulation. If the number is too
/// low, the simulation will not be safe.
/// Must be non-negative.
- virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) const = 0;
+ virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0;
/// The maximum number of other agents this
/// agent takes into account in the navigation.
@@ -205,7 +202,7 @@ public:
/// running time of the simulation. If the
/// number is too low, the simulation will not
/// be safe.
- virtual void agent_set_max_neighbors(RID p_agent, int p_count) const = 0;
+ 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
@@ -215,39 +212,39 @@ 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) const = 0;
+ virtual void agent_set_time_horizon(RID p_agent, real_t p_time) = 0;
/// The radius of this agent.
/// Must be non-negative.
- virtual void agent_set_radius(RID p_agent, real_t p_radius) const = 0;
+ virtual void agent_set_radius(RID p_agent, real_t p_radius) = 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) const = 0;
+ 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) const = 0;
+ virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
/// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) const = 0;
+ virtual void agent_set_target_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) const = 0;
+ 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) const = 0;
+ 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, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) const = 0;
+ virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0;
/// Destroy the `RID`
- virtual void free(RID p_object) const = 0;
+ virtual void free(RID p_object) = 0;
/// Control activation of this server.
- virtual void set_active(bool p_active) const = 0;
+ virtual void set_active(bool p_active) = 0;
/// Process the collision avoidance agents.
/// The result of this process is needed by the physics server,
@@ -263,6 +260,20 @@ public:
NavigationServer3D();
virtual ~NavigationServer3D();
+ enum ProcessInfo {
+ INFO_ACTIVE_MAPS,
+ INFO_REGION_COUNT,
+ INFO_AGENT_COUNT,
+ INFO_LINK_COUNT,
+ INFO_POLYGON_COUNT,
+ INFO_EDGE_COUNT,
+ INFO_EDGE_MERGE_COUNT,
+ INFO_EDGE_CONNECTION_COUNT,
+ INFO_EDGE_FREE_COUNT,
+ };
+
+ virtual int get_process_info(ProcessInfo p_info) const = 0;
+
#ifdef DEBUG_ENABLED
private:
bool debug_enabled = false;
@@ -360,4 +371,6 @@ public:
static NavigationServer3D *new_default_server();
};
+VARIANT_ENUM_CAST(NavigationServer3D::ProcessInfo);
+
#endif // NAVIGATION_SERVER_3D_H
diff --git a/servers/physics_2d/godot_body_2d.h b/servers/physics_2d/godot_body_2d.h
index 34da64dac9..71dc826604 100644
--- a/servers/physics_2d/godot_body_2d.h
+++ b/servers/physics_2d/godot_body_2d.h
@@ -132,6 +132,7 @@ class GodotBody2D : public GodotCollisionObject2D {
ObjectID collider_instance_id;
RID collider;
Vector2 collider_velocity_at_pos;
+ Vector2 impulse;
};
Vector<Contact> contacts; //no contacts by default
@@ -190,7 +191,7 @@ public:
_FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); }
_FORCE_INLINE_ bool can_report_contacts() const { return !contacts.is_empty(); }
- _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos);
+ _FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse);
_FORCE_INLINE_ void add_exception(const RID &p_exception) { exceptions.insert(p_exception); }
_FORCE_INLINE_ void remove_exception(const RID &p_exception) { exceptions.erase(p_exception); }
@@ -340,7 +341,7 @@ public:
//add contact inline
-void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos) {
+void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse) {
int c_max = contacts.size();
if (c_max == 0) {
@@ -380,6 +381,7 @@ void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local
c[idx].collider_instance_id = p_collider_instance_id;
c[idx].collider = p_collider;
c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos;
+ c[idx].impulse = p_impulse;
}
#endif // GODOT_BODY_2D_H
diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/servers/physics_2d/godot_body_direct_state_2d.cpp
index 35092b631a..2fa933ce17 100644
--- a/servers/physics_2d/godot_body_direct_state_2d.cpp
+++ b/servers/physics_2d/godot_body_direct_state_2d.cpp
@@ -210,6 +210,11 @@ Vector2 GodotPhysicsDirectBodyState2D::get_contact_collider_velocity_at_position
return body->contacts[p_contact_idx].collider_velocity_at_pos;
}
+Vector2 GodotPhysicsDirectBodyState2D::get_contact_impulse(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
+ return body->contacts[p_contact_idx].impulse;
+}
+
PhysicsDirectSpaceState2D *GodotPhysicsDirectBodyState2D::get_space_state() {
return body->get_space()->get_direct_state();
}
diff --git a/servers/physics_2d/godot_body_direct_state_2d.h b/servers/physics_2d/godot_body_direct_state_2d.h
index c01ee62920..545d52ad23 100644
--- a/servers/physics_2d/godot_body_direct_state_2d.h
+++ b/servers/physics_2d/godot_body_direct_state_2d.h
@@ -92,8 +92,8 @@ public:
virtual Vector2 get_contact_collider_position(int p_contact_idx) const override;
virtual ObjectID get_contact_collider_id(int p_contact_idx) const override;
virtual int get_contact_collider_shape(int p_contact_idx) const override;
-
virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override;
+ virtual Vector2 get_contact_impulse(int p_contact_idx) const override;
virtual PhysicsDirectSpaceState2D *get_space_state() override;
diff --git a/servers/physics_2d/godot_body_pair_2d.cpp b/servers/physics_2d/godot_body_pair_2d.cpp
index 474367def0..40dbb4fcf4 100644
--- a/servers/physics_2d/godot_body_pair_2d.cpp
+++ b/servers/physics_2d/godot_body_pair_2d.cpp
@@ -434,21 +434,6 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) {
c.rA = global_A - A->get_center_of_mass();
c.rB = global_B - B->get_center_of_mass() - offset_B;
- if (A->can_report_contacts()) {
- Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x);
- A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity());
- }
-
- if (B->can_report_contacts()) {
- Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
- B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity());
- }
-
- if (report_contacts_only) {
- collided = false;
- continue;
- }
-
// Precompute normal mass, tangent mass, and bias.
real_t rnA = c.rA.dot(c.normal);
real_t rnB = c.rB.dot(c.normal);
@@ -466,11 +451,28 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) {
c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
c.depth = depth;
+ Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent;
+
+ c.acc_impulse -= P;
+
+ if (A->can_report_contacts()) {
+ Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x);
+ A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity(), c.acc_impulse);
+ }
+
+ if (B->can_report_contacts()) {
+ Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
+ B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity(), c.acc_impulse);
+ }
+
+ if (report_contacts_only) {
+ collided = false;
+ continue;
+ }
+
#ifdef ACCUMULATE_IMPULSES
{
// Apply normal + friction impulse
- Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent;
-
if (collide_A) {
A->apply_impulse(-P, c.rA + A->get_center_of_mass());
}
@@ -581,6 +583,7 @@ void GodotBodyPair2D::solve(real_t p_step) {
if (collide_B) {
B->apply_impulse(j, c.rB + B->get_center_of_mass());
}
+ c.acc_impulse -= j;
}
}
diff --git a/servers/physics_2d/godot_body_pair_2d.h b/servers/physics_2d/godot_body_pair_2d.h
index 7e7a9839c1..4e9bfa6022 100644
--- a/servers/physics_2d/godot_body_pair_2d.h
+++ b/servers/physics_2d/godot_body_pair_2d.h
@@ -59,6 +59,7 @@ class GodotBodyPair2D : public GodotConstraint2D {
Vector2 position;
Vector2 normal;
Vector2 local_A, local_B;
+ Vector2 acc_impulse; // accumulated impulse
real_t acc_normal_impulse = 0.0; // accumulated normal impulse (Pn)
real_t acc_tangent_impulse = 0.0; // accumulated tangent impulse (Pt)
real_t acc_bias_impulse = 0.0; // accumulated normal impulse for position bias (Pnb)
diff --git a/servers/physics_3d/godot_body_3d.h b/servers/physics_3d/godot_body_3d.h
index fbab27a176..51b360d705 100644
--- a/servers/physics_3d/godot_body_3d.h
+++ b/servers/physics_3d/godot_body_3d.h
@@ -126,6 +126,7 @@ class GodotBody3D : public GodotCollisionObject3D {
ObjectID collider_instance_id;
RID collider;
Vector3 collider_velocity_at_pos;
+ Vector3 impulse;
};
Vector<Contact> contacts; //no contacts by default
@@ -183,7 +184,7 @@ public:
_FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); }
_FORCE_INLINE_ bool can_report_contacts() const { return !contacts.is_empty(); }
- _FORCE_INLINE_ void add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_normal, real_t p_depth, int p_local_shape, const Vector3 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector3 &p_collider_velocity_at_pos);
+ _FORCE_INLINE_ void add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_normal, real_t p_depth, int p_local_shape, const Vector3 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector3 &p_collider_velocity_at_pos, const Vector3 &p_impulse);
_FORCE_INLINE_ void add_exception(const RID &p_exception) { exceptions.insert(p_exception); }
_FORCE_INLINE_ void remove_exception(const RID &p_exception) { exceptions.erase(p_exception); }
@@ -347,7 +348,7 @@ public:
//add contact inline
-void GodotBody3D::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_normal, real_t p_depth, int p_local_shape, const Vector3 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector3 &p_collider_velocity_at_pos) {
+void GodotBody3D::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_normal, real_t p_depth, int p_local_shape, const Vector3 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector3 &p_collider_velocity_at_pos, const Vector3 &p_impulse) {
int c_max = contacts.size();
if (c_max == 0) {
@@ -387,6 +388,7 @@ void GodotBody3D::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local
c[idx].collider_instance_id = p_collider_instance_id;
c[idx].collider = p_collider;
c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos;
+ c[idx].impulse = p_impulse;
}
#endif // GODOT_BODY_3D_H
diff --git a/servers/physics_3d/godot_body_direct_state_3d.cpp b/servers/physics_3d/godot_body_direct_state_3d.cpp
index 7d13fb0615..9f28f3809a 100644
--- a/servers/physics_3d/godot_body_direct_state_3d.cpp
+++ b/servers/physics_3d/godot_body_direct_state_3d.cpp
@@ -188,8 +188,9 @@ Vector3 GodotPhysicsDirectBodyState3D::get_contact_local_normal(int p_contact_id
return body->contacts[p_contact_idx].local_normal;
}
-real_t GodotPhysicsDirectBodyState3D::get_contact_impulse(int p_contact_idx) const {
- return 0.0f; // Only implemented for bullet
+Vector3 GodotPhysicsDirectBodyState3D::get_contact_impulse(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3());
+ return body->contacts[p_contact_idx].impulse;
}
int GodotPhysicsDirectBodyState3D::get_contact_local_shape(int p_contact_idx) const {
diff --git a/servers/physics_3d/godot_body_direct_state_3d.h b/servers/physics_3d/godot_body_direct_state_3d.h
index 1ce535953d..be2e851b4d 100644
--- a/servers/physics_3d/godot_body_direct_state_3d.h
+++ b/servers/physics_3d/godot_body_direct_state_3d.h
@@ -89,7 +89,7 @@ public:
virtual Vector3 get_contact_local_position(int p_contact_idx) const override;
virtual Vector3 get_contact_local_normal(int p_contact_idx) const override;
- virtual real_t get_contact_impulse(int p_contact_idx) const override;
+ virtual Vector3 get_contact_impulse(int p_contact_idx) const override;
virtual int get_contact_local_shape(int p_contact_idx) const override;
virtual RID get_contact_collider(int p_contact_idx) const override;
diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp
index 78e3bed007..ce3da390cb 100644
--- a/servers/physics_3d/godot_body_pair_3d.cpp
+++ b/servers/physics_3d/godot_body_pair_3d.cpp
@@ -364,16 +364,30 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
c.rA = global_A - A->get_center_of_mass();
c.rB = global_B - B->get_center_of_mass() - offset_B;
+ // Precompute normal mass, tangent mass, and bias.
+ Vector3 inertia_A = inv_inertia_tensor_A.xform(c.rA.cross(c.normal));
+ Vector3 inertia_B = inv_inertia_tensor_B.xform(c.rB.cross(c.normal));
+ real_t kNormal = inv_mass_A + inv_mass_B;
+ kNormal += c.normal.dot(inertia_A.cross(c.rA)) + c.normal.dot(inertia_B.cross(c.rB));
+ c.mass_normal = 1.0f / kNormal;
+
+ c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
+ c.depth = depth;
+
+ Vector3 j_vec = c.normal * c.acc_normal_impulse + c.acc_tangent_impulse;
+
+ c.acc_impulse -= j_vec;
+
// contact query reporting...
if (A->can_report_contacts()) {
Vector3 crA = A->get_angular_velocity().cross(c.rA) + A->get_linear_velocity();
- A->add_contact(global_A, -c.normal, depth, shape_A, global_B, shape_B, B->get_instance_id(), B->get_self(), crA);
+ A->add_contact(global_A, -c.normal, depth, shape_A, global_B, shape_B, B->get_instance_id(), B->get_self(), crA, c.acc_impulse);
}
if (B->can_report_contacts()) {
Vector3 crB = B->get_angular_velocity().cross(c.rB) + B->get_linear_velocity();
- B->add_contact(global_B, c.normal, depth, shape_B, global_A, shape_A, A->get_instance_id(), A->get_self(), crB);
+ B->add_contact(global_B, c.normal, depth, shape_B, global_A, shape_A, A->get_instance_id(), A->get_self(), crB, -c.acc_impulse);
}
if (report_contacts_only) {
@@ -384,17 +398,6 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
c.active = true;
do_process = true;
- // Precompute normal mass, tangent mass, and bias.
- Vector3 inertia_A = inv_inertia_tensor_A.xform(c.rA.cross(c.normal));
- Vector3 inertia_B = inv_inertia_tensor_B.xform(c.rB.cross(c.normal));
- real_t kNormal = inv_mass_A + inv_mass_B;
- kNormal += c.normal.dot(inertia_A.cross(c.rA)) + c.normal.dot(inertia_B.cross(c.rB));
- c.mass_normal = 1.0f / kNormal;
-
- c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
- c.depth = depth;
-
- Vector3 j_vec = c.normal * c.acc_normal_impulse + c.acc_tangent_impulse;
if (collide_A) {
A->apply_impulse(-j_vec, c.rA + A->get_center_of_mass());
}
@@ -504,6 +507,7 @@ void GodotBodyPair3D::solve(real_t p_step) {
if (collide_B) {
B->apply_impulse(j, c.rB + B->get_center_of_mass());
}
+ c.acc_impulse -= j;
c.active = true;
}
@@ -550,6 +554,7 @@ void GodotBodyPair3D::solve(real_t p_step) {
if (collide_B) {
B->apply_impulse(jt, c.rB + B->get_center_of_mass());
}
+ c.acc_impulse -= jt;
c.active = true;
}
@@ -745,23 +750,6 @@ bool GodotBodySoftBodyPair3D::pre_solve(real_t p_step) {
c.rA = global_A - transform_A.origin - body->get_center_of_mass();
c.rB = global_B;
- if (body->can_report_contacts()) {
- Vector3 crA = body->get_angular_velocity().cross(c.rA) + body->get_linear_velocity();
- body->add_contact(global_A, -c.normal, depth, body_shape, global_B, 0, soft_body->get_instance_id(), soft_body->get_self(), crA);
- }
-
- if (report_contacts_only) {
- collided = false;
- continue;
- }
-
- c.active = true;
- do_process = true;
-
- if (body_collides) {
- body->set_active(true);
- }
-
// Precompute normal mass, tangent mass, and bias.
Vector3 inertia_A = body_inv_inertia_tensor.xform(c.rA.cross(c.normal));
real_t kNormal = body_inv_mass + node_inv_mass;
@@ -778,6 +766,24 @@ bool GodotBodySoftBodyPair3D::pre_solve(real_t p_step) {
if (soft_body_collides) {
soft_body->apply_node_impulse(c.index_B, j_vec);
}
+ c.acc_impulse -= j_vec;
+
+ if (body->can_report_contacts()) {
+ Vector3 crA = body->get_angular_velocity().cross(c.rA) + body->get_linear_velocity();
+ body->add_contact(global_A, -c.normal, depth, body_shape, global_B, 0, soft_body->get_instance_id(), soft_body->get_self(), crA, c.acc_impulse);
+ }
+
+ if (report_contacts_only) {
+ collided = false;
+ continue;
+ }
+
+ c.active = true;
+ do_process = true;
+
+ if (body_collides) {
+ body->set_active(true);
+ }
c.bounce = body->get_bounce();
@@ -880,6 +886,7 @@ void GodotBodySoftBodyPair3D::solve(real_t p_step) {
if (soft_body_collides) {
soft_body->apply_node_impulse(c.index_B, j);
}
+ c.acc_impulse -= j;
c.active = true;
}
@@ -924,6 +931,7 @@ void GodotBodySoftBodyPair3D::solve(real_t p_step) {
if (soft_body_collides) {
soft_body->apply_node_impulse(c.index_B, jt);
}
+ c.acc_impulse -= jt;
c.active = true;
}
diff --git a/servers/physics_3d/godot_body_pair_3d.h b/servers/physics_3d/godot_body_pair_3d.h
index d69215f145..c3165c7fcf 100644
--- a/servers/physics_3d/godot_body_pair_3d.h
+++ b/servers/physics_3d/godot_body_pair_3d.h
@@ -44,6 +44,7 @@ protected:
Vector3 normal;
int index_A = 0, index_B = 0;
Vector3 local_A, local_B;
+ Vector3 acc_impulse; // accumulated impulse - only one of the object's impulse is needed as impulse_a == -impulse_b
real_t acc_normal_impulse = 0.0; // accumulated normal impulse (Pn)
Vector3 acc_tangent_impulse; // accumulated tangent impulse (Pt)
real_t acc_bias_impulse = 0.0; // accumulated normal impulse for position bias (Pnb)
diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/servers/physics_3d/godot_collision_solver_3d_sat.cpp
index 36c47a07b9..d13f4ee801 100644
--- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d_sat.cpp
@@ -758,24 +758,72 @@ public:
typedef void (*CollisionFunc)(const GodotShape3D *, const Transform3D &, const GodotShape3D *, const Transform3D &, _CollectorCallback *p_callback, real_t, real_t);
+// Perform analytic sphere-sphere collision and report results to collector
template <bool withMargin>
-static void _collision_sphere_sphere(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {
- const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);
- const GodotSphereShape3D *sphere_B = static_cast<const GodotSphereShape3D *>(p_b);
+static void analytic_sphere_collision(const Vector3 &p_origin_a, real_t p_radius_a, const Vector3 &p_origin_b, real_t p_radius_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {
+ // Expand the spheres by the margins if enabled
+ if (withMargin) {
+ p_radius_a += p_margin_a;
+ p_radius_b += p_margin_b;
+ }
- SeparatorAxisTest<GodotSphereShape3D, GodotSphereShape3D, withMargin> separator(sphere_A, p_transform_a, sphere_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
+ // Get the vector from sphere B to A
+ Vector3 b_to_a = p_origin_a - p_origin_b;
- // previous axis
+ // Get the length from B to A
+ real_t b_to_a_len = b_to_a.length();
- if (!separator.test_previous_axis()) {
+ // Calculate the sphere overlap, and bail if not overlapping
+ real_t overlap = p_radius_a + p_radius_b - b_to_a_len;
+ if (overlap < 0)
return;
- }
- if (!separator.test_axis((p_transform_a.origin - p_transform_b.origin).normalized())) {
+ // Report collision
+ p_collector->collided = true;
+
+ // Bail if there is no callback to receive the A and B collision points.
+ if (!p_collector->callback) {
return;
}
- separator.generate_contacts();
+ // Normalize the B to A vector
+ if (b_to_a_len < CMP_EPSILON) {
+ b_to_a = Vector3(0, 1, 0); // Spheres coincident, use arbitrary direction
+ } else {
+ b_to_a /= b_to_a_len;
+ }
+
+ // Report collision points. The operations below are intended to minimize
+ // floating-point precision errors. This is done by calculating the first
+ // collision point from the smaller sphere, and then jumping across to
+ // the larger spheres collision point using the overlap distance. This
+ // jump is usually small even if the large sphere is massive, and so the
+ // second point will not suffer from precision errors.
+ if (p_radius_a < p_radius_b) {
+ Vector3 point_a = p_origin_a - b_to_a * p_radius_a;
+ Vector3 point_b = point_a + b_to_a * overlap;
+ p_collector->call(point_a, point_b); // Consider adding b_to_a vector
+ } else {
+ Vector3 point_b = p_origin_b + b_to_a * p_radius_b;
+ Vector3 point_a = point_b - b_to_a * overlap;
+ p_collector->call(point_a, point_b); // Consider adding b_to_a vector
+ }
+}
+
+template <bool withMargin>
+static void _collision_sphere_sphere(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {
+ const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);
+ const GodotSphereShape3D *sphere_B = static_cast<const GodotSphereShape3D *>(p_b);
+
+ // Perform an analytic sphere collision between the two spheres
+ analytic_sphere_collision<withMargin>(
+ p_transform_a.origin,
+ sphere_A->get_radius(),
+ p_transform_b.origin,
+ sphere_B->get_radius(),
+ p_collector,
+ p_margin_a,
+ p_margin_b);
}
template <bool withMargin>
@@ -783,50 +831,36 @@ static void _collision_sphere_box(const GodotShape3D *p_a, const Transform3D &p_
const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);
const GodotBoxShape3D *box_B = static_cast<const GodotBoxShape3D *>(p_b);
- SeparatorAxisTest<GodotSphereShape3D, GodotBoxShape3D, withMargin> separator(sphere_A, p_transform_a, box_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
+ // Find the point on the box nearest to the center of the sphere.
- if (!separator.test_previous_axis()) {
- return;
- }
+ Vector3 center = p_transform_b.xform_inv(p_transform_a.origin);
+ Vector3 extents = box_B->get_half_extents();
+ Vector3 nearest(MIN(MAX(center.x, -extents.x), extents.x),
+ MIN(MAX(center.y, -extents.y), extents.y),
+ MIN(MAX(center.z, -extents.z), extents.z));
+ nearest = p_transform_b.xform(nearest);
- // test faces
+ // See if it is inside the sphere.
- for (int i = 0; i < 3; i++) {
- Vector3 axis = p_transform_b.basis.get_column(i).normalized();
-
- if (!separator.test_axis(axis)) {
- return;
- }
+ Vector3 delta = nearest - p_transform_a.origin;
+ real_t length = delta.length();
+ if (length > sphere_A->get_radius() + p_margin_a + p_margin_b) {
+ return;
}
-
- // calculate closest point to sphere
-
- Vector3 cnormal = p_transform_b.xform_inv(p_transform_a.origin);
-
- Vector3 cpoint = p_transform_b.xform(Vector3(
-
- (cnormal.x < 0) ? -box_B->get_half_extents().x : box_B->get_half_extents().x,
- (cnormal.y < 0) ? -box_B->get_half_extents().y : box_B->get_half_extents().y,
- (cnormal.z < 0) ? -box_B->get_half_extents().z : box_B->get_half_extents().z));
-
- // use point to test axis
- Vector3 point_axis = (p_transform_a.origin - cpoint).normalized();
-
- if (!separator.test_axis(point_axis)) {
+ p_collector->collided = true;
+ if (!p_collector->callback) {
return;
}
-
- // test edges
-
- for (int i = 0; i < 3; i++) {
- Vector3 axis = point_axis.cross(p_transform_b.basis.get_column(i)).cross(p_transform_b.basis.get_column(i)).normalized();
-
- if (!separator.test_axis(axis)) {
- return;
- }
+ Vector3 axis;
+ if (length == 0) {
+ // The box passes through the sphere center. Select an axis based on the box's center.
+ axis = (p_transform_b.origin - nearest).normalized();
+ } else {
+ axis = delta / length;
}
-
- separator.generate_contacts();
+ Vector3 point_a = p_transform_a.origin + (sphere_A->get_radius() + p_margin_a) * axis;
+ Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest);
+ p_collector->call(point_a, point_b);
}
template <bool withMargin>
@@ -834,41 +868,66 @@ static void _collision_sphere_capsule(const GodotShape3D *p_a, const Transform3D
const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);
const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b);
- SeparatorAxisTest<GodotSphereShape3D, GodotCapsuleShape3D, withMargin> separator(sphere_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
-
- if (!separator.test_previous_axis()) {
- return;
- }
-
- //capsule sphere 1, sphere
-
- Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius());
-
- Vector3 capsule_ball_1 = p_transform_b.origin + capsule_axis;
+ real_t capsule_B_radius = capsule_B->get_radius();
+
+ // Construct the capsule segment (ball-center to ball-center)
+ Vector3 capsule_segment[2];
+ Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B_radius);
+ capsule_segment[0] = p_transform_b.origin + capsule_axis;
+ capsule_segment[1] = p_transform_b.origin - capsule_axis;
+
+ // Get the capsules closest segment-point to the sphere
+ Vector3 capsule_closest = Geometry3D::get_closest_point_to_segment(p_transform_a.origin, capsule_segment);
+
+ // Perform an analytic sphere collision between the sphere and the sphere-collider in the capsule
+ analytic_sphere_collision<withMargin>(
+ p_transform_a.origin,
+ sphere_A->get_radius(),
+ capsule_closest,
+ capsule_B_radius,
+ p_collector,
+ p_margin_a,
+ p_margin_b);
+}
- if (!separator.test_axis((capsule_ball_1 - p_transform_a.origin).normalized())) {
+template <bool withMargin>
+static void analytic_sphere_cylinder_collision(real_t p_radius_a, real_t p_radius_b, real_t p_height_b, const Transform3D &p_transform_a, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {
+ // Find the point on the cylinder nearest to the center of the sphere.
+
+ Vector3 center = p_transform_b.xform_inv(p_transform_a.origin);
+ Vector3 nearest = center;
+ real_t radius = p_radius_b;
+ real_t r = Math::sqrt(center.x * center.x + center.z * center.z);
+ if (r > radius) {
+ real_t scale = radius / r;
+ nearest.x *= scale;
+ nearest.z *= scale;
+ }
+ real_t half_height = p_height_b / 2;
+ nearest.y = MIN(MAX(center.y, -half_height), half_height);
+ nearest = p_transform_b.xform(nearest);
+
+ // See if it is inside the sphere.
+
+ Vector3 delta = nearest - p_transform_a.origin;
+ real_t length = delta.length();
+ if (length > p_radius_a + p_margin_a + p_margin_b) {
return;
}
-
- //capsule sphere 2, sphere
-
- Vector3 capsule_ball_2 = p_transform_b.origin - capsule_axis;
-
- if (!separator.test_axis((capsule_ball_2 - p_transform_a.origin).normalized())) {
+ p_collector->collided = true;
+ if (!p_collector->callback) {
return;
}
-
- //capsule edge, sphere
-
- Vector3 b2a = p_transform_a.origin - p_transform_b.origin;
-
- Vector3 axis = b2a.cross(capsule_axis).cross(capsule_axis).normalized();
-
- if (!separator.test_axis(axis)) {
- return;
+ Vector3 axis;
+ if (length == 0) {
+ // The cylinder passes through the sphere center. Select an axis based on the cylinder's center.
+ axis = (p_transform_b.origin - nearest).normalized();
+ } else {
+ axis = delta / length;
}
-
- separator.generate_contacts();
+ Vector3 point_a = p_transform_a.origin + (p_radius_a + p_margin_a) * axis;
+ Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest);
+ p_collector->call(point_a, point_b);
}
template <bool withMargin>
@@ -876,58 +935,7 @@ static void _collision_sphere_cylinder(const GodotShape3D *p_a, const Transform3
const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);
const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);
- SeparatorAxisTest<GodotSphereShape3D, GodotCylinderShape3D, withMargin> separator(sphere_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
-
- if (!separator.test_previous_axis()) {
- return;
- }
-
- // Cylinder B end caps.
- Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1).normalized();
- if (!separator.test_axis(cylinder_B_axis)) {
- return;
- }
-
- Vector3 cylinder_diff = p_transform_b.origin - p_transform_a.origin;
-
- // Cylinder B lateral surface.
- if (!separator.test_axis(cylinder_B_axis.cross(cylinder_diff).cross(cylinder_B_axis).normalized())) {
- return;
- }
-
- // Closest point to cylinder caps.
- const Vector3 &sphere_center = p_transform_a.origin;
- Vector3 cyl_axis = p_transform_b.basis.get_column(1);
- Vector3 cap_axis = p_transform_b.basis.get_column(0);
- real_t height_scale = cyl_axis.length();
- real_t cap_dist = cylinder_B->get_height() * 0.5 * height_scale;
- cyl_axis /= height_scale;
- real_t radius_scale = cap_axis.length();
- real_t cap_radius = cylinder_B->get_radius() * radius_scale;
-
- for (int i = 0; i < 2; i++) {
- Vector3 cap_dir = ((i == 0) ? cyl_axis : -cyl_axis);
- Vector3 cap_pos = p_transform_b.origin + cap_dir * cap_dist;
-
- Vector3 closest_point;
-
- Vector3 diff = sphere_center - cap_pos;
- Vector3 proj = diff - cap_dir.dot(diff) * cap_dir;
-
- real_t proj_len = proj.length();
- if (Math::is_zero_approx(proj_len)) {
- // Point is equidistant to all circle points.
- continue;
- }
-
- closest_point = cap_pos + (cap_radius / proj_len) * proj;
-
- if (!separator.test_axis((closest_point - sphere_center).normalized())) {
- return;
- }
- }
-
- separator.generate_contacts();
+ analytic_sphere_cylinder_collision<withMargin>(sphere_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), p_transform_a, p_transform_b, p_collector, p_margin_a, p_margin_b);
}
template <bool withMargin>
@@ -1615,63 +1623,31 @@ static void _collision_capsule_capsule(const GodotShape3D *p_a, const Transform3
const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);
const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b);
- SeparatorAxisTest<GodotCapsuleShape3D, GodotCapsuleShape3D, withMargin> separator(capsule_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
-
- if (!separator.test_previous_axis()) {
- return;
- }
-
- // some values
-
- Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());
- Vector3 capsule_B_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius());
-
- Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis;
- Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis;
- Vector3 capsule_B_ball_1 = p_transform_b.origin + capsule_B_axis;
- Vector3 capsule_B_ball_2 = p_transform_b.origin - capsule_B_axis;
-
- //balls-balls
-
- if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_1).normalized())) {
- return;
- }
- if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_2).normalized())) {
- return;
- }
-
- if (!separator.test_axis((capsule_A_ball_2 - capsule_B_ball_1).normalized())) {
- return;
- }
- if (!separator.test_axis((capsule_A_ball_2 - capsule_B_ball_2).normalized())) {
- return;
- }
-
- // edges-balls
-
- if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_1).cross(capsule_A_axis).cross(capsule_A_axis).normalized())) {
- return;
- }
-
- if (!separator.test_axis((capsule_A_ball_1 - capsule_B_ball_2).cross(capsule_A_axis).cross(capsule_A_axis).normalized())) {
- return;
- }
-
- if (!separator.test_axis((capsule_B_ball_1 - capsule_A_ball_1).cross(capsule_B_axis).cross(capsule_B_axis).normalized())) {
- return;
- }
-
- if (!separator.test_axis((capsule_B_ball_1 - capsule_A_ball_2).cross(capsule_B_axis).cross(capsule_B_axis).normalized())) {
- return;
- }
-
- // edges
-
- if (!separator.test_axis(capsule_A_axis.cross(capsule_B_axis).normalized())) {
- return;
- }
-
- separator.generate_contacts();
+ real_t capsule_A_radius = capsule_A->get_radius();
+ real_t capsule_B_radius = capsule_B->get_radius();
+
+ // Get the closest points between the capsule segments
+ Vector3 capsule_A_closest;
+ Vector3 capsule_B_closest;
+ Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A_radius);
+ Vector3 capsule_B_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B_radius);
+ Geometry3D::get_closest_points_between_segments(
+ p_transform_a.origin + capsule_A_axis,
+ p_transform_a.origin - capsule_A_axis,
+ p_transform_b.origin + capsule_B_axis,
+ p_transform_b.origin - capsule_B_axis,
+ capsule_A_closest,
+ capsule_B_closest);
+
+ // Perform the analytic collision between the two closest capsule spheres
+ analytic_sphere_collision<withMargin>(
+ capsule_A_closest,
+ capsule_A_radius,
+ capsule_B_closest,
+ capsule_B_radius,
+ p_collector,
+ p_margin_a,
+ p_margin_b);
}
template <bool withMargin>
@@ -1679,61 +1655,24 @@ static void _collision_capsule_cylinder(const GodotShape3D *p_a, const Transform
const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);
const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);
- SeparatorAxisTest<GodotCapsuleShape3D, GodotCylinderShape3D, withMargin> separator(capsule_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b);
-
- if (!separator.test_previous_axis()) {
- return;
- }
-
- // Cylinder B end caps.
- Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1).normalized();
- if (!separator.test_axis(cylinder_B_axis)) {
- return;
- }
-
- // Cylinder edge against capsule balls.
-
- Vector3 capsule_A_axis = p_transform_a.basis.get_column(1);
-
- Vector3 capsule_A_ball_1 = p_transform_a.origin + capsule_A_axis * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());
- Vector3 capsule_A_ball_2 = p_transform_a.origin - capsule_A_axis * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());
-
- if (!separator.test_axis((p_transform_b.origin - capsule_A_ball_1).cross(cylinder_B_axis).cross(cylinder_B_axis).normalized())) {
- return;
- }
-
- if (!separator.test_axis((p_transform_b.origin - capsule_A_ball_2).cross(cylinder_B_axis).cross(cylinder_B_axis).normalized())) {
- return;
- }
-
- // Cylinder edge against capsule edge.
+ // Find the closest points between the axes of the two objects.
- Vector3 center_diff = p_transform_b.origin - p_transform_a.origin;
-
- if (!separator.test_axis(capsule_A_axis.cross(center_diff).cross(capsule_A_axis).normalized())) {
- return;
- }
-
- if (!separator.test_axis(cylinder_B_axis.cross(center_diff).cross(cylinder_B_axis).normalized())) {
- return;
- }
-
- real_t proj = capsule_A_axis.cross(cylinder_B_axis).cross(cylinder_B_axis).dot(capsule_A_axis);
- if (Math::is_zero_approx(proj)) {
- // Parallel capsule and cylinder axes, handle with specific axes only.
- // Note: GJKEPA with no margin can lead to degenerate cases in this situation.
- separator.generate_contacts();
- return;
- }
-
- GodotCollisionSolver3D::CallbackResult callback = SeparatorAxisTest<GodotCapsuleShape3D, GodotCylinderShape3D, withMargin>::test_contact_points;
-
- // Fallback to generic algorithm to find the best separating axis.
- if (!fallback_collision_solver(p_a, p_transform_a, p_b, p_transform_b, callback, &separator, false, p_margin_a, p_margin_b)) {
- return;
- }
-
- separator.generate_contacts();
+ Vector3 capsule_A_closest;
+ Vector3 cylinder_B_closest;
+ Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());
+ Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1) * (cylinder_B->get_height() * 0.5);
+ Geometry3D::get_closest_points_between_segments(
+ p_transform_a.origin + capsule_A_axis,
+ p_transform_a.origin - capsule_A_axis,
+ p_transform_b.origin + cylinder_B_axis,
+ p_transform_b.origin - cylinder_B_axis,
+ capsule_A_closest,
+ cylinder_B_closest);
+
+ // Perform the collision test between the cylinder and the nearest sphere on the capsule axis.
+
+ Transform3D sphere_transform(p_transform_a.basis, capsule_A_closest);
+ analytic_sphere_cylinder_collision<withMargin>(capsule_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), sphere_transform, p_transform_b, p_collector, p_margin_a, p_margin_b);
}
template <bool withMargin>
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index 4973b9970a..214de27b35 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -126,6 +126,7 @@ void PhysicsDirectBodyState2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_contact_collider_object", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_object);
ClassDB::bind_method(D_METHOD("get_contact_collider_shape", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_shape);
ClassDB::bind_method(D_METHOD("get_contact_collider_velocity_at_position", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_collider_velocity_at_position);
+ ClassDB::bind_method(D_METHOD("get_contact_impulse", "contact_idx"), &PhysicsDirectBodyState2D::get_contact_impulse);
ClassDB::bind_method(D_METHOD("get_step"), &PhysicsDirectBodyState2D::get_step);
ClassDB::bind_method(D_METHOD("integrate_forces"), &PhysicsDirectBodyState2D::integrate_forces);
ClassDB::bind_method(D_METHOD("get_space_state"), &PhysicsDirectBodyState2D::get_space_state);
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index aac4d9d69e..836ab5bd76 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -99,6 +99,7 @@ public:
virtual Object *get_contact_collider_object(int p_contact_idx) const;
virtual int get_contact_collider_shape(int p_contact_idx) const = 0;
virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const = 0;
+ virtual Vector2 get_contact_impulse(int p_contact_idx) const = 0;
virtual real_t get_step() const = 0;
virtual void integrate_forces();
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index f77ec06ccf..d1c644d51a 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -95,7 +95,7 @@ public:
virtual Vector3 get_contact_local_position(int p_contact_idx) const = 0;
virtual Vector3 get_contact_local_normal(int p_contact_idx) const = 0;
- virtual real_t get_contact_impulse(int p_contact_idx) const = 0;
+ virtual Vector3 get_contact_impulse(int p_contact_idx) const = 0;
virtual int get_contact_local_shape(int p_contact_idx) const = 0;
virtual RID get_contact_collider(int p_contact_idx) const = 0;
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index e9ffac1faa..8cc2cc0756 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -301,8 +301,8 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer2D", PhysicsServer2D::get_singleton(), "PhysicsServer2D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton(), "PhysicsServer3D"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut(), "NavigationServer2D"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut(), "NavigationServer3D"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton(), "NavigationServer2D"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer"));
}
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 3510b415d8..df9dfaa276 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -597,7 +597,7 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from,
Vector2 end_left;
Vector2 end_right;
- if (p_width > 1.001) {
+ if (p_width >= 0.0) {
begin_left = p_from + t;
begin_right = p_from - t;
end_left = p_to + t;
@@ -628,7 +628,7 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from,
// This value is empirically determined to provide good antialiasing quality
// while not making lines appear too soft.
float border_size = 1.25f;
- if (p_width < 1.0f) {
+ if (0.0f <= p_width && p_width < 1.0f) {
border_size *= p_width;
}
Vector2 dir2 = diff.normalized();
@@ -769,6 +769,49 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from,
}
}
+static Vector2 compute_polyline_segment_dir(const Vector<Point2> &p_points, int p_index, const Vector2 &p_prev_segment_dir) {
+ int point_count = p_points.size();
+
+ bool is_last_point = (p_index == point_count - 1);
+
+ Vector2 segment_dir;
+
+ if (is_last_point) {
+ segment_dir = p_prev_segment_dir;
+ } else {
+ segment_dir = (p_points[p_index + 1] - p_points[p_index]).normalized();
+
+ if (segment_dir.is_zero_approx()) {
+ segment_dir = p_prev_segment_dir;
+ }
+ }
+
+ return segment_dir;
+}
+
+static Vector2 compute_polyline_edge_offset_clamped(const Vector2 &p_segment_dir, const Vector2 &p_prev_segment_dir) {
+ Vector2 bisector;
+ float length = 1.0f;
+
+ bisector = (p_prev_segment_dir * p_segment_dir.length() - p_segment_dir * p_prev_segment_dir.length()).normalized();
+
+ float angle = atan2f(bisector.cross(p_prev_segment_dir), bisector.dot(p_prev_segment_dir));
+ float sin_angle = sinf(angle);
+
+ if (!Math::is_zero_approx(sin_angle) && !p_segment_dir.is_equal_approx(p_prev_segment_dir)) {
+ length = 1.0f / sin_angle;
+ length = CLAMP(length, -3.0f, 3.0f);
+ } else {
+ bisector = p_segment_dir.orthogonal();
+ }
+
+ if (bisector.is_zero_approx()) {
+ bisector = p_segment_dir.orthogonal();
+ }
+
+ return bisector * length;
+}
+
void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
ERR_FAIL_COND(p_points.size() < 2);
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
@@ -777,11 +820,30 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
Color color = Color(1, 1, 1, 1);
Vector<int> indices;
- int pc = p_points.size();
- int pc2 = pc * 2;
+ int point_count = p_points.size();
+ int polyline_point_count = point_count * 2;
- Vector2 prev_t;
- int j2;
+ bool loop = p_points[0].is_equal_approx(p_points[point_count - 1]);
+ Vector2 first_segment_dir;
+ Vector2 last_segment_dir;
+
+ // Search for first non-zero vector between two segments.
+ for (int i = 1; i < point_count; i++) {
+ first_segment_dir = (p_points[i] - p_points[i - 1]).normalized();
+
+ if (!first_segment_dir.is_zero_approx()) {
+ break;
+ }
+ }
+
+ // Search for last non-zero vector between two segments.
+ for (int i = point_count - 1; i >= 1; i--) {
+ last_segment_dir = (p_points[i] - p_points[i - 1]).normalized();
+
+ if (!last_segment_dir.is_zero_approx()) {
+ break;
+ }
+ }
Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>();
ERR_FAIL_COND(!pline);
@@ -789,8 +851,8 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
PackedColorArray colors;
PackedVector2Array points;
- colors.resize(pc2);
- points.resize(pc2);
+ colors.resize(polyline_point_count);
+ points.resize(polyline_point_count);
Vector2 *points_ptr = points.ptrw();
Color *colors_ptr = colors.ptrw();
@@ -845,14 +907,14 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
PackedColorArray colors_left;
PackedVector2Array points_left;
- colors_left.resize(pc2);
- points_left.resize(pc2);
+ colors_left.resize(polyline_point_count);
+ points_left.resize(polyline_point_count);
PackedColorArray colors_right;
PackedVector2Array points_right;
- colors_right.resize(pc2);
- points_right.resize(pc2);
+ colors_right.resize(polyline_point_count);
+ points_right.resize(polyline_point_count);
Item::CommandPolygon *pline_begin = canvas_item->alloc_command<Item::CommandPolygon>();
ERR_FAIL_COND(!pline_begin);
@@ -898,79 +960,81 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
Color *colors_left_ptr = colors_left.ptrw();
Color *colors_right_ptr = colors_right.ptrw();
- for (int i = 0, j = 0; i < pc; i++, j += 2) {
- bool is_begin = i == 0;
- bool is_end = i == pc - 1;
+ Vector2 prev_segment_dir;
+ for (int i = 0; i < point_count; i++) {
+ bool is_first_point = (i == 0);
+ bool is_last_point = (i == point_count - 1);
- Vector2 t;
- Vector2 end_border;
- Vector2 begin_border;
- if (is_end) {
- t = prev_t;
- end_border = (p_points[i] - p_points[i - 1]).normalized() * border_size;
- } else {
- t = (p_points[i + 1] - p_points[i]).normalized().orthogonal();
- if (is_begin) {
- prev_t = t;
- begin_border = (p_points[i] - p_points[i + 1]).normalized() * border_size;
- }
+ Vector2 segment_dir = compute_polyline_segment_dir(p_points, i, prev_segment_dir);
+ if (is_first_point && loop) {
+ prev_segment_dir = last_segment_dir;
+ } else if (is_last_point && loop) {
+ prev_segment_dir = first_segment_dir;
}
- j2 = j + 1;
+ Vector2 base_edge_offset;
+ if (is_first_point && !loop) {
+ base_edge_offset = first_segment_dir.orthogonal();
+ } else if (is_last_point && !loop) {
+ base_edge_offset = last_segment_dir.orthogonal();
+ } else {
+ base_edge_offset = compute_polyline_edge_offset_clamped(segment_dir, prev_segment_dir);
+ }
- Vector2 dir = (t + prev_t).normalized();
- Vector2 tangent = dir * p_width * 0.5;
- Vector2 border = dir * border_size;
+ Vector2 edge_offset = base_edge_offset * (p_width * 0.5f);
+ Vector2 border = base_edge_offset * border_size;
Vector2 pos = p_points[i];
- points_ptr[j] = pos + tangent;
- points_ptr[j2] = pos - tangent;
+ points_ptr[i * 2 + 0] = pos + edge_offset;
+ points_ptr[i * 2 + 1] = pos - edge_offset;
- points_left_ptr[j] = pos + tangent + border;
- points_left_ptr[j2] = pos + tangent;
+ points_left_ptr[i * 2 + 0] = pos + edge_offset + border;
+ points_left_ptr[i * 2 + 1] = pos + edge_offset;
- points_right_ptr[j] = pos - tangent;
- points_right_ptr[j2] = pos - tangent - border;
+ points_right_ptr[i * 2 + 0] = pos - edge_offset;
+ points_right_ptr[i * 2 + 1] = pos - edge_offset - border;
if (i < p_colors.size()) {
color = p_colors[i];
color2 = Color(color.r, color.g, color.b, 0);
}
- colors_ptr[j] = color;
- colors_ptr[j2] = color;
+ colors_ptr[i * 2 + 0] = color;
+ colors_ptr[i * 2 + 1] = color;
- colors_left_ptr[j] = color2;
- colors_left_ptr[j2] = color;
+ colors_left_ptr[i * 2 + 0] = color2;
+ colors_left_ptr[i * 2 + 1] = color;
- colors_right_ptr[j] = color;
- colors_right_ptr[j2] = color2;
+ colors_right_ptr[i * 2 + 0] = color;
+ colors_right_ptr[i * 2 + 1] = color2;
- if (is_begin) {
- points_begin_ptr[0] = pos + tangent + begin_border;
- points_begin_ptr[1] = pos - tangent + begin_border;
- points_begin_ptr[2] = pos + tangent;
- points_begin_ptr[3] = pos - tangent;
+ if (is_first_point) {
+ Vector2 begin_border = loop ? Vector2() : -segment_dir * border_size;
+
+ points_begin_ptr[0] = pos + edge_offset + begin_border;
+ points_begin_ptr[1] = pos - edge_offset + begin_border;
+ points_begin_ptr[2] = pos + edge_offset;
+ points_begin_ptr[3] = pos - edge_offset;
colors_begin_ptr[0] = color2;
colors_begin_ptr[1] = color2;
colors_begin_ptr[2] = color;
colors_begin_ptr[3] = color;
- points_begin_left_corner_ptr[0] = pos - tangent - border;
- points_begin_left_corner_ptr[1] = pos - tangent + begin_border - border;
- points_begin_left_corner_ptr[2] = pos - tangent;
- points_begin_left_corner_ptr[3] = pos - tangent + begin_border;
+ points_begin_left_corner_ptr[0] = pos - edge_offset - border;
+ points_begin_left_corner_ptr[1] = pos - edge_offset + begin_border - border;
+ points_begin_left_corner_ptr[2] = pos - edge_offset;
+ points_begin_left_corner_ptr[3] = pos - edge_offset + begin_border;
colors_begin_left_corner_ptr[0] = color2;
colors_begin_left_corner_ptr[1] = color2;
colors_begin_left_corner_ptr[2] = color;
colors_begin_left_corner_ptr[3] = color2;
- points_begin_right_corner_ptr[0] = pos + tangent + begin_border;
- points_begin_right_corner_ptr[1] = pos + tangent + begin_border + border;
- points_begin_right_corner_ptr[2] = pos + tangent;
- points_begin_right_corner_ptr[3] = pos + tangent + border;
+ points_begin_right_corner_ptr[0] = pos + edge_offset + begin_border;
+ points_begin_right_corner_ptr[1] = pos + edge_offset + begin_border + border;
+ points_begin_right_corner_ptr[2] = pos + edge_offset;
+ points_begin_right_corner_ptr[3] = pos + edge_offset + border;
colors_begin_right_corner_ptr[0] = color2;
colors_begin_right_corner_ptr[1] = color2;
@@ -978,31 +1042,33 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
colors_begin_right_corner_ptr[3] = color2;
}
- if (is_end) {
- points_end_ptr[0] = pos + tangent + end_border;
- points_end_ptr[1] = pos - tangent + end_border;
- points_end_ptr[2] = pos + tangent;
- points_end_ptr[3] = pos - tangent;
+ if (is_last_point) {
+ Vector2 end_border = loop ? Vector2() : prev_segment_dir * border_size;
+
+ points_end_ptr[0] = pos + edge_offset + end_border;
+ points_end_ptr[1] = pos - edge_offset + end_border;
+ points_end_ptr[2] = pos + edge_offset;
+ points_end_ptr[3] = pos - edge_offset;
colors_end_ptr[0] = color2;
colors_end_ptr[1] = color2;
colors_end_ptr[2] = color;
colors_end_ptr[3] = color;
- points_end_left_corner_ptr[0] = pos - tangent - border;
- points_end_left_corner_ptr[1] = pos - tangent + end_border - border;
- points_end_left_corner_ptr[2] = pos - tangent;
- points_end_left_corner_ptr[3] = pos - tangent + end_border;
+ points_end_left_corner_ptr[0] = pos - edge_offset - border;
+ points_end_left_corner_ptr[1] = pos - edge_offset + end_border - border;
+ points_end_left_corner_ptr[2] = pos - edge_offset;
+ points_end_left_corner_ptr[3] = pos - edge_offset + end_border;
colors_end_left_corner_ptr[0] = color2;
colors_end_left_corner_ptr[1] = color2;
colors_end_left_corner_ptr[2] = color;
colors_end_left_corner_ptr[3] = color2;
- points_end_right_corner_ptr[0] = pos + tangent + end_border;
- points_end_right_corner_ptr[1] = pos + tangent + end_border + border;
- points_end_right_corner_ptr[2] = pos + tangent;
- points_end_right_corner_ptr[3] = pos + tangent + border;
+ points_end_right_corner_ptr[0] = pos + edge_offset + end_border;
+ points_end_right_corner_ptr[1] = pos + edge_offset + end_border + border;
+ points_end_right_corner_ptr[2] = pos + edge_offset;
+ points_end_right_corner_ptr[3] = pos + edge_offset + border;
colors_end_right_corner_ptr[0] = color2;
colors_end_right_corner_ptr[1] = color2;
@@ -1010,7 +1076,7 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
colors_end_right_corner_ptr[3] = color2;
}
- prev_t = t;
+ prev_segment_dir = segment_dir;
}
pline_begin->primitive = RS::PRIMITIVE_TRIANGLE_STRIP;
@@ -1039,33 +1105,41 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point
} else {
// Makes a single triangle strip for drawing the line.
- for (int i = 0, j = 0; i < pc; i++, j += 2) {
- Vector2 t;
- if (i == pc - 1) {
- t = prev_t;
- } else {
- t = (p_points[i + 1] - p_points[i]).normalized().orthogonal();
- if (i == 0) {
- prev_t = t;
- }
+ Vector2 prev_segment_dir;
+ for (int i = 0; i < point_count; i++) {
+ bool is_first_point = (i == 0);
+ bool is_last_point = (i == point_count - 1);
+
+ Vector2 segment_dir = compute_polyline_segment_dir(p_points, i, prev_segment_dir);
+ if (is_first_point && loop) {
+ prev_segment_dir = last_segment_dir;
+ } else if (is_last_point && loop) {
+ prev_segment_dir = first_segment_dir;
}
- j2 = j + 1;
+ Vector2 base_edge_offset;
+ if (is_first_point && !loop) {
+ base_edge_offset = first_segment_dir.orthogonal();
+ } else if (is_last_point && !loop) {
+ base_edge_offset = last_segment_dir.orthogonal();
+ } else {
+ base_edge_offset = compute_polyline_edge_offset_clamped(segment_dir, prev_segment_dir);
+ }
- Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5;
+ Vector2 edge_offset = base_edge_offset * (p_width * 0.5f);
Vector2 pos = p_points[i];
- points_ptr[j] = pos + tangent;
- points_ptr[j2] = pos - tangent;
+ points_ptr[i * 2 + 0] = pos + edge_offset;
+ points_ptr[i * 2 + 1] = pos - edge_offset;
if (i < p_colors.size()) {
color = p_colors[i];
}
- colors_ptr[j] = color;
- colors_ptr[j2] = color;
+ colors_ptr[i * 2 + 0] = color;
+ colors_ptr[i * 2 + 1] = color;
- prev_t = t;
+ prev_segment_dir = segment_dir;
}
}
@@ -1077,7 +1151,7 @@ void RendererCanvasCull::canvas_item_add_multiline(RID p_item, const Vector<Poin
ERR_FAIL_COND(p_points.size() < 2);
// TODO: `canvas_item_add_line`(`multiline`, `polyline`) share logic, should factor out.
- if (p_width <= 1) {
+ if (p_width < 0) {
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!canvas_item);
@@ -1313,7 +1387,7 @@ void RendererCanvasCull::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_r
style->axis_y = p_y_axis_mode;
}
-void RendererCanvasCull::canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width) {
+void RendererCanvasCull::canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture) {
uint32_t pc = p_points.size();
ERR_FAIL_COND(pc == 0 || pc > 4);
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 4d0be48cc0..2494ddf631 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -221,9 +221,9 @@ public:
void canvas_item_set_update_when_visible(RID p_item, bool p_update);
- void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
+ void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = -1.0, bool p_antialiased = false);
void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
- void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0);
+ void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = -1.0);
void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color);
void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color);
void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
@@ -231,7 +231,7 @@ public:
void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0, float p_scale = 1.0);
void canvas_item_add_lcd_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1));
void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, RS::NinePatchAxisMode p_x_axis_mode = RS::NINE_PATCH_STRETCH, RS::NinePatchAxisMode p_y_axis_mode = RS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1));
- void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0);
+ void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture);
void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID());
void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1);
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), RID p_texture = RID());
diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp
new file mode 100644
index 0000000000..7462282932
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/luminance.cpp
@@ -0,0 +1,255 @@
+/**************************************************************************/
+/* luminance.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 "luminance.h"
+#include "../framebuffer_cache_rd.h"
+#include "../uniform_set_cache_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+
+using namespace RendererRD;
+
+Luminance::Luminance(bool p_prefer_raster_effects) {
+ prefer_raster_effects = p_prefer_raster_effects;
+
+ if (prefer_raster_effects) {
+ Vector<String> luminance_reduce_modes;
+ luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST
+ luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT
+ luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL
+
+ luminance_reduce_raster.shader.initialize(luminance_reduce_modes);
+ luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create();
+
+ for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
+ luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+ } else {
+ // Initialize luminance_reduce
+ Vector<String> luminance_reduce_modes;
+ luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n");
+ luminance_reduce_modes.push_back("\n");
+ luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n");
+
+ luminance_reduce.shader.initialize(luminance_reduce_modes);
+ luminance_reduce.shader_version = luminance_reduce.shader.version_create();
+
+ for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) {
+ luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i));
+ }
+
+ for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
+ luminance_reduce_raster.pipelines[i].clear();
+ }
+ }
+}
+
+Luminance::~Luminance() {
+ if (prefer_raster_effects) {
+ luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
+ } else {
+ luminance_reduce.shader.version_free(luminance_reduce.shader_version);
+ }
+}
+
+void Luminance::LuminanceBuffers::set_prefer_raster_effects(bool p_prefer_raster_effects) {
+ prefer_raster_effects = p_prefer_raster_effects;
+}
+
+void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffers) {
+ Size2i internal_size = p_render_buffers->get_internal_size();
+ int w = internal_size.x;
+ int h = internal_size.y;
+
+ while (true) {
+ w = MAX(w / 8, 1);
+ h = MAX(h / 8, 1);
+
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+ tf.width = w;
+ tf.height = h;
+
+ bool final = w == 1 && h == 1;
+
+ if (prefer_raster_effects) {
+ tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+ } else {
+ tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
+ if (final) {
+ tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
+ }
+ }
+
+ RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ reduce.push_back(texture);
+
+ if (final) {
+ current = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ break;
+ }
+ }
+}
+
+void Luminance::LuminanceBuffers::free_data() {
+ for (int i = 0; i < reduce.size(); i++) {
+ RD::get_singleton()->free(reduce[i]);
+ }
+ reduce.clear();
+
+ if (current.is_valid()) {
+ RD::get_singleton()->free(current);
+ current = RID();
+ }
+}
+
+Ref<Luminance::LuminanceBuffers> Luminance::get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers) {
+ if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
+ return p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
+ }
+
+ Ref<LuminanceBuffers> buffers;
+ buffers.instantiate();
+ buffers->set_prefer_raster_effects(prefer_raster_effects);
+ buffers->configure(p_render_buffers.ptr());
+
+ p_render_buffers->set_custom_data(RB_LUMINANCE_BUFFERS, buffers);
+
+ return buffers;
+}
+
+RID Luminance::get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers) {
+ if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
+ Ref<LuminanceBuffers> buffers = p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
+ return buffers->current;
+ }
+
+ return RID();
+}
+
+void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ if (prefer_raster_effects) {
+ LuminanceReduceRasterPushConstant push_constant;
+ memset(&push_constant, 0, sizeof(LuminanceReduceRasterPushConstant));
+
+ push_constant.max_luminance = p_max_luminance;
+ push_constant.min_luminance = p_min_luminance;
+ push_constant.exposure_adjust = p_adjust;
+
+ for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
+ push_constant.source_size[0] = i == 0 ? p_source_size.x : push_constant.dest_size[0];
+ push_constant.source_size[1] = i == 0 ? p_source_size.y : push_constant.dest_size[1];
+ push_constant.dest_size[0] = MAX(push_constant.source_size[0] / 8, 1);
+ push_constant.dest_size[1] = MAX(push_constant.source_size[1] / 8, 1);
+
+ bool final = !p_set && (push_constant.dest_size[0] == 1) && (push_constant.dest_size[1] == 1);
+ LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT);
+ RID shader = luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, mode);
+
+ RID framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_luminance_buffers->reduce[i]);
+
+ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] }));
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
+ if (final) {
+ RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1);
+ }
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant));
+
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+ }
+ } else {
+ LuminanceReducePushConstant push_constant;
+ memset(&push_constant, 0, sizeof(LuminanceReducePushConstant));
+
+ push_constant.source_size[0] = p_source_size.x;
+ push_constant.source_size[1] = p_source_size.y;
+ push_constant.max_luminance = p_max_luminance;
+ push_constant.min_luminance = p_min_luminance;
+ push_constant.exposure_adjust = p_adjust;
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
+ RID shader;
+
+ if (i == 0) {
+ shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_READ);
+ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
+ } else {
+ RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done
+
+ if (i == p_luminance_buffers->reduce.size() - 1 && !p_set) {
+ shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_WRITE);
+ RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_current_texture), 2);
+ } else {
+ shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]);
+ }
+
+ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i - 1]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
+ }
+
+ RD::Uniform u_reduce_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_reduce_texture), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(LuminanceReducePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.source_size[0], push_constant.source_size[1], 1);
+
+ push_constant.source_size[0] = MAX(push_constant.source_size[0] / 8, 1);
+ push_constant.source_size[1] = MAX(push_constant.source_size[1] / 8, 1);
+ }
+
+ RD::get_singleton()->compute_list_end();
+ }
+
+ SWAP(p_luminance_buffers->current, p_luminance_buffers->reduce.write[p_luminance_buffers->reduce.size() - 1]);
+}
diff --git a/servers/rendering/renderer_rd/effects/luminance.h b/servers/rendering/renderer_rd/effects/luminance.h
new file mode 100644
index 0000000000..0f343fceab
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/luminance.h
@@ -0,0 +1,120 @@
+/**************************************************************************/
+/* luminance.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 LUMINANCE_RD_H
+#define LUMINANCE_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl.gen.h"
+#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+#define RB_LUMINANCE_BUFFERS SNAME("luminance_buffers")
+
+namespace RendererRD {
+
+class Luminance {
+private:
+ bool prefer_raster_effects;
+
+ enum LuminanceReduceMode {
+ LUMINANCE_REDUCE_READ,
+ LUMINANCE_REDUCE,
+ LUMINANCE_REDUCE_WRITE,
+ LUMINANCE_REDUCE_MAX
+ };
+
+ struct LuminanceReducePushConstant {
+ int32_t source_size[2];
+ float max_luminance;
+ float min_luminance;
+ float exposure_adjust;
+ float pad[3];
+ };
+
+ struct LuminanceReduce {
+ LuminanceReduceShaderRD shader;
+ RID shader_version;
+ RID pipelines[LUMINANCE_REDUCE_MAX];
+ } luminance_reduce;
+
+ enum LuminanceReduceRasterMode {
+ LUMINANCE_REDUCE_FRAGMENT_FIRST,
+ LUMINANCE_REDUCE_FRAGMENT,
+ LUMINANCE_REDUCE_FRAGMENT_FINAL,
+ LUMINANCE_REDUCE_FRAGMENT_MAX
+ };
+
+ struct LuminanceReduceRasterPushConstant {
+ int32_t source_size[2];
+ int32_t dest_size[2];
+ float exposure_adjust;
+ float min_luminance;
+ float max_luminance;
+ uint32_t pad1;
+ };
+
+ struct LuminanceReduceFragment {
+ LuminanceReduceRasterShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX];
+ } luminance_reduce_raster;
+
+public:
+ class LuminanceBuffers : public RenderBufferCustomDataRD {
+ GDCLASS(LuminanceBuffers, RenderBufferCustomDataRD);
+
+ private:
+ bool prefer_raster_effects;
+
+ public:
+ Vector<RID> reduce;
+ RID current;
+
+ virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
+ virtual void free_data() override;
+
+ void set_prefer_raster_effects(bool p_prefer_raster_effects);
+ };
+
+ Ref<LuminanceBuffers> get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers);
+ RID get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers);
+ void luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
+
+ Luminance(bool p_prefer_raster_effects);
+ ~Luminance();
+};
+
+} // namespace RendererRD
+
+#endif // LUMINANCE_RD_H
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 6d15d5c77b..b7a1396f9c 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -55,36 +55,13 @@ RID EffectsRD::_get_uniform_set_from_image(RID p_image) {
u.append_id(p_image);
uniforms.push_back(u);
//any thing with the same configuration (one texture in binding 0 for set 0), is good
- RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 1);
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 1);
image_to_uniform_set_cache[p_image] = uniform_set;
return uniform_set;
}
-RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) {
- if (texture_to_uniform_set_cache.has(p_texture)) {
- RID uniform_set = texture_to_uniform_set_cache[p_texture];
- if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- return uniform_set;
- }
- }
-
- Vector<RD::Uniform> uniforms;
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
- u.binding = 0;
- u.append_id(p_use_mipmaps ? default_mipmap_sampler : default_sampler);
- u.append_id(p_texture);
- uniforms.push_back(u);
- // anything with the same configuration (one texture in binding 0 for set 0), is good
- RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, 0), 0);
-
- texture_to_uniform_set_cache[p_texture] = uniform_set;
-
- return uniform_set;
-}
-
RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) {
if (texture_to_compute_uniform_set_cache.has(p_texture)) {
RID uniform_set = texture_to_compute_uniform_set_cache[p_texture];
@@ -101,86 +78,13 @@ RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_m
u.append_id(p_texture);
uniforms.push_back(u);
//any thing with the same configuration (one texture in binding 0 for set 0), is good
- RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, 0), 0);
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0), 0);
texture_to_compute_uniform_set_cache[p_texture] = uniform_set;
return uniform_set;
}
-void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
- ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of luminance reduction with the mobile renderer.");
-
- luminance_reduce.push_constant.source_size[0] = p_source_size.x;
- luminance_reduce.push_constant.source_size[1] = p_source_size.y;
- luminance_reduce.push_constant.max_luminance = p_max_luminance;
- luminance_reduce.push_constant.min_luminance = p_min_luminance;
- luminance_reduce.push_constant.exposure_adjust = p_adjust;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
-
- for (int i = 0; i < p_reduce.size(); i++) {
- if (i == 0) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_texture), 0);
- } else {
- RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done
-
- if (i == p_reduce.size() - 1 && !p_set) {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_prev_luminance), 2);
- } else {
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]);
- }
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i - 1]), 0);
- }
-
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_reduce[i]), 1);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &luminance_reduce.push_constant, sizeof(LuminanceReducePushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, luminance_reduce.push_constant.source_size[0], luminance_reduce.push_constant.source_size[1], 1);
-
- luminance_reduce.push_constant.source_size[0] = MAX(luminance_reduce.push_constant.source_size[0] / 8, 1);
- luminance_reduce.push_constant.source_size[1] = MAX(luminance_reduce.push_constant.source_size[1] / 8, 1);
- }
-
- RD::get_singleton()->compute_list_end();
-}
-
-void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
- ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster version of luminance reduction with the clustered renderer.");
- ERR_FAIL_COND_MSG(p_reduce.size() != p_fb.size(), "Incorrect frame buffer account for luminance reduction.");
-
- luminance_reduce_raster.push_constant.max_luminance = p_max_luminance;
- luminance_reduce_raster.push_constant.min_luminance = p_min_luminance;
- luminance_reduce_raster.push_constant.exposure_adjust = p_adjust;
-
- for (int i = 0; i < p_reduce.size(); i++) {
- luminance_reduce_raster.push_constant.source_size[0] = i == 0 ? p_source_size.x : luminance_reduce_raster.push_constant.dest_size[0];
- luminance_reduce_raster.push_constant.source_size[1] = i == 0 ? p_source_size.y : luminance_reduce_raster.push_constant.dest_size[1];
- luminance_reduce_raster.push_constant.dest_size[0] = MAX(luminance_reduce_raster.push_constant.source_size[0] / 8, 1);
- luminance_reduce_raster.push_constant.dest_size[1] = MAX(luminance_reduce_raster.push_constant.source_size[1] / 8, 1);
-
- bool final = !p_set && (luminance_reduce_raster.push_constant.dest_size[0] == 1) && (luminance_reduce_raster.push_constant.dest_size[1] == 1);
- LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT);
-
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_fb[i])));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(i == 0 ? p_source_texture : p_reduce[i - 1]), 0);
- if (final) {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_prev_luminance), 1);
- }
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
-
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &luminance_reduce_raster.push_constant, sizeof(LuminanceReduceRasterPushConstant));
-
- RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end();
- }
-}
-
void EffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) {
roughness_limiter.push_constant.screen_size[0] = p_size.x;
roughness_limiter.push_constant.screen_size[1] = p_size.y;
@@ -270,39 +174,6 @@ void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) {
EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
prefer_raster_effects = p_prefer_raster_effects;
- if (prefer_raster_effects) {
- Vector<String> luminance_reduce_modes;
- luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST
- luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT
- luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL
-
- luminance_reduce_raster.shader.initialize(luminance_reduce_modes);
- memset(&luminance_reduce_raster.push_constant, 0, sizeof(LuminanceReduceRasterPushConstant));
- luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create();
-
- for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
- luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
- }
- } else {
- // Initialize luminance_reduce
- Vector<String> luminance_reduce_modes;
- luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n");
- luminance_reduce_modes.push_back("\n");
- luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n");
-
- luminance_reduce.shader.initialize(luminance_reduce_modes);
-
- luminance_reduce.shader_version = luminance_reduce.shader.version_create();
-
- for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) {
- luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i));
- }
-
- for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
- luminance_reduce_raster.pipelines[i].clear();
- }
- }
-
if (!prefer_raster_effects) {
// Initialize roughness limiter
Vector<String> shader_modes;
@@ -368,11 +239,6 @@ EffectsRD::~EffectsRD() {
RD::get_singleton()->free(default_mipmap_sampler);
RD::get_singleton()->free(index_buffer); //array gets freed as dependency
- if (prefer_raster_effects) {
- luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
- } else {
- luminance_reduce.shader.version_free(luminance_reduce.shader_version);
- }
if (!prefer_raster_effects) {
roughness_limiter.shader.version_free(roughness_limiter.shader_version);
}
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index bbe240b241..45198e5fc5 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -33,8 +33,6 @@
#include "core/math/projection.h"
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
-#include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/sort.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
@@ -45,51 +43,6 @@ class EffectsRD {
private:
bool prefer_raster_effects;
- enum LuminanceReduceMode {
- LUMINANCE_REDUCE_READ,
- LUMINANCE_REDUCE,
- LUMINANCE_REDUCE_WRITE,
- LUMINANCE_REDUCE_MAX
- };
-
- struct LuminanceReducePushConstant {
- int32_t source_size[2];
- float max_luminance;
- float min_luminance;
- float exposure_adjust;
- float pad[3];
- };
-
- struct LuminanceReduce {
- LuminanceReducePushConstant push_constant;
- LuminanceReduceShaderRD shader;
- RID shader_version;
- RID pipelines[LUMINANCE_REDUCE_MAX];
- } luminance_reduce;
-
- enum LuminanceReduceRasterMode {
- LUMINANCE_REDUCE_FRAGMENT_FIRST,
- LUMINANCE_REDUCE_FRAGMENT,
- LUMINANCE_REDUCE_FRAGMENT_FINAL,
- LUMINANCE_REDUCE_FRAGMENT_MAX
- };
-
- struct LuminanceReduceRasterPushConstant {
- int32_t source_size[2];
- int32_t dest_size[2];
- float exposure_adjust;
- float min_luminance;
- float max_luminance;
- uint32_t pad1;
- };
-
- struct LuminanceReduceFragment {
- LuminanceReduceRasterPushConstant push_constant;
- LuminanceReduceRasterShaderRD shader;
- RID shader_version;
- PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX];
- } luminance_reduce_raster;
-
struct RoughnessLimiterPushConstant {
int32_t screen_size[2];
float curve;
@@ -164,15 +117,11 @@ private:
RBMap<TextureSamplerPair, RID> texture_sampler_to_compute_uniform_set_cache;
RID _get_uniform_set_from_image(RID p_texture);
- RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
public:
bool get_prefer_raster_effects();
- void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
- void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
-
void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
void sort_buffer(RID p_uniform_set, int p_size);
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index 863e8d6c15..7e02f98ce9 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -1085,8 +1085,8 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
sky->reflection.dirty = true;
}
+ sky_scene_state.ubo.directional_light_count = 0;
if (shader_data->uses_light) {
- sky_scene_state.ubo.directional_light_count = 0;
// Run through the list of lights in the scene and pick out the Directional Lights.
// This can't be done in RenderSceneRenderRD::_setup lights because that needs to be called
// after the depth prepass, but this runs before the depth prepass
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 85b5ef5e09..93c741fd34 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1668,39 +1668,15 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
using_voxelgi = true;
}
- if (p_render_data->environment.is_null() && using_voxelgi) {
- depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI;
- } else if (p_render_data->environment.is_valid() && (environment_get_ssr_enabled(p_render_data->environment) || environment_get_sdfgi_enabled(p_render_data->environment) || using_voxelgi)) {
+ if (p_render_data->environment.is_valid()) {
if (environment_get_sdfgi_enabled(p_render_data->environment)) {
- depth_pass_mode = using_voxelgi ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; // also voxelgi
using_sdfgi = true;
- } else {
- depth_pass_mode = using_voxelgi ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI : PASS_MODE_DEPTH_NORMAL_ROUGHNESS;
}
if (environment_get_ssr_enabled(p_render_data->environment)) {
using_separate_specular = true;
using_ssr = true;
color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR;
}
- } else if (p_render_data->environment.is_valid() && (environment_get_ssao_enabled(p_render_data->environment) || using_ssil || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER)) {
- depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS;
- }
-
- switch (depth_pass_mode) {
- case PASS_MODE_DEPTH: {
- depth_framebuffer = rb_data->get_depth_fb();
- } break;
- case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
- depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS);
- depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
- } break;
- case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: {
- depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI);
- depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
- depth_pass_clear.push_back(Color(0, 0, 0, 0));
- } break;
- default: {
- };
}
if (p_render_data->scene_data->view_count > 1) {
@@ -1731,6 +1707,38 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->draw_command_end_label();
+ if (rb.is_valid() && !p_render_data->reflection_probe.is_valid()) {
+ if (using_voxelgi) {
+ depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI;
+ } else if (p_render_data->environment.is_valid()) {
+ if (environment_get_ssr_enabled(p_render_data->environment) ||
+ environment_get_sdfgi_enabled(p_render_data->environment) ||
+ environment_get_ssao_enabled(p_render_data->environment) ||
+ using_ssil ||
+ get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER ||
+ scene_state.used_normal_texture) {
+ depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS;
+ }
+ }
+
+ switch (depth_pass_mode) {
+ case PASS_MODE_DEPTH: {
+ depth_framebuffer = rb_data->get_depth_fb();
+ } break;
+ case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
+ depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS);
+ depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
+ } break;
+ case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: {
+ depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI);
+ depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
+ depth_pass_clear.push_back(Color(0, 0, 0, 0));
+ } break;
+ default: {
+ };
+ }
+ }
+
bool using_sss = rb_data.is_valid() && scene_state.used_sss && ss_effects->sss_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED;
if (using_sss && !using_separate_specular) {
@@ -1914,7 +1922,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true);
bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss;
- bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss;
+ bool can_continue_depth = !(scene_state.used_depth_texture || scene_state.used_normal_texture) && !using_ssr && !using_sss;
{
bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes);
@@ -3436,7 +3444,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
- bool has_base_alpha = (p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha;
+ bool has_base_alpha = (p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
bool has_alpha = has_base_alpha || has_blend_alpha;
@@ -3465,7 +3473,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED) {
//material is only meant for alpha pass
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA;
- if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) {
+ if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH;
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW;
}
@@ -3481,7 +3489,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
SceneShaderForwardClustered::MaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
- if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size) {
+ if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<SceneShaderForwardClustered::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
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 9117320eab..7eabce2f79 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
@@ -60,6 +60,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
uses_point_size = false;
uses_alpha = false;
uses_alpha_clip = false;
+ uses_alpha_antialiasing = false;
uses_blend_alpha = false;
uses_depth_prepass_alpha = false;
uses_discard = false;
@@ -111,9 +112,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.usage_flag_pointers["ALPHA"] = &uses_alpha;
actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip;
- // Use alpha clip pipeline for alpha hash/dither.
- // This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows.
actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip;
+ actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing;
+ actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing;
actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha;
actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss;
@@ -121,7 +122,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture;
actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture;
- actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture;
+ actions.usage_flag_pointers["NORMAL_ROUGHNESS_TEXTURE"] = &uses_normal_texture;
actions.usage_flag_pointers["DISCARD"] = &uses_discard;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
@@ -309,14 +310,6 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
}
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
- if (depth_pre_pass_enabled && casts_shadows() && !uses_depth_prepass_alpha) {
- // We already have a depth from the depth pre-pass, there is no need to write it again.
- // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL.
- // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts.
- // This cannot be used with depth_prepass_alpha as it uses a different threshold during the depth-prepass and regular drawing.
- depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL;
- depth_stencil.enable_depth_write = false;
- }
RD::PipelineColorBlendState blend_state;
RD::PipelineMultisampleState multisample_state;
@@ -338,6 +331,14 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
} else {
blend_state = blend_state_color_opaque;
+ if (depth_pre_pass_enabled) {
+ // We already have a depth from the depth pre-pass, there is no need to write it again.
+ // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL.
+ // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts.
+ depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL;
+ depth_stencil.enable_depth_write = false;
+ }
+
if (l & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR) {
shader_flags |= SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
}
@@ -392,7 +393,7 @@ bool SceneShaderForwardClustered::ShaderData::is_animated() const {
bool SceneShaderForwardClustered::ShaderData::casts_shadows() const {
bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
- bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
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 1169ae784c..ffaf091b36 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
@@ -153,6 +153,7 @@ public:
bool uses_alpha = false;
bool uses_blend_alpha = false;
bool uses_alpha_clip = false;
+ bool uses_alpha_antialiasing = false;
bool uses_depth_prepass_alpha = false;
bool uses_discard = false;
bool uses_roughness = false;
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 72857cdea7..816248567b 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -251,7 +251,7 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe
Size2i target_size = render_buffers->get_target_size();
Size2i internal_size = render_buffers->get_internal_size();
- // can't do our blit pass if resolutions don't match
+ // can't do our blit pass if resolutions don't match, this should already have been checked.
ERR_FAIL_COND_V(target_size != internal_size, RID());
// - opaque pass
@@ -715,20 +715,26 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
// setup rendering to render buffer
screen_size = p_render_data->render_buffers->get_internal_size();
- if (rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES).is_null()) {
- // can't do blit subpass
+ if (rb->get_scaling_3d_mode() != RS::VIEWPORT_SCALING_3D_MODE_OFF) {
+ // can't do blit subpass because we're scaling
using_subpass_post_process = false;
} else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) || RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes))) {
- // can't do blit subpass
+ // can't do blit subpass because we're using post processes
using_subpass_post_process = false;
}
if (scene_state.used_screen_texture || scene_state.used_depth_texture) {
- // can't use our last two subpasses
+ // can't use our last two subpasses because we're reading from screen texture or depth texture
using_subpass_transparent = false;
using_subpass_post_process = false;
}
+ // We do this last because our get_color_fbs creates and caches the framebuffer if we need it.
+ if (using_subpass_post_process && rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES).is_null()) {
+ // can't do blit subpass because we don't have all subpasses
+ using_subpass_post_process = false;
+ }
+
if (using_subpass_post_process) {
// all as subpasses
framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES);
@@ -2349,9 +2355,9 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
- bool has_base_alpha = ((p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha);
+ bool has_base_alpha = p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing);
bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
- bool has_alpha = has_base_alpha || has_blend_alpha;
+ bool has_alpha = has_base_alpha || has_blend_alpha || has_read_screen_alpha;
uint32_t flags = 0;
@@ -2375,10 +2381,10 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}
- if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED) {
+ if (has_alpha || p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED) {
//material is only meant for alpha pass
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA;
- if (p_material->shader_data->uses_depth_prepass_alpha && !(p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED)) {
+ if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED)) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH;
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW;
}
@@ -2394,7 +2400,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
SceneShaderForwardMobile::MaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
- if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip) {
+ if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<SceneShaderForwardMobile::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
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 2e9a33a636..ee4c8001eb 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
@@ -62,6 +62,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
uses_point_size = false;
uses_alpha = false;
uses_alpha_clip = false;
+ uses_alpha_antialiasing = false;
uses_blend_alpha = false;
uses_depth_prepass_alpha = false;
uses_discard = false;
@@ -112,9 +113,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.usage_flag_pointers["ALPHA"] = &uses_alpha;
actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip;
- // Use alpha clip pipeline for alpha hash/dither.
- // This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows.
actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip;
+ actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing;
+ actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing;
actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha;
// actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss;
@@ -122,7 +123,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture;
actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture;
- actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture;
+ actions.usage_flag_pointers["NORMAL_ROUGHNESS_TEXTURE"] = &uses_normal_texture;
actions.usage_flag_pointers["DISCARD"] = &uses_discard;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
@@ -338,7 +339,7 @@ bool SceneShaderForwardMobile::ShaderData::is_animated() const {
bool SceneShaderForwardMobile::ShaderData::casts_shadows() const {
bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
- bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
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 99f252b9ca..1f92697ecc 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
@@ -114,6 +114,7 @@ public:
bool uses_alpha = false;
bool uses_blend_alpha = false;
bool uses_alpha_clip = false;
+ bool uses_alpha_antialiasing = false;
bool uses_depth_prepass_alpha = false;
bool uses_discard = false;
bool uses_roughness = false;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 0c6dcb553a..07557eab81 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -525,10 +525,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
if (rect->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
+ push_constant.flags |= FLAGS_FLIP_H;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
+ push_constant.flags |= FLAGS_FLIP_V;
}
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 266083fc49..7dea4a1a65 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -86,6 +86,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_USE_MSDF = (1 << 28),
FLAGS_USE_LCD = (1 << 29),
+
+ FLAGS_FLIP_H = (1 << 30),
+ FLAGS_FLIP_V = (1 << 31),
};
enum {
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index d426c4fc2e..2a87c4fa8d 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -249,57 +249,6 @@ Ref<RenderSceneBuffers> RendererSceneRenderRD::render_buffers_create() {
return rb;
}
-void RendererSceneRenderRD::_allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb) {
- ERR_FAIL_COND(!rb->luminance.current.is_null());
-
- Size2i internal_size = rb->get_internal_size();
- int w = internal_size.x;
- int h = internal_size.y;
-
- while (true) {
- w = MAX(w / 8, 1);
- h = MAX(h / 8, 1);
-
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R32_SFLOAT;
- tf.width = w;
- tf.height = h;
-
- bool final = w == 1 && h == 1;
-
- if (_render_buffers_can_be_storage()) {
- tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
- if (final) {
- tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
- }
- } else {
- tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
- }
-
- RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- rb->luminance.reduce.push_back(texture);
- if (!_render_buffers_can_be_storage()) {
- Vector<RID> fb;
- fb.push_back(texture);
-
- rb->luminance.fb.push_back(RD::get_singleton()->framebuffer_create(fb));
- }
-
- if (final) {
- rb->luminance.current = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- if (!_render_buffers_can_be_storage()) {
- Vector<RID> fb;
- fb.push_back(rb->luminance.current);
-
- rb->luminance.current_fb = RD::get_singleton()->framebuffer_create(fb);
- }
- break;
- }
- }
-}
-
void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderDataRD *p_render_data) {
Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers;
ERR_FAIL_COND(rb.is_null());
@@ -443,9 +392,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
RENDER_TIMESTAMP("Auto exposure");
RD::get_singleton()->draw_command_begin_label("Auto exposure");
- if (rb->luminance.current.is_null()) {
- _allocate_luminance_textures(rb);
- }
+
+ Ref<RendererRD::Luminance::LuminanceBuffers> luminance_buffers = luminance->get_luminance_buffers(rb);
+
uint64_t auto_exposure_version = RSG::camera_attributes->camera_attributes_get_auto_exposure_version(p_render_data->camera_attributes);
bool set_immediate = auto_exposure_version != rb->get_auto_exposure_version();
rb->set_auto_exposure_version(auto_exposure_version);
@@ -453,16 +402,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
double step = RSG::camera_attributes->camera_attributes_get_auto_exposure_adjust_speed(p_render_data->camera_attributes) * time_step;
float auto_exposure_min_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_min_sensitivity(p_render_data->camera_attributes);
float auto_exposure_max_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_max_sensitivity(p_render_data->camera_attributes);
- if (can_use_storage) {
- RendererCompositorRD::singleton->get_effects()->luminance_reduction(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate);
- } else {
- RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate);
- }
+ luminance->luminance_reduction(internal_texture, internal_size, luminance_buffers, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate);
+
// Swap final reduce with prev luminance.
- SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]);
- if (!can_use_storage) {
- SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]);
- }
auto_exposure_scale = RSG::camera_attributes->camera_attributes_get_auto_exposure_scale(p_render_data->camera_attributes);
@@ -496,8 +438,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
if (i == 0) {
RID luminance_texture;
- if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) {
- luminance_texture = rb->luminance.current;
+ if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) {
+ luminance_texture = luminance->get_current_luminance_buffer(rb); // this will return and empty RID if we don't have an auto exposure buffer
}
RID source = rb->get_internal_texture(l);
RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i);
@@ -530,9 +472,9 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
RendererRD::ToneMapper::TonemapSettings tonemap;
- if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) {
+ tonemap.exposure_texture = luminance->get_current_luminance_buffer(rb);
+ if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && tonemap.exposure_texture.is_valid()) {
tonemap.use_auto_exposure = true;
- tonemap.exposure_texture = rb->luminance.current;
tonemap.auto_exposure_scale = auto_exposure_scale;
} else {
tonemap.exposure_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE);
@@ -597,7 +539,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.view_count = rb->get_view_count();
RID dest_fb;
- if (fsr && can_use_effects && (internal_size.x != target_size.x || internal_size.y != target_size.y)) {
+ if (fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR) {
// If we use FSR to upscale we need to write our result into an intermediate buffer.
// Note that this is cached so we only create the texture the first time.
RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT);
@@ -614,10 +556,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
RD::get_singleton()->draw_command_end_label();
}
- if (fsr && can_use_effects && (internal_size.x != target_size.x || internal_size.y != target_size.y)) {
- // TODO Investigate? Does this work? We never write into our render target and we've already done so up above in our tonemapper.
- // I think FSR should either work before our tonemapper or as an alternative of our tonemapper.
-
+ if (fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR) {
RD::get_singleton()->draw_command_begin_label("FSR 1.0 Upscale");
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
@@ -746,10 +685,11 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) {
- if (p_render_buffers->luminance.current.is_valid()) {
+ RID luminance_texture = luminance->get_current_luminance_buffer(p_render_buffers);
+ if (luminance_texture.is_valid()) {
Size2i rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(p_render_buffers->luminance.current, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true);
+ copy_effects->copy_to_fb_rect(luminance_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true);
}
}
@@ -1334,6 +1274,7 @@ void RendererSceneRenderRD::init() {
bool can_use_vrs = is_vrs_supported();
bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage));
copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
+ luminance = memnew(RendererRD::Luminance(!can_use_storage));
tone_mapper = memnew(RendererRD::ToneMapper);
if (can_use_vrs) {
vrs = memnew(RendererRD::VRS);
@@ -1354,6 +1295,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() {
if (copy_effects) {
memdelete(copy_effects);
}
+ if (luminance) {
+ memdelete(luminance);
+ }
if (tone_mapper) {
memdelete(tone_mapper);
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 54f068c314..6fa2f7a570 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -38,6 +38,7 @@
#include "servers/rendering/renderer_rd/effects/bokeh_dof.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/effects/fsr.h"
+#include "servers/rendering/renderer_rd/effects/luminance.h"
#include "servers/rendering/renderer_rd/effects/tone_mapper.h"
#include "servers/rendering/renderer_rd/effects/vrs.h"
#include "servers/rendering/renderer_rd/environment/fog.h"
@@ -105,6 +106,7 @@ protected:
RendererRD::ForwardIDStorage *forward_id_storage = nullptr;
RendererRD::BokehDOF *bokeh_dof = nullptr;
RendererRD::CopyEffects *copy_effects = nullptr;
+ RendererRD::Luminance *luminance = nullptr;
RendererRD::ToneMapper *tone_mapper = nullptr;
RendererRD::FSR *fsr = nullptr;
RendererRD::VRS *vrs = nullptr;
@@ -180,9 +182,6 @@ private:
/* RENDER BUFFERS */
- // TODO move into effects/luminance.h/cpp
- void _allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb);
-
/* GI */
bool screen_space_roughness_limiter = false;
float screen_space_roughness_limiter_amount = 0.25;
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index ab64ae1055..2d5263a3e2 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -33,6 +33,7 @@
#include "core/io/compression.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
+#include "core/version.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/rendering_device.h"
#include "thirdparty/misc/smolv.h"
@@ -116,6 +117,10 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con
}
StringBuilder tohash;
+ tohash.append("[GodotVersionNumber]");
+ tohash.append(VERSION_NUMBER);
+ tohash.append("[GodotVersionHash]");
+ tohash.append(VERSION_HASH);
tohash.append("[SpirvCacheKey]");
tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key());
tohash.append("[BinaryCacheKey]");
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index eb5f68849e..1fb8b28b15 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -502,6 +502,12 @@ void main() {
if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
+ if (bool(draw_data.flags & FLAGS_FLIP_H)) {
+ normal.x = -normal.x;
+ }
+ if (bool(draw_data.flags & FLAGS_FLIP_V)) {
+ normal.y = -normal.y;
+ }
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
} else {
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 1b627a3e81..fe4bf4bed0 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -27,6 +27,9 @@
#define FLAGS_USE_MSDF (1 << 28)
#define FLAGS_USE_LCD (1 << 29)
+#define FLAGS_FLIP_H (1 << 30)
+#define FLAGS_FLIP_V (1 << 31)
+
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl
index 0ee4cf6e31..0ee4cf6e31 100644
--- a/servers/rendering/renderer_rd/shaders/luminance_reduce.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl
diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl
index 29ebd74a90..29ebd74a90 100644
--- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl
diff --git a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl
index b8860f6518..b8860f6518 100644
--- a/servers/rendering/renderer_rd/shaders/luminance_reduce_raster_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster_inc.glsl
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 be53a7ae49..d32e6d717f 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
@@ -118,9 +118,15 @@ layout(location = 10) out flat uint instance_index_interp;
// !BAS! This needs to become an input once we implement our fallback!
#define ViewIndex 0
#endif // has_VK_KHR_multiview
+vec3 normal_roughness_uv(vec2 uv) {
+ return vec3(uv, ViewIndex);
+}
#else // USE_MULTIVIEW
// Set to zero, not supported in non stereo
#define ViewIndex 0
+vec2 normal_roughness_uv(vec2 uv) {
+ return uv;
+}
#endif //USE_MULTIVIEW
invariant gl_Position;
@@ -544,9 +550,15 @@ layout(location = 10) in flat uint instance_index_interp;
// !BAS! This needs to become an input once we implement our fallback!
#define ViewIndex 0
#endif // has_VK_KHR_multiview
+vec3 normal_roughness_uv(vec2 uv) {
+ return vec3(uv, ViewIndex);
+}
#else // USE_MULTIVIEW
// Set to zero, not supported in non stereo
#define ViewIndex 0
+vec2 normal_roughness_uv(vec2 uv) {
+ return uv;
+}
#endif //USE_MULTIVIEW
//defines to keep compatibility with vertex
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index 3a45ab0059..1f524313f2 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -268,6 +268,7 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid;
#define color_buffer shadow_atlas
#define normal_roughness_buffer shadow_atlas
+#define multiviewSampler sampler2D
#else
layout(set = 1, binding = 10) uniform texture2D depth_buffer;
@@ -277,10 +278,12 @@ layout(set = 1, binding = 11) uniform texture2D color_buffer;
layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer;
layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer;
layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer;
+#define multiviewSampler sampler2DArray
#else // USE_MULTIVIEW
layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer;
layout(set = 1, binding = 14) uniform texture2D ambient_buffer;
layout(set = 1, binding = 15) uniform texture2D reflection_buffer;
+#define multiviewSampler sampler2D
#endif
layout(set = 1, binding = 13) uniform texture2D ao_buffer;
layout(set = 1, binding = 16) uniform texture2DArray sdfgi_lightprobe_texture;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 66ae1e8d1a..d631a89dd2 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -2685,6 +2685,14 @@ void MaterialStorage::material_free(RID p_rid) {
Material *material = material_owner.get_or_null(p_rid);
ERR_FAIL_COND(!material);
+ // Need to clear texture arrays to prevent spin locking of their RID's.
+ // This happens when the app is being closed.
+ for (KeyValue<StringName, Variant> &E : material->params) {
+ if (E.value.get_type() == Variant::ARRAY) {
+ Array(E.value).clear();
+ }
+ }
+
material_set_shader(p_rid, RID()); //clean up shader
material->dependency.deleted_notify(p_rid);
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 6401d0f5d0..e4149f6bbd 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -1415,7 +1415,6 @@ void ParticlesStorage::update_particles() {
}
bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
- bool updated = false;
if (particles->clear && particles->pre_process_time > 0.0) {
double frame_time;
@@ -1430,7 +1429,6 @@ void ParticlesStorage::update_particles() {
while (todo >= 0) {
_particles_process(particles, frame_time);
todo -= frame_time;
- updated = true;
}
}
@@ -1452,10 +1450,9 @@ void ParticlesStorage::update_particles() {
}
double todo = particles->frame_remainder + delta;
- while (todo >= frame_time || (particles->clear && !updated)) {
+ while (todo >= frame_time || particles->clear) {
_particles_process(particles, frame_time);
todo -= decr;
- updated = true;
}
particles->frame_remainder = todo;
@@ -1463,16 +1460,16 @@ void ParticlesStorage::update_particles() {
} else {
if (zero_time_scale) {
_particles_process(particles, 0.0);
- updated = true;
} else {
_particles_process(particles, RendererCompositorRD::singleton->get_frame_delta_time());
- updated = true;
}
}
- //copy particles to instance buffer
+ // Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing).
+ DEV_ASSERT(!particles->clear);
- if (updated && particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
+ // Copy particles to instance buffer.
+ if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
//does not need view dependent operation, do copy here
ParticlesShader::CopyPushConstant copy_push_constant;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index a953bac433..31377a10a0 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -94,31 +94,9 @@ void RenderSceneBuffersRD::cleanup() {
free_named_texture(E.value);
}
named_textures.clear();
-
- // old stuff, to be re-evaluated...
-
- for (int i = 0; i < luminance.fb.size(); i++) {
- RD::get_singleton()->free(luminance.fb[i]);
- }
- luminance.fb.clear();
-
- for (int i = 0; i < luminance.reduce.size(); i++) {
- RD::get_singleton()->free(luminance.reduce[i]);
- }
- luminance.reduce.clear();
-
- if (luminance.current_fb.is_valid()) {
- RD::get_singleton()->free(luminance.current_fb);
- luminance.current_fb = RID();
- }
-
- if (luminance.current.is_valid()) {
- RD::get_singleton()->free(luminance.current);
- luminance.current = RID();
- }
}
-void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
+void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -126,12 +104,7 @@ void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_interna
target_size = p_target_size;
internal_size = p_internal_size;
-
- // FIXME, right now we do this because only our clustered renderer supports FSR upscale
- // this does mean that with linear upscale if we use subpasses, we could get into trouble.
- if (!can_be_storage) {
- internal_size = target_size;
- }
+ scaling_3d_mode = p_scaling_3d_mode;
if (p_use_taa) {
// Use negative mipmap LOD bias when TAA is enabled to compensate for loss of sharpness.
@@ -215,6 +188,7 @@ void RenderSceneBuffersRD::configure_for_reflections(const Size2i p_reflection_s
target_size = p_reflection_size;
internal_size = p_reflection_size;
render_target = RID();
+ scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
fsr_sharpness = 0.0;
msaa_3d = RS::VIEWPORT_MSAA_DISABLED;
screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
index 1bd542500c..dc849fd56a 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
@@ -73,6 +73,7 @@ private:
// The internal size of the textures we render 3D to in case we render at a lower resolution and upscale
Size2i internal_size = Size2i(0, 0);
+ RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
float fsr_sharpness = 0.2f;
// Aliassing settings
@@ -139,7 +140,7 @@ public:
void set_vrs(RendererRD::VRS *p_vrs) { vrs = p_vrs; }
void cleanup();
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
+ virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
void configure_for_reflections(const Size2i p_reflection_size);
virtual void set_fsr_sharpness(float p_fsr_sharpness) override;
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override;
@@ -172,6 +173,7 @@ public:
_FORCE_INLINE_ uint32_t get_view_count() const { return view_count; }
_FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; }
_FORCE_INLINE_ Size2i get_target_size() const { return target_size; }
+ _FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; }
_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; }
_FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa_3d; }
_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; }
@@ -213,15 +215,6 @@ public:
// 2 full size, 2 half size
WeightBuffers weight_buffers[4]; // Only used in raster
-
- struct Luminance {
- Vector<RID> reduce;
- RID current;
-
- // used only on mobile renderer
- Vector<RID> fb;
- RID current_fb;
- } luminance;
};
#endif // RENDER_SCENE_BUFFERS_RD_H
diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp
index e2e6ea5aa2..a389e3e767 100644
--- a/servers/rendering/renderer_scene_render.cpp
+++ b/servers/rendering/renderer_scene_render.cpp
@@ -84,7 +84,7 @@ void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count
Transform3D main_transform_inv = main_transform.inverse();
// 5. figure out far plane, this could use some improvement, we may have our far plane too close like this, not sure if this matters
- Vector3 far_center = (planes[0][Projection::PLANE_FAR].center() + planes[1][Projection::PLANE_FAR].center()) * 0.5;
+ Vector3 far_center = (planes[0][Projection::PLANE_FAR].get_center() + planes[1][Projection::PLANE_FAR].get_center()) * 0.5;
Plane far(-z, far_center);
/////////////////////////////////////////////////////////////////////////////
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 05a3fccf1c..3886f5b379 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -115,9 +115,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
if (p_viewport->size.width == 0 || p_viewport->size.height == 0) {
p_viewport->render_buffers.unref();
} else {
- const float scaling_3d_scale = p_viewport->scaling_3d_scale;
+ float scaling_3d_scale = p_viewport->scaling_3d_scale;
RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode;
- bool scaling_enabled = true;
if ((scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) && (scaling_3d_scale > 1.0)) {
// FSR is not designed for downsampling.
@@ -133,7 +132,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
}
if (scaling_3d_scale == 1.0) {
- scaling_enabled = false;
+ scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
}
int width;
@@ -141,36 +140,37 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
int render_width;
int render_height;
- if (scaling_enabled) {
- switch (scaling_3d_mode) {
- case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR:
- // Clamp 3D rendering resolution to reasonable values supported on most hardware.
- // This prevents freezing the engine or outright crashing on lower-end GPUs.
- width = CLAMP(p_viewport->size.width * scaling_3d_scale, 1, 16384);
- height = CLAMP(p_viewport->size.height * scaling_3d_scale, 1, 16384);
- render_width = width;
- render_height = height;
- break;
- case RS::VIEWPORT_SCALING_3D_MODE_FSR:
- width = p_viewport->size.width;
- height = p_viewport->size.height;
- render_width = MAX(width * scaling_3d_scale, 1.0); // width / (width * scaling)
- render_height = MAX(height * scaling_3d_scale, 1.0);
- break;
- default:
- // This is an unknown mode.
- WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling.", scaling_3d_mode));
- width = p_viewport->size.width;
- height = p_viewport->size.height;
- render_width = width;
- render_height = height;
- break;
- }
- } else {
- width = p_viewport->size.width;
- height = p_viewport->size.height;
- render_width = width;
- render_height = height;
+ switch (scaling_3d_mode) {
+ case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR:
+ // Clamp 3D rendering resolution to reasonable values supported on most hardware.
+ // This prevents freezing the engine or outright crashing on lower-end GPUs.
+ width = CLAMP(p_viewport->size.width * scaling_3d_scale, 1, 16384);
+ height = CLAMP(p_viewport->size.height * scaling_3d_scale, 1, 16384);
+ render_width = width;
+ render_height = height;
+ break;
+ case RS::VIEWPORT_SCALING_3D_MODE_FSR:
+ width = p_viewport->size.width;
+ height = p_viewport->size.height;
+ render_width = MAX(width * scaling_3d_scale, 1.0); // width / (width * scaling)
+ render_height = MAX(height * scaling_3d_scale, 1.0);
+ break;
+ case RS::VIEWPORT_SCALING_3D_MODE_OFF:
+ width = p_viewport->size.width;
+ height = p_viewport->size.height;
+ render_width = width;
+ render_height = height;
+ break;
+ default:
+ // This is an unknown mode.
+ WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling.", scaling_3d_mode));
+ scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
+ scaling_3d_scale = 1.0;
+ width = p_viewport->size.width;
+ height = p_viewport->size.height;
+ render_width = width;
+ render_height = height;
+ break;
}
p_viewport->internal_size = Size2(render_width, render_height);
@@ -179,7 +179,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
// to compensate for the loss of sharpness.
const float texture_mipmap_bias = log2f(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias;
- p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->view_count);
+ p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), scaling_3d_mode, p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->view_count);
}
}
}
@@ -1138,6 +1138,7 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport
void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_COND(!viewport);
+ ERR_FAIL_COND_EDMSG(OS::get_singleton()->get_current_rendering_method() != "forward_plus", "TAA is only available when using the Forward+ renderer.");
if (viewport->use_taa == p_use_taa) {
return;
diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp
index 5967835d03..3678b70254 100644
--- a/servers/rendering/rendering_device_binds.cpp
+++ b/servers/rendering/rendering_device_binds.cpp
@@ -31,6 +31,8 @@
#include "rendering_device_binds.h"
Error RDShaderFile::parse_versions_from_text(const String &p_text, const String p_defines, OpenIncludeFunction p_include_func, void *p_include_func_userdata) {
+ ERR_FAIL_NULL_V(RenderingDevice::get_singleton(), ERR_UNAVAILABLE);
+
Vector<String> lines = p_text.split("\n");
bool reading_versions = false;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 4c4b3d13f9..6017eff55e 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -291,6 +291,10 @@ void RenderingServerDefault::set_boot_image(const Ref<Image> &p_image, const Col
RSG::rasterizer->set_boot_image(p_image, p_color, p_scale, p_use_filter);
}
+Color RenderingServerDefault::get_default_clear_color() {
+ return RSG::texture_storage->get_default_clear_color();
+}
+
void RenderingServerDefault::set_default_clear_color(const Color &p_color) {
RSG::viewport->set_default_clear_color(p_color);
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index f2fadd5e1f..8ac522bafe 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -854,7 +854,7 @@ public:
FUNC8(canvas_item_add_msdf_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &, int, float, float)
FUNC5(canvas_item_add_lcd_texture_rect_region, RID, const Rect2 &, RID, const Rect2 &, const Color &)
FUNC10(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &)
- FUNC6(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float)
+ FUNC5(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID)
FUNC5(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID)
FUNC9(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int)
FUNC5(canvas_item_add_mesh, RID, const RID &, const Transform2D &, const Color &, RID)
@@ -986,6 +986,7 @@ public:
virtual double get_frame_setup_time_cpu() const override;
virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override;
+ virtual Color get_default_clear_color() override;
virtual void set_default_clear_color(const Color &p_color) override;
virtual bool has_feature(Features p_feature) const override;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 57215d9d63..626da90168 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -1183,6 +1183,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code += "(";
+ // if normal roughness texture is used, we will add logic to automatically switch between
+ // sampler2D and sampler2D array and vec2 UV and vec3 UV.
+ bool normal_roughness_texture_used = false;
+
for (int i = 1; i < onode->arguments.size(); i++) {
if (i > 1) {
code += ", ";
@@ -1282,11 +1286,24 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
}
- code += ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()) + "(" + node_code + ", " + sampler_name + ")";
+ String data_type_name = "";
+ if (texture_uniform == "NORMAL_ROUGHNESS_TEXTURE") {
+ data_type_name = "multiviewSampler";
+ normal_roughness_texture_used = true;
+ } else {
+ data_type_name = ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype());
+ }
+
+ code += data_type_name + "(" + node_code + ", " + sampler_name + ")";
} else {
code += node_code;
}
} else {
+ if (normal_roughness_texture_used && i == 2) {
+ // UV coordinate after using normal roughness texture.
+ node_code = "normal_roughness_uv(" + node_code + ".xy)";
+ }
+
code += node_code;
}
}
diff --git a/servers/rendering/storage/render_scene_buffers.cpp b/servers/rendering/storage/render_scene_buffers.cpp
index 1320199833..6369139aa6 100644
--- a/servers/rendering/storage/render_scene_buffers.cpp
+++ b/servers/rendering/storage/render_scene_buffers.cpp
@@ -34,8 +34,8 @@ void RenderSceneBuffers::_bind_methods() {
ClassDB::bind_method(D_METHOD("configure", "render_target", "internal_size", "target_size", "fsr_sharpness", "texture_mipmap_bias", "msaa", "screen_space_aa", "use_taa", "use_debanding", "view_count"), &RenderSceneBuffers::configure);
}
-void RenderSceneBuffers::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
- GDVIRTUAL_CALL(_configure, p_render_target, p_internal_size, p_target_size, p_fsr_sharpness, p_texture_mipmap_bias, p_msaa, p_screen_space_aa, p_use_taa, p_use_debanding, p_view_count);
+void RenderSceneBuffers::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
+ GDVIRTUAL_CALL(_configure, p_render_target, p_internal_size, p_target_size, p_scaling_3d_mode, p_fsr_sharpness, p_texture_mipmap_bias, p_msaa, p_screen_space_aa, p_use_taa, p_use_debanding, p_view_count);
};
void RenderSceneBuffers::set_fsr_sharpness(float p_fsr_sharpness) {
diff --git a/servers/rendering/storage/render_scene_buffers.h b/servers/rendering/storage/render_scene_buffers.h
index 83fc78ca1c..cf96a9f372 100644
--- a/servers/rendering/storage/render_scene_buffers.h
+++ b/servers/rendering/storage/render_scene_buffers.h
@@ -40,7 +40,7 @@ class RenderSceneBuffers : public RefCounted {
protected:
static void _bind_methods();
- GDVIRTUAL10(_configure, RID, Size2i, Size2i, float, float, RS::ViewportMSAA, RenderingServer::ViewportScreenSpaceAA, bool, bool, uint32_t)
+ GDVIRTUAL11(_configure, RID, Size2i, Size2i, RS::ViewportScaling3DMode, float, float, RS::ViewportMSAA, RenderingServer::ViewportScreenSpaceAA, bool, bool, uint32_t)
GDVIRTUAL1(_set_fsr_sharpness, float)
GDVIRTUAL1(_set_texture_mipmap_bias, float)
GDVIRTUAL1(_set_use_debanding, bool)
@@ -49,7 +49,7 @@ public:
RenderSceneBuffers(){};
virtual ~RenderSceneBuffers(){};
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count);
+ virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count);
// for those settings that are unlikely to require buffers to be recreated, we'll add setters
virtual void set_fsr_sharpness(float p_fsr_sharpness);
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 13891e59fb..e24b2af976 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -700,9 +700,9 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
return OK;
}
-uint32_t RenderingServer::mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_array_index) const {
+uint32_t RenderingServer::mesh_surface_get_format_offset(BitField<ArrayFormat> p_format, int p_vertex_len, int p_array_index) const {
ERR_FAIL_INDEX_V(p_array_index, ARRAY_MAX, 0);
- p_format &= ~ARRAY_FORMAT_INDEX;
+ p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t astr;
@@ -711,8 +711,8 @@ uint32_t RenderingServer::mesh_surface_get_format_offset(uint32_t p_format, int
return offsets[p_array_index];
}
-uint32_t RenderingServer::mesh_surface_get_format_vertex_stride(uint32_t p_format, int p_vertex_len) const {
- p_format &= ~ARRAY_FORMAT_INDEX;
+uint32_t RenderingServer::mesh_surface_get_format_vertex_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
+ p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t astr;
@@ -720,8 +720,8 @@ uint32_t RenderingServer::mesh_surface_get_format_vertex_stride(uint32_t p_forma
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
return vstr;
}
-uint32_t RenderingServer::mesh_surface_get_format_attribute_stride(uint32_t p_format, int p_vertex_len) const {
- p_format &= ~ARRAY_FORMAT_INDEX;
+uint32_t RenderingServer::mesh_surface_get_format_attribute_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
+ p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t astr;
@@ -729,8 +729,8 @@ uint32_t RenderingServer::mesh_surface_get_format_attribute_stride(uint32_t p_fo
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
return astr;
}
-uint32_t RenderingServer::mesh_surface_get_format_skin_stride(uint32_t p_format, int p_vertex_len) const {
- p_format &= ~ARRAY_FORMAT_INDEX;
+uint32_t RenderingServer::mesh_surface_get_format_skin_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
+ p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t astr;
@@ -1049,7 +1049,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
return OK;
}
-void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, uint32_t p_compress_format) {
+void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, BitField<ArrayFormat> p_compress_format) {
SurfaceData sd;
Error err = mesh_create_surface_data_from_arrays(&sd, p_primitive, p_arrays, p_blend_shapes, p_lods, p_compress_format);
if (err != OK) {
@@ -1797,35 +1797,35 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_FLOAT);
BIND_ENUM_CONSTANT(ARRAY_CUSTOM_MAX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BITS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3_SHIFT);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_MASK);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_FLAGS_BASE);
-
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_VERTEX);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_NORMAL);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TANGENT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_COLOR);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TEX_UV);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_TEX_UV2);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM0);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM1);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM2);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM3);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_BONES);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_INDEX);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_BLEND_SHAPE_MASK);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_BASE);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_BITS);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM0_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM1_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM2_SHIFT);
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM3_SHIFT);
+
+ BIND_BITFIELD_FLAG(ARRAY_FORMAT_CUSTOM_MASK);
+ BIND_BITFIELD_FLAG(ARRAY_COMPRESS_FLAGS_BASE);
+
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
BIND_ENUM_CONSTANT(PRIMITIVE_LINES);
@@ -2592,7 +2592,7 @@ void RenderingServer::_bind_methods() {
/* Primitives */
- 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_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_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);
@@ -2601,7 +2601,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_add_lcd_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate"), &RenderingServer::canvas_item_add_lcd_texture_rect_region);
ClassDB::bind_method(D_METHOD("canvas_item_add_texture_rect_region", "item", "rect", "texture", "src_rect", "modulate", "transpose", "clip_uv"), &RenderingServer::canvas_item_add_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("canvas_item_add_nine_patch", "item", "rect", "source", "texture", "topleft", "bottomright", "x_axis_mode", "y_axis_mode", "draw_center", "modulate"), &RenderingServer::canvas_item_add_nine_patch, DEFVAL(NINE_PATCH_STRETCH), DEFVAL(NINE_PATCH_STRETCH), DEFVAL(true), DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("canvas_item_add_primitive", "item", "points", "colors", "uvs", "texture", "width"), &RenderingServer::canvas_item_add_primitive, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("canvas_item_add_primitive", "item", "points", "colors", "uvs", "texture"), &RenderingServer::canvas_item_add_primitive);
ClassDB::bind_method(D_METHOD("canvas_item_add_polygon", "item", "points", "colors", "uvs", "texture"), &RenderingServer::canvas_item_add_polygon, DEFVAL(Vector<Point2>()), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "bones", "weights", "texture", "count"), &RenderingServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(Vector<int>()), DEFVAL(Vector<float>()), DEFVAL(RID()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("canvas_item_add_mesh", "item", "mesh", "transform", "modulate", "texture"), &RenderingServer::canvas_item_add_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1)), DEFVAL(RID()));
@@ -2763,6 +2763,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_white_texture"), &RenderingServer::get_white_texture);
ClassDB::bind_method(D_METHOD("set_boot_image", "image", "color", "scale", "use_filter"), &RenderingServer::set_boot_image, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("get_default_clear_color"), &RenderingServer::get_default_clear_color);
ClassDB::bind_method(D_METHOD("set_default_clear_color", "color"), &RenderingServer::set_default_clear_color);
ClassDB::bind_method(D_METHOD("has_feature", "feature"), &RenderingServer::has_feature);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index a5862b80cd..4b51099ef0 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -320,10 +320,10 @@ public:
virtual void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) = 0;
- virtual uint32_t mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_array_index) const;
- virtual uint32_t mesh_surface_get_format_vertex_stride(uint32_t p_format, int p_vertex_len) const;
- virtual uint32_t mesh_surface_get_format_attribute_stride(uint32_t p_format, int p_vertex_len) const;
- virtual uint32_t mesh_surface_get_format_skin_stride(uint32_t p_format, int p_vertex_len) const;
+ virtual uint32_t mesh_surface_get_format_offset(BitField<ArrayFormat> p_format, int p_vertex_len, int p_array_index) const;
+ virtual uint32_t mesh_surface_get_format_vertex_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
+ virtual uint32_t mesh_surface_get_format_attribute_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
+ virtual uint32_t mesh_surface_get_format_skin_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
/// Returns stride
virtual void mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t &r_vertex_element_size, uint32_t &r_attrib_element_size, uint32_t &r_skin_element_size) const;
@@ -333,7 +333,7 @@ public:
TypedArray<Array> mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const;
Dictionary mesh_surface_get_lods(RID p_mesh, int p_surface) const;
- virtual void mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_compress_format = 0);
+ virtual void mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), BitField<ArrayFormat> p_compress_format = 0);
virtual void mesh_add_surface(RID p_mesh, const SurfaceData &p_surface) = 0;
virtual int mesh_get_blend_shape_count(RID p_mesh) const = 0;
@@ -802,7 +802,8 @@ public:
enum ViewportScaling3DMode {
VIEWPORT_SCALING_3D_MODE_BILINEAR,
VIEWPORT_SCALING_3D_MODE_FSR,
- VIEWPORT_SCALING_3D_MODE_MAX
+ VIEWPORT_SCALING_3D_MODE_MAX,
+ VIEWPORT_SCALING_3D_MODE_OFF = 255, // for internal use only
};
virtual void viewport_set_use_xr(RID p_viewport, bool p_use_xr) = 0;
@@ -1333,9 +1334,9 @@ public:
NINE_PATCH_TILE_FIT,
};
- virtual void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false) = 0;
+ virtual void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = -1.0, bool p_antialiased = false) = 0;
virtual void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false) = 0;
- virtual void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0) = 0;
+ virtual void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = -1.0) = 0;
virtual void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) = 0;
virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0;
virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) = 0;
@@ -1343,7 +1344,7 @@ public:
virtual void canvas_item_add_msdf_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, float p_px_range = 1.0, float p_scale = 1.0) = 0;
virtual void canvas_item_add_lcd_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)) = 0;
virtual void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, NinePatchAxisMode p_x_axis_mode = NINE_PATCH_STRETCH, NinePatchAxisMode p_y_axis_mode = NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1)) = 0;
- virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0) = 0;
+ virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture) = 0;
virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID()) = 0;
virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1) = 0;
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), RID p_texture = RID()) = 0;
@@ -1560,6 +1561,7 @@ public:
virtual void mesh_add_surface_from_planes(RID p_mesh, const Vector<Plane> &p_planes);
virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0;
+ virtual Color get_default_clear_color() = 0;
virtual void set_default_clear_color(const Color &p_color) = 0;
enum Features {
@@ -1610,7 +1612,7 @@ VARIANT_ENUM_CAST(RenderingServer::TextureLayeredType);
VARIANT_ENUM_CAST(RenderingServer::CubeMapLayer);
VARIANT_ENUM_CAST(RenderingServer::ShaderMode);
VARIANT_ENUM_CAST(RenderingServer::ArrayType);
-VARIANT_ENUM_CAST(RenderingServer::ArrayFormat);
+VARIANT_BITFIELD_CAST(RenderingServer::ArrayFormat);
VARIANT_ENUM_CAST(RenderingServer::ArrayCustomFormat);
VARIANT_ENUM_CAST(RenderingServer::PrimitiveType);
VARIANT_ENUM_CAST(RenderingServer::BlendShapeMode);
diff --git a/tests/core/math/test_plane.h b/tests/core/math/test_plane.h
index b2b857ca69..f784a29a17 100644
--- a/tests/core/math/test_plane.h
+++ b/tests/core/math/test_plane.h
@@ -87,8 +87,8 @@ TEST_CASE("[Plane] Plane-point operations") {
const Plane y_facing_plane = Plane(0, 1, 0, 4);
CHECK_MESSAGE(
- plane.center().is_equal_approx(Vector3(32 * 3, 22 * 3, 16 * 3)),
- "center() should return a vector pointing to the center of the plane.");
+ plane.get_center().is_equal_approx(Vector3(32 * 3, 22 * 3, 16 * 3)),
+ "get_center() should return a vector pointing to the center of the plane.");
CHECK_MESSAGE(
y_facing_plane.is_point_over(Vector3(0, 5, 0)),
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index d262785864..5447c99a64 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -2894,7 +2894,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
CHECK(code_edit->get_caret_column() == 6);
code_edit->undo();
- // brace completion disbaled
+ // brace completion disabled
code_edit->set_auto_brace_completion_enabled(false);
// Full completion.
@@ -2930,11 +2930,11 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test", "\"test");
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
- CHECK(code_edit->get_line(0) == "\"\"test\"\"");
+ CHECK(code_edit->get_line(0) == "\"\"test\"");
CHECK(code_edit->get_caret_column() == 7);
code_edit->undo();
- // brace completion disbaled
+ // brace completion disabled
code_edit->set_auto_brace_completion_enabled(false);
// Full completion.
@@ -3111,15 +3111,15 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
Point2 caret_pos = code_edit->get_caret_draw_pos();
caret_pos.y += code_edit->get_line_height();
- SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_DOWN, MouseButton::NONE, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_DOWN, 0, Key::NONE);
CHECK(code_edit->get_code_completion_selected_index() == 1);
- SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_UP, MouseButton::NONE, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_UP, 0, Key::NONE);
CHECK(code_edit->get_code_completion_selected_index() == 0);
/* Single click selects. */
caret_pos.y += code_edit->get_line_height() * 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK(code_edit->get_code_completion_selected_index() == 2);
/* Double click inserts. */
@@ -3330,7 +3330,7 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") {
Point2 caret_pos = code_edit->get_caret_draw_pos();
caret_pos.x += 60;
- SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::NONE, MouseButton::NONE, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::NONE, 0, Key::NONE);
CHECK(code_edit->get_text_for_symbol_lookup() == "this is s" + String::chr(0xFFFF) + "ome text");
SIGNAL_WATCH(code_edit, "symbol_validate");
diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h
new file mode 100644
index 0000000000..fc141f3d09
--- /dev/null
+++ b/tests/scene/test_curve_2d.h
@@ -0,0 +1,228 @@
+/**************************************************************************/
+/* test_curve_2d.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_2D_H
+#define TEST_CURVE_2D_H
+
+#include "core/math/math_funcs.h"
+#include "scene/resources/curve.h"
+
+#include "tests/test_macros.h"
+
+namespace TestCurve2D {
+
+void add_sample_curve_points(Ref<Curve2D> &curve) {
+ Vector2 p0 = Vector2(0, 0);
+ Vector2 p1 = Vector2(50, 0);
+ Vector2 p2 = Vector2(50, 50);
+ Vector2 p3 = Vector2(0, 50);
+
+ Vector2 control0 = p1 - p0;
+ Vector2 control1 = p3 - p2;
+
+ curve->add_point(p0, Vector2(), control0);
+ curve->add_point(p3, control1, Vector2());
+}
+
+TEST_CASE("[Curve2D] Default curve is empty") {
+ const Ref<Curve2D> curve = memnew(Curve2D);
+ CHECK(curve->get_point_count() == 0);
+}
+
+TEST_CASE("[Curve2D] Point management") {
+ Ref<Curve2D> curve = memnew(Curve2D);
+
+ 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(Vector2());
+ 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") {
+ Vector2 new_in = Vector2(1, 1);
+ Vector2 new_out = Vector2(1, 1);
+ Vector2 new_pos = Vector2(1, 1);
+
+ curve->add_point(Vector2());
+
+ 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);
+ }
+}
+
+TEST_CASE("[Curve2D] Baked") {
+ Ref<Curve2D> curve = memnew(Curve2D);
+
+ SUBCASE("Single Point") {
+ curve->add_point(Vector2());
+
+ CHECK(curve->get_baked_length() == 0);
+ CHECK(curve->get_baked_points().size() == 1);
+ }
+
+ SUBCASE("Straight line") {
+ curve->add_point(Vector2());
+ curve->add_point(Vector2(0, 50));
+
+ CHECK(Math::is_equal_approx(curve->get_baked_length(), 50));
+ CHECK(curve->get_baked_points().size() == 15);
+ }
+
+ 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 between points
+ CHECK(len > 50);
+
+ SUBCASE("Increase bake interval") {
+ curve->set_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);
+ }
+ }
+}
+
+TEST_CASE("[Curve2D] Sampling") {
+ // Sampling over a simple straight line to make assertions simpler
+ Ref<Curve2D> curve = memnew(Curve2D);
+ curve->add_point(Vector2());
+ curve->add_point(Vector2(0, 50));
+
+ SUBCASE("sample") {
+ CHECK(curve->sample(0, 0) == Vector2(0, 0));
+ CHECK(curve->sample(0, 0.5) == Vector2(0, 25));
+ CHECK(curve->sample(0, 1) == Vector2(0, 50));
+ }
+
+ SUBCASE("samplef") {
+ CHECK(curve->samplef(0) == Vector2(0, 0));
+ CHECK(curve->samplef(0.5) == Vector2(0, 25));
+ CHECK(curve->samplef(1) == Vector2(0, 50));
+ }
+
+ SUBCASE("sample_baked") {
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0))) == Vector2(0, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25))) == Vector2(0, 25));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50))) == Vector2(0, 50));
+ }
+
+ SUBCASE("sample_baked_with_rotation") {
+ const real_t pi = 3.14159;
+ Transform2D t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 0)));
+ CHECK(t.get_origin() == Vector2(0, 0));
+ CHECK(Math::is_equal_approx(t.get_rotation(), pi));
+
+ t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 25)));
+ CHECK(t.get_origin() == Vector2(0, 25));
+ CHECK(Math::is_equal_approx(t.get_rotation(), pi));
+
+ t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 50)));
+ CHECK(t.get_origin() == Vector2(0, 50));
+ CHECK(Math::is_equal_approx(t.get_rotation(), pi));
+ }
+
+ SUBCASE("get_closest_point") {
+ CHECK(curve->get_closest_point(Vector2(0, 0)) == Vector2(0, 0));
+ CHECK(curve->get_closest_point(Vector2(0, 25)) == Vector2(0, 25));
+ CHECK(curve->get_closest_point(Vector2(50, 25)) == Vector2(0, 25));
+ CHECK(curve->get_closest_point(Vector2(0, 50)) == Vector2(0, 50));
+ CHECK(curve->get_closest_point(Vector2(50, 50)) == Vector2(0, 50));
+ CHECK(curve->get_closest_point(Vector2(0, 100)) == Vector2(0, 50));
+ }
+}
+
+TEST_CASE("[Curve2D] Tessellation") {
+ Ref<Curve2D> curve = memnew(Curve2D);
+ 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(Vector2(0, 100));
+ PackedVector2Array tes = curve->tessellate();
+ CHECK(tes.size() == default_size + 1);
+ CHECK(tes[tes.size() - 1] == Vector2(0, 100));
+ CHECK(tes[tes.size() - 2] == Vector2(0, 50));
+ }
+}
+
+TEST_CASE("[Curve2D] Even length tessellation") {
+ Ref<Curve2D> curve = memnew(Curve2D);
+ 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(Vector2(0, 150));
+ CHECK(curve->tessellate_even_length().size() > default_size + 5);
+}
+
+} // namespace TestCurve2D
+
+#endif // TEST_CURVE_2D_H
diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h
index 20c42d9fc2..0ddf027970 100644
--- a/tests/scene/test_node.h
+++ b/tests/scene/test_node.h
@@ -37,7 +37,7 @@
namespace TestNode {
-TEST_CASE("[SceneTree][Node] Simple Add/Remove/Move/Find") {
+TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
Node *node = memnew(Node);
// Check initial scene tree setup.
@@ -135,10 +135,30 @@ TEST_CASE("[SceneTree][Node] Simple Add/Remove/Move/Find") {
CHECK(node->is_inside_tree());
}
+ SUBCASE("Node should be possible to reparent") {
+ node->reparent(SceneTree::get_singleton()->get_root());
+
+ Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child, node);
+ CHECK(node->is_inside_tree());
+ }
+
+ SUBCASE("Node should be possible to duplicate") {
+ node->set_name("MyName");
+
+ Node *duplicate = node->duplicate();
+
+ CHECK_FALSE(node == duplicate);
+ CHECK_FALSE(duplicate->is_inside_tree());
+ CHECK_EQ(duplicate->get_name(), node->get_name());
+
+ memdelete(duplicate);
+ }
+
memdelete(node);
}
-TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") {
+TEST_CASE("[SceneTree][Node] Testing node operations with a more complex simple scene tree") {
Node *node1 = memnew(Node);
Node *node2 = memnew(Node);
Node *node1_1 = memnew(Node);
@@ -209,7 +229,7 @@ TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") {
CHECK_EQ(child2, node1);
}
- SUBCASE("Nodes should be in the expected order when reparented") {
+ SUBCASE("Nodes should be in the expected order when reparented (remove/add)") {
CHECK_EQ(node2->get_child_count(), 0);
node1->remove_child(node1_1);
@@ -227,6 +247,22 @@ TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") {
CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
}
+ SUBCASE("Nodes should be in the expected order when reparented") {
+ CHECK_EQ(node2->get_child_count(), 0);
+
+ node1_1->reparent(node2);
+
+ CHECK_EQ(node1->get_child_count(), 0);
+ CHECK_EQ(node2->get_child_count(), 1);
+ CHECK_EQ(node1_1->get_parent(), node2);
+
+ Node *child = node2->get_child(0);
+ CHECK_EQ(child, node1_1);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
+ }
+
SUBCASE("Nodes should be possible to find") {
Node *child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false);
CHECK_EQ(child, nullptr);
@@ -315,6 +351,70 @@ TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") {
CHECK_EQ(E->get(), node1_1);
}
+ SUBCASE("Nodes added as siblings of another node should be right next to it") {
+ node1->remove_child(node1_1);
+
+ node1->add_sibling(node1_1);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 3);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(0), node1);
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(1), node1_1);
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child(2), node2);
+ }
+
+ SUBCASE("Replaced nodes should be be removed and the replacing node added") {
+ SceneTree::get_singleton()->get_root()->remove_child(node2);
+
+ node1->replace_by(node2);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3);
+
+ CHECK_FALSE(node1->is_inside_tree());
+ CHECK(node2->is_inside_tree());
+
+ CHECK_EQ(node1->get_parent(), nullptr);
+ CHECK_EQ(node2->get_parent(), SceneTree::get_singleton()->get_root());
+ CHECK_EQ(node2->get_child_count(), 1);
+ CHECK_EQ(node2->get_child(0), node1_1);
+ }
+
+ SUBCASE("Replacing nodes should keep the groups of the replaced nodes") {
+ SceneTree::get_singleton()->get_root()->remove_child(node2);
+
+ node1->add_to_group("nodes");
+ node1->replace_by(node2, true);
+
+ List<Node *> nodes;
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK_EQ(nodes.size(), 1);
+
+ List<Node *>::Element *E = nodes.front();
+ CHECK_EQ(E->get(), node2);
+ }
+
+ SUBCASE("Duplicating a node should also duplicate the children") {
+ node1->set_name("MyName1");
+ node1_1->set_name("MyName1_1");
+ Node *duplicate1 = node1->duplicate();
+
+ CHECK_EQ(duplicate1->get_child_count(), node1->get_child_count());
+ Node *duplicate1_1 = duplicate1->get_child(0);
+
+ CHECK_EQ(duplicate1_1->get_child_count(), node1_1->get_child_count());
+
+ CHECK_EQ(duplicate1->get_name(), node1->get_name());
+ CHECK_EQ(duplicate1_1->get_name(), node1_1->get_name());
+
+ CHECK_FALSE(duplicate1->is_inside_tree());
+ CHECK_FALSE(duplicate1_1->is_inside_tree());
+
+ memdelete(duplicate1_1);
+ memdelete(duplicate1);
+ }
+
memdelete(node1_1);
memdelete(node1);
memdelete(node2);
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index e5b80911b2..944f9cb9f6 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -891,8 +891,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->grab_focus();
MessageQueue::get_singleton()->flush();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for s");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
@@ -903,12 +903,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
text_edit->set_selecting_enabled(false);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
@@ -935,7 +935,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == 3);
SIGNAL_CHECK("caret_changed", empty_signal_args);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for selection");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
@@ -949,7 +949,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
text_edit->set_selecting_enabled(false);
@@ -968,7 +968,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
SEND_GUI_DOUBLE_CLICK(text_edit, text_edit->get_pos_at_line_column(0, 2), Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for selection");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
@@ -981,12 +981,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
text_edit->set_selecting_enabled(false);
SEND_GUI_DOUBLE_CLICK(text_edit, text_edit->get_pos_at_line_column(0, 2), Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
@@ -1000,8 +1000,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("this is some text\nfor selection");
MessageQueue::get_singleton()->flush();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for s");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
@@ -1012,12 +1012,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
text_edit->set_selecting_enabled(false);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
@@ -1149,19 +1149,19 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->is_mouse_over_selection());
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->get_viewport()->gui_is_dragging());
CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag me");
line_0 = target_text_edit->get_pos_at_line_column(0, 0);
line_0.y /= 2;
line_0.x += 401; // As empty add one.
- SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit, line_0, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit, line_0, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->get_viewport()->gui_is_dragging());
- SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
CHECK(text_edit->get_text() == "");
@@ -3093,14 +3093,14 @@ TEST_CASE("[SceneTree][TextEdit] context menu") {
CHECK_FALSE(text_edit->is_context_menu_enabled());
CHECK_FALSE(text_edit->is_menu_visible());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(600, 10), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(600, 10), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
CHECK_FALSE(text_edit->is_menu_visible());
text_edit->set_context_menu_enabled(true);
CHECK(text_edit->is_context_menu_enabled());
CHECK_FALSE(text_edit->is_menu_visible());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(700, 10), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(700, 10), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
CHECK(text_edit->is_menu_visible());
memdelete(text_edit);
@@ -3340,13 +3340,13 @@ TEST_CASE("[SceneTree][TextEdit] caret") {
text_edit->set_move_caret_on_right_click_enabled(false);
CHECK_FALSE(text_edit->is_move_caret_on_right_click_enabled());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
CHECK(text_edit->get_caret_column() == caret_col);
text_edit->set_move_caret_on_right_click_enabled(true);
CHECK(text_edit->is_move_caret_on_right_click_enabled());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
CHECK(text_edit->get_caret_column() != caret_col);
text_edit->set_move_caret_on_right_click_enabled(false);
@@ -3860,28 +3860,28 @@ TEST_CASE("[SceneTree][TextEdit] viewport") {
// Scroll.
int v_scroll = text_edit->get_v_scroll();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE);
CHECK(text_edit->get_v_scroll() > v_scroll);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE);
CHECK(text_edit->get_v_scroll() == v_scroll);
// smooth scroll speed.
text_edit->set_smooth_scroll_enabled(true);
v_scroll = text_edit->get_v_scroll();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE);
text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
CHECK(text_edit->get_v_scroll() >= v_scroll);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE);
text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
CHECK(text_edit->get_v_scroll() == v_scroll);
v_scroll = text_edit->get_v_scroll();
text_edit->set_v_scroll_speed(10000);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE);
text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
CHECK(text_edit->get_v_scroll() >= v_scroll);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE);
text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
CHECK(text_edit->get_v_scroll() == v_scroll);
diff --git a/tests/test_macros.h b/tests/test_macros.h
index 8f339f1d9b..3074c1abf5 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -136,7 +136,7 @@ int register_test_command(String p_command, TestFunc p_function);
// SEND_GUI_KEY_EVENT - takes an object and a keycode set. e.g SEND_GUI_KEY_EVENT(code_edit, Key::A | KeyModifierMask::META).
// SEND_GUI_MOUSE_BUTTON_EVENT - takes an object, position, mouse button, mouse mask and modifiers e.g SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, Vector2(50, 50), MOUSE_BUTTON_NONE, MOUSE_BUTTON_NONE, Key::None);
// SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT - takes an object, position, mouse button, mouse mask and modifiers e.g SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(code_edit, Vector2(50, 50), MOUSE_BUTTON_NONE, MOUSE_BUTTON_NONE, Key::None);
-// SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButton::MASK_LEFT, KeyModifierMask::META);
+// SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META);
// SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META);
#define SEND_GUI_ACTION(m_object, m_action) \
@@ -188,12 +188,12 @@ int register_test_command(String p_command, TestFunc p_function);
MessageQueue::get_singleton()->flush(); \
}
-#define SEND_GUI_DOUBLE_CLICK(m_object, m_local_pos, m_modifers) \
- { \
- _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, MouseButton::LEFT, m_modifers); \
- event->set_double_click(true); \
- m_object->get_viewport()->push_input(event); \
- MessageQueue::get_singleton()->flush(); \
+#define SEND_GUI_DOUBLE_CLICK(m_object, m_local_pos, m_modifers) \
+ { \
+ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \
+ event->set_double_click(true); \
+ m_object->get_viewport()->push_input(event); \
+ MessageQueue::get_singleton()->flush(); \
}
// We toggle _print_error_enabled to prevent display server not supported warnings.
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 85a271fc9c..8c563f94ac 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -91,6 +91,7 @@
#include "tests/scene/test_bit_map.h"
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_curve.h"
+#include "tests/scene/test_curve_2d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_path_2d.h"