summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml2
-rw-r--r--AUTHORS.md2
-rw-r--r--CHANGELOG.md2
-rw-r--r--CONTRIBUTING.md28
-rw-r--r--DONORS.md43
-rw-r--r--README.md10
-rw-r--r--SConstruct18
-rw-r--r--core/bind/core_bind.cpp27
-rw-r--r--core/bind/core_bind.h6
-rw-r--r--core/class_db.cpp39
-rw-r--r--core/class_db.h4
-rw-r--r--core/color.cpp2
-rw-r--r--core/color.h2
-rw-r--r--core/dictionary.cpp9
-rw-r--r--core/dictionary.h1
-rw-r--r--core/global_constants.cpp5
-rw-r--r--core/hash_map.h4
-rw-r--r--core/image.cpp9
-rw-r--r--core/image.h1
-rw-r--r--core/io/file_access_network.cpp4
-rw-r--r--core/io/file_access_network.h1
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/image_loader.h2
-rw-r--r--core/io/ip_address.cpp2
-rw-r--r--core/io/logger.cpp4
-rw-r--r--core/io/resource_format_binary.cpp9
-rw-r--r--core/io/resource_loader.cpp10
-rw-r--r--core/io/resource_loader.h4
-rw-r--r--core/io/resource_saver.cpp2
-rw-r--r--core/io/resource_saver.h2
-rw-r--r--core/math/expression.cpp4
-rw-r--r--core/math/geometry.h15
-rw-r--r--core/math/math_defs.h4
-rw-r--r--core/math/math_funcs.cpp25
-rw-r--r--core/math/math_funcs.h13
-rw-r--r--core/math/random_number_generator.cpp45
-rw-r--r--core/math/random_number_generator.h61
-rw-r--r--core/math/random_pcg.cpp55
-rw-r--r--core/math/random_pcg.h61
-rw-r--r--core/message_queue.cpp1
-rw-r--r--core/oa_hash_map.h4
-rw-r--r--core/object.cpp63
-rw-r--r--core/object.h13
-rw-r--r--core/os/file_access.cpp28
-rw-r--r--core/os/file_access.h3
-rw-r--r--core/os/input.cpp2
-rw-r--r--core/os/input.h2
-rw-r--r--core/project_settings.cpp86
-rw-r--r--core/register_core_types.cpp3
-rw-r--r--core/resource.cpp4
-rw-r--r--core/ring_buffer.h6
-rw-r--r--core/script_debugger_remote.cpp4
-rw-r--r--core/script_language.cpp3
-rw-r--r--core/set.h1
-rw-r--r--core/undo_redo.cpp7
-rw-r--r--core/undo_redo.h2
-rw-r--r--core/ustring.cpp33
-rw-r--r--core/variant_call.cpp4
-rw-r--r--core/vmap.h21
-rw-r--r--doc/Doxyfile126
-rw-r--r--doc/classes/@GDScript.xml16
-rw-r--r--doc/classes/@GlobalScope.xml4
-rw-r--r--doc/classes/AABB.xml2
-rw-r--r--doc/classes/ARVRInterface.xml4
-rw-r--r--doc/classes/Animation.xml4
-rw-r--r--doc/classes/AnimationNodeAnimation.xml6
-rw-r--r--doc/classes/AnimationPlayer.xml6
-rw-r--r--doc/classes/AnimationTreePlayer.xml2
-rw-r--r--doc/classes/AudioEffectReverb.xml4
-rw-r--r--doc/classes/AudioServer.xml4
-rw-r--r--doc/classes/AudioStream.xml2
-rw-r--r--doc/classes/AudioStreamPlayer.xml4
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml4
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml4
-rw-r--r--doc/classes/BakedLightmap.xml2
-rw-r--r--doc/classes/BaseButton.xml3
-rw-r--r--doc/classes/Basis.xml4
-rw-r--r--doc/classes/CPUParticles.xml4
-rw-r--r--doc/classes/CPUParticles2D.xml4
-rw-r--r--doc/classes/CanvasItem.xml4
-rw-r--r--doc/classes/CanvasItemMaterial.xml8
-rw-r--r--doc/classes/CanvasLayer.xml4
-rw-r--r--doc/classes/CollisionShape.xml2
-rw-r--r--doc/classes/CollisionShape2D.xml2
-rw-r--r--doc/classes/ConfigFile.xml1
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/Dictionary.xml11
-rw-r--r--doc/classes/DirectionalLight.xml2
-rw-r--r--doc/classes/Directory.xml2
-rw-r--r--doc/classes/DynamicFontData.xml3
-rw-r--r--doc/classes/EditorImportPlugin.xml2
-rw-r--r--doc/classes/EditorInspector.xml8
-rw-r--r--doc/classes/EditorPlugin.xml8
-rw-r--r--doc/classes/EditorScenePostImport.xml2
-rw-r--r--doc/classes/Environment.xml7
-rw-r--r--doc/classes/Expression.xml23
-rw-r--r--doc/classes/File.xml15
-rw-r--r--doc/classes/FileDialog.xml10
-rw-r--r--doc/classes/GIProbe.xml2
-rw-r--r--doc/classes/Generic6DOFJoint.xml78
-rw-r--r--doc/classes/Gradient.xml2
-rw-r--r--doc/classes/GridContainer.xml10
-rw-r--r--doc/classes/HTTPClient.xml4
-rw-r--r--doc/classes/HTTPRequest.xml2
-rw-r--r--doc/classes/Input.xml7
-rw-r--r--doc/classes/InputEvent.xml4
-rw-r--r--doc/classes/InputEventAction.xml2
-rw-r--r--doc/classes/InputEventJoypadButton.xml2
-rw-r--r--doc/classes/InputEventJoypadMotion.xml2
-rw-r--r--doc/classes/InputEventKey.xml2
-rw-r--r--doc/classes/InputEventMouse.xml2
-rw-r--r--doc/classes/InputEventMouseButton.xml2
-rw-r--r--doc/classes/InputEventMouseMotion.xml2
-rw-r--r--doc/classes/InputEventScreenDrag.xml2
-rw-r--r--doc/classes/InputEventScreenTouch.xml2
-rw-r--r--doc/classes/InputEventWithModifiers.xml2
-rw-r--r--doc/classes/InputMap.xml2
-rw-r--r--doc/classes/ItemList.xml18
-rw-r--r--doc/classes/JavaScript.xml2
-rw-r--r--doc/classes/KinematicBody.xml12
-rw-r--r--doc/classes/KinematicBody2D.xml22
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/Light.xml2
-rw-r--r--doc/classes/LineEdit.xml2
-rw-r--r--doc/classes/MeshDataTool.xml57
-rw-r--r--doc/classes/MeshInstance.xml6
-rw-r--r--doc/classes/NavigationPolygon.xml33
-rw-r--r--doc/classes/NetworkedMultiplayerPeer.xml4
-rw-r--r--doc/classes/Node.xml20
-rw-r--r--doc/classes/Node2D.xml2
-rw-r--r--doc/classes/OS.xml7
-rw-r--r--doc/classes/Object.xml10
-rw-r--r--doc/classes/OmniLight.xml2
-rw-r--r--doc/classes/OrientedPathFollow.xml2
-rw-r--r--doc/classes/PHashTranslation.xml1
-rw-r--r--doc/classes/PackedScene.xml19
-rw-r--r--doc/classes/Particles2D.xml6
-rw-r--r--doc/classes/ParticlesMaterial.xml5
-rw-r--r--doc/classes/PathFollow2D.xml1
-rw-r--r--doc/classes/Physics2DDirectSpaceState.xml22
-rw-r--r--doc/classes/Physics2DServer.xml36
-rw-r--r--doc/classes/PhysicsBody.xml9
-rw-r--r--doc/classes/PhysicsBody2D.xml9
-rw-r--r--doc/classes/PhysicsDirectSpaceState.xml2
-rw-r--r--doc/classes/PhysicsServer.xml24
-rw-r--r--doc/classes/Plane.xml2
-rw-r--r--doc/classes/ProjectSettings.xml31
-rw-r--r--doc/classes/Quat.xml4
-rw-r--r--doc/classes/RandomNumberGenerator.xml52
-rw-r--r--doc/classes/Rect2.xml2
-rw-r--r--doc/classes/ReflectionProbe.xml2
-rw-r--r--doc/classes/ResourceSaver.xml2
-rw-r--r--doc/classes/RichTextLabel.xml5
-rw-r--r--doc/classes/RigidBody.xml2
-rw-r--r--doc/classes/SceneTree.xml4
-rw-r--r--doc/classes/Script.xml2
-rw-r--r--doc/classes/Shader.xml2
-rw-r--r--doc/classes/Shape.xml2
-rw-r--r--doc/classes/Shape2D.xml2
-rw-r--r--doc/classes/SoftBody.xml7
-rw-r--r--doc/classes/Spatial.xml2
-rw-r--r--doc/classes/SpatialMaterial.xml8
-rw-r--r--doc/classes/SpotLight.xml2
-rw-r--r--doc/classes/StreamPeerSSL.xml2
-rw-r--r--doc/classes/StreamPeerTCP.xml3
-rw-r--r--doc/classes/String.xml1
-rw-r--r--doc/classes/StyleBoxFlat.xml33
-rw-r--r--doc/classes/SurfaceTool.xml4
-rw-r--r--doc/classes/TabContainer.xml6
-rw-r--r--doc/classes/Tabs.xml20
-rw-r--r--doc/classes/TileMap.xml2
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--doc/classes/Transform.xml4
-rw-r--r--doc/classes/Translation.xml12
-rw-r--r--doc/classes/TranslationServer.xml12
-rw-r--r--doc/classes/Tree.xml2
-rw-r--r--doc/classes/TreeItem.xml2
-rw-r--r--doc/classes/Tween.xml2
-rw-r--r--doc/classes/UndoRedo.xml3
-rw-r--r--doc/classes/Vector2.xml6
-rw-r--r--doc/classes/Vector3.xml8
-rw-r--r--doc/classes/Viewport.xml18
-rw-r--r--doc/classes/VisualServer.xml19
-rw-r--r--doc/classes/World.xml2
-rw-r--r--doc/classes/World2D.xml2
-rw-r--r--doc/classes/WorldEnvironment.xml2
-rw-r--r--doc/classes/YSort.xml1
-rw-r--r--doc/tools/doc_merge.py2
-rw-r--r--doc/tools/doc_status.py4
-rwxr-xr-xdoc/tools/makerst.py8
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp207
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h4
-rw-r--r--drivers/dummy/rasterizer_dummy.h18
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.cpp2
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp5
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.cpp37
-rw-r--r--drivers/gles2/rasterizer_scene_gles2.h4
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp137
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h66
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp17
-rw-r--r--drivers/gles2/shader_gles2.cpp14
-rw-r--r--drivers/gles2/shaders/canvas.glsl3
-rw-r--r--drivers/gles2/shaders/copy.glsl22
-rw-r--r--drivers/gles2/shaders/scene.glsl81
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp32
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp6
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp62
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h6
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp266
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h27
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp18
-rw-r--r--drivers/gles3/shader_gles3.cpp6
-rw-r--r--drivers/gles3/shaders/canvas.glsl14
-rw-r--r--drivers/gles3/shaders/scene.glsl91
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp24
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h1
-rw-r--r--drivers/rtaudio/audio_driver_rtaudio.cpp11
-rw-r--r--drivers/unix/net_socket_posix.cpp5
-rw-r--r--drivers/unix/os_unix.cpp6
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp5
-rw-r--r--drivers/windows/file_access_windows.cpp4
-rw-r--r--editor/animation_track_editor.cpp29
-rw-r--r--editor/array_property_edit.cpp11
-rw-r--r--editor/code_editor.cpp56
-rw-r--r--editor/code_editor.h9
-rw-r--r--editor/create_dialog.cpp2
-rw-r--r--editor/dependency_editor.cpp14
-rw-r--r--editor/dependency_editor.h10
-rw-r--r--editor/editor_about.cpp4
-rw-r--r--editor/editor_autoload_settings.cpp2
-rw-r--r--editor/editor_data.cpp18
-rw-r--r--editor/editor_export.cpp25
-rw-r--r--editor/editor_export.h8
-rw-r--r--editor/editor_file_dialog.cpp29
-rw-r--r--editor/editor_file_system.cpp39
-rw-r--r--editor/editor_file_system.h5
-rw-r--r--editor/editor_folding.cpp175
-rw-r--r--editor/editor_folding.h25
-rw-r--r--editor/editor_fonts.cpp16
-rw-r--r--editor/editor_help.cpp476
-rw-r--r--editor/editor_help.h89
-rw-r--r--editor/editor_help_search.cpp591
-rw-r--r--editor/editor_help_search.h156
-rw-r--r--editor/editor_inspector.cpp129
-rw-r--r--editor/editor_inspector.h11
-rw-r--r--editor/editor_node.cpp217
-rw-r--r--editor/editor_node.h12
-rw-r--r--editor/editor_plugin.cpp1
-rw-r--r--editor/editor_properties.cpp106
-rw-r--r--editor/editor_properties.h12
-rw-r--r--editor/editor_properties_array_dict.cpp54
-rw-r--r--editor/editor_properties_array_dict.h3
-rw-r--r--editor/editor_settings.cpp8
-rw-r--r--editor/fileserver/editor_file_server.cpp10
-rw-r--r--editor/filesystem_dock.cpp138
-rw-r--r--editor/filesystem_dock.h10
-rw-r--r--editor/find_in_files.cpp57
-rw-r--r--editor/icons/icon_GUI_viewport_hdiagsplitter.svg67
-rw-r--r--editor/icons/icon_GUI_viewport_vdiagsplitter.svg73
-rw-r--r--editor/icons/icon_GUI_viewport_vhsplitter.svg66
-rw-r--r--editor/icons/icon_script_extend.svg8
-rw-r--r--editor/import/editor_import_collada.cpp2
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp11
-rw-r--r--editor/import/resource_importer_texture.cpp8
-rw-r--r--editor/import_dock.cpp51
-rw-r--r--editor/import_dock.h1
-rw-r--r--editor/inspector_dock.cpp1
-rw-r--r--editor/multi_node_edit.cpp2
-rw-r--r--editor/plugin_config_dialog.cpp2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp40
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp15
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp6
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp3
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.cpp2
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.h2
-rw-r--r--editor/plugins/camera_editor_plugin.cpp12
-rw-r--r--editor/plugins/camera_editor_plugin.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp448
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h29
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp54
-rw-r--r--editor/plugins/gi_probe_editor_plugin.cpp2
-rw-r--r--editor/plugins/gi_probe_editor_plugin.h2
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp2
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.cpp30
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp22
-rw-r--r--editor/plugins/particles_2d_editor_plugin.h3
-rw-r--r--editor/plugins/particles_editor_plugin.cpp1
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp38
-rw-r--r--editor/plugins/script_editor_plugin.cpp60
-rw-r--r--editor/plugins/script_editor_plugin.h5
-rw-r--r--editor/plugins/script_text_editor.cpp43
-rw-r--r--editor/plugins/shader_editor_plugin.cpp3
-rw-r--r--editor/plugins/skeleton_editor_plugin.cpp1
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp193
-rw-r--r--editor/plugins/spatial_editor_plugin.h9
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp22
-rw-r--r--editor/plugins/text_editor.cpp4
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp3
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp296
-rw-r--r--editor/plugins/tile_map_editor_plugin.h20
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp84
-rw-r--r--editor/plugins/tile_set_editor_plugin.h8
-rw-r--r--editor/progress_dialog.cpp6
-rw-r--r--editor/project_export.cpp179
-rw-r--r--editor/project_export.h10
-rw-r--r--editor/project_manager.cpp2
-rw-r--r--editor/property_editor.cpp16
-rw-r--r--editor/property_selector.cpp2
-rw-r--r--editor/quick_open.cpp2
-rw-r--r--editor/scene_tree_dock.cpp71
-rw-r--r--editor/scene_tree_dock.h4
-rw-r--r--editor/scene_tree_editor.cpp14
-rw-r--r--editor/script_editor_debugger.cpp49
-rw-r--r--editor/spatial_editor_gizmos.cpp3
-rw-r--r--gles_builders.py8
-rw-r--r--main/input_default.cpp40
-rw-r--r--main/input_default.h3
-rw-r--r--main/main.cpp59
-rw-r--r--main/tests/test_oa_hash_map.cpp29
-rw-r--r--main/tests/test_string.cpp173
-rw-r--r--methods.py28
-rw-r--r--misc/dist/document_icon.svg12
-rw-r--r--misc/dist/linux/org.godotengine.Godot.appdata.xml4
-rwxr-xr-xmisc/dist/osx_tools.app/Contents/Info.plist83
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/Document.icnsbin0 -> 143464 bytes
-rw-r--r--modules/bullet/bullet_physics_server.h2
-rw-r--r--modules/bullet/collision_object_bullet.cpp9
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp40
-rw-r--r--modules/bullet/rigid_body_bullet.cpp26
-rw-r--r--modules/bullet/rigid_body_bullet.h15
-rw-r--r--modules/bullet/space_bullet.cpp10
-rw-r--r--modules/csg/csg_shape.cpp171
-rw-r--r--modules/csg/csg_shape.h18
-rw-r--r--modules/csg/doc_classes/CSGShape.xml3
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml2
-rw-r--r--modules/freetype/SCsub2
-rw-r--r--modules/gdnative/gdnative.cpp19
-rw-r--r--modules/gdnative/gdnative/array.cpp32
-rw-r--r--modules/gdnative/gdnative/basis.cpp9
-rw-r--r--modules/gdnative/gdnative/color.cpp41
-rw-r--r--modules/gdnative/gdnative/node_path.cpp9
-rw-r--r--modules/gdnative/gdnative/quat.cpp6
-rw-r--r--modules/gdnative/gdnative/rect2.cpp21
-rw-r--r--modules/gdnative/gdnative/string.cpp58
-rw-r--r--modules/gdnative/gdnative_api.json177
-rw-r--r--modules/gdnative/gdnative_builders.py24
-rw-r--r--modules/gdnative/include/gdnative/array.h8
-rw-r--r--modules/gdnative/include/gdnative/basis.h2
-rw-r--r--modules/gdnative/include/gdnative/color.h14
-rw-r--r--modules/gdnative/include/gdnative/node_path.h2
-rw-r--r--modules/gdnative/include/gdnative/quat.h2
-rw-r--r--modules/gdnative/include/gdnative/rect2.h6
-rw-r--r--modules/gdnative/include/gdnative/string.h6
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp11
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp18
-rw-r--r--modules/gdscript/gdscript.cpp12
-rw-r--r--modules/gdscript/gdscript_compiler.cpp20
-rw-r--r--modules/gdscript/gdscript_compiler.h10
-rw-r--r--modules/gdscript/gdscript_editor.cpp60
-rw-r--r--modules/gdscript/gdscript_function.cpp106
-rw-r--r--modules/gdscript/gdscript_function.h10
-rw-r--r--modules/gdscript/gdscript_functions.cpp51
-rw-r--r--modules/gdscript/gdscript_functions.h3
-rw-r--r--modules/gdscript/gdscript_parser.cpp107
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml2
-rw-r--r--modules/gridmap/grid_map.cpp2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp4
-rw-r--r--modules/jpg/image_loader_jpegd.cpp22
-rw-r--r--modules/mono/SCsub111
-rw-r--r--modules/mono/config.py6
-rw-r--r--modules/mono/csharp_script.cpp139
-rw-r--r--modules/mono/csharp_script.h8
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj6
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs16
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs41
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs58
-rw-r--r--modules/mono/editor/GodotSharpTools/StringExtensions.cs4
-rw-r--r--modules/mono/editor/GodotSharpTools/packages.config4
-rw-r--r--modules/mono/editor/bindings_generator.cpp369
-rw-r--r--modules/mono/editor/bindings_generator.h35
-rw-r--r--modules/mono/editor/csharp_project.cpp129
-rw-r--r--modules/mono/editor/csharp_project.h3
-rw-r--r--modules/mono/editor/dotnet_solution.cpp (renamed from modules/mono/editor/net_solution.cpp)63
-rw-r--r--modules/mono/editor/dotnet_solution.h (renamed from modules/mono/editor/net_solution.h)23
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp121
-rw-r--r--modules/mono/editor/godotsharp_builds.h4
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp51
-rw-r--r--modules/mono/editor/godotsharp_editor.h2
-rw-r--r--modules/mono/editor/godotsharp_export.cpp21
-rw-r--r--modules/mono/editor/godotsharp_export.h2
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp6
-rw-r--r--modules/mono/editor/script_class_parser.cpp542
-rw-r--r--modules/mono/editor/script_class_parser.h79
-rw-r--r--modules/mono/glue/Managed/Files/AABB.cs4
-rw-r--r--modules/mono/glue/Managed/Files/Basis.cs45
-rw-r--r--modules/mono/glue/Managed/Files/Color.cs4
-rw-r--r--modules/mono/glue/Managed/Files/GD.cs16
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs4
-rw-r--r--modules/mono/glue/Managed/Files/MathfEx.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Plane.cs6
-rw-r--r--modules/mono/glue/Managed/Files/Quat.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Rect2.cs4
-rw-r--r--modules/mono/glue/Managed/Files/StringExtensions.cs8
-rw-r--r--modules/mono/glue/Managed/Files/Transform.cs8
-rw-r--r--modules/mono/glue/Managed/Files/Transform2D.cs6
-rw-r--r--modules/mono/glue/Managed/Files/Vector2.cs4
-rw-r--r--modules/mono/glue/Managed/Files/Vector3.cs12
-rw-r--r--modules/mono/glue/Managed/Properties/AssemblyInfo.cs4
-rw-r--r--modules/mono/glue/gd_glue.cpp10
-rw-r--r--modules/mono/godotsharp_defs.h3
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp67
-rw-r--r--modules/mono/mono_gd/gd_mono.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp318
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h10
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp317
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h285
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp20
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h10
-rw-r--r--modules/mono/signal_awaiter_utils.cpp2
-rw-r--r--modules/mono/utils/macros.h9
-rw-r--r--modules/mono/utils/path_utils.cpp10
-rw-r--r--modules/mono/utils/string_utils.cpp28
-rw-r--r--modules/mono/utils/string_utils.h2
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml6
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp19
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.h4
-rw-r--r--modules/regex/regex.cpp8
-rw-r--r--modules/squish/image_compress_squish.cpp7
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml2
-rw-r--r--modules/visual_script/visual_script.cpp24
-rw-r--r--modules/visual_script/visual_script.h1
-rw-r--r--modules/visual_script/visual_script_editor.cpp23
-rw-r--r--modules/visual_script/visual_script_editor.h4
-rw-r--r--modules/visual_script/visual_script_nodes.cpp14
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp56
-rw-r--r--modules/websocket/emws_client.cpp14
-rw-r--r--modules/websocket/emws_client.h3
-rw-r--r--modules/websocket/emws_peer.cpp57
-rw-r--r--modules/websocket/emws_peer.h22
-rw-r--r--modules/websocket/emws_server.cpp7
-rw-r--r--modules/websocket/emws_server.h1
-rw-r--r--modules/websocket/lws_client.cpp37
-rw-r--r--modules/websocket/lws_client.h7
-rw-r--r--modules/websocket/lws_peer.cpp110
-rw-r--r--modules/websocket/lws_peer.h24
-rw-r--r--modules/websocket/lws_server.cpp11
-rw-r--r--modules/websocket/lws_server.h5
-rw-r--r--modules/websocket/packet_buffer.h122
-rw-r--r--modules/websocket/register_types.cpp17
-rw-r--r--modules/websocket/websocket_client.cpp2
-rw-r--r--modules/websocket/websocket_macros.h10
-rw-r--r--modules/websocket/websocket_multiplayer.cpp9
-rw-r--r--modules/websocket/websocket_multiplayer.h6
-rw-r--r--modules/websocket/websocket_peer.h1
-rw-r--r--platform/android/build.gradle.template2
-rw-r--r--platform/android/detect.py10
-rw-r--r--platform/android/export/export.cpp81
-rw-r--r--platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml4
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java2
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotView.java188
-rw-r--r--platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java2
-rw-r--r--platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java2
-rw-r--r--platform/android/java/src/org/godotengine/godot/utils/RequestParams.java2
-rw-r--r--platform/android/java_glue.cpp8
-rw-r--r--platform/android/os_android.cpp10
-rw-r--r--platform/android/power_android.cpp2
-rw-r--r--platform/haiku/os_haiku.cpp13
-rw-r--r--platform/iphone/detect.py11
-rw-r--r--platform/iphone/export/export.cpp6
-rw-r--r--platform/iphone/os_iphone.cpp10
-rw-r--r--platform/javascript/detect.py4
-rw-r--r--platform/javascript/engine.js3
-rw-r--r--platform/javascript/export/export.cpp8
-rw-r--r--platform/javascript/os_javascript.cpp18
-rw-r--r--platform/javascript/os_javascript.h4
-rw-r--r--platform/javascript/pre.js3
-rw-r--r--platform/osx/detect.py8
-rw-r--r--platform/osx/export/export.cpp16
-rw-r--r--platform/osx/os_osx.h4
-rw-r--r--platform/osx/os_osx.mm68
-rw-r--r--platform/server/SCsub15
-rw-r--r--platform/server/detect.py12
-rw-r--r--platform/server/os_server.cpp8
-rw-r--r--platform/server/os_server.h10
-rw-r--r--platform/server/platform_config.h5
-rw-r--r--platform/uwp/detect.py2
-rw-r--r--platform/uwp/export/export.cpp6
-rw-r--r--platform/uwp/os_uwp.cpp8
-rw-r--r--platform/windows/context_gl_win.cpp24
-rw-r--r--platform/windows/detect.py4
-rw-r--r--platform/windows/joypad.cpp4
-rw-r--r--platform/windows/os_windows.cpp118
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--platform/x11/detect.py14
-rw-r--r--platform/x11/os_x11.cpp12
-rw-r--r--scene/2d/animated_sprite.cpp20
-rw-r--r--scene/2d/area_2d.cpp24
-rw-r--r--scene/2d/audio_stream_player_2d.cpp15
-rw-r--r--scene/2d/camera_2d.cpp2
-rw-r--r--scene/2d/canvas_item.cpp132
-rw-r--r--scene/2d/canvas_item.h27
-rw-r--r--scene/2d/collision_object_2d.cpp16
-rw-r--r--scene/2d/collision_polygon_2d.cpp4
-rw-r--r--scene/2d/collision_shape_2d.cpp6
-rw-r--r--scene/2d/cpu_particles_2d.cpp77
-rw-r--r--scene/2d/cpu_particles_2d.h3
-rw-r--r--scene/2d/line_2d.cpp12
-rw-r--r--scene/2d/line_builder.cpp1
-rw-r--r--scene/2d/mesh_instance_2d.cpp4
-rw-r--r--scene/2d/node_2d.cpp18
-rw-r--r--scene/2d/node_2d.h2
-rw-r--r--scene/2d/particles_2d.cpp50
-rw-r--r--scene/2d/particles_2d.h8
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.cpp54
-rw-r--r--scene/2d/physics_body_2d.h7
-rw-r--r--scene/2d/skeleton_2d.cpp1
-rw-r--r--scene/2d/sprite.cpp24
-rw-r--r--scene/2d/tile_map.cpp11
-rw-r--r--scene/2d/visibility_notifier_2d.cpp2
-rw-r--r--scene/3d/area.cpp8
-rw-r--r--scene/3d/arvr_nodes.cpp4
-rw-r--r--scene/3d/arvr_nodes.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp12
-rw-r--r--scene/3d/cpu_particles.cpp41
-rw-r--r--scene/3d/cpu_particles.h2
-rw-r--r--scene/3d/light.cpp9
-rw-r--r--scene/3d/mesh_instance.cpp6
-rw-r--r--scene/3d/mesh_instance.h1
-rw-r--r--scene/3d/particles.cpp24
-rw-r--r--scene/3d/path.cpp39
-rw-r--r--scene/3d/path.h4
-rw-r--r--scene/3d/physics_body.cpp117
-rw-r--r--scene/3d/physics_body.h21
-rw-r--r--scene/3d/physics_joint.cpp51
-rw-r--r--scene/3d/physics_joint.h8
-rw-r--r--scene/3d/soft_body.cpp19
-rw-r--r--scene/3d/soft_body.h1
-rw-r--r--scene/3d/spatial.cpp6
-rw-r--r--scene/3d/sprite_3d.cpp38
-rw-r--r--scene/3d/visibility_notifier.cpp2
-rw-r--r--scene/3d/voxel_light_baker.cpp8
-rw-r--r--scene/animation/animation_blend_space_2d.cpp185
-rw-r--r--scene/animation/animation_blend_space_2d.h15
-rw-r--r--scene/animation/animation_blend_tree.cpp23
-rw-r--r--scene/animation/animation_blend_tree.h7
-rw-r--r--scene/animation/animation_node_state_machine.cpp1
-rw-r--r--scene/animation/animation_player.cpp3
-rw-r--r--scene/animation/skeleton_ik.cpp22
-rw-r--r--scene/animation/skeleton_ik.h6
-rw-r--r--scene/audio/audio_player.cpp1
-rw-r--r--scene/gui/base_button.cpp24
-rw-r--r--scene/gui/base_button.h4
-rw-r--r--scene/gui/button.cpp6
-rw-r--r--scene/gui/control.cpp52
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/gradient_edit.cpp4
-rw-r--r--scene/gui/graph_edit.cpp12
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/item_list.cpp66
-rw-r--r--scene/gui/item_list.h4
-rw-r--r--scene/gui/label.cpp17
-rw-r--r--scene/gui/line_edit.cpp22
-rw-r--r--scene/gui/link_button.cpp6
-rw-r--r--scene/gui/nine_patch_rect.cpp18
-rw-r--r--scene/gui/popup_menu.cpp25
-rw-r--r--scene/gui/rich_text_label.cpp5
-rw-r--r--scene/gui/scroll_container.cpp2
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/split_container.cpp50
-rw-r--r--scene/gui/split_container.h4
-rw-r--r--scene/gui/tabs.cpp83
-rw-r--r--scene/gui/tabs.h2
-rw-r--r--scene/gui/text_edit.cpp23
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/gui/texture_button.cpp18
-rw-r--r--scene/gui/texture_progress.cpp14
-rw-r--r--scene/gui/texture_rect.cpp6
-rw-r--r--scene/gui/tree.cpp11
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/gui/video_player.cpp57
-rw-r--r--scene/gui/viewport_container.cpp28
-rw-r--r--scene/gui/viewport_container.h1
-rw-r--r--scene/main/canvas_layer.cpp17
-rw-r--r--scene/main/node.cpp138
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/scene_tree.cpp12
-rw-r--r--scene/main/viewport.cpp246
-rw-r--r--scene/main/viewport.h31
-rw-r--r--scene/resources/animation.cpp2
-rw-r--r--scene/resources/convex_polygon_shape.cpp3
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp7
-rw-r--r--scene/resources/default_theme/default_theme.cpp1
-rw-r--r--scene/resources/default_theme/error_icon.pngbin111 -> 125 bytes
-rw-r--r--scene/resources/dynamic_font.cpp50
-rw-r--r--scene/resources/dynamic_font.h5
-rw-r--r--scene/resources/environment.cpp26
-rw-r--r--scene/resources/environment.h4
-rw-r--r--scene/resources/material.cpp51
-rw-r--r--scene/resources/material.h13
-rw-r--r--scene/resources/mesh.cpp3
-rw-r--r--scene/resources/mesh_data_tool.cpp2
-rw-r--r--scene/resources/packed_scene.cpp20
-rw-r--r--scene/resources/particles_material.cpp18
-rw-r--r--scene/resources/particles_material.h1
-rw-r--r--scene/resources/primitive_meshes.cpp49
-rw-r--r--scene/resources/scene_format_text.cpp8
-rw-r--r--scene/resources/shape.cpp2
-rw-r--r--scene/resources/sky_box.cpp6
-rw-r--r--scene/resources/style_box.cpp34
-rw-r--r--scene/resources/visual_shader.cpp2
-rw-r--r--servers/audio/audio_rb_resampler.cpp32
-rw-r--r--servers/audio/audio_stream.cpp16
-rw-r--r--servers/audio/audio_stream.h2
-rw-r--r--servers/audio/effects/audio_effect_filter.h13
-rw-r--r--servers/audio/effects/reverb.cpp24
-rw-r--r--servers/audio_server.cpp19
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/physics/area_pair_sw.cpp4
-rw-r--r--servers/physics/collision_solver_sat.cpp1
-rw-r--r--servers/physics/collision_solver_sw.cpp1
-rw-r--r--servers/physics/joints/generic_6dof_joint_sw.cpp48
-rw-r--r--servers/physics/physics_server_sw.cpp17
-rw-r--r--servers/physics/physics_server_sw.h4
-rw-r--r--servers/physics/space_sw.cpp29
-rw-r--r--servers/physics_2d/area_pair_2d_sw.cpp4
-rw-r--r--servers/physics_2d/body_2d_sw.cpp3
-rw-r--r--servers/physics_2d/broad_phase_2d_hash_grid.cpp4
-rw-r--r--servers/physics_2d/collision_object_2d_sw.cpp1
-rw-r--r--servers/physics_2d/collision_object_2d_sw.h4
-rw-r--r--servers/physics_2d/physics_2d_server_sw.cpp60
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h10
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h10
-rw-r--r--servers/physics_2d/space_2d_sw.cpp205
-rw-r--r--servers/physics_2d/space_2d_sw.h3
-rw-r--r--servers/physics_2d_server.cpp26
-rw-r--r--servers/physics_2d_server.h12
-rw-r--r--servers/physics_server.h10
-rw-r--r--servers/visual/rasterizer.h10
-rw-r--r--servers/visual/shader_language.cpp91
-rw-r--r--servers/visual/shader_language.h13
-rw-r--r--servers/visual/shader_types.cpp1
-rw-r--r--servers/visual/visual_server_canvas.cpp78
-rw-r--r--servers/visual/visual_server_canvas.h14
-rw-r--r--servers/visual/visual_server_raster.h6
-rw-r--r--servers/visual/visual_server_scene.cpp91
-rw-r--r--servers/visual/visual_server_scene.h13
-rw-r--r--servers/visual/visual_server_viewport.cpp10
-rw-r--r--servers/visual/visual_server_viewport.h19
-rw-r--r--servers/visual/visual_server_wrap_mt.h6
-rw-r--r--servers/visual_server.cpp13
-rw-r--r--servers/visual_server.h8
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/enet/godot.cpp64
-rw-r--r--thirdparty/libwebsockets/core/context.c25
-rw-r--r--thirdparty/libwebsockets/core/libwebsockets.c55
-rw-r--r--thirdparty/libwebsockets/core/output.c12
-rw-r--r--thirdparty/libwebsockets/core/private.h33
-rw-r--r--thirdparty/libwebsockets/libwebsockets.h44
-rw-r--r--thirdparty/libwebsockets/lws_config.h2
-rw-r--r--thirdparty/libwebsockets/lws_config_private.h2
-rw-r--r--thirdparty/libwebsockets/misc/lejp.c25
-rw-r--r--thirdparty/libwebsockets/plat/lws-plat-unix.c10
-rw-r--r--thirdparty/libwebsockets/plat/lws-plat-win.c34
-rw-r--r--thirdparty/libwebsockets/roles/h1/ops-h1.c14
-rw-r--r--thirdparty/libwebsockets/roles/http/client/client-handshake.c10
-rw-r--r--thirdparty/libwebsockets/roles/http/client/client.c38
-rw-r--r--thirdparty/libwebsockets/roles/http/header.c76
-rw-r--r--thirdparty/libwebsockets/roles/http/private.h1
-rw-r--r--thirdparty/libwebsockets/roles/http/server/lejp-conf.c64
-rw-r--r--thirdparty/libwebsockets/roles/http/server/parsers.c26
-rw-r--r--thirdparty/libwebsockets/roles/http/server/server.c50
-rw-r--r--thirdparty/libwebsockets/roles/ws/client-parser-ws.c11
-rw-r--r--thirdparty/libwebsockets/roles/ws/ops-ws.c26
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c2
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/ssl.c2
-rw-r--r--thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h4
-rw-r--r--thirdparty/libwebsockets/uwp_fixes.diff10
-rw-r--r--thirdparty/libwebsockets/win32helpers/getopt.c306
-rw-r--r--thirdparty/libwebsockets/win32helpers/getopt_long.c480
-rw-r--r--thirdparty/libwebsockets/win32helpers/gettimeofday.c70
689 files changed, 12942 insertions, 5914 deletions
diff --git a/.gitignore b/.gitignore
index 52937b8679..fe504482b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -238,6 +238,7 @@ ClientBin/
*.pfx
*.publishsettings
node_modules/
+__pycache__/
# KDE
.directory
@@ -310,5 +311,8 @@ platform/windows/godot_res.res
/.vs
/.vscode
+# Visual Studio Code workspace file
+*.code-workspace
+
# Scons progress indicator
.scons_node_count
diff --git a/.travis.yml b/.travis.yml
index a5217c1d9c..a8bc8289e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,7 +38,7 @@ matrix:
- mono
packages:
- &linux_deps [libasound2-dev, libfreetype6-dev, libgl1-mesa-dev, libglu1-mesa-dev, libx11-dev, libxcursor-dev, libxi-dev, libxinerama-dev, libxrandr-dev]
- - &linux_mono_deps [mono-devel, msbuild]
+ - &linux_mono_deps [mono-devel, msbuild, nuget]
coverity_scan:
project:
diff --git a/AUTHORS.md b/AUTHORS.md
index 13a67ce3f5..2f109aff6e 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -73,7 +73,7 @@ name is available.
J08nY
Jakub Grzesik (kubecz3k)
Jérôme GULLY (Nutriz)
- JFonS
+ Joan Fons Sanchez (JFonS)
Johan Manuel (29jm)
Joshua Grams (JoshuaGrams)
Juan Linietsky (reduz)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 533968886e..6a0310f904 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,7 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Setters and getters for node properties.
- Underscores in number literals are now allowed for improved readability (for example,`1_000_000`).
- Improved performance (+20% to +40%, based on various benchmarks).
-- [Feature tags](http://docs.godotengine.org/en/latest/learning/workflow/export/feature_tags.html) in the Project Settings, for custom per-platform settings.
+- [Feature tags](http://docs.godotengine.org/en/latest/getting_started/workflow/export/feature_tags.html) in the Project Settings, for custom per-platform settings.
- Full support for the [glTF 2.0](https://www.khronos.org/gltf/) 3D interchange format.
- Freelook and fly navigation to the 3D editor.
- Built-in editor logging (logging standard output to a file), disabled by default.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 68ec20a525..04e42d14b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -59,7 +59,8 @@ by drag and dropping the file in the GitHub edition field.
If you want to add new engine functionalities, please make sure that:
-* This functionality is desired.
+* This functionality is desired, which means that it solves a common use case
+ that several users will need in their real-life projects.
* You talked to other developers on how to implement it best (on either
communication channel, and maybe in a GitHub issue first before making your
PR).
@@ -70,11 +71,23 @@ Similar rules can be applied when contributing bug fixes - it's always best to
discuss the implementation in the bug report first if you are not 100% about
what would be the best fix.
+[This blog post](https://godotengine.org/article/will-your-contribution-be-merged-heres-how-tell)
+outlines the process used by core developers when assessing PRs. We strongly
+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/)
+for an introduction to developing on Godot.
+
+The [Contributing docs](http://docs.godotengine.org/en/latest/community/contributing/index.html)
+also have important information on the PR workflow and the code style we use.
+
#### Be nice to the git history
-Try to make simple PRs with that handle one specific topic. Just like for
-reporting issues, it's better to open 3 different PRs that each address a
-different issue than one big PR with three commits.
+Try to make simple PRs that handle one specific topic. Just like for reporting
+issues, it's better to open 3 different PRs that each address a different issue
+than one big PR with three commits.
When updating your fork with upstream changes, please use ``git pull --rebase``
to avoid creating "merge commits". Those commits unnecessarily pollute the git
@@ -89,6 +102,9 @@ Internet).
This git style guide has some good practices to have in mind:
[Git Style Guide](https://github.com/agis-/git-style-guide)
+See our [PR workflow](http://docs.godotengine.org/en/latest/community/contributing/pr_workflow.html)
+documentation for tips on using Git, amending commits and rebasing branches.
+
#### Format your commit logs with readability in mind
The way you format your commit logs is quite important to ensure that the
@@ -134,6 +150,10 @@ Weblate](https://hosted.weblate.org/projects/godot-engine/godot), an open
source and web-based translation platform. Please refer to the [translation
readme](editor/translations/README.md) for more information.
+You can also help translate [Godot's
+documentation](https://hosted.weblate.org/projects/godot-engine/godot-docs/)
+on Weblate.
+
## Communicating with developers
The Godot Engine community has [many communication
diff --git a/DONORS.md b/DONORS.md
index 6fdb02e3fa..2c9bb1eb73 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -13,30 +13,29 @@ generous deed immortalized in the next stable release of Godot Engine.
## Platinum sponsors
Enjin Coin <https://enjincoin.io>
- GameDev.TV <https://gdev.tv/godot>
## Gold sponsors
Gamblify <https://www.gamblify.com>
+ GameDev.TV <https://gdev.tv/godot>
+ Image Campus <https://www.imagecampus.edu.ar>
## Mini sponsors
Andrew Dunai
Brandon Lamb
Christian Uldall Pedersen
- Christopher Igoe
Christoph Woinke
+ David Graham
GameDev.net
Hein-Pieter van Braam
iDev.Network Studios
Jamal Alyafei
- Jay Sistar
Leona Eden
Matthieu Huvé
Mike King
Nathan Warden
Neal Gompa (Conan Kudo)
- Pascal Julien
Patrick Aarstad
Ruslan Mustakov
Slobodan Milnovic
@@ -67,7 +66,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Gary Oberbrunner
Jay Horton
Johannes Wuensch
- Josep G. Camarasa
Joshua Lesperance
Krzysztof Dluzniewski
Kyle Szklenski
@@ -77,7 +75,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Ranoller
Sergey
Svenne Krap
- Timothy Hagberg
+ Xananax
BanjoNode2D
Beliar
@@ -92,6 +90,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Justo Delgado Baudí
KTL
Laurence Bannister
+ Markus Wiesner
paul gruenbacher
Rami
Robert Willes
@@ -106,7 +105,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Alessandra Pereyra
Alexey Dyadchenko
Amanda Haldy
- Branwen Danielle Zakariasen
Chau Siu Hung
Chris Brown
Chris Petrich
@@ -130,9 +128,8 @@ generous deed immortalized in the next stable release of Godot Engine.
joe513
Juraj Móza
Justin Arnold
- Lars Wuethrich
Leandro Voltolino
- Markus Wiesner
+ Marius Kamm
Marvin
Nahuel Sacchetti
Nick Nikitin
@@ -140,8 +137,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Pete Goodwin
ray-tracer
Ruben Soares Luis
- Rufus Xavier Sarsaparilla
- Sindre Sømme
Sofox
Stoned Xander
Trent McPheron
@@ -167,7 +162,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Bailey
Bastian Böhm
Benedikt
- Benjamin Beshara
Ben Vercammen
Blair Allen
Brandon
@@ -178,7 +172,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Chris Chapin
Christian Baune
Christian Winter
- Christopher Schmitt
Collin Shooltz
Daniel Johnson
Daniel Kaplan
@@ -192,11 +185,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Edward Herbert
Elias Nykrem
Eric Martini
+ Eric Williams
Eugenio Hugo Salgüero Jáñez
Fabian Becker
fengjiongmax
Foomf
G3Dev sàrl
+ George Lesica
Gerrit Großkopf
Gilberto K. Otubo
Greg Olson
@@ -206,6 +201,7 @@ generous deed immortalized in the next stable release of Godot Engine.
ialex32x
Jahn Johansen
Jaime Ruiz-Borau Vizárraga
+ Jako Danar
Jeff Hungerford
Joel Fivat
Johan Lindberg
@@ -213,9 +209,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Jonas Yamazaki
Jonathan Martin
Jonathan Nieto
- Jonathon
Jon Bonazza
Jon Sully
+ Jose Malheiro
Josh 'Cheeseness' Bush
Juanfran
Juan Negrier
@@ -227,14 +223,16 @@ generous deed immortalized in the next stable release of Godot Engine.
Kevin Kamper Meejach Petersen
Klavdij Voncina
Krzysztof Jankowski
+ Krzysztof Sierszecki
+ KsyTek Games
Linus Lind Lundgren
Loreshaper Games
- Luc Magitem
Luis Moraes
Macil
magodev
- Manolis Makris
+ Malcolm
Martin Eigel
+ Matt Welke
Max R.R. Collada
Maxwell
Mertcan Mermerkaya
@@ -245,17 +243,18 @@ generous deed immortalized in the next stable release of Godot Engine.
Mikael Olsson
MoM
Moritz Laass
+ Moritz Weissenberger
Natrim
nee
Neil Blakey-Milner
Nick Pavlica
Niclas Eriksen
+ Nicolás Montaña
Nicolas SAN AGUSTIN
- Niko Leopold
+ Nik
Noi Sek
Pan Ip
Pascal Grüter
- Pat LaBine
Patrick Nafarrete
Paul E Hansen
Paul Gieske
@@ -273,10 +272,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Roman Tinkov
Sasori Olkof
Sootstone
- Stefan Butucea
- The K-B
Theo Cranmore
- Thibaud Galloy
Thibault Barbaroux
Thomas Bell
Thomas Holmes
@@ -284,18 +280,15 @@ generous deed immortalized in the next stable release of Godot Engine.
tiansheng li
Tim
Tom Larrow
- Tristan Crawford
- Tryggve Sollid
Trym Nilsen
+ Tyggve Sollid
Tyler Stafos
UltyX
Vaiktorg
Victor
Viktor Ferenczi
waka nya
- werner mendizabal
Wout Standaert
- Yu He
## Bronze donors
diff --git a/README.md b/README.md
index 8ad738c23d..06380b5e0a 100644
--- a/README.md
+++ b/README.md
@@ -37,10 +37,10 @@ Official binaries for the Godot editor and the export templates can be found
#### Compiling from source
-[See the official docs](http://docs.godotengine.org/en/latest/development/compiling/)
+[See the official docs](https://docs.godotengine.org/en/latest/development/compiling/)
for compilation instructions for every supported platform.
-### Community
+### Community and contributing
Godot is not only an engine but an ever-growing community of users and engine
developers. The main community channels are listed [on the homepage](https://godotengine.org/community).
@@ -49,12 +49,14 @@ To get in touch with the developers, the best way is to join the
[#godotengine IRC channel](https://webchat.freenode.net/?channels=godotengine)
on Freenode.
+To get started contributing to the project, see the [contributing guide](CONTRIBUTING.md).
+
### Documentation and demos
-The official documentation is hosted on [ReadTheDocs](http://docs.godotengine.org).
+The official documentation is hosted on [ReadTheDocs](https://docs.godotengine.org).
It is maintained by the Godot community in its own [GitHub repository](https://github.com/godotengine/godot-docs).
-The [class reference](http://docs.godotengine.org/en/latest/classes/)
+The [class reference](https://docs.godotengine.org/en/latest/classes/)
is also accessible from within the engine.
The official demos are maintained in their own [GitHub repository](https://github.com/godotengine/godot-demo-projects)
diff --git a/SConstruct b/SConstruct
index f8c1c82961..8b7b95a600 100644
--- a/SConstruct
+++ b/SConstruct
@@ -349,7 +349,10 @@ if selected_platform in platform_list:
else: # always enable those errors
env.Append(CCFLAGS=['-Werror=return-type'])
- suffix = "." + selected_platform
+ if (hasattr(detect, 'get_program_suffix')):
+ suffix = "." + detect.get_program_suffix()
+ else:
+ suffix = "." + selected_platform
if (env["target"] == "release"):
if env["tools"]:
@@ -422,8 +425,13 @@ if selected_platform in platform_list:
# (SH)LIBSUFFIX will be used for our own built libraries
# LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default,
# so we need to append the default suffixes to keep the ability
- # to link against thirdparty libraries (.a, .so, .dll, etc.).
- env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]]
+ # to link against thirdparty libraries (.a, .so, .lib, etc.).
+ if os.name == "nt":
+ # On Windows, only static libraries and import libraries can be
+ # statically linked - both using .lib extension
+ env["LIBSUFFIXES"] += [env["LIBSUFFIX"]]
+ else:
+ env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]]
env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
@@ -544,7 +552,7 @@ if 'env' in locals():
[os.remove(f) for f in files]
def file_list(self):
- if self.path == None:
+ if self.path is None:
# Nothing to do
return []
# Gather a list of (filename, (size, atime)) within the
@@ -569,7 +577,7 @@ if 'env' in locals():
if sum > self.limit:
mark = i
break
- if mark == None:
+ if mark is None:
return []
else:
return [x[0] for x in file_stat[mark:]]
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 02b8c71465..0032c43179 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -180,6 +180,7 @@ void _ResourceSaver::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_OMIT_EDITOR_PROPERTIES);
BIND_ENUM_CONSTANT(FLAG_SAVE_BIG_ENDIAN);
BIND_ENUM_CONSTANT(FLAG_COMPRESS);
+ BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS);
}
_ResourceSaver::_ResourceSaver() {
@@ -1019,6 +1020,11 @@ void _OS::center_window() {
OS::get_singleton()->center_window();
}
+void _OS::move_window_to_foreground() {
+
+ OS::get_singleton()->move_window_to_foreground();
+}
+
bool _OS::is_debug_build() const {
#ifdef DEBUG_ENABLED
@@ -1120,6 +1126,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("request_attention"), &_OS::request_attention);
ClassDB::bind_method(D_METHOD("get_real_window_size"), &_OS::get_real_window_size);
ClassDB::bind_method(D_METHOD("center_window"), &_OS::center_window);
+ ClassDB::bind_method(D_METHOD("move_window_to_foreground"), &_OS::move_window_to_foreground);
ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window);
ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window);
@@ -1747,9 +1754,9 @@ String _File::get_line() const {
return f->get_line();
}
-Vector<String> _File::get_csv_line(String delim) const {
+Vector<String> _File::get_csv_line(const String &p_delim) const {
ERR_FAIL_COND_V(!f, Vector<String>());
- return f->get_csv_line(delim);
+ return f->get_csv_line(p_delim);
}
/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
@@ -1846,6 +1853,11 @@ void _File::store_line(const String &p_string) {
f->store_line(p_string);
}
+void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
+ ERR_FAIL_COND(!f);
+ f->store_csv_line(p_values, p_delim);
+}
+
void _File::store_buffer(const PoolVector<uint8_t> &p_buffer) {
ERR_FAIL_COND(!f);
@@ -1929,6 +1941,7 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_real"), &_File::get_real);
ClassDB::bind_method(D_METHOD("get_buffer", "len"), &_File::get_buffer);
ClassDB::bind_method(D_METHOD("get_line"), &_File::get_line);
+ ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("get_as_text"), &_File::get_as_text);
ClassDB::bind_method(D_METHOD("get_md5", "path"), &_File::get_md5);
ClassDB::bind_method(D_METHOD("get_sha256", "path"), &_File::get_sha256);
@@ -1936,7 +1949,6 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_endian_swap", "enable"), &_File::set_endian_swap);
ClassDB::bind_method(D_METHOD("get_error"), &_File::get_error);
ClassDB::bind_method(D_METHOD("get_var"), &_File::get_var);
- ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("store_8", "value"), &_File::store_8);
ClassDB::bind_method(D_METHOD("store_16", "value"), &_File::store_16);
@@ -1947,6 +1959,7 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("store_real", "value"), &_File::store_real);
ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &_File::store_buffer);
ClassDB::bind_method(D_METHOD("store_line", "line"), &_File::store_line);
+ ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &_File::store_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("store_string", "string"), &_File::store_string);
ClassDB::bind_method(D_METHOD("store_var", "value"), &_File::store_var);
@@ -2859,10 +2872,10 @@ void JSONParseResult::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_error_line", "error_line"), &JSONParseResult::set_error_line);
ClassDB::bind_method(D_METHOD("set_result", "result"), &JSONParseResult::set_result);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line");
- ADD_PROPERTYNZ(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line");
+ ADD_PROPERTY(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result");
}
void JSONParseResult::set_error(Error p_error) {
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 3a913e01ed..720b14bf56 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -80,6 +80,7 @@ public:
FLAG_OMIT_EDITOR_PROPERTIES = 8,
FLAG_SAVE_BIG_ENDIAN = 16,
FLAG_COMPRESS = 32,
+ FLAG_REPLACE_SUBRESOURCE_PATHS = 64,
};
static _ResourceSaver *get_singleton() { return singleton; }
@@ -184,6 +185,7 @@ public:
virtual bool is_window_always_on_top() const;
virtual void request_attention();
virtual void center_window();
+ virtual void move_window_to_foreground();
virtual void set_borderless_window(bool p_borderless);
virtual bool get_borderless_window() const;
@@ -453,6 +455,7 @@ public:
PoolVector<uint8_t> get_buffer(int p_length) const; ///< get an array of bytes
String get_line() const;
+ Vector<String> get_csv_line(const String &p_delim = ",") const;
String get_as_text() const;
String get_md5(const String &p_path) const;
String get_sha256(const String &p_path) const;
@@ -478,12 +481,11 @@ public:
void store_string(const String &p_string);
void store_line(const String &p_string);
+ void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
virtual void store_pascal_string(const String &p_string);
virtual String get_pascal_string();
- Vector<String> get_csv_line(String delim = ",") const;
-
void store_buffer(const PoolVector<uint8_t> &p_buffer); ///< store an array of bytes
void store_var(const Variant &p_var);
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 71809d5454..052a4586fe 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -936,9 +936,8 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
}
#ifdef DEBUG_METHODS_ENABLED
-
if (type->property_setget.has(p_pinfo.name)) {
- ERR_EXPLAIN("Object already has property: " + p_class);
+ ERR_EXPLAIN("Object " + p_class + " already has property: " + p_pinfo.name);
ERR_FAIL();
}
#endif
@@ -1367,6 +1366,41 @@ void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p
}
}
+HashMap<StringName, HashMap<StringName, Variant> > ClassDB::default_values;
+
+Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property) {
+
+ if (!default_values.has(p_class)) {
+
+ default_values[p_class] = HashMap<StringName, Variant>();
+
+ if (ClassDB::can_instance(p_class)) {
+
+ Object *c = ClassDB::instance(p_class);
+ List<PropertyInfo> plist;
+ c->get_property_list(&plist);
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) {
+
+ Variant v = c->get(E->get().name);
+ default_values[p_class][E->get().name] = v;
+ }
+ }
+ memdelete(c);
+ }
+ }
+
+ if (!default_values.has(p_class)) {
+ return Variant();
+ }
+
+ if (!default_values[p_class].has(p_property)) {
+ return Variant();
+ }
+
+ return default_values[p_class][p_property];
+}
+
RWLock *ClassDB::lock = NULL;
void ClassDB::init() {
@@ -1393,6 +1427,7 @@ void ClassDB::cleanup() {
classes.clear();
resource_base_extensions.clear();
compat_classes.clear();
+ default_values.clear();
memdelete(lock);
}
diff --git a/core/class_db.h b/core/class_db.h
index 11cc3033cf..75f9e8d6a7 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -161,6 +161,8 @@ public:
static void _add_class2(const StringName &p_class, const StringName &p_inherits);
+ static HashMap<StringName, HashMap<StringName, Variant> > default_values;
+
public:
// DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!!
template <class T>
@@ -352,6 +354,8 @@ public:
static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
+ static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property);
+
static StringName get_category(const StringName &p_node);
static void set_class_enabled(StringName p_class, bool p_enable);
diff --git a/core/color.cpp b/core/color.cpp
index 55dd1ec6b9..ac314417ec 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -468,7 +468,7 @@ String Color::to_html(bool p_alpha) const {
return txt;
}
-Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) {
+Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
p_h = Math::fmod(p_h * 360.0f, 360.0f);
if (p_h < 0.0)
diff --git a/core/color.h b/core/color.h
index add61343ff..d6ad5f91c5 100644
--- a/core/color.h
+++ b/core/color.h
@@ -194,7 +194,7 @@ struct Color {
static bool html_is_valid(const String &p_color);
static Color named(const String &p_name);
String to_html(bool p_alpha = true) const;
- Color from_hsv(float p_h, float p_s, float p_v, float p_a);
+ Color from_hsv(float p_h, float p_s, float p_v, float p_a) const;
static Color from_rgbe9995(uint32_t p_color);
_FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index ccbdff3816..6a3ab82879 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -112,6 +112,15 @@ Variant Dictionary::get_valid(const Variant &p_key) const {
return E.get();
}
+Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const {
+ const Variant *result = getptr(p_key);
+ if (!result) {
+ return p_default;
+ }
+
+ return *result;
+}
+
int Dictionary::size() const {
return _p->variant_map.size();
diff --git a/core/dictionary.h b/core/dictionary.h
index d3b98c2f63..b77cc55254 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -58,6 +58,7 @@ public:
Variant *getptr(const Variant &p_key);
Variant get_valid(const Variant &p_key) const;
+ Variant get(const Variant &p_key, const Variant &p_default) const;
int size() const;
bool empty() const;
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index c70f960a66..7e9b8b393c 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -547,8 +547,9 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_GROUP);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY);
- BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO);
- BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE);
+ //deprecated, replaced by ClassDB function to check default value
+ //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO);
+ //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE);
diff --git a/core/hash_map.h b/core/hash_map.h
index 8620edba73..3869cd3c36 100644
--- a/core/hash_map.h
+++ b/core/hash_map.h
@@ -150,7 +150,7 @@ private:
if (new_hash_table_power == -1)
return;
- Element **new_hash_table = memnew_arr(Element *, (1 << new_hash_table_power));
+ Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
if (!new_hash_table) {
ERR_PRINT("Out of Memory");
@@ -230,7 +230,7 @@ private:
if (!p_t.hash_table || p_t.hash_table_power == 0)
return; /* not copying from empty table */
- hash_table = memnew_arr(Element *, 1 << p_t.hash_table_power);
+ hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power);
hash_table_power = p_t.hash_table_power;
elements = p_t.elements;
diff --git a/core/image.cpp b/core/image.cpp
index 172f5e517a..698a0b0b98 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -1766,6 +1766,15 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format
return mm;
}
+int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) {
+
+ if (p_mipmap <= 0) {
+ return 0;
+ }
+ int mm;
+ return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1);
+}
+
bool Image::is_compressed() const {
return format > FORMAT_RGBE9995;
}
diff --git a/core/image.h b/core/image.h
index 11f9380c3c..0770eb953e 100644
--- a/core/image.h
+++ b/core/image.h
@@ -286,6 +286,7 @@ public:
static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false);
static int get_image_required_mipmaps(int p_width, int p_height, Format p_format);
+ static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
enum CompressMode {
COMPRESS_S3TC,
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 6b6856dcc8..b9544ac166 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -500,8 +500,9 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) {
void FileAccessNetwork::configure() {
GLOBAL_DEF("network/remote_fs/page_size", 65536);
+ ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero
GLOBAL_DEF("network/remote_fs/page_read_ahead", 4);
- GLOBAL_DEF("network/remote_fs/max_pages", 20);
+ ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_read_ahead", PropertyInfo(Variant::INT, "network/remote_fs/page_read_ahead", PROPERTY_HINT_RANGE, "0,8,1,or_greater"));
}
FileAccessNetwork::FileAccessNetwork() {
@@ -519,7 +520,6 @@ FileAccessNetwork::FileAccessNetwork() {
nc->unlock_mutex();
page_size = GLOBAL_GET("network/remote_fs/page_size");
read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead");
- max_pages = GLOBAL_GET("network/remote_fs/max_pages");
last_activity_val = 0;
waiting_on_page = -1;
last_page = -1;
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index e32dcea990..c929e8446d 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -98,7 +98,6 @@ class FileAccessNetwork : public FileAccess {
int page_size;
int read_ahead;
- int max_pages;
mutable int waiting_on_page;
mutable int last_activity_val;
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 40f756ba9a..3823285792 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -455,7 +455,7 @@ String DirAccessPack::get_current_dir() {
while (pd->parent) {
pd = pd->parent;
- p = pd->name + "/" + p;
+ p = pd->name.plus_file(p);
}
return "res://" + p;
diff --git a/core/io/image_loader.h b/core/io/image_loader.h
index 561f275e0c..d95a483c0d 100644
--- a/core/io/image_loader.h
+++ b/core/io/image_loader.h
@@ -71,7 +71,7 @@ public:
class ImageLoader {
enum {
- MAX_LOADERS = 8
+ MAX_LOADERS = 32
};
friend class ResourceFormatLoaderImage;
static ImageFormatLoader *loader[MAX_LOADERS];
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 6d979d10eb..194d1af6bf 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -184,7 +184,7 @@ bool IP_Address::is_ipv4() const {
}
const uint8_t *IP_Address::get_ipv4() const {
- ERR_FAIL_COND_V(!is_ipv4(), 0);
+ ERR_FAIL_COND_V(!is_ipv4(), &(field8[12])); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash.
return &(field8[12]);
}
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index 051c02ab32..01755c8ee9 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -45,6 +45,10 @@
#endif
#endif
+#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#define sprintf sprintf_s
+#endif
+
bool Logger::should_log(bool p_err) {
return (!p_err || _print_error_enabled) && (p_err || _print_line_enabled);
}
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index e5741014a4..6f3a8c3d2e 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1309,7 +1309,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
case Variant::INT: {
int64_t val = p_property;
- if (val > 0x7FFFFFFF || val < -0x80000000) {
+ if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) {
f->store_32(VARIANT_INT64);
f->store_64(val);
@@ -1813,8 +1813,13 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
Property p;
p.name_idx = get_string_index(F->get().name);
p.value = E->get()->get(F->get().name);
- if (((F->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && p.value.is_zero()) || ((F->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && p.value.is_one()))
+
+ Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name);
+
+ if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) {
continue;
+ }
+
p.pi = F->get();
rd.properties.push_back(p);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index d156a9f4bd..71b01aa94a 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -259,6 +259,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
}
#endif
+ if (_loaded_callback) {
+ _loaded_callback(res, p_path);
+ }
+
return res;
}
@@ -635,6 +639,12 @@ void ResourceLoader::clear_path_remaps() {
path_remaps.clear();
}
+void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) {
+ _loaded_callback = p_callback;
+}
+
+ResourceLoadedCallback ResourceLoader::_loaded_callback = NULL;
+
ResourceLoadErrorNotify ResourceLoader::err_notify = NULL;
void *ResourceLoader::err_notify_ud = NULL;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 96bc6fa8dd..7ade4a2dfc 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -78,6 +78,7 @@ typedef void (*ResourceLoadErrorNotify)(void *p_ud, const String &p_text);
typedef void (*DependencyErrorNotify)(void *p_ud, const String &p_loading, const String &p_which, const String &p_type);
typedef Error (*ResourceLoaderImport)(const String &p_path);
+typedef void (*ResourceLoadedCallback)(RES p_resource, const String &p_path);
class ResourceLoader {
@@ -106,6 +107,8 @@ class ResourceLoader {
//internal load function
static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error);
+ static ResourceLoadedCallback _loaded_callback;
+
public:
static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL);
static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL);
@@ -150,6 +153,7 @@ public:
static void load_translation_remaps();
static void clear_translation_remaps();
+ static void set_load_callback(ResourceLoadedCallback p_callback);
static ResourceLoaderImport import;
};
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 5c8188f735..097e81e308 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -90,7 +90,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t
rwcopy->set_path(old_path);
if (save_callback && p_path.begins_with("res://"))
- save_callback(p_path);
+ save_callback(p_resource, p_path);
return OK;
} else {
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 7ed580f2d6..6134d9db57 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -46,7 +46,7 @@ public:
virtual ~ResourceFormatSaver() {}
};
-typedef void (*ResourceSavedCallback)(const String &p_path);
+typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path);
class ResourceSaver {
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index a16267cf0a..0cfb54234c 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -2120,6 +2120,10 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu
}
Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
+ if (error_set) {
+ ERR_EXPLAIN("There was previously a parse error: " + error_str);
+ ERR_FAIL_V(Variant());
+ }
execution_error = false;
Variant output;
diff --git a/core/math/geometry.h b/core/math/geometry.h
index a813a90774..df63f0dabe 100644
--- a/core/math/geometry.h
+++ b/core/math/geometry.h
@@ -800,6 +800,21 @@ public:
return Vector<Vector<Vector2> >();
}
+ static bool is_polygon_clockwise(const Vector<Vector2> &p_polygon) {
+ int c = p_polygon.size();
+ if (c < 3)
+ return false;
+ const Vector2 *p = p_polygon.ptr();
+ real_t sum = 0;
+ for (int i = 0; i < c; i++) {
+ const Vector2 &v1 = p[i];
+ const Vector2 &v2 = p[(i + 1) % c];
+ sum += (v2.x - v1.x) * (v2.y + v1.y);
+ }
+
+ return sum > 0.0f;
+ }
+
static PoolVector<PoolVector<Face3> > separate_objects(PoolVector<Face3> p_array);
static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL); ///< create a "wrap" that encloses the given geometry
diff --git a/core/math/math_defs.h b/core/math/math_defs.h
index a5feee6eb5..db9055cee2 100644
--- a/core/math/math_defs.h
+++ b/core/math/math_defs.h
@@ -93,9 +93,9 @@ enum Corner {
};
/**
- * The "Real" type is an abstract type used for real numbers, such as 1.5,
+ * The "Real" type is an abstract type used for real numbers, such as 1.5,
* in contrast to integer numbers. Precision can be controlled with the
- * presence or absence of the REAL_T_IS_DOUBLE define.
+ * presence or absence of the REAL_T_IS_DOUBLE define.
*/
#ifdef REAL_T_IS_DOUBLE
typedef double real_t;
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index 0c06d2a2b5..06355d15ed 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -30,30 +30,27 @@
#include "math_funcs.h"
-#include "core/os/os.h"
-
-pcg32_random_t Math::default_pcg = { 12047754176567800795ULL, PCG_DEFAULT_INC_64 };
+RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC);
#define PHI 0x9e3779b9
-// TODO: we should eventually expose pcg.inc too
uint32_t Math::rand_from_seed(uint64_t *seed) {
- pcg32_random_t pcg = { *seed, PCG_DEFAULT_INC_64 };
- uint32_t r = pcg32_random_r(&pcg);
- *seed = pcg.state;
+ RandomPCG rng = RandomPCG(*seed, RandomPCG::DEFAULT_INC);
+ uint32_t r = rng.rand();
+ *seed = rng.get_seed();
return r;
}
void Math::seed(uint64_t x) {
- default_pcg.state = x;
+ default_rand.seed(x);
}
void Math::randomize() {
- seed(OS::get_singleton()->get_ticks_usec() * default_pcg.state + PCG_DEFAULT_INC_64);
+ default_rand.randomize();
}
uint32_t Math::rand() {
- return pcg32_random_r(&default_pcg);
+ return default_rand.rand();
}
int Math::step_decimals(double p_step) {
@@ -169,13 +166,9 @@ uint32_t Math::larger_prime(uint32_t p_val) {
}
double Math::random(double from, double to) {
- unsigned int r = Math::rand();
- double ret = (double)r / (double)RANDOM_MAX;
- return (ret) * (to - from) + from;
+ return default_rand.random(from, to);
}
float Math::random(float from, float to) {
- unsigned int r = Math::rand();
- float ret = (float)r / (float)RANDOM_MAX;
- return (ret) * (to - from) + from;
+ return default_rand.random(from, to);
}
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 9a486a49d0..f9d89d5d5a 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -32,6 +32,7 @@
#define MATH_FUNCS_H
#include "core/math/math_defs.h"
+#include "core/math/random_pcg.h"
#include "core/typedefs.h"
#include "thirdparty/misc/pcg.h"
@@ -41,7 +42,7 @@
class Math {
- static pcg32_random_t default_pcg;
+ static RandomPCG default_rand;
public:
Math() {} // useless to instance
@@ -305,16 +306,6 @@ public:
return b;
}
-#if defined(__GNUC__)
-
- static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE
- static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename
-#else
-
- static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE
- static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename
-#endif
-
static _ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t h) {
uint16_t h_exp, h_sig;
uint32_t f_sgn, f_exp, f_sig;
diff --git a/core/math/random_number_generator.cpp b/core/math/random_number_generator.cpp
new file mode 100644
index 0000000000..e4ec0dac99
--- /dev/null
+++ b/core/math/random_number_generator.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* random_number_generator.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "random_number_generator.h"
+
+RandomNumberGenerator::RandomNumberGenerator() :
+ randbase() {}
+
+void RandomNumberGenerator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed);
+ ClassDB::bind_method(D_METHOD("get_seed"), &RandomNumberGenerator::get_seed);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");
+
+ ClassDB::bind_method(D_METHOD("randi"), &RandomNumberGenerator::randi);
+ ClassDB::bind_method(D_METHOD("randf"), &RandomNumberGenerator::randf);
+ ClassDB::bind_method(D_METHOD("rand_range", "from", "to"), &RandomNumberGenerator::rand_range);
+ ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize);
+}
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
new file mode 100644
index 0000000000..557863fdbd
--- /dev/null
+++ b/core/math/random_number_generator.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* random_number_generator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 RANDOM_NUMBER_GENERATOR_H
+#define RANDOM_NUMBER_GENERATOR_H
+
+#include "core/math/random_pcg.h"
+#include "core/reference.h"
+
+class RandomNumberGenerator : public Reference {
+ GDCLASS(RandomNumberGenerator, Reference);
+
+ RandomPCG randbase;
+
+protected:
+ static void _bind_methods();
+
+public:
+ _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); }
+
+ _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); }
+
+ _FORCE_INLINE_ void randomize() { return randbase.randomize(); }
+
+ _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); }
+
+ _FORCE_INLINE_ real_t randf() { return randbase.randf(); }
+
+ _FORCE_INLINE_ real_t rand_range(real_t from, real_t to) { return randbase.random(from, to); }
+
+ RandomNumberGenerator();
+};
+
+#endif // RANDOM_NUMBER_GENERATOR_H
diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp
new file mode 100644
index 0000000000..16899f79da
--- /dev/null
+++ b/core/math/random_pcg.cpp
@@ -0,0 +1,55 @@
+/*************************************************************************/
+/* random_pcg.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "random_pcg.h"
+
+#include "core/os/os.h"
+
+RandomPCG::RandomPCG(uint64_t seed, uint64_t inc) :
+ pcg() {
+ pcg.state = seed;
+ pcg.inc = inc;
+}
+
+void RandomPCG::randomize() {
+ seed(OS::get_singleton()->get_ticks_usec() * pcg.state + PCG_DEFAULT_INC_64);
+}
+
+double RandomPCG::random(double from, double to) {
+ unsigned int r = rand();
+ double ret = (double)r / (double)RANDOM_MAX;
+ return (ret) * (to - from) + from;
+}
+
+float RandomPCG::random(float from, float to) {
+ unsigned int r = rand();
+ float ret = (float)r / (float)RANDOM_MAX;
+ return (ret) * (to - from) + from;
+}
diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h
new file mode 100644
index 0000000000..4a43c36ede
--- /dev/null
+++ b/core/math/random_pcg.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* random_pcg.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 RANDOM_PCG_H
+#define RANDOM_PCG_H
+
+#include "core/math/math_defs.h"
+
+#include "thirdparty/misc/pcg.h"
+
+class RandomPCG {
+ pcg32_random_t pcg;
+
+public:
+ static const uint64_t DEFAULT_SEED = 12047754176567800795ULL;
+ static const uint64_t DEFAULT_INC = PCG_DEFAULT_INC_64;
+ static const uint64_t RANDOM_MAX = 4294967295;
+
+ RandomPCG(uint64_t seed = DEFAULT_SEED, uint64_t inc = PCG_DEFAULT_INC_64);
+
+ _FORCE_INLINE_ void seed(uint64_t seed) { pcg.state = seed; }
+ _FORCE_INLINE_ uint64_t get_seed() { return pcg.state; }
+
+ void randomize();
+ _FORCE_INLINE_ uint32_t rand() { return pcg32_random_r(&pcg); }
+ _FORCE_INLINE_ double randf() { return (double)rand() / (double)RANDOM_MAX; }
+ _FORCE_INLINE_ float randd() { return (float)rand() / (float)RANDOM_MAX; }
+
+ double random(double from, double to);
+ float random(float from, float to);
+ real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); }
+};
+
+#endif // RANDOM_PCG_H
diff --git a/core/message_queue.cpp b/core/message_queue.cpp
index 7f788c90a7..abfc73407a 100644
--- a/core/message_queue.cpp
+++ b/core/message_queue.cpp
@@ -338,6 +338,7 @@ MessageQueue::MessageQueue() {
buffer_end = 0;
buffer_max_used = 0;
buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB);
+ ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater"));
buffer_size *= 1024;
buffer = memnew_arr(uint8_t, buffer_size);
}
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index 3705762d6c..9840442519 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -125,7 +125,7 @@ private:
while (42) {
if (hashes[pos] == EMPTY_HASH) {
- _construct(pos, hash, p_key, p_value);
+ _construct(pos, hash, key, value);
return;
}
@@ -136,7 +136,7 @@ private:
if (hashes[pos] & DELETED_HASH_BIT) {
// we found a place where we can fit in!
- _construct(pos, hash, p_key, p_value);
+ _construct(pos, hash, key, value);
return;
}
diff --git a/core/object.cpp b/core/object.cpp
index 946040ba34..ea77090a45 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -440,16 +440,6 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
if (r_valid)
*r_valid = true;
return;
-#ifdef TOOLS_ENABLED
- } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) {
- Array arr = p_value;
- for (int i = 0; i < arr.size(); i++) {
- editor_section_folding.insert(arr[i]);
- }
- if (r_valid)
- *r_valid = true;
- return;
-#endif
}
//something inside the object... :|
@@ -520,16 +510,7 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
if (r_valid)
*r_valid = true;
return ret;
-#ifdef TOOLS_ENABLED
- } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) {
- Array array;
- for (Set<String>::Element *E = editor_section_folding.front(); E; E = E->next()) {
- array.push_back(E->get());
- }
- if (r_valid)
- *r_valid = true;
- return array;
-#endif
+
} else {
//something inside the object... :|
bool success = _getv(p_name, ret);
@@ -655,15 +636,11 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
#ifdef TOOLS_ENABLED
p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
#endif
- p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
}
-#ifdef TOOLS_ENABLED
- if (editor_section_folding.size()) {
- p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ if (!metadata.empty()) {
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
}
-#endif
- if (!metadata.empty())
- p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_STORE_IF_NONZERO));
if (script_instance && !p_reversed) {
p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
script_instance->get_property_list(p_list);
@@ -1738,6 +1715,8 @@ void Object::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred", &Object::_call_deferred_bind, mi);
}
+ ClassDB::bind_method(D_METHOD("set_deferred", "property", "value"), &Object::set_deferred);
+
ClassDB::bind_method(D_METHOD("callv", "method", "arg_array"), &Object::callv);
ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method);
@@ -1794,6 +1773,10 @@ void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) {
MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS);
}
+void Object::set_deferred(const StringName &p_property, const Variant &p_value) {
+ MessageQueue::get_singleton()->push_set(this, p_property, p_value);
+}
+
void Object::set_block_signals(bool p_block) {
_block_signals = p_block;
@@ -1958,30 +1941,30 @@ Object::~Object() {
memdelete(script_instance);
script_instance = NULL;
- List<Connection> sconnections;
const StringName *S = NULL;
- while ((S = signal_map.next(S))) {
+ while ((S = signal_map.next(NULL))) {
Signal *s = &signal_map[*S];
- ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it");
- ERR_CONTINUE(s->lock > 0);
-
- for (int i = 0; i < s->slot_map.size(); i++) {
-
- sconnections.push_back(s->slot_map.getv(i).conn);
+ if (s->lock) {
+ ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it");
+ ERR_CONTINUE(s->lock > 0);
}
- }
- for (List<Connection>::Element *E = sconnections.front(); E; E = E->next()) {
+ //brute force disconnect for performance
+ int slot_count = s->slot_map.size();
+ const VMap<Signal::Target, Signal::Slot>::Pair *slot_list = s->slot_map.get_array();
- Connection &c = E->get();
- ERR_CONTINUE(c.source != this); //bug?
+ for (int i = 0; i < slot_count; i++) {
+
+ slot_list[i].value.conn.target->connections.erase(slot_list[i].value.cE);
+ }
- this->_disconnect(c.signal, c.target, c.method, true);
+ signal_map.erase(*S);
}
+ //signals from nodes that connect to this node
while (connections.size()) {
Connection c = connections.front()->get();
diff --git a/core/object.h b/core/object.h
index b23160c1df..88a98dacbe 100644
--- a/core/object.h
+++ b/core/object.h
@@ -103,8 +103,9 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
PROPERTY_USAGE_CATEGORY = 256,
- PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
- PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
+ //those below are deprecated thanks to ClassDB's now class value cache
+ //PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
+ //PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
PROPERTY_USAGE_RESTART_IF_CHANGED = 4096,
PROPERTY_USAGE_SCRIPT_VARIABLE = 8192,
@@ -126,10 +127,6 @@ enum PropertyUsageFlags {
#define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal)
#define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter))
#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index)
-#define ADD_PROPERTYNZ(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONZERO), _scs_create(m_setter), _scs_create(m_getter))
-#define ADD_PROPERTYINZ(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONZERO), _scs_create(m_setter), _scs_create(m_getter), m_index)
-#define ADD_PROPERTYNO(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONONE), _scs_create(m_setter), _scs_create(m_getter))
-#define ADD_PROPERTYINO(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONONE), _scs_create(m_setter), _scs_create(m_getter), m_index)
#define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
struct PropertyInfo {
@@ -701,6 +698,7 @@ public:
bool is_connected(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const;
void call_deferred(const StringName &p_method, VARIANT_ARG_LIST);
+ void set_deferred(const StringName &p_property, const Variant &p_value);
void set_block_signals(bool p_block);
bool is_blocking_signals() const;
@@ -723,6 +721,9 @@ public:
#ifdef TOOLS_ENABLED
void editor_set_section_unfold(const String &p_section, bool p_unfolded);
bool editor_is_section_unfolded(const String &p_section);
+ const Set<String> &editor_get_section_folding() const { return editor_section_folding; }
+ void editor_clear_section_folding() { editor_section_folding.clear(); }
+
#endif
//used by script languages to store binding data
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
index e09e5e16ad..679b1c9054 100644
--- a/core/os/file_access.cpp
+++ b/core/os/file_access.cpp
@@ -346,9 +346,9 @@ String FileAccess::get_line() const {
return String::utf8(line.get_data());
}
-Vector<String> FileAccess::get_csv_line(String delim) const {
+Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
- ERR_FAIL_COND_V(delim.length() != 1, Vector<String>());
+ ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>());
String l;
int qc = 0;
@@ -376,7 +376,7 @@ Vector<String> FileAccess::get_csv_line(String delim) const {
CharType c = l[i];
CharType s[2] = { 0, 0 };
- if (!in_quote && c == delim[0]) {
+ if (!in_quote && c == p_delim[0]) {
strings.push_back(current);
current = String();
} else if (c == '"') {
@@ -525,6 +525,28 @@ void FileAccess::store_line(const String &p_line) {
store_8('\n');
}
+void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
+
+ ERR_FAIL_COND(p_delim.length() != 1);
+
+ String line = "";
+ int size = p_values.size();
+ for (int i = 0; i < size; ++i) {
+ String value = p_values[i];
+
+ if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) {
+ value = "\"" + value.replace("\"", "\"\"") + "\"";
+ }
+ if (i < size - 1) {
+ value += p_delim;
+ }
+
+ line += value;
+ }
+
+ store_line(line);
+}
+
void FileAccess::store_buffer(const uint8_t *p_src, int p_length) {
for (int i = 0; i < p_length; i++)
diff --git a/core/os/file_access.h b/core/os/file_access.h
index b7d93e9f5d..f1f3005dd9 100644
--- a/core/os/file_access.h
+++ b/core/os/file_access.h
@@ -112,7 +112,7 @@ public:
virtual int get_buffer(uint8_t *p_dst, int p_length) const; ///< get an array of bytes
virtual String get_line() const;
virtual String get_token() const;
- virtual Vector<String> get_csv_line(String delim = ",") const;
+ virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
* It's not about the current CPU type but file formats.
@@ -136,6 +136,7 @@ public:
virtual void store_string(const String &p_string);
virtual void store_line(const String &p_line);
+ virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
virtual void store_pascal_string(const String &p_string);
virtual String get_pascal_string();
diff --git a/core/os/input.cpp b/core/os/input.cpp
index 4cd1f0b24a..3b895b16b4 100644
--- a/core/os/input.cpp
+++ b/core/os/input.cpp
@@ -86,7 +86,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode);
ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode);
ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position);
- ClassDB::bind_method(D_METHOD("action_press", "action"), &Input::action_press);
+ ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f));
ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
diff --git a/core/os/input.h b/core/os/input.h
index db523d6789..dc2c213db2 100644
--- a/core/os/input.h
+++ b/core/os/input.h
@@ -113,7 +113,7 @@ public:
virtual Vector3 get_magnetometer() const = 0;
virtual Vector3 get_gyroscope() const = 0;
- virtual void action_press(const StringName &p_action) = 0;
+ virtual void action_press(const StringName &p_action, float p_strength = 1.f) = 0;
virtual void action_release(const StringName &p_action) = 0;
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 99a23bbee1..407bb78375 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -288,9 +288,28 @@ void ProjectSettings::_convert_to_last_version() {
}
}
+/*
+ * This method is responsible for loading a project.godot file and/or data file
+ * using the following merit order:
+ * - If using NetworkClient, try to lookup project file or fail.
+ * - If --main-pack was passed by the user (`p_main_pack`), load it or fail.
+ * - Search for .pck file matching binary name. There are two possibilities:
+ * o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck')
+ * o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck')
+ * For each tentative, if the file exists, load it or fail.
+ * - On relevant platforms (Android/iOS), lookup project file in OS resource path.
+ * If found, load it or fail.
+ * - Lookup project file in passed `p_path` (--path passed by the user), i.e. we
+ * are running from source code.
+ * If not found and `p_upwards` is true (--upwards passed by the user), look for
+ * project files in parent folders up to the system root (used to run a game
+ * from command line while in a subfolder).
+ * If a project file is found, load it or fail.
+ * If nothing was found, error out.
+ */
Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
- //If looking for files in network, just use network!
+ // If looking for files in a network client, use it directly
if (FileAccessNetworkClient::get_singleton()) {
@@ -302,9 +321,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
return err;
}
- String exec_path = OS::get_singleton()->get_executable_path();
-
- //Attempt with a passed main pack first
+ // Attempt with a user-defined main pack first
if (p_main_pack != "") {
@@ -320,25 +337,39 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
return err;
}
- //Attempt with execname.pck
+ // Attempt with exec_name.pck
+ // (This is the usual case when distributing a Godot game.)
+
+ // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle)
+ // or the exec path's basename + '.pck' (Windows).
+ // We need to test both possibilities as extensions for Linux binaries are optional
+ // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck').
+
+ String exec_path = OS::get_singleton()->get_executable_path();
+
if (exec_path != "") {
bool found = false;
- // get our filename without our path (note, using exec_path.get_file before get_basename anymore because not all file systems have dots in their file names!)
- String filebase_name = exec_path.get_file().get_basename();
+ String exec_dir = exec_path.get_base_dir();
+ String exec_filename = exec_path.get_file();
+ String exec_basename = exec_filename.get_basename();
+
+ // Try to load data pack at the location of the executable
+ // As mentioned above, we have two potential names to attempt
- // try to open at the location of executable
- String datapack_name = exec_path.get_base_dir().plus_file(filebase_name) + ".pck";
- if (_load_resource_pack(datapack_name)) {
+ if (_load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) ||
+ _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"))) {
found = true;
} else {
- datapack_name = filebase_name + ".pck";
- if (_load_resource_pack(datapack_name)) {
+ // If we couldn't find them next to the executable, we attempt
+ // the current working directory. Same story, two tests.
+ if (_load_resource_pack(exec_basename + ".pck") ||
+ _load_resource_pack(exec_filename + ".pck")) {
found = true;
}
}
- // if we opened our package, try and load our project...
+ // If we opened our package, try and load our project
if (found) {
Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
if (err == OK) {
@@ -350,17 +381,15 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
}
}
- //Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this)
- if (OS::get_singleton()->get_resource_dir() != "") {
- //OS will call Globals->get_resource_path which will be empty if not overridden!
- //if the OS would rather use somewhere else, then it will not be empty.
+ // Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this)
+ if (OS::get_singleton()->get_resource_dir() != "") {
+ // OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
+ // If the OS would rather use a specific location, then it will not be empty.
resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
- if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
+ if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
-
- // data.pck and data.zip are deprecated and no longer supported, apologies.
- // make sure this is loaded from the resource path
+ }
Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
if (err == OK) {
@@ -371,21 +400,19 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
return err;
}
- //Nothing was found, try to find a project.godot somewhere!
+ // Nothing was found, try to find a project file in provided path (`p_path`)
+ // or, if requested (`p_upwards`) in parent directories.
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!d, ERR_CANT_CREATE);
-
d->change_dir(p_path);
- String candidate = d->get_current_dir();
String current_dir = d->get_current_dir();
-
+ String candidate = current_dir;
bool found = false;
Error err;
while (true) {
-
err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
if (err == OK) {
// Optional, we don't mind if it fails
@@ -396,10 +423,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
}
if (p_upwards) {
- // Try to load settings ascending through dirs shape!
+ // Try to load settings ascending through parent directories
d->change_dir("..");
if (d->get_current_dir() == current_dir)
- break; //not doing anything useful
+ break; // not doing anything useful
current_dir = d->get_current_dir();
} else {
break;
@@ -416,6 +443,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
+ // If we're loading a project.godot from source code, we can operate some
+ // ProjectSettings conversions if need be.
_convert_to_last_version();
return OK;
@@ -1133,6 +1162,7 @@ ProjectSettings::ProjectSettings() {
custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects");
GLOBAL_DEF("debug/settings/profiler/max_functions", 16384);
+ custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1");
//assigning here, because using GLOBAL_GET on every block for compressing can be slow
Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false);
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index b0023b4c26..6b776cb0b1 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -55,6 +55,7 @@
#include "core/math/a_star.h"
#include "core/math/expression.h"
#include "core/math/geometry.h"
+#include "core/math/random_number_generator.h"
#include "core/math/triangle_mesh.h"
#include "core/os/input.h"
#include "core/os/main_loop.h"
@@ -180,6 +181,7 @@ void register_core_types() {
ClassDB::register_virtual_class<PackedDataContainerRef>();
ClassDB::register_class<AStar>();
ClassDB::register_class<EncodedObjectAsID>();
+ ClassDB::register_class<RandomNumberGenerator>();
ClassDB::register_class<JSONParseResult>();
@@ -199,6 +201,7 @@ void register_core_types() {
void register_core_settings() {
//since in register core types, globals may not e present
GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16));
+ ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater"));
}
void register_core_singletons() {
diff --git a/core/resource.cpp b/core/resource.cpp
index 4dcd338e94..a76e16232e 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -379,9 +379,9 @@ void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false));
ADD_SIGNAL(MethodInfo("changed"));
ADD_GROUP("Resource", "resource_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
BIND_VMETHOD(MethodInfo("_setup_local_to_scene"));
}
diff --git a/core/ring_buffer.h b/core/ring_buffer.h
index 2516880064..54486f8cad 100644
--- a/core/ring_buffer.h
+++ b/core/ring_buffer.h
@@ -135,6 +135,12 @@ public:
return p_n;
};
+ inline int decrease_write(int p_n) {
+ p_n = MIN(p_n, data_left());
+ inc(write_pos, size_mask + 1 - p_n);
+ return p_n;
+ }
+
Error write(const T &p_v) {
ERR_FAIL_COND_V(space_left() < 1, FAILED);
data.write[inc(write_pos, 1)] = p_v;
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 3e984fae32..a03ddd0983 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -108,7 +108,7 @@ void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_
}
int len = 0;
- Error err = encode_variant(var, NULL, len);
+ Error err = encode_variant(var, NULL, len, true);
if (err != OK)
ERR_PRINT("Failed to encode variant");
@@ -1100,7 +1100,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
eh.userdata = this;
add_error_handler(&eh);
- profile_info.resize(CLAMP(int(ProjectSettings::get_singleton()->get("debug/settings/profiler/max_functions")), 128, 65535));
+ profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions"));
profile_info_ptrs.resize(profile_info.size());
}
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 5b65da9ef1..496521486e 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -563,7 +563,8 @@ Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_nam
PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) :
owner(p_owner),
language(p_language),
- script(p_script) {
+ script(p_script),
+ build_failed(false) {
}
PlaceHolderScriptInstance::~PlaceHolderScriptInstance() {
diff --git a/core/set.h b/core/set.h
index 744019d5b4..59aa54128e 100644
--- a/core/set.h
+++ b/core/set.h
@@ -595,6 +595,7 @@ public:
return e;
}
+ inline bool empty() const { return _data.size_cache == 0; }
inline int size() const { return _data.size_cache; }
int calculate_depth() const {
diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp
index 7d67076df5..3d41c374ea 100644
--- a/core/undo_redo.cpp
+++ b/core/undo_redo.cpp
@@ -325,7 +325,7 @@ bool UndoRedo::undo() {
return true;
}
-void UndoRedo::clear_history() {
+void UndoRedo::clear_history(bool p_increase_version) {
ERR_FAIL_COND(action_level > 0);
_discard_redo();
@@ -333,7 +333,8 @@ void UndoRedo::clear_history() {
while (actions.size())
_pop_history_tail();
- //version++;
+ if (p_increase_version)
+ version++;
}
String UndoRedo::get_current_action_name() const {
@@ -493,7 +494,7 @@ void UndoRedo::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property);
ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference);
ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference);
- ClassDB::bind_method(D_METHOD("clear_history"), &UndoRedo::clear_history);
+ ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name);
ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version);
ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo);
diff --git a/core/undo_redo.h b/core/undo_redo.h
index 22dcd60472..f09fca9a78 100644
--- a/core/undo_redo.h
+++ b/core/undo_redo.h
@@ -112,7 +112,7 @@ public:
bool redo();
bool undo();
String get_current_action_name() const;
- void clear_history();
+ void clear_history(bool p_increase_version = true);
uint64_t get_version() const;
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 28bbe1d834..3f017fa985 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -49,7 +49,7 @@
#endif
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
-#define snprintf _snprintf
+#define snprintf _snprintf_s
#endif
#define MAX_DIGITS 6
@@ -586,6 +586,8 @@ String String::camelcase_to_underscore(bool lowercase) const {
bool is_upper = cstr[i] >= A && cstr[i] <= Z;
bool is_number = cstr[i] >= '0' && cstr[i] <= '9';
bool are_next_2_lower = false;
+ bool is_next_lower = false;
+ bool is_next_number = false;
bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z;
bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9';
@@ -593,7 +595,18 @@ String String::camelcase_to_underscore(bool lowercase) const {
are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z;
}
- bool should_split = ((is_upper && !was_precedent_upper && !was_precedent_number) || (was_precedent_upper && is_upper && are_next_2_lower) || (is_number && !was_precedent_number));
+ if (i + 1 < this->size()) {
+ is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z;
+ is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9';
+ }
+
+ const bool a = is_upper && !was_precedent_upper && !was_precedent_number;
+ const bool b = was_precedent_upper && is_upper && are_next_2_lower;
+ const bool c = is_number && !was_precedent_number;
+ const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower;
+ const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number);
+
+ bool should_split = a || b || c || can_break_number_letter || can_break_letter_number;
if (should_split) {
new_string += this->substr(start_index, i - start_index) + "_";
start_index = i;
@@ -2761,16 +2774,13 @@ String String::format(const Variant &values, String placeholder) const {
if (value_arr.size() == 2) {
Variant v_key = value_arr[0];
- String key;
-
- key = v_key.get_construct_string();
+ String key = v_key;
if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") {
key = key.substr(1, key.length() - 2);
}
Variant v_val = value_arr[1];
- String val;
- val = v_val.get_construct_string();
+ String val = v_val;
if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") {
val = val.substr(1, val.length() - 2);
@@ -2782,8 +2792,7 @@ String String::format(const Variant &values, String placeholder) const {
}
} else { //Array structure ["RobotGuy","Logis","rookie"]
Variant v_val = values_arr[i];
- String val;
- val = v_val.get_construct_string();
+ String val = v_val;
if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") {
val = val.substr(1, val.length() - 2);
@@ -2802,8 +2811,8 @@ String String::format(const Variant &values, String placeholder) const {
d.get_key_list(&keys);
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- String key = E->get().get_construct_string();
- String val = d[E->get()].get_construct_string();
+ String key = E->get();
+ String val = d[E->get()];
if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") {
key = key.substr(1, key.length() - 2);
@@ -4026,7 +4035,7 @@ String String::sprintf(const Array &values, bool *error) const {
str = str.pad_decimals(min_decimals);
// Show sign
- if (show_sign && value >= 0) {
+ if (show_sign && str.left(1) != "-") {
str = str.insert(0, "+");
}
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 8693a584f2..0c6e43fe36 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -155,9 +155,7 @@ struct _VariantCall {
funcdata.default_args = p_defaultarg;
funcdata._const = p_const;
funcdata.returns = p_has_return;
-#ifdef DEBUG_ENABLED
funcdata.return_type = p_return;
-#endif
if (p_argtype1.name) {
funcdata.arg_types.push_back(p_argtype1.type);
@@ -486,6 +484,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Dictionary, keys);
VCALL_LOCALMEM0R(Dictionary, values);
VCALL_LOCALMEM1R(Dictionary, duplicate);
+ VCALL_LOCALMEM2R(Dictionary, get);
VCALL_LOCALMEM2(Array, set);
VCALL_LOCALMEM1R(Array, get);
@@ -1679,6 +1678,7 @@ void register_variant_methods() {
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, keys, varray());
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, values, varray());
ADDFUNC1R(DICTIONARY, DICTIONARY, Dictionary, duplicate, BOOL, "deep", varray(false));
+ ADDFUNC2R(DICTIONARY, NIL, Dictionary, get, NIL, "key", NIL, "default", varray(Variant()));
ADDFUNC0R(ARRAY, INT, Array, size, varray());
ADDFUNC0R(ARRAY, BOOL, Array, empty, varray());
diff --git a/core/vmap.h b/core/vmap.h
index 9fc99e636d..5f6d8190c6 100644
--- a/core/vmap.h
+++ b/core/vmap.h
@@ -36,22 +36,23 @@
template <class T, class V>
class VMap {
-
- struct _Pair {
+public:
+ struct Pair {
T key;
V value;
- _FORCE_INLINE_ _Pair() {}
+ _FORCE_INLINE_ Pair() {}
- _FORCE_INLINE_ _Pair(const T &p_key, const V &p_value) {
+ _FORCE_INLINE_ Pair(const T &p_key, const V &p_value) {
key = p_key;
value = p_value;
}
};
- CowData<_Pair> _cowdata;
+private:
+ CowData<Pair> _cowdata;
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
@@ -61,7 +62,7 @@ class VMap {
int low = 0;
int high = _cowdata.size() - 1;
- const _Pair *a = _cowdata.ptr();
+ const Pair *a = _cowdata.ptr();
int middle = 0;
#if DEBUG_ENABLED
@@ -95,7 +96,7 @@ class VMap {
int low = 0;
int high = _cowdata.size() - 1;
int middle;
- const _Pair *a = _cowdata.ptr();
+ const Pair *a = _cowdata.ptr();
while (low <= high) {
middle = (low + high) / 2;
@@ -121,7 +122,7 @@ public:
_cowdata.get_m(pos).value = p_val;
return pos;
}
- _cowdata.insert(pos, _Pair(p_key, p_val));
+ _cowdata.insert(pos, Pair(p_key, p_val));
return pos;
}
@@ -152,12 +153,12 @@ public:
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
_FORCE_INLINE_ bool empty() const { return _cowdata.empty(); }
- const _Pair *get_array() const {
+ const Pair *get_array() const {
return _cowdata.ptr();
}
- _Pair *get_array() {
+ Pair *get_array() {
return _cowdata.ptrw();
}
diff --git a/doc/Doxyfile b/doc/Doxyfile
index c1904f17c9..a7aa204741 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = Godot
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -162,7 +162,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH =
+STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -171,7 +171,7 @@ STRIP_FROM_PATH =
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
-STRIP_FROM_INC_PATH =
+STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
@@ -238,13 +238,13 @@ TAB_SIZE = 4
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
-ALIASES =
+ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
-TCL_SUBST =
+TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
@@ -291,7 +291,7 @@ OPTIMIZE_OUTPUT_VHDL = NO
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
-EXTENSION_MAPPING =
+EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
@@ -632,7 +632,7 @@ GENERATE_DEPRECATEDLIST= YES
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
-ENABLED_SECTIONS =
+ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
@@ -674,7 +674,7 @@ SHOW_NAMESPACES = YES
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
-FILE_VERSION_FILTER =
+FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
@@ -687,7 +687,7 @@ FILE_VERSION_FILTER =
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
-LAYOUT_FILE =
+LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
@@ -697,7 +697,7 @@ LAYOUT_FILE =
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
-CITE_BIB_FILES =
+CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
@@ -756,7 +756,7 @@ WARN_FORMAT = "$file:$line: $text"
# messages should be written. If left blank the output is written to standard
# error (stderr).
-WARN_LOGFILE =
+WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
@@ -844,7 +844,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
-EXCLUDE =
+EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -860,7 +860,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
-EXCLUDE_PATTERNS =
+EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -871,13 +871,13 @@ EXCLUDE_PATTERNS =
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
-EXAMPLE_PATH =
+EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
@@ -897,7 +897,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
-IMAGE_PATH =
+IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -914,7 +914,7 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
-INPUT_FILTER =
+INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
@@ -923,7 +923,7 @@ INPUT_FILTER =
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
@@ -938,7 +938,7 @@ FILTER_SOURCE_FILES = NO
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
-FILTER_SOURCE_PATTERNS =
+FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
@@ -1050,7 +1050,7 @@ CLANG_ASSISTED_PARSING = NO
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
-CLANG_OPTIONS =
+CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
@@ -1076,7 +1076,7 @@ COLS_IN_ALPHA_INDEX = 5
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-IGNORE_PREFIX =
+IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
@@ -1120,7 +1120,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_HEADER =
+HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1130,7 +1130,7 @@ HTML_HEADER =
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_FOOTER =
+HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1142,7 +1142,7 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_STYLESHEET =
+HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
@@ -1155,7 +1155,7 @@ HTML_STYLESHEET =
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_STYLESHEET =
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1165,7 +1165,7 @@ HTML_EXTRA_STYLESHEET =
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_EXTRA_FILES =
+HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
@@ -1293,7 +1293,7 @@ GENERATE_HTMLHELP = NO
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-CHM_FILE =
+CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
@@ -1301,7 +1301,7 @@ CHM_FILE =
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-HHC_LOCATION =
+HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO).
@@ -1314,7 +1314,7 @@ GENERATE_CHI = NO
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
-CHM_INDEX_ENCODING =
+CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
@@ -1345,7 +1345,7 @@ GENERATE_QHP = NO
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
-QCH_FILE =
+QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
@@ -1370,7 +1370,7 @@ QHP_VIRTUAL_FOLDER = doc
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
@@ -1378,21 +1378,21 @@ QHP_CUST_FILTER_NAME =
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_CUST_FILTER_ATTRS =
+QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
-QHP_SECT_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
-QHG_LOCATION =
+QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
@@ -1525,7 +1525,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# This tag requires that the tag USE_MATHJAX is set to YES.
-MATHJAX_EXTENSIONS =
+MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
@@ -1533,7 +1533,7 @@ MATHJAX_EXTENSIONS =
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
-MATHJAX_CODEFILE =
+MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
@@ -1593,7 +1593,7 @@ EXTERNAL_SEARCH = NO
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
-SEARCHENGINE_URL =
+SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
@@ -1609,7 +1609,7 @@ SEARCHDATA_FILE = searchdata.xml
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
-EXTERNAL_SEARCH_ID =
+EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
@@ -1619,7 +1619,7 @@ EXTERNAL_SEARCH_ID =
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
-EXTRA_SEARCH_MAPPINGS =
+EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
@@ -1680,7 +1680,7 @@ PAPER_TYPE = a4
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-EXTRA_PACKAGES =
+EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
@@ -1696,7 +1696,7 @@ EXTRA_PACKAGES =
# to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_HEADER =
+LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
@@ -1707,7 +1707,7 @@ LATEX_HEADER =
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_FOOTER =
+LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
@@ -1718,7 +1718,7 @@ LATEX_FOOTER =
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_EXTRA_STYLESHEET =
+LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
@@ -1726,7 +1726,7 @@ LATEX_EXTRA_STYLESHEET =
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_EXTRA_FILES =
+LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
@@ -1826,14 +1826,14 @@ RTF_HYPERLINKS = NO
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
-RTF_STYLESHEET_FILE =
+RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
-RTF_EXTENSIONS_FILE =
+RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
@@ -1878,7 +1878,7 @@ MAN_EXTENSION = .3
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
-MAN_SUBDIR =
+MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
@@ -1991,7 +1991,7 @@ PERLMOD_PRETTY = YES
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
-PERLMOD_MAKEVAR_PREFIX =
+PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
@@ -2032,7 +2032,7 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
-INCLUDE_PATH =
+INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -2040,7 +2040,7 @@ INCLUDE_PATH =
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-INCLUDE_FILE_PATTERNS =
+INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
@@ -2050,7 +2050,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED =
+PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2059,7 +2059,7 @@ PREDEFINED =
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-EXPAND_AS_DEFINED =
+EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
@@ -2088,13 +2088,13 @@ SKIP_FUNCTION_MACROS = YES
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
-TAGFILES =
+TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
-GENERATE_TAGFILE =
+GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
@@ -2143,14 +2143,14 @@ CLASS_DIAGRAMS = YES
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
-MSCGEN_PATH =
+MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
-DIA_PATH =
+DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
@@ -2199,7 +2199,7 @@ DOT_FONTSIZE = 10
# the path where dot can find it using this tag.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTPATH =
+DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
@@ -2337,26 +2337,26 @@ INTERACTIVE_SVG = NO
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_PATH =
+DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
-DOTFILE_DIRS =
+DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
-MSCFILE_DIRS =
+MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
-DIAFILE_DIRS =
+DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
@@ -2364,12 +2364,12 @@ DIAFILE_DIRS =
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
-PLANTUML_JAR_PATH =
+PLANTUML_JAR_PATH =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
-PLANTUML_INCLUDE_PATH =
+PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index 493f55e89b..20ec9141c6 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -745,6 +745,22 @@
[/codeblock]
</description>
</method>
+ <method name="push_error">
+ <return type="void">
+ </return>
+ <argument index="0" name="message" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="push_warning">
+ <return type="void">
+ </return>
+ <argument index="0" name="message" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="rad2deg">
<return type="float">
</return>
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 65d339c0d5..e05af0f1ac 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1298,10 +1298,6 @@
</constant>
<constant name="PROPERTY_USAGE_CATEGORY" value="256" enum="PropertyUsageFlags">
</constant>
- <constant name="PROPERTY_USAGE_STORE_IF_NONZERO" value="512" enum="PropertyUsageFlags">
- </constant>
- <constant name="PROPERTY_USAGE_STORE_IF_NONONE" value="1024" enum="PropertyUsageFlags">
- </constant>
<constant name="PROPERTY_USAGE_NO_INSTANCE_STATE" value="2048" enum="PropertyUsageFlags">
</constant>
<constant name="PROPERTY_USAGE_RESTART_IF_CHANGED" value="4096" enum="PropertyUsageFlags">
diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml
index b9061e0b87..2e0d0c15b2 100644
--- a/doc/classes/AABB.xml
+++ b/doc/classes/AABB.xml
@@ -7,7 +7,7 @@
AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ARVRInterface.xml b/doc/classes/ARVRInterface.xml
index 413370ed0b..bf72902410 100644
--- a/doc/classes/ARVRInterface.xml
+++ b/doc/classes/ARVRInterface.xml
@@ -45,8 +45,8 @@
</return>
<description>
Call this to initialize this interface. The first interface that is initialized is identified as the primary interface and it will be used for rendering output.
- After initializing the interface you want to use you then need to enable the AR/VR mode of a viewport and rendering should commence.
- Note that you must enable the AR/VR mode on the main viewport for any device that uses the main output of Godot such as for mobile VR.
+ After initializing the interface you want to use you then need to enable the AR/VR mode of a viewport and rendering should commence.
+ Note that you must enable the AR/VR mode on the main viewport for any device that uses the main output of Godot such as for mobile VR.
If you do this for a platform that handles its own output (such as OpenVR) Godot will show just one eye without distortion on screen. Alternatively you can add a separate viewport node to your scene and enable AR/VR on that viewport and it will be used to output to the HMD leaving you free to do anything you like in the main window such as using a separate camera as a spectator camera or render out something completely different.
While currently not used you can activate additional interfaces, you may wish to do this if you want to track controllers from other platforms. However at this point in time only one interface can render to an HMD.
</description>
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index bb161cd87c..169fbcd5a8 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -7,7 +7,7 @@
An Animation resource contains data used to animate everything in the engine. Animations are divided into tracks, and each track must be linked to a node. The state of that node can be changed through time, by adding timed keys (events) to the track.
[codeblock]
# This creates an animation that makes the node "Enemy" move to the right by
- # 100 pixels in 1 second.
+ # 100 pixels in 1 second.
var animation = Animation.new()
var track_index = animation.add_track(Animation.TYPE_VALUE)
animation.track_set_path(track_index, "Enemy:position.x")
@@ -17,7 +17,7 @@
Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] or [AnimationTreePlayer] to be played back.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml
index 22f5e0838e..de8e918f68 100644
--- a/doc/classes/AnimationNodeAnimation.xml
+++ b/doc/classes/AnimationNodeAnimation.xml
@@ -9,12 +9,6 @@
<demos>
</demos>
<methods>
- <method name="get_playback_time" qualifiers="const">
- <return type="float">
- </return>
- <description>
- </description>
- </method>
</methods>
<members>
<member name="animation" type="String" setter="set_animation" getter="get_animation">
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 6dc91a234a..3b3638a4f3 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -7,8 +7,8 @@
An animation player is used for general purpose playback of [Animation] resources. It contains a dictionary of animations (referenced by name) and custom blend times between their transitions. Additionally, animations can be played and blended in different channels.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/animations.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link>
</tutorials>
<demos>
</demos>
@@ -238,7 +238,7 @@
The default time in which to blend animations. Ranges from 0 to 4096 with 0.01 precision. Default value: [code]0[/code].
</member>
<member name="playback_process_mode" type="int" setter="set_animation_process_mode" getter="get_animation_process_mode" enum="AnimationPlayer.AnimationProcessMode">
- The process notification in which to update animations. Default value: [enum ANIMATION_PROCESS_IDLE].
+ The process notification in which to update animations. Default value: [code]ANIMATION_PROCESS_IDLE[/code].
</member>
<member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale">
The speed scaling ratio. For instance, if this value is 1 then the animation plays at normal speed. If it's 0.5 then it plays at half speed. If it's 2 then it plays at double speed. Default value: [code]1[/code].
diff --git a/doc/classes/AnimationTreePlayer.xml b/doc/classes/AnimationTreePlayer.xml
index 8c32d5f6a3..a081c64f6d 100644
--- a/doc/classes/AnimationTreePlayer.xml
+++ b/doc/classes/AnimationTreePlayer.xml
@@ -618,7 +618,7 @@
Once set, Animation nodes can be added to the AnimationTreePlayer.
</member>
<member name="playback_process_mode" type="int" setter="set_animation_process_mode" getter="get_animation_process_mode" enum="AnimationTreePlayer.AnimationProcessMode">
- The thread in which to update animations. Default value: [enum ANIMATION_PROCESS_IDLE].
+ The thread in which to update animations. Default value: [code]ANIMATION_PROCESS_IDLE[/code].
</member>
</members>
<constants>
diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml
index fb2009105d..008c644466 100644
--- a/doc/classes/AudioEffectReverb.xml
+++ b/doc/classes/AudioEffectReverb.xml
@@ -15,7 +15,7 @@
</methods>
<members>
<member name="damping" type="float" setter="set_damping" getter="get_damping">
- Widens or narrows the stereo image of the reverb tail. 1 means fully widens. Value can range from 0 to 1. Default value: [code]1[/code].
+ Defines how reflective the imaginary room's walls are. Value can range from 0 to 1. Default value: [code]1[/code].
</member>
<member name="dry" type="float" setter="set_dry" getter="get_dry">
Output percent of original sound. At 0, only modified sound is outputted. Value can range from 0 to 1. Default value: [code]1[/code].
@@ -33,7 +33,7 @@
Dimensions of simulated room. Bigger means more echoes. Value can range from 0 to 1. Default value: [code]0.8[/code].
</member>
<member name="spread" type="float" setter="set_spread" getter="get_spread">
- Defines how reflective the imaginary room's walls are. Value can range from 0 to 1. Default value: [code]1[/code].
+ Widens or narrows the stereo image of the reverb tail. 1 means fully widens. Value can range from 0 to 1. Default value: [code]1[/code].
</member>
<member name="wet" type="float" setter="set_wet" getter="get_wet">
Output percent of modified sound. At 0, only original sound is outputted. Value can range from 0 to 1. Default value: [code]0.5[/code].
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 37756bcfd8..3ae5454e65 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -7,7 +7,7 @@
AudioServer is a low level server interface for audio access. It is in charge of creating sample data (playable audio) as well as its playback via a voice interface.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_buses.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_buses.html</link>
</tutorials>
<demos>
</demos>
@@ -385,6 +385,8 @@
<constant name="SPEAKER_MODE_STEREO" value="0" enum="SpeakerMode">
Two or fewer speakers are detected.
</constant>
+ <constant name="SPEAKER_SURROUND_31" value="1" enum="SpeakerMode">
+ </constant>
<constant name="SPEAKER_SURROUND_51" value="2" enum="SpeakerMode">
A 5.1 channel surround setup detected.
</constant>
diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml
index 753a506058..96ce53b14f 100644
--- a/doc/classes/AudioStream.xml
+++ b/doc/classes/AudioStream.xml
@@ -7,7 +7,7 @@
Base class for audio streams. Audio streams are used for music playback, or other types of streamed sounds that don't fit or require more flexibility than a [Sample].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index 26d0b1a83d..be4e4f137a 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -7,8 +7,8 @@
Plays background audio.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
+ <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index 5cc87e0e7a..3b81894c14 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -7,8 +7,8 @@
Plays audio that dampens with distance from screen center.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
+ <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index e61515ffc0..5841f1f6dc 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -7,8 +7,8 @@
Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link>
+ <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml
index 966b6dd7c4..8e27dc8d5d 100644
--- a/doc/classes/BakedLightmap.xml
+++ b/doc/classes/BakedLightmap.xml
@@ -7,7 +7,7 @@
Baked lightmaps are an alternative workflow for adding indirect (or baked) lighting to a scene. Unlike the [GIProbe] approach, baked lightmaps work fine on low-end PCs and mobile devices as they consume almost no resources in run-time.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/baked_lightmaps.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml
index 3364770280..ffdc010987 100644
--- a/doc/classes/BaseButton.xml
+++ b/doc/classes/BaseButton.xml
@@ -68,6 +68,9 @@
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode">
If [code]true[/code] the button is in toggle mode. Makes the button flip state between pressed and unpressed each time its area is clicked.
</member>
+ <member name="shortcut_in_tooltip" type="bool" setter="set_shortcut_in_tooltip" getter="is_shortcut_in_tooltip_enabled">
+ If [code]true[/code] the button will add information about its shortcut in the tooltip.
+ </member>
</members>
<signals>
<signal name="button_down">
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index fe8debe1a9..95f6346321 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -8,8 +8,8 @@
For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S).
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
- <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/CPUParticles.xml b/doc/classes/CPUParticles.xml
index 9d3dc5d70a..c778cd56b3 100644
--- a/doc/classes/CPUParticles.xml
+++ b/doc/classes/CPUParticles.xml
@@ -39,8 +39,6 @@
</member>
<member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
</member>
- <member name="anim_loop" type="bool" setter="set_particle_flag" getter="get_particle_flag">
- </member>
<member name="anim_offset" type="float" setter="set_param" getter="get_param">
</member>
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
@@ -181,7 +179,7 @@
</constant>
<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
</constant>
- <constant name="FLAG_MAX" value="4" enum="Flags">
+ <constant name="FLAG_MAX" value="3" enum="Flags">
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
</constant>
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index 6d115e2650..bae725d47c 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -39,8 +39,6 @@
</member>
<member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
</member>
- <member name="anim_loop" type="bool" setter="set_particle_flag" getter="get_particle_flag">
- </member>
<member name="anim_offset" type="float" setter="set_param" getter="get_param">
</member>
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
@@ -177,7 +175,7 @@
</constant>
<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
</constant>
- <constant name="FLAG_MAX" value="2" enum="Flags">
+ <constant name="FLAG_MAX" value="1" enum="Flags">
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
</constant>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 60f097f3f9..4bae656740 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -11,8 +11,8 @@
Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/CanvasItemMaterial.xml b/doc/classes/CanvasItemMaterial.xml
index fe7194dcfe..69d873f446 100644
--- a/doc/classes/CanvasItemMaterial.xml
+++ b/doc/classes/CanvasItemMaterial.xml
@@ -19,6 +19,14 @@
<member name="light_mode" type="int" setter="set_light_mode" getter="get_light_mode" enum="CanvasItemMaterial.LightMode">
The manner in which material reacts to lighting.
</member>
+ <member name="particles_anim_h_frames" type="int" setter="set_particles_anim_h_frames" getter="get_particles_anim_h_frames">
+ </member>
+ <member name="particles_anim_loop" type="bool" setter="set_particles_anim_loop" getter="get_particles_anim_loop">
+ </member>
+ <member name="particles_anim_v_frames" type="int" setter="set_particles_anim_v_frames" getter="get_particles_anim_v_frames">
+ </member>
+ <member name="particles_animation" type="bool" setter="set_particles_animation" getter="get_particles_animation">
+ </member>
</members>
<constants>
<constant name="BLEND_MODE_MIX" value="0" enum="BlendMode">
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index d4412e15c9..c39b47ab07 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -7,8 +7,8 @@
Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [code]CanvasLayer[/code] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [code]CanvasLayer[/code] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below).
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/canvas_layers.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/canvas_layers.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml
index 5639c14192..740a6b7ab1 100644
--- a/doc/classes/CollisionShape.xml
+++ b/doc/classes/CollisionShape.xml
@@ -7,7 +7,7 @@
Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject.shape_owner_get_shape] to get the actual shape.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml
index 713cb8d098..d3c616ee3d 100644
--- a/doc/classes/CollisionShape2D.xml
+++ b/doc/classes/CollisionShape2D.xml
@@ -7,7 +7,7 @@
Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject2D.shape_owner_get_shape] to get the actual shape.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml
index a4709c1c86..703043294b 100644
--- a/doc/classes/ConfigFile.xml
+++ b/doc/classes/ConfigFile.xml
@@ -25,6 +25,7 @@
# Save the changes by overwriting the previous file
config.save("user://settings.cfg")
[/codeblock]
+ Keep in mind that section and property names can't contain spaces. Anything after a space will be ignored on save and on load.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index ee82afd592..76a475e49d 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -13,8 +13,8 @@
[Theme] resources change the Control's appearance. If you change the [Theme] on a [code]Control[/code] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/gui/index.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/gui/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index 06c996e13e..a9e2a38dcf 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -41,6 +41,17 @@
Erase a dictionary key/value pair by key.
</description>
</method>
+ <method name="get">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="key" type="Variant">
+ </argument>
+ <argument index="1" name="default" type="Variant" default="Null">
+ </argument>
+ <description>
+ Returns the current value for the specified key in the [code]Dictionary[/code]. If the key does not exist, the method returns the value of the optional default argument, or Null if it is omitted.
+ </description>
+ </method>
<method name="has">
<return type="bool">
</return>
diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml
index 2dc522083d..86c8f2f03a 100644
--- a/doc/classes/DirectionalLight.xml
+++ b/doc/classes/DirectionalLight.xml
@@ -7,7 +7,7 @@
A directional light is a type of [Light] node that models an infinite number of parallel rays covering the entire scene. It is used for lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored. Only the basis is used do determine light direction.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml
index d8ad208fa7..57301f954f 100644
--- a/doc/classes/Directory.xml
+++ b/doc/classes/Directory.xml
@@ -23,7 +23,7 @@
[/codeblock]
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/DynamicFontData.xml b/doc/classes/DynamicFontData.xml
index 7b34d02316..5fcccf7db9 100644
--- a/doc/classes/DynamicFontData.xml
+++ b/doc/classes/DynamicFontData.xml
@@ -13,6 +13,9 @@
<methods>
</methods>
<members>
+ <member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased">
+ Controls whether the font should be rendered with anti-aliasing.
+ </member>
<member name="font_path" type="String" setter="set_font_path" getter="get_font_path">
The path to the vector font file.
</member>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index aaba30ccc9..24201c39b9 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -49,7 +49,7 @@
[/codeblock]
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/plugins/editor/import_plugins.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/plugins/editor/import_plugins.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
index a2a39fc8b6..5601f9b5ae 100644
--- a/doc/classes/EditorInspector.xml
+++ b/doc/classes/EditorInspector.xml
@@ -41,6 +41,14 @@
<description>
</description>
</signal>
+ <signal name="property_toggled">
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="checked" type="bool">
+ </argument>
+ <description>
+ </description>
+ </signal>
<signal name="resource_selected">
<argument index="0" name="res" type="Object">
</argument>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index ac139f18c9..feaa24b0ab 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -7,7 +7,7 @@
Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. Also see [EditorScript] to add functions to the editor.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/development/plugins/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/development/plugins/index.html</link>
</tutorials>
<demos>
</demos>
@@ -453,7 +453,7 @@
<argument index="0" name="screen_name" type="String">
</argument>
<description>
- Emitted when user change the workspace (2D, 3D, Script, AssetLib). Also works with custom screens defined by plugins.
+ Emitted when user changes the workspace (2D, 3D, Script, AssetLib). Also works with custom screens defined by plugins.
</description>
</signal>
<signal name="resource_saved">
@@ -466,14 +466,14 @@
<argument index="0" name="scene_root" type="Node">
</argument>
<description>
- Emitted when user change scene. The argument is a root node of freshly opened scene.
+ Emitted when the scene is changed in the editor. The argument will return the root node of the scene that has just become active. If this scene is new and empty, the argument will be null.
</description>
</signal>
<signal name="scene_closed">
<argument index="0" name="filepath" type="String">
</argument>
<description>
- Emitted when user close scene. The argument is file path to a closed scene.
+ Emitted when user closes a scene. The argument is file path to a closed scene.
</description>
</signal>
</signals>
diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml
index 76c105dd25..0c5de68d98 100644
--- a/doc/classes/EditorScenePostImport.xml
+++ b/doc/classes/EditorScenePostImport.xml
@@ -26,7 +26,7 @@
[/codeblock]
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 566322e6c5..949c93761e 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -5,7 +5,6 @@
</brief_description>
<description>
Resource for environment nodes (like [WorldEnvironment]) that define multiple environment operations (such as background [Sky] or [Color], ambient light, fog, depth-of-field...). These parameters affect the final render of the scene. The order of these operations is:
-
- DOF Blur
- Motion Blur
- Bloom
@@ -13,8 +12,8 @@
- Adjustments
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/high_dynamic_range.html</link>
</tutorials>
<demos>
</demos>
@@ -120,6 +119,8 @@
<member name="fog_depth_enabled" type="bool" setter="set_fog_depth_enabled" getter="is_fog_depth_enabled">
Enables the fog depth.
</member>
+ <member name="fog_depth_end" type="float" setter="set_fog_depth_end" getter="get_fog_depth_end">
+ </member>
<member name="fog_enabled" type="bool" setter="set_fog_enabled" getter="is_fog_enabled">
Enables the fog. Needs fog_height_enabled and/or for_depth_enabled to actually display fog.
</member>
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
index d5b831a61a..c5f71fe55e 100644
--- a/doc/classes/Expression.xml
+++ b/doc/classes/Expression.xml
@@ -1,8 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Expression" inherits="Reference" category="Core" version="3.1">
<brief_description>
+ A class that stores an expression you can execute.
</brief_description>
<description>
+ An expression can be made of any arithmetic operation, built-in math function call, method call of a passed instance, or built-in type construction call.
+ An example expression text using the built-in math functions could be [code]sqrt(pow(3,2) + pow(4,2))[/code].
+ In the following example we use a [LineEdit] node to write our expression and show the result.
+ [codeblock]
+ onready var expression = Expression.new()
+
+ func _ready():
+ $LineEdit.connect("text_entered", self, "_on_text_entered")
+
+ func _on_text_entered(command):
+ var error = expression.parse(command, [])
+ if error != OK:
+ print(get_error_text())
+ return
+ var result = expression.execute([], null, true)
+ if not expression.has_execute_failed():
+ $LineEdit.text = str(result)
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -19,18 +38,21 @@
<argument index="2" name="show_error" type="bool" default="true">
</argument>
<description>
+ Executes the expression that was previously parsed by [method parse] and returns the result. Before you use the returned object, you should check if the method failed by calling [method has_execute_failed].
</description>
</method>
<method name="get_error_text" qualifiers="const">
<return type="String">
</return>
<description>
+ Returns the error text if [method parse] has failed.
</description>
</method>
<method name="has_execute_failed" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if [method execute] has failed.
</description>
</method>
<method name="parse">
@@ -41,6 +63,7 @@
<argument index="1" name="input_names" type="PoolStringArray" default="PoolStringArray( )">
</argument>
<description>
+ Parses the expression and returns a [enum @GlobalScope.Error].
</description>
</method>
</methods>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 20054ac9dc..058bb09090 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -22,7 +22,7 @@
[/codeblock]
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link>
</tutorials>
<demos>
</demos>
@@ -100,7 +100,7 @@
<argument index="0" name="delim" type="String" default="&quot;,&quot;">
</argument>
<description>
- Returns the next value of the file in CSV (Comma Separated Values) format. You can pass a different delimiter to use other than the default "," (comma).
+ Returns the next value of the file in CSV (Comma Separated Values) format. You can pass a different delimiter to use other than the default "," (comma), it should be one character long.
</description>
</method>
<method name="get_double" qualifiers="const">
@@ -327,6 +327,17 @@
Stores the given array of bytes in the file.
</description>
</method>
+ <method name="store_csv_line">
+ <return type="void">
+ </return>
+ <argument index="0" name="values" type="PoolStringArray">
+ </argument>
+ <argument index="1" name="delim" type="String" default="&quot;,&quot;">
+ </argument>
+ <description>
+ Store the given [PoolStringArray] in the file as a line formatted in the CSV (Comma Separated Values) format. You can pass a different delimiter to use other than the default "," (comma), it should be one character long.
+ </description>
+ </method>
<method name="store_double">
<return type="void">
</return>
diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml
index 247228d265..29aa26b67f 100644
--- a/doc/classes/FileDialog.xml
+++ b/doc/classes/FileDialog.xml
@@ -17,7 +17,7 @@
<argument index="0" name="filter" type="String">
</argument>
<description>
- Add a custom filter. Filter format is: "mask ; description", example (C++): dialog-&gt;add_filter("*.png ; PNG Images");
+ Add a custom filter. Example: [code]add_filter("*.png ; PNG Images")[/code]
</description>
</method>
<method name="clear_filters">
@@ -31,12 +31,14 @@
<return type="void">
</return>
<description>
+ Clear currently selected items in the dialog.
</description>
</method>
<method name="get_line_edit">
<return type="LineEdit">
</return>
<description>
+ Returns the LineEdit for the selected file.
</description>
</method>
<method name="get_vbox">
@@ -56,6 +58,7 @@
</methods>
<members>
<member name="access" type="int" setter="set_access" getter="get_access" enum="FileDialog.Access">
+ The file system access scope. See enum [code]Access[/code] constants.
</member>
<member name="current_dir" type="String" setter="set_current_dir" getter="get_current_dir">
The current working directory of the file dialog.
@@ -67,13 +70,16 @@
The currently selected file path of the file dialog.
</member>
<member name="filters" type="PoolStringArray" setter="set_filters" getter="get_filters">
+ Set file type filters. This example shows only .png and .gd files [code]set_filters(PoolStringArray(["*.png ; PNG Images","*.gd ; GD Script"]))[/code].
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="FileDialog.Mode">
+ Set dialog to open or save mode, changes selection behavior. See enum [code]Mode[/code] constants.
</member>
<member name="mode_overrides_title" type="bool" setter="set_mode_overrides_title" getter="is_mode_overriding_title">
- If [code]true[/code], changing the [code]mode[/code] property will set the window title accordingly (e. g. setting mode to [code]MODE_OPEN_FILE[/code] will change the window title to "Open a File").
+ If [code]true[/code], changing the [code]Mode[/code] property will set the window title accordingly (e. g. setting mode to [code]MODE_OPEN_FILE[/code] will change the window title to "Open a File").
</member>
<member name="show_hidden_files" type="bool" setter="set_show_hidden_files" getter="is_showing_hidden_files">
+ If [code]true[/code], the dialog will show hidden files.
</member>
</members>
<signals>
diff --git a/doc/classes/GIProbe.xml b/doc/classes/GIProbe.xml
index 77dea73564..5fb0ccc33d 100644
--- a/doc/classes/GIProbe.xml
+++ b/doc/classes/GIProbe.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/gi_probes.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/gi_probes.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml
index 0863ead4ec..e1046e282a 100644
--- a/doc/classes/Generic6DOFJoint.xml
+++ b/doc/classes/Generic6DOFJoint.xml
@@ -113,6 +113,30 @@
<member name="angular_motor_z/target_velocity" type="float" setter="set_param_z" getter="get_param_z">
Target speed for the motor at the z-axis.
</member>
+ <member name="angular_spring_x/damping" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="angular_spring_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x">
+ </member>
+ <member name="angular_spring_x/equilibrium_point" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="angular_spring_x/stiffness" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="angular_spring_y/damping" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="angular_spring_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y">
+ </member>
+ <member name="angular_spring_y/equilibrium_point" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="angular_spring_y/stiffness" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="angular_spring_z/damping" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
+ <member name="angular_spring_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z">
+ </member>
+ <member name="angular_spring_z/equilibrium_point" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
+ <member name="angular_spring_z/stiffness" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
<member name="linear_limit_x/damping" type="float" setter="set_param_x" getter="get_param_x">
The amount of damping that happens at the x-motion.
</member>
@@ -194,6 +218,30 @@
<member name="linear_motor_z/target_velocity" type="float" setter="set_param_z" getter="get_param_z">
The speed that the linear motor will attempt to reach on the z-axis.
</member>
+ <member name="linear_spring_x/damping" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="linear_spring_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x">
+ </member>
+ <member name="linear_spring_x/equilibrium_point" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="linear_spring_x/stiffness" type="float" setter="set_param_x" getter="get_param_x">
+ </member>
+ <member name="linear_spring_y/damping" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="linear_spring_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y">
+ </member>
+ <member name="linear_spring_y/equilibrium_point" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="linear_spring_y/stiffness" type="float" setter="set_param_y" getter="get_param_y">
+ </member>
+ <member name="linear_spring_z/damping" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
+ <member name="linear_spring_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z">
+ </member>
+ <member name="linear_spring_z/equilibrium_point" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
+ <member name="linear_spring_z/stiffness" type="float" setter="set_param_z" getter="get_param_z">
+ </member>
</members>
<constants>
<constant name="PARAM_LINEAR_LOWER_LIMIT" value="0" enum="Param">
@@ -217,34 +265,34 @@
<constant name="PARAM_LINEAR_MOTOR_FORCE_LIMIT" value="6" enum="Param">
The maximum force the linear motor will apply while trying to reach the velocity target.
</constant>
- <constant name="PARAM_ANGULAR_LOWER_LIMIT" value="7" enum="Param">
+ <constant name="PARAM_ANGULAR_LOWER_LIMIT" value="10" enum="Param">
The minimum rotation in negative direction to break loose and rotate around the axes.
</constant>
- <constant name="PARAM_ANGULAR_UPPER_LIMIT" value="8" enum="Param">
+ <constant name="PARAM_ANGULAR_UPPER_LIMIT" value="11" enum="Param">
The minimum rotation in positive direction to break loose and rotate around the axes.
</constant>
- <constant name="PARAM_ANGULAR_LIMIT_SOFTNESS" value="9" enum="Param">
+ <constant name="PARAM_ANGULAR_LIMIT_SOFTNESS" value="12" enum="Param">
The speed of all rotations across the axes.
</constant>
- <constant name="PARAM_ANGULAR_DAMPING" value="10" enum="Param">
+ <constant name="PARAM_ANGULAR_DAMPING" value="13" enum="Param">
The amount of rotational damping across the axes. The lower, the more dampening occurs.
</constant>
- <constant name="PARAM_ANGULAR_RESTITUTION" value="11" enum="Param">
+ <constant name="PARAM_ANGULAR_RESTITUTION" value="14" enum="Param">
The amount of rotational restitution across the axes. The lower, the more restitution occurs.
</constant>
- <constant name="PARAM_ANGULAR_FORCE_LIMIT" value="12" enum="Param">
+ <constant name="PARAM_ANGULAR_FORCE_LIMIT" value="15" enum="Param">
The maximum amount of force that can occur, when rotating around the axes.
</constant>
- <constant name="PARAM_ANGULAR_ERP" value="13" enum="Param">
+ <constant name="PARAM_ANGULAR_ERP" value="16" enum="Param">
When rotating across the axes, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower.
</constant>
- <constant name="PARAM_ANGULAR_MOTOR_TARGET_VELOCITY" value="14" enum="Param">
+ <constant name="PARAM_ANGULAR_MOTOR_TARGET_VELOCITY" value="17" enum="Param">
Target speed for the motor at the axes.
</constant>
- <constant name="PARAM_ANGULAR_MOTOR_FORCE_LIMIT" value="15" enum="Param">
+ <constant name="PARAM_ANGULAR_MOTOR_FORCE_LIMIT" value="18" enum="Param">
Maximum acceleration for the motor at the axes.
</constant>
- <constant name="PARAM_MAX" value="16" enum="Param">
+ <constant name="PARAM_MAX" value="22" enum="Param">
End flag of PARAM_* constants, used internally.
</constant>
<constant name="FLAG_ENABLE_LINEAR_LIMIT" value="0" enum="Flag">
@@ -253,12 +301,16 @@
<constant name="FLAG_ENABLE_ANGULAR_LIMIT" value="1" enum="Flag">
If [code]set[/code] there is rotational motion possible.
</constant>
- <constant name="FLAG_ENABLE_MOTOR" value="2" enum="Flag">
+ <constant name="FLAG_ENABLE_LINEAR_SPRING" value="3" enum="Flag">
+ </constant>
+ <constant name="FLAG_ENABLE_ANGULAR_SPRING" value="2" enum="Flag">
+ </constant>
+ <constant name="FLAG_ENABLE_MOTOR" value="4" enum="Flag">
If [code]set[/code] there is a rotational motor across these axes.
</constant>
- <constant name="FLAG_ENABLE_LINEAR_MOTOR" value="3" enum="Flag">
+ <constant name="FLAG_ENABLE_LINEAR_MOTOR" value="5" enum="Flag">
</constant>
- <constant name="FLAG_MAX" value="4" enum="Flag">
+ <constant name="FLAG_MAX" value="6" enum="Flag">
End flag of FLAG_* constants, used internally.
</constant>
</constants>
diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml
index df4a507b65..bf3f125e48 100644
--- a/doc/classes/Gradient.xml
+++ b/doc/classes/Gradient.xml
@@ -4,7 +4,7 @@
Color interpolator node.
</brief_description>
<description>
- Given a set of colors, this node will interpolate them in order, meaning, that if you have color 1, color 2 and color3, the ramp will interpolate (generate the colors between two colors) from color 1 to color 2 and from color 2 to color 3. Initially the ramp will have 2 colors (black and white), one (black) at ramp lower offset offset 0 and the other (white) at the ramp higher offset 1.
+ Given a set of colors, this node will interpolate them in order, meaning, that if you have color 1, color 2 and color 3, the ramp will interpolate (generate the colors between two colors) from color 1 to color 2 and from color 2 to color 3. Initially the ramp will have 2 colors (black and white), one (black) at ramp lower offset 0 and the other (white) at the ramp higher offset 1.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml
index 8a8a9a2d24..346ab9d357 100644
--- a/doc/classes/GridContainer.xml
+++ b/doc/classes/GridContainer.xml
@@ -11,16 +11,6 @@
<demos>
</demos>
<methods>
- <method name="get_child_control_at_cell">
- <return type="Control">
- </return>
- <argument index="0" name="row" type="int">
- </argument>
- <argument index="1" name="column" type="int">
- </argument>
- <description>
- </description>
- </method>
</methods>
<members>
<member name="columns" type="int" setter="set_columns" getter="get_columns">
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 08e2f649a0..2f21505757 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -10,8 +10,8 @@
For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616).
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/http_client_class.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/http_client_class.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml
index c5bb10a23a..3916eb2b5b 100644
--- a/doc/classes/HTTPRequest.xml
+++ b/doc/classes/HTTPRequest.xml
@@ -8,7 +8,7 @@
Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 10ec15b99d..724e6a078d 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -7,7 +7,7 @@
A Singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the Project Settings / Input Map tab. Or be set with [InputMap].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/index.html</link>
</tutorials>
<demos>
</demos>
@@ -17,8 +17,11 @@
</return>
<argument index="0" name="action" type="String">
</argument>
+ <argument index="1" name="strength" type="float" default="1.0">
+ </argument>
<description>
This will simulate pressing the specified action.
+ The strength can be used for non-boolean actions, it's ranged between 0 and 1 representing the intensity of the given action.
</description>
</method>
<method name="action_release">
@@ -123,7 +126,7 @@
<argument index="0" name="button_index" type="int">
</argument>
<description>
- Receives a [code]JOY_BUTTON_*[/code] Enum and returns it's equivalent name as a string.
+ Receives a [code]JOY_BUTTON_*[/code] Enum and returns its equivalent name as a string.
</description>
</method>
<method name="get_joy_guid" qualifiers="const">
diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml
index 993d62d188..c880823aee 100644
--- a/doc/classes/InputEvent.xml
+++ b/doc/classes/InputEvent.xml
@@ -7,8 +7,8 @@
Base class of all sort of input event. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml
index 16000231cb..8a10dc6ab9 100644
--- a/doc/classes/InputEventAction.xml
+++ b/doc/classes/InputEventAction.xml
@@ -7,7 +7,7 @@
Contains a generic action which can be targeted from several type of inputs. Actions can be created from the project settings menu [code]Project &gt; Project Settings &gt; Input Map[/code]. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#actions</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#actions</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventJoypadButton.xml b/doc/classes/InputEventJoypadButton.xml
index adaeae685e..18e33c2131 100644
--- a/doc/classes/InputEventJoypadButton.xml
+++ b/doc/classes/InputEventJoypadButton.xml
@@ -7,7 +7,7 @@
Input event type for gamepad buttons. For joysticks see [InputEventJoypadMotion].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventJoypadMotion.xml b/doc/classes/InputEventJoypadMotion.xml
index f86aec4ce0..0c73f53158 100644
--- a/doc/classes/InputEventJoypadMotion.xml
+++ b/doc/classes/InputEventJoypadMotion.xml
@@ -7,7 +7,7 @@
Stores information about joystick motions. One [code]InputEventJoypadMotion[/code] represents one axis at a time.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml
index a013ee6266..0118fda2df 100644
--- a/doc/classes/InputEventKey.xml
+++ b/doc/classes/InputEventKey.xml
@@ -7,7 +7,7 @@
Stores key presses on the keyboard. Supports key presses, key releases and [member echo] events.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml
index 06de96890a..27e8d17407 100644
--- a/doc/classes/InputEventMouse.xml
+++ b/doc/classes/InputEventMouse.xml
@@ -7,7 +7,7 @@
Stores general mouse events information.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml
index a3a9055087..1342c7bf7e 100644
--- a/doc/classes/InputEventMouseButton.xml
+++ b/doc/classes/InputEventMouseButton.xml
@@ -7,7 +7,7 @@
Contains mouse click information. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml
index 05e3e79d26..f7f6358910 100644
--- a/doc/classes/InputEventMouseMotion.xml
+++ b/doc/classes/InputEventMouseMotion.xml
@@ -7,7 +7,7 @@
Contains mouse motion information. Supports relative, absolute positions and speed. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml
index f777d90ccb..a320c0a028 100644
--- a/doc/classes/InputEventScreenDrag.xml
+++ b/doc/classes/InputEventScreenDrag.xml
@@ -8,7 +8,7 @@
Contains screen drag information. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml
index 39cd0a9657..55f04cdd94 100644
--- a/doc/classes/InputEventScreenTouch.xml
+++ b/doc/classes/InputEventScreenTouch.xml
@@ -8,7 +8,7 @@
Stores multi-touch press/release information. Supports touch press, touch release and [member index] for multi-touch count and order.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml
index 9c1814fedd..fcf2fd545e 100644
--- a/doc/classes/InputEventWithModifiers.xml
+++ b/doc/classes/InputEventWithModifiers.xml
@@ -7,7 +7,7 @@
Contains keys events information with modifiers support like [code]SHIFT[/code] or [code]ALT[/code]. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml
index 7fd1d7e8ac..8c25f851db 100644
--- a/doc/classes/InputMap.xml
+++ b/doc/classes/InputMap.xml
@@ -7,7 +7,7 @@
Manages all [InputEventAction] which can be created/modified from the project settings menu [code]Project &gt; Project Settings &gt; Input Map[/code] or in code with [method add_action] and [method action_add_event]. See [method Node._input].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#inputmap</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#inputmap</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 4e4947de6c..0b8ede92d5 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -166,6 +166,14 @@
Returns whether or not the item at the specified index is disabled
</description>
</method>
+ <method name="is_item_icon_transposed" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="is_item_selectable" qualifiers="const">
<return type="bool">
</return>
@@ -289,6 +297,16 @@
<description>
</description>
</method>
+ <method name="set_item_icon_transposed">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="rect" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_item_metadata">
<return type="void">
</return>
diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml
index 17588717c2..fa3aaed684 100644
--- a/doc/classes/JavaScript.xml
+++ b/doc/classes/JavaScript.xml
@@ -7,7 +7,7 @@
The JavaScript singleton is implemented only in HTML5 export. It's used to access the browser's JavaScript context. This allows interaction with embedding pages or calling third-party JavaScript APIs.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml
index 82638fc57a..df8fb251ba 100644
--- a/doc/classes/KinematicBody.xml
+++ b/doc/classes/KinematicBody.xml
@@ -9,7 +9,7 @@
Kinematic Characters: KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/kinematic_character_2d.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/kinematic_character_2d.html</link>
</tutorials>
<demos>
</demos>
@@ -106,13 +106,13 @@
</argument>
<argument index="2" name="floor_normal" type="Vector3" default="Vector3( 0, 0, 0 )">
</argument>
- <argument index="3" name="infinite_inertia" type="bool" default="true">
+ <argument index="3" name="stop_on_slope" type="bool" default="false">
</argument>
- <argument index="4" name="stop_on_slope" type="bool" default="false">
+ <argument index="4" name="max_slides" type="int" default="4">
</argument>
- <argument index="5" name="max_bounces" type="int" default="4">
+ <argument index="5" name="floor_max_angle" type="float" default="0.785398">
</argument>
- <argument index="6" name="floor_max_angle" type="float" default="0.785398">
+ <argument index="6" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Moves the body while keeping it attached to slopes. Similar to [method move_and_slide].
@@ -126,7 +126,7 @@
</argument>
<argument index="1" name="rel_vec" type="Vector3">
</argument>
- <argument index="2" name="infinite_inertia" type="bool">
+ <argument index="2" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Checks for collisions without moving the body. Virtually sets the node's position, scale and rotation to that of the given [Transform], then tries to move the body along the vector [code]rel_vec[/code]. Returns [code]true[/code] if a collision would occur.
diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml
index e48660a889..8a644447ca 100644
--- a/doc/classes/KinematicBody2D.xml
+++ b/doc/classes/KinematicBody2D.xml
@@ -79,20 +79,20 @@
</argument>
<argument index="1" name="floor_normal" type="Vector2" default="Vector2( 0, 0 )">
</argument>
- <argument index="2" name="infinite_inertia" type="bool" default="true">
+ <argument index="2" name="stop_on_slope" type="bool" default="false">
</argument>
- <argument index="3" name="stop_on_slope" type="bool" default="false">
+ <argument index="3" name="max_slides" type="int" default="4">
</argument>
- <argument index="4" name="max_bounces" type="int" default="4">
+ <argument index="4" name="floor_max_angle" type="float" default="0.785398">
</argument>
- <argument index="5" name="floor_max_angle" type="float" default="0.785398">
+ <argument index="5" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody2D[/code] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes.
[code]linear_velocity[/code] is a value in pixels per second. Unlike in for example [method move_and_collide], you should [i]not[/i] multiply it with [code]delta[/code] — this is done by the method.
[code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
[i]TODO: Update for stop_on_slope argument.[/i] If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes.
- If the body collides, it will change direction a maximum of [code]max_bounces[/code] times before it stops.
+ If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops.
[code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees.
Returns the movement that remained when the body stopped. To get more detailed information about collisions that occurred, use [method get_slide_collision].
</description>
@@ -106,17 +106,17 @@
</argument>
<argument index="2" name="floor_normal" type="Vector2" default="Vector2( 0, 0 )">
</argument>
- <argument index="3" name="infinite_inertia" type="bool" default="true">
+ <argument index="3" name="stop_on_slope" type="bool" default="false">
</argument>
- <argument index="4" name="stop_on_slope" type="bool" default="false">
+ <argument index="4" name="max_slides" type="int" default="4">
</argument>
- <argument index="5" name="max_bounces" type="int" default="4">
+ <argument index="5" name="floor_max_angle" type="float" default="0.785398">
</argument>
- <argument index="6" name="floor_max_angle" type="float" default="0.785398">
+ <argument index="6" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Moves the body while keeping it attached to slopes. Similar to [method move_and_slide].
- As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting[code]snap[/code] to[code](0, 0)[/code] or by using [method move_and_slide] instead.
+ As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code](0, 0)[/code] or by using [method move_and_slide] instead.
</description>
</method>
<method name="test_move">
@@ -126,7 +126,7 @@
</argument>
<argument index="1" name="rel_vec" type="Vector2">
</argument>
- <argument index="2" name="infinite_inertia" type="bool">
+ <argument index="2" name="infinite_inertia" type="bool" default="true">
</argument>
<description>
Checks for collisions without moving the body. Virtually sets the node's position, scale and rotation to that of the given [Transform2D], then tries to move the body along the vector [code]rel_vec[/code]. Returns [code]true[/code] if a collision would occur.
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 1e78a196b1..90547b7c2f 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -30,7 +30,7 @@
<return type="int">
</return>
<description>
- Returns the total length of the text.
+ Returns the total number of printable characters in the text (excluding spaces and newlines).
</description>
</method>
<method name="get_visible_line_count" qualifiers="const">
diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml
index 04191136a8..cf873e5b28 100644
--- a/doc/classes/Light.xml
+++ b/doc/classes/Light.xml
@@ -7,7 +7,7 @@
Light is the abstract base class for light nodes, so it shouldn't be used directly (It can't be instanced). Other types of light nodes inherit from it. Light contains the common variables and parameters used for lighting.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index c244b8b7a7..f842dd8311 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -89,6 +89,7 @@
The cursor's position inside the [code]LineEdit[/code]. When set, the text may scroll to accommodate it.
</member>
<member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled">
+ If [code]true[/code] the [code]LineEdit[/code] will show a clear button if [code]text[/code] is not empty.
</member>
<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled">
If [code]true[/code] the context menu will appear when right clicked.
@@ -169,6 +170,7 @@
Undoes the previous action.
</constant>
<constant name="MENU_REDO" value="6" enum="MenuItems">
+ Reverse the last undo action.
</constant>
<constant name="MENU_MAX" value="7" enum="MenuItems">
</constant>
diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml
index 43d94004a8..c90dba1561 100644
--- a/doc/classes/MeshDataTool.xml
+++ b/doc/classes/MeshDataTool.xml
@@ -1,8 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="MeshDataTool" inherits="Reference" category="Core" version="3.1">
<brief_description>
+ Helper tool to access and edit [Mesh] data.
</brief_description>
<description>
+ The MeshDataTool provides access to individual vertices in a [Mesh]. It allows users to read and edit vertex data of meshes. It also creates an array of faces and edges.
+ To use the MeshDataTool, load a mesh with [method create_from_surface]. When you are finished editing the data commit the data to a mesh with [method commit_to_surface].
+ Below is an example of how the MeshDataTool may be used.
+ [codeblock]
+ var mdt = MeshDataTool.new()
+ mdt.create_from_surface(mesh, 0)
+ for i in range(mdt.get_vertex_count()):
+ var vertex = mdt.get_vertex(i)
+ ...
+ mdt.set_vertex(i, vertex)
+ mesh.surface_remove(0)
+ mdt.commit_to_surface(mesh)
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -13,6 +27,7 @@
<return type="void">
</return>
<description>
+ Clears all data currently in MeshDataTool.
</description>
</method>
<method name="commit_to_surface">
@@ -21,6 +36,7 @@
<argument index="0" name="mesh" type="ArrayMesh">
</argument>
<description>
+ Adds a new surface to specified [Mesh] with edited data.
</description>
</method>
<method name="create_from_surface">
@@ -31,12 +47,15 @@
<argument index="1" name="surface" type="int">
</argument>
<description>
+ Uses specified surface of given [Mesh] to populate data for MeshDataTool.
+ Requires [Mesh] with primitive type [code]PRIMITIVE_TRIANGLES[/code].
</description>
</method>
<method name="get_edge_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of edges in this [Mesh].
</description>
</method>
<method name="get_edge_faces" qualifiers="const">
@@ -45,6 +64,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns array of faces that touch given edge.
</description>
</method>
<method name="get_edge_meta" qualifiers="const">
@@ -53,6 +73,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns meta information assigned to given edge.
</description>
</method>
<method name="get_edge_vertex" qualifiers="const">
@@ -63,12 +84,15 @@
<argument index="1" name="vertex" type="int">
</argument>
<description>
+ Returns index of specified vertex connected to given edge.
+ Vertex argument can only be 0 or 1 because edges are comprised of two vertices.
</description>
</method>
<method name="get_face_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of faces in this [Mesh].
</description>
</method>
<method name="get_face_edge" qualifiers="const">
@@ -79,6 +103,8 @@
<argument index="1" name="edge" type="int">
</argument>
<description>
+ Returns specified edge associated with given face.
+ Edge argument must 2 or less because a face only has three edges.
</description>
</method>
<method name="get_face_meta" qualifiers="const">
@@ -87,6 +113,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns meta data associated with given face.
</description>
</method>
<method name="get_face_normal" qualifiers="const">
@@ -95,6 +122,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Calculates and returns face normal of given face.
</description>
</method>
<method name="get_face_vertex" qualifiers="const">
@@ -105,18 +133,23 @@
<argument index="1" name="vertex" type="int">
</argument>
<description>
+ Returns specified vertex of given face.
+ Vertex argument must be 2 or less because faces contain three vertices.
</description>
</method>
<method name="get_format" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns format of [Mesh]. Format is an integer made up of [Mesh] format flags combined together. For example, a mesh containing both vertices and normals would return a format of [code]3[/code] because [code]ARRAY_FORMAT_VERTEX[/code] is [code]1[/code] and [code]ARRAY_FORMAT_NORMAL[/code] is [code]2[/code].
+ For list of format flags see [ArrayMesh].
</description>
</method>
<method name="get_material" qualifiers="const">
<return type="Material">
</return>
<description>
+ Returns material assigned to the [Mesh].
</description>
</method>
<method name="get_vertex" qualifiers="const">
@@ -125,6 +158,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns the vertex at given index.
</description>
</method>
<method name="get_vertex_bones" qualifiers="const">
@@ -133,6 +167,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns the bones of the given vertex.
</description>
</method>
<method name="get_vertex_color" qualifiers="const">
@@ -141,12 +176,14 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns the color of the given vertex.
</description>
</method>
<method name="get_vertex_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the total number of vertices in [Mesh].
</description>
</method>
<method name="get_vertex_edges" qualifiers="const">
@@ -155,6 +192,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns array of edges that share given vertex.
</description>
</method>
<method name="get_vertex_faces" qualifiers="const">
@@ -163,6 +201,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns array of faces that share given vertex.
</description>
</method>
<method name="get_vertex_meta" qualifiers="const">
@@ -171,6 +210,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns meta data associated with given vertex.
</description>
</method>
<method name="get_vertex_normal" qualifiers="const">
@@ -179,6 +219,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns normal of given vertex.
</description>
</method>
<method name="get_vertex_tangent" qualifiers="const">
@@ -187,6 +228,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns tangent of given vertex.
</description>
</method>
<method name="get_vertex_uv" qualifiers="const">
@@ -195,6 +237,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns UV of given vertex.
</description>
</method>
<method name="get_vertex_uv2" qualifiers="const">
@@ -203,6 +246,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns UV2 of given vertex.
</description>
</method>
<method name="get_vertex_weights" qualifiers="const">
@@ -211,6 +255,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns bone weights of given vertex.
</description>
</method>
<method name="set_edge_meta">
@@ -221,6 +266,7 @@
<argument index="1" name="meta" type="Variant">
</argument>
<description>
+ Sets the meta data of given edge.
</description>
</method>
<method name="set_face_meta">
@@ -231,6 +277,7 @@
<argument index="1" name="meta" type="Variant">
</argument>
<description>
+ Sets the meta data of given face.
</description>
</method>
<method name="set_material">
@@ -239,6 +286,7 @@
<argument index="0" name="material" type="Material">
</argument>
<description>
+ Sets the material to be used by newly constructed [Mesh].
</description>
</method>
<method name="set_vertex">
@@ -249,6 +297,7 @@
<argument index="1" name="vertex" type="Vector3">
</argument>
<description>
+ Sets the position of given vertex.
</description>
</method>
<method name="set_vertex_bones">
@@ -259,6 +308,7 @@
<argument index="1" name="bones" type="PoolIntArray">
</argument>
<description>
+ Sets the bones of given vertex.
</description>
</method>
<method name="set_vertex_color">
@@ -269,6 +319,7 @@
<argument index="1" name="color" type="Color">
</argument>
<description>
+ Sets the color of given vertex.
</description>
</method>
<method name="set_vertex_meta">
@@ -279,6 +330,7 @@
<argument index="1" name="meta" type="Variant">
</argument>
<description>
+ Sets the meta data associated with given vertex.
</description>
</method>
<method name="set_vertex_normal">
@@ -289,6 +341,7 @@
<argument index="1" name="normal" type="Vector3">
</argument>
<description>
+ Sets the normal of given vertex.
</description>
</method>
<method name="set_vertex_tangent">
@@ -299,6 +352,7 @@
<argument index="1" name="tangent" type="Plane">
</argument>
<description>
+ Sets the tangent of given vertex.
</description>
</method>
<method name="set_vertex_uv">
@@ -309,6 +363,7 @@
<argument index="1" name="uv" type="Vector2">
</argument>
<description>
+ Sets the UV of given vertex.
</description>
</method>
<method name="set_vertex_uv2">
@@ -319,6 +374,7 @@
<argument index="1" name="uv2" type="Vector2">
</argument>
<description>
+ Sets the UV2 of given vertex.
</description>
</method>
<method name="set_vertex_weights">
@@ -329,6 +385,7 @@
<argument index="1" name="weights" type="PoolRealArray">
</argument>
<description>
+ Sets the bone weights of given vertex.
</description>
</method>
</methods>
diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml
index ef42726ca9..bcc9a7cad9 100644
--- a/doc/classes/MeshInstance.xml
+++ b/doc/classes/MeshInstance.xml
@@ -41,6 +41,12 @@
Returns the [Material] for a surface of the [Mesh] resource.
</description>
</method>
+ <method name="get_surface_material_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_surface_material">
<return type="void">
</return>
diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml
index b29e19e5d8..4ede80b98c 100644
--- a/doc/classes/NavigationPolygon.xml
+++ b/doc/classes/NavigationPolygon.xml
@@ -1,8 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationPolygon" inherits="Resource" category="Core" version="3.1">
<brief_description>
+ A node that has methods to draw outlines or use indices of vertices to create navigation polygons.
</brief_description>
<description>
+ There are two ways to create polygons. Either by using the [method add_outline] method or using the [method add_polygon] method.
+ Using [method add_outline]:
+ [code]
+ var polygon = NavigationPolygon.new()
+ var outline = PoolVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)])
+ polygon.add_outline(outline)
+ polygon.make_polygons_from_outlines()
+ $NavigationPolygonInstance.navpoly = polygon
+ [/code]
+ Using [method add_polygon] and indices of the vertices array.
+ [code]
+ var polygon = NavigationPolygon.new()
+ var vertices = PoolVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)])
+ polygon.set_vertices(vertices)
+ var indices = PoolIntArray(0, 3, 1)
+ polygon.add_polygon(indices)
+ $NavigationPolygonInstance.navpoly = polygon
+ [/code]
</description>
<tutorials>
</tutorials>
@@ -15,6 +34,7 @@
<argument index="0" name="outline" type="PoolVector2Array">
</argument>
<description>
+ Appends a [PoolVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use.
</description>
</method>
<method name="add_outline_at_index">
@@ -25,6 +45,7 @@
<argument index="1" name="index" type="int">
</argument>
<description>
+ Adds a [PoolVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines at a fixed position. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use.
</description>
</method>
<method name="add_polygon">
@@ -33,18 +54,21 @@
<argument index="0" name="polygon" type="PoolIntArray">
</argument>
<description>
+ Adds a polygon using the indices of the vertices you get when calling [method get_vertices].
</description>
</method>
<method name="clear_outlines">
<return type="void">
</return>
<description>
+ Clears the array of the outlines, but it doesn't clear the vertices and the polygons that were created by them.
</description>
</method>
<method name="clear_polygons">
<return type="void">
</return>
<description>
+ Clears the array of polygons, but it doesn't clear the array of outlines and vertices.
</description>
</method>
<method name="get_outline" qualifiers="const">
@@ -53,12 +77,14 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns a [PoolVector2Array] containing the vertices of an outline that was created in the editor or by script.
</description>
</method>
<method name="get_outline_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of outlines that were created in the editor or by script.
</description>
</method>
<method name="get_polygon">
@@ -67,24 +93,28 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Returns a [PoolIntArray] containing the indices of the vertices of a created polygon.
</description>
</method>
<method name="get_polygon_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the count of all polygons.
</description>
</method>
<method name="get_vertices" qualifiers="const">
<return type="PoolVector2Array">
</return>
<description>
+ Returns a [PoolVector2Array] containing all the vertices being used to create the polygons.
</description>
</method>
<method name="make_polygons_from_outlines">
<return type="void">
</return>
<description>
+ Creates polygons from the outlines added in the editor or by script.
</description>
</method>
<method name="remove_outline">
@@ -93,6 +123,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Removes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update.
</description>
</method>
<method name="set_outline">
@@ -103,6 +134,7 @@
<argument index="1" name="outline" type="PoolVector2Array">
</argument>
<description>
+ Changes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update.
</description>
</method>
<method name="set_vertices">
@@ -111,6 +143,7 @@
<argument index="0" name="vertices" type="PoolVector2Array">
</argument>
<description>
+ Sets the vertices that can be then indexed to create polygons with the [method add_polygon] method.
</description>
</method>
</methods>
diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml
index ea6fe6d11c..990f928b25 100644
--- a/doc/classes/NetworkedMultiplayerPeer.xml
+++ b/doc/classes/NetworkedMultiplayerPeer.xml
@@ -7,7 +7,7 @@
Manages the connection to network peers. Assigns unique IDs to each client connected to the server.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
</tutorials>
<demos>
</demos>
@@ -92,7 +92,7 @@
</signals>
<constants>
<constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode">
- Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters.
+ Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters.
</constant>
<constant name="TRANSFER_MODE_UNRELIABLE_ORDERED" value="1" enum="TransferMode">
Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [code]TRANSFER_MODE_RELIABLE[/code]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data.
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index a33ee5c363..dcdb6d0500 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -17,7 +17,7 @@
[b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections). To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). Also take a look at the high-level networking tutorial and corresponding demos.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scenes_and_nodes.html</link>
</tutorials>
<demos>
</demos>
@@ -179,6 +179,15 @@
If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through script, because those scenes don't have an owner.
</description>
</method>
+ <method name="find_parent" qualifiers="const">
+ <return type="Node">
+ </return>
+ <argument index="0" name="mask" type="String">
+ </argument>
+ <description>
+ Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names.
+ </description>
+ </method>
<method name="get_child" qualifiers="const">
<return type="Node">
</return>
@@ -267,15 +276,6 @@
Returns the parent node of the current node, or an empty [code]Node[/code] if the node lacks a parent.
</description>
</method>
- <method name="find_parent" qualifiers="const">
- <return type="Node">
- </return>
- <argument index="0" name="mask" type="String">
- </argument>
- <description>
- Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names.
- </description>
- </method>
<method name="get_path" qualifiers="const">
<return type="NodePath">
</return>
diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml
index 13eabeca17..e955f6bb7d 100644
--- a/doc/classes/Node2D.xml
+++ b/doc/classes/Node2D.xml
@@ -7,7 +7,7 @@
A 2D game object, with a position, rotation and scale. All 2D physics nodes and sprites inherit from Node2D. Use Node2D as a parent node to move, scale and rotate children in a 2D project. Also gives control on the node's render order.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 26684836ea..e218949757 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -556,6 +556,13 @@
Note that this method can also be used to kill processes that were not spawned by the game.
</description>
</method>
+ <method name="move_window_to_foreground">
+ <return type="void">
+ </return>
+ <description>
+ Moves the window to the front.
+ </description>
+ </method>
<method name="native_video_is_playing">
<return type="bool">
</return>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index fcd105d66b..86de830d56 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -348,6 +348,16 @@
If set to true, signal emission is blocked.
</description>
</method>
+ <method name="set_deferred">
+ <return type="void">
+ </return>
+ <argument index="0" name="property" type="String">
+ </argument>
+ <argument index="1" name="value" type="Variant">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_indexed">
<return type="void">
</return>
diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml
index 5ed058bb06..8d67cb626b 100644
--- a/doc/classes/OmniLight.xml
+++ b/doc/classes/OmniLight.xml
@@ -7,7 +7,7 @@
An Omnidirectional light is a type of [Light] that emits light in all directions. The light is attenuated by distance and this attenuation can be configured by changing its energy, radius, and attenuation parameters.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/OrientedPathFollow.xml b/doc/classes/OrientedPathFollow.xml
index 85d60936ad..bc6af4711b 100644
--- a/doc/classes/OrientedPathFollow.xml
+++ b/doc/classes/OrientedPathFollow.xml
@@ -4,7 +4,7 @@
Oriented point sampler for a [Path].
</brief_description>
<description>
- This node behaves like [PathFollow], except it uses its parent [Path] up vector information to enforce orientation.
+ This node behaves like [PathFollow], except it uses its parent [Path] up vector information to enforce orientation.
Make sure to check if the curve of this node's parent [Path] has up vectors enabled. See [PathFollow] and [Curve3D] for further information.
</description>
<tutorials>
diff --git a/doc/classes/PHashTranslation.xml b/doc/classes/PHashTranslation.xml
index 18c72a0576..e5745375b0 100644
--- a/doc/classes/PHashTranslation.xml
+++ b/doc/classes/PHashTranslation.xml
@@ -17,6 +17,7 @@
<argument index="0" name="from" type="Translation">
</argument>
<description>
+ Generates and sets an optimized translation from the given [Translation] resource.
</description>
</method>
</methods>
diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml
index 08df3f0ad6..7dea004b90 100644
--- a/doc/classes/PackedScene.xml
+++ b/doc/classes/PackedScene.xml
@@ -6,10 +6,23 @@
<description>
A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself.
Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). Note that the node doesn't need to own itself.
- Example of saving a node:
+ Example of saving a node with different owners: The following example creates 3 objects: [code]Node2D[/code] ([code]node[/code]), [code]RigidBody2D[/code] ([code]rigid[/code]) and [code]CollisionObject2D[/code] ([code]collision[/code]). [code]collision[/code] is a child of [code]rigid[/code] which is a child of [code]node[/code]. Only [code]rigid[/code] is owned by [code]node[/code] and [code]pack[/code] will therefore only save those two nodes, but not [code]collision[/code].
[codeblock]
+ # create the objects
+ var node = Node2D.new()
+ var rigid = RigidBody2D.new()
+ var collision = CollisionShape2D.new()
+
+ # create the object hierarchy
+ rigid.add_child(collision)
+ node.add_child(rigid)
+
+ # change owner of rigid, but not of collision
+ rigid.set_owner(node)
+
var scene = PackedScene.new()
- var result = scene.pack(child)
+ # only node and rigid are now packed
+ var result = scene.pack(node)
if result == OK:
ResourceSaver.save("res://path/name.scn", scene) # or user://...
[/codeblock]
@@ -39,7 +52,7 @@
<argument index="0" name="edit_state" type="int" enum="PackedScene.GenEditState" default="0">
</argument>
<description>
- Instantiates the scene's node hierarchy. Triggers child scene instantiation(s). Triggers the [enum Node.NOTIFICATION_INSTANCED] notification on the root node.
+ Instantiates the scene's node hierarchy. Triggers child scene instantiation(s). Triggers [Node]'s [code]NOTIFICATION_INSTANCED[/code] notification on the root node.
</description>
</method>
<method name="pack">
diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml
index f872552a49..6416e409a3 100644
--- a/doc/classes/Particles2D.xml
+++ b/doc/classes/Particles2D.xml
@@ -42,9 +42,6 @@
</member>
<member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta">
</member>
- <member name="h_frames" type="int" setter="set_h_frames" getter="get_h_frames">
- Number of horizontal frames in [code]texture[/code].
- </member>
<member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime">
Amount of time each particle will exist. Default value: [code]1[/code].
</member>
@@ -71,9 +68,6 @@
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
Particle texture. If [code]null[/code] particles will be squares.
</member>
- <member name="v_frames" type="int" setter="set_v_frames" getter="get_v_frames">
- Number of vertical frames in [code]texture[/code].
- </member>
<member name="visibility_rect" type="Rect2" setter="set_visibility_rect" getter="get_visibility_rect">
Editor visibility helper.
</member>
diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml
index 354b98485e..2904d4c578 100644
--- a/doc/classes/ParticlesMaterial.xml
+++ b/doc/classes/ParticlesMaterial.xml
@@ -32,9 +32,6 @@
<member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness">
Angular velocity randomness ratio. Default value: [code]0[/code].
</member>
- <member name="anim_loop" type="bool" setter="set_flag" getter="get_flag">
- If [code]true[/code] animation will loop. Default value: [code]false[/code].
- </member>
<member name="anim_offset" type="float" setter="set_param" getter="get_param">
Particle animation offset.
</member>
@@ -216,7 +213,7 @@
<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
Use with [method set_flag] to set [member flag_rotate_y]
</constant>
- <constant name="FLAG_MAX" value="4" enum="Flags">
+ <constant name="FLAG_MAX" value="3" enum="Flags">
</constant>
<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
All particles will be emitted from a single point.
diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml
index f9940dab2f..515c921d0d 100644
--- a/doc/classes/PathFollow2D.xml
+++ b/doc/classes/PathFollow2D.xml
@@ -23,6 +23,7 @@
The node's offset along the curve.
</member>
<member name="lookahead" type="float" setter="set_lookahead" getter="get_lookahead">
+ How far to look ahead of the curve to calculate the tangent if the node is rotating. E.g. shorter lookaheads will lead to faster rotations. Default value: [code]4[/code].
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop">
If [code]true[/code], any offset outside the path's length will wrap around, instead of stopping at the ends. Use it for cyclic paths.
diff --git a/doc/classes/Physics2DDirectSpaceState.xml b/doc/classes/Physics2DDirectSpaceState.xml
index aa54d9a11a..81db70f435 100644
--- a/doc/classes/Physics2DDirectSpaceState.xml
+++ b/doc/classes/Physics2DDirectSpaceState.xml
@@ -7,7 +7,7 @@
Direct access object to a space in the [Physics2DServer]. It's used mainly to do queries against objects and areas residing in a given space.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
@@ -75,6 +75,26 @@
Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody]s or [Area]s, respectively.
</description>
</method>
+ <method name="intersect_point_on_canvas">
+ <return type="Array">
+ </return>
+ <argument index="0" name="point" type="Vector2">
+ </argument>
+ <argument index="1" name="canvas_instance_id" type="int">
+ </argument>
+ <argument index="2" name="max_results" type="int" default="32">
+ </argument>
+ <argument index="3" name="exclude" type="Array" default="[ ]">
+ </argument>
+ <argument index="4" name="collision_layer" type="int" default="2147483647">
+ </argument>
+ <argument index="5" name="collide_with_bodies" type="bool" default="true">
+ </argument>
+ <argument index="6" name="collide_with_areas" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="intersect_ray">
<return type="Dictionary">
</return>
diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml
index a473de4ce8..84e15d3b26 100644
--- a/doc/classes/Physics2DServer.xml
+++ b/doc/classes/Physics2DServer.xml
@@ -24,6 +24,16 @@
Adds a shape to the area, along with a transform matrix. Shapes are usually referenced by their index, so you should track which shape has a given index.
</description>
</method>
+ <method name="area_attach_canvas_instance_id">
+ <return type="void">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <argument index="1" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_attach_object_instance_id">
<return type="void">
</return>
@@ -51,6 +61,14 @@
Creates an [Area2D].
</description>
</method>
+ <method name="area_get_canvas_instance_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="area" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="area_get_object_instance_id" qualifiers="const">
<return type="int">
</return>
@@ -377,6 +395,16 @@
<description>
</description>
</method>
+ <method name="body_attach_canvas_instance_id">
+ <return type="void">
+ </return>
+ <argument index="0" name="body" type="RID">
+ </argument>
+ <argument index="1" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="body_attach_object_instance_id">
<return type="void">
</return>
@@ -404,6 +432,14 @@
Creates a physics body. The first parameter can be any value from constants BODY_MODE*, for the type of body created. Additionally, the body can be created in sleeping state to save processing time.
</description>
</method>
+ <method name="body_get_canvas_instance_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="body" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="body_get_collision_layer" qualifiers="const">
<return type="int">
</return>
diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml
index af00027ed3..2658732f84 100644
--- a/doc/classes/PhysicsBody.xml
+++ b/doc/classes/PhysicsBody.xml
@@ -7,7 +7,7 @@
PhysicsBody is an abstract base class for implementing a physics body. All *Body types inherit from it.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
@@ -21,6 +21,13 @@
Adds a body to the list of bodies that this body can't collide with.
</description>
</method>
+ <method name="get_collision_exceptions">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of nodes that were added as collision exceptions for this body.
+ </description>
+ </method>
<method name="get_collision_layer_bit" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml
index 4278979049..f848b6df70 100644
--- a/doc/classes/PhysicsBody2D.xml
+++ b/doc/classes/PhysicsBody2D.xml
@@ -7,7 +7,7 @@
PhysicsBody2D is an abstract base class for implementing a physics body. All *Body2D types inherit from it.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
@@ -21,6 +21,13 @@
Adds a body to the list of bodies that this body can't collide with.
</description>
</method>
+ <method name="get_collision_exceptions">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of nodes that were added as collision exceptions for this body.
+ </description>
+ </method>
<method name="get_collision_layer_bit" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/PhysicsDirectSpaceState.xml b/doc/classes/PhysicsDirectSpaceState.xml
index 1fd00fa21c..350a9ed3ce 100644
--- a/doc/classes/PhysicsDirectSpaceState.xml
+++ b/doc/classes/PhysicsDirectSpaceState.xml
@@ -7,7 +7,7 @@
Direct access object to a space in the [PhysicsServer]. It's used mainly to do queries against objects and areas residing in a given space.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml
index f79baea0be..7dfc8e66d4 100644
--- a/doc/classes/PhysicsServer.xml
+++ b/doc/classes/PhysicsServer.xml
@@ -1260,7 +1260,7 @@
The higher, the stronger.
</constant>
<constant name="PIN_JOINT_IMPULSE_CLAMP" value="2" enum="PinJointParam">
- If above 0, this value is the maximum value for an impulse that this Joint puts on it's ends.
+ If above 0, this value is the maximum value for an impulse that this Joint puts on its ends.
</constant>
<constant name="HINGE_JOINT_BIAS" value="0" enum="HingeJointParam">
The speed with which the two bodies get pulled together when they move in different directions.
@@ -1401,31 +1401,31 @@
<constant name="G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT" value="6" enum="G6DOFJointAxisParam">
The maximum force that the linear motor can apply while trying to reach the target velocity.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_LOWER_LIMIT" value="7" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_LOWER_LIMIT" value="10" enum="G6DOFJointAxisParam">
The minimum rotation in negative direction to break loose and rotate around the axes.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_UPPER_LIMIT" value="8" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_UPPER_LIMIT" value="11" enum="G6DOFJointAxisParam">
The minimum rotation in positive direction to break loose and rotate around the axes.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS" value="9" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS" value="12" enum="G6DOFJointAxisParam">
A factor that gets multiplied onto all rotations across the axes.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_DAMPING" value="10" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_DAMPING" value="13" enum="G6DOFJointAxisParam">
The amount of rotational damping across the axes. The lower, the more dampening occurs.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_RESTITUTION" value="11" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_RESTITUTION" value="14" enum="G6DOFJointAxisParam">
The amount of rotational restitution across the axes. The lower, the more restitution occurs.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_FORCE_LIMIT" value="12" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_FORCE_LIMIT" value="15" enum="G6DOFJointAxisParam">
The maximum amount of force that can occur, when rotating around the axes.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_ERP" value="13" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_ERP" value="16" enum="G6DOFJointAxisParam">
When correcting the crossing of limits in rotation across the axes, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY" value="14" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY" value="17" enum="G6DOFJointAxisParam">
Target speed for the motor at the axes.
</constant>
- <constant name="G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT" value="15" enum="G6DOFJointAxisParam">
+ <constant name="G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT" value="18" enum="G6DOFJointAxisParam">
Maximum acceleration for the motor at the axes.
</constant>
<constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT" value="0" enum="G6DOFJointAxisFlag">
@@ -1434,10 +1434,10 @@
<constant name="G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT" value="1" enum="G6DOFJointAxisFlag">
If [code]set[/code] there is rotational motion possible.
</constant>
- <constant name="G6DOF_JOINT_FLAG_ENABLE_MOTOR" value="2" enum="G6DOFJointAxisFlag">
+ <constant name="G6DOF_JOINT_FLAG_ENABLE_MOTOR" value="4" enum="G6DOFJointAxisFlag">
If [code]set[/code] there is a rotational motor across these axes.
</constant>
- <constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR" value="3" enum="G6DOFJointAxisFlag">
+ <constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR" value="5" enum="G6DOFJointAxisFlag">
If [code]set[/code] there is a linear motor on this axis that targets a specific velocity.
</constant>
<constant name="SHAPE_PLANE" value="0" enum="ShapeType">
diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml
index 3d5eada066..a57659abda 100644
--- a/doc/classes/Plane.xml
+++ b/doc/classes/Plane.xml
@@ -7,7 +7,7 @@
Plane represents a normalized plane equation. Basically, "normal" is the normal of the plane (a,b,c normalized), and "d" is the distance from the origin to the plane (in the direction of "normal"). "Over" or "Above" the plane is considered the side of the plane towards where the normal is pointing.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 4516fc522a..9706ea4e60 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -182,7 +182,7 @@
Name of the project. It is used from both project manager and by the exporters. Overriding this as name.locale allows setting it in multiple languages.
</member>
<member name="application/config/use_custom_user_dir" type="bool" setter="" getter="">
- Allow the project to save to it's own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect.
+ Allow the project to save to its own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect.
</member>
<member name="application/run/disable_stderr" type="bool" setter="" getter="">
Disable printing to stderr on exported build.
@@ -213,6 +213,8 @@
<member name="audio/mix_rate" type="int" setter="" getter="">
Mix rate used for audio. In general, it's better to not touch this and leave it to the host operating system.
</member>
+ <member name="audio/output_latency" type="int" setter="" getter="">
+ </member>
<member name="audio/video_delay_compensation_ms" type="int" setter="" getter="">
Setting to hardcode audio delay when playing video. Best to leave this untouched unless you know what you are doing.
</member>
@@ -230,6 +232,8 @@
</member>
<member name="compression/formats/zstd/window_log_size" type="int" setter="" getter="">
</member>
+ <member name="debug/gdscript/completion/autocomplete_setters_and_getters" type="bool" setter="" getter="">
+ </member>
<member name="debug/gdscript/warnings/constant_used_as_function" type="bool" setter="" getter="">
</member>
<member name="debug/gdscript/warnings/deprecated_keyword" type="bool" setter="" getter="">
@@ -372,7 +376,7 @@
<member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="">
Timer setting for incremental search in Tree, IntemList, etc. controls.
</member>
- <member name="gui/timers/text_edit_idle_detect_sec" type="int" setter="" getter="">
+ <member name="gui/timers/text_edit_idle_detect_sec" type="float" setter="" getter="">
Timer for detecting idle in the editor.
</member>
<member name="input/ui_accept" type="Dictionary" setter="" getter="">
@@ -604,8 +608,21 @@
<member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter="">
Default size of packet peer stream for deserializing godot data. Over this size, data is dropped.
</member>
- <member name="network/remote_fs/max_pages" type="int" setter="" getter="">
- Maximum amount of pages used for remote filesystem (used by debugging).
+ <member name="network/limits/websocket_client/max_in_buffer_kb" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_client/max_in_packets" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_client/max_out_buffer_kb" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_client/max_out_packets" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_server/max_in_buffer_kb" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_server/max_in_packets" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_server/max_out_buffer_kb" type="int" setter="" getter="">
+ </member>
+ <member name="network/limits/websocket_server/max_out_packets" type="int" setter="" getter="">
</member>
<member name="network/remote_fs/page_read_ahead" type="int" setter="" getter="">
Amount of read ahead used by remote filesystem. Improves latency.
@@ -652,7 +669,7 @@
<member name="rendering/limits/rendering/max_renderable_elements" type="int" setter="" getter="">
Max amount of elements renderable in a frame. If more than this are visible per frame, they will be dropped. Keep in mind elements refer to mesh surfaces and not mesh themselves.
</member>
- <member name="rendering/limits/time/time_rollover_secs" type="int" setter="" getter="">
+ <member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="">
Shaders have a time variable that constantly increases. At some point it needs to be rolled back to zero to avoid numerical errors on shader animations. This setting specifies when.
</member>
<member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="">
@@ -691,7 +708,7 @@
<member name="rendering/quality/reflections/high_quality_ggx.mobile" type="bool" setter="" getter="">
</member>
<member name="rendering/quality/reflections/texture_array_reflections" type="bool" setter="" getter="">
- For reflection probes and panorama backgrounds (sky), use a texure array instead of mipmaps. This reduces jitter noise on reflections, but costs more performance and memory.
+ For reflection probes and panorama backgrounds (sky), use a texture array instead of mipmaps. This reduces jitter noise on reflections, but costs more performance and memory.
</member>
<member name="rendering/quality/reflections/texture_array_reflections.mobile" type="bool" setter="" getter="">
</member>
@@ -704,7 +721,7 @@
<member name="rendering/quality/shading/force_lambert_over_burley.mobile" type="bool" setter="" getter="">
</member>
<member name="rendering/quality/shading/force_vertex_shading" type="bool" setter="" getter="">
- Force vertex shading for all rendering. This can increase performance a lot, but also reduces quality inmensely. Can work to optimize on very low end mobile.
+ Force vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can work to optimize on very low end mobile.
</member>
<member name="rendering/quality/shading/force_vertex_shading.mobile" type="bool" setter="" getter="">
</member>
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index 67631bbc92..468839dfba 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -9,8 +9,8 @@
Quaternions need to be (re)normalized.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link>
- <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/RandomNumberGenerator.xml b/doc/classes/RandomNumberGenerator.xml
new file mode 100644
index 0000000000..aee9654561
--- /dev/null
+++ b/doc/classes/RandomNumberGenerator.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RandomNumberGenerator" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ A class for generation pseudo-random numbers.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="rand_range">
+ <return type="float">
+ </return>
+ <argument index="0" name="from" type="float">
+ </argument>
+ <argument index="1" name="to" type="float">
+ </argument>
+ <description>
+ Generates pseudo-random float between [code]from[/code] and [code]to[/code].
+ </description>
+ </method>
+ <method name="randf">
+ <return type="float">
+ </return>
+ <description>
+ Generates pseudo-random float between '0.0' and '1.0'.
+ </description>
+ </method>
+ <method name="randi">
+ <return type="int">
+ </return>
+ <description>
+ Generates pseudo-random 32-bit integer between '0' and '4294967295'.
+ </description>
+ </method>
+ <method name="randomize">
+ <return type="void">
+ </return>
+ <description>
+ Setups a time-based seed to generator.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="seed" type="int" setter="set_seed" getter="get_seed">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index 1eea940da9..1392e53f8d 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -7,7 +7,7 @@
Rect2 consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml
index a9a897ebaf..7662f72eca 100644
--- a/doc/classes/ReflectionProbe.xml
+++ b/doc/classes/ReflectionProbe.xml
@@ -5,7 +5,7 @@
<description>
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/reflection_probes.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/reflection_probes.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml
index bf3ea95bce..f388667891 100644
--- a/doc/classes/ResourceSaver.xml
+++ b/doc/classes/ResourceSaver.xml
@@ -47,5 +47,7 @@
</constant>
<constant name="FLAG_COMPRESS" value="32" enum="SaverFlags">
</constant>
+ <constant name="FLAG_REPLACE_SUBRESOURCE_PATHS" value="64" enum="SaverFlags">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 357d2e7a15..145ce7537b 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -8,7 +8,7 @@
Note that assignments to [member bbcode_text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member bbcode_text] will erase previous edits made from other manual sources such as [method append_bbcode] and the [code]push_*[/code] / [method pop] methods.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/gui/bbcode_in_richtextlabel.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link>
</tutorials>
<demos>
</demos>
@@ -111,7 +111,7 @@
<argument index="0" name="align" type="int" enum="RichTextLabel.Align">
</argument>
<description>
- Adds a [code][right][/code] tag to the tag stack.
+ Adds an alignment tag based on the given [code]align[/code] value. See [enum Align] for possible values.
</description>
</method>
<method name="push_cell">
@@ -170,6 +170,7 @@
<return type="void">
</return>
<description>
+ Adds a [code][s][/code] tag to the tag stack.
</description>
</method>
<method name="push_table">
diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml
index 0977b7ea01..4ff332402b 100644
--- a/doc/classes/RigidBody.xml
+++ b/doc/classes/RigidBody.xml
@@ -10,7 +10,7 @@
If you need to override the default physics behavior, you can write a custom force integration. See [member custom_integrator].
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index b89ecd1de9..dd94ee66d2 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -7,8 +7,8 @@
As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scene_tree.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml
index 128d7475cc..ec1d544c03 100644
--- a/doc/classes/Script.xml
+++ b/doc/classes/Script.xml
@@ -8,7 +8,7 @@
The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scripting.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scripting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index 7f9a124683..dd6a023af4 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -7,7 +7,7 @@
This class allows you to define a custom shader program that can be used for various materials to render objects.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/shading/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/shading/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Shape.xml b/doc/classes/Shape.xml
index bc1a429418..6e51d3156f 100644
--- a/doc/classes/Shape.xml
+++ b/doc/classes/Shape.xml
@@ -7,7 +7,7 @@
Base class for all 3D shape resources. Nodes that inherit from this can be used as shapes for a [PhysicsBody] or [Area] objects.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml
index 2772538cec..96b8a77d9e 100644
--- a/doc/classes/Shape2D.xml
+++ b/doc/classes/Shape2D.xml
@@ -7,7 +7,7 @@
Base class for all 2D Shapes. All 2D shape types inherit from this.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SoftBody.xml b/doc/classes/SoftBody.xml
index 196d29fc60..6c3929d65b 100644
--- a/doc/classes/SoftBody.xml
+++ b/doc/classes/SoftBody.xml
@@ -20,6 +20,13 @@
Adds a body to the list of bodies that this body can't collide with.
</description>
</method>
+ <method name="get_collision_exceptions">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an array of nodes that were added as collision exceptions for this body.
+ </description>
+ </method>
<method name="get_collision_layer_bit" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml
index 7fd7b5deed..6f9d779737 100644
--- a/doc/classes/Spatial.xml
+++ b/doc/classes/Spatial.xml
@@ -8,7 +8,7 @@
Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the Spatial object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the Spatial's transform. The word local below refers to this coordinate system. The coordinate system that is attached to the Spatial object itself is referred to as object-local coordinate system.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/introduction_to_3d.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/introduction_to_3d.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml
index 57fb267e91..3503505999 100644
--- a/doc/classes/SpatialMaterial.xml
+++ b/doc/classes/SpatialMaterial.xml
@@ -7,7 +7,7 @@
This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/spatial_material.html</link>
</tutorials>
<demos>
</demos>
@@ -52,6 +52,10 @@
<member name="depth_enabled" type="bool" setter="set_feature" getter="get_feature">
If [code]true[/code] Depth mapping is enabled. See also [member normal_enabled].
</member>
+ <member name="depth_flip_binormal" type="bool" setter="set_depth_deep_parallax_flip_binormal" getter="get_depth_deep_parallax_flip_binormal">
+ </member>
+ <member name="depth_flip_tangent" type="bool" setter="set_depth_deep_parallax_flip_tangent" getter="get_depth_deep_parallax_flip_tangent">
+ </member>
<member name="depth_max_layers" type="int" setter="set_depth_deep_parallax_max_layers" getter="get_depth_deep_parallax_max_layers">
</member>
<member name="depth_min_layers" type="int" setter="set_depth_deep_parallax_min_layers" getter="get_depth_deep_parallax_min_layers">
@@ -180,7 +184,7 @@
<member name="particles_anim_h_frames" type="int" setter="set_particles_anim_h_frames" getter="get_particles_anim_h_frames">
The number of horizontal frames in the particle spritesheet. Only enabled when using [code]BillboardMode.BILLBOARD_PARTICLES[/code]. See [member params_billboard_mode].
</member>
- <member name="particles_anim_loop" type="int" setter="set_particles_anim_loop" getter="get_particles_anim_loop">
+ <member name="particles_anim_loop" type="bool" setter="set_particles_anim_loop" getter="get_particles_anim_loop">
If [code]true[/code] particle animations are looped. Only enabled when using [code]BillboardMode.BILLBOARD_PARTICLES[/code]. See [member params_billboard_mode].
</member>
<member name="particles_anim_v_frames" type="int" setter="set_particles_anim_v_frames" getter="get_particles_anim_v_frames">
diff --git a/doc/classes/SpotLight.xml b/doc/classes/SpotLight.xml
index 1f81e9e5c2..11f914aac2 100644
--- a/doc/classes/SpotLight.xml
+++ b/doc/classes/SpotLight.xml
@@ -7,7 +7,7 @@
A SpotLight light is a type of [Light] node that emits lights in a specific direction, in the shape of a cone. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. TODO: Image of a spotlight.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml
index 33948df509..40a4e04a80 100644
--- a/doc/classes/StreamPeerSSL.xml
+++ b/doc/classes/StreamPeerSSL.xml
@@ -7,7 +7,7 @@
SSL Stream peer. This object can be used to connect to SSL servers.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/StreamPeerTCP.xml b/doc/classes/StreamPeerTCP.xml
index 664ffc60c3..9a0fceddab 100644
--- a/doc/classes/StreamPeerTCP.xml
+++ b/doc/classes/StreamPeerTCP.xml
@@ -4,7 +4,7 @@
TCP Stream peer.
</brief_description>
<description>
- TCP Stream peer. This object can be used to connect to TCP servers, or also is returned by a tcp server.
+ TCP Stream peer. This object can be used to connect to TCP servers, or also is returned by a TCP server.
</description>
<tutorials>
</tutorials>
@@ -54,6 +54,7 @@
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if this peer is currently connected to a host, [code]false[code] otherwise.
</description>
</method>
<method name="set_no_delay">
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index d404c32b38..536165487d 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -671,6 +671,7 @@
<return type="PoolByteArray">
</return>
<description>
+ Returns the SHA-256 hash of the string as an array of bytes.
</description>
</method>
<method name="sha256_text">
diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml
index 641d6214a4..bf544d2b78 100644
--- a/doc/classes/StyleBoxFlat.xml
+++ b/doc/classes/StyleBoxFlat.xml
@@ -5,22 +5,21 @@
</brief_description>
<description>
This stylebox can be used to achieve all kinds of looks without the need of a texture. Those properties are customizable:
- - Color
- - Border width (individual width for each border)
- - Rounded corners (individual radius for each corner)
- - Shadow
- About corner radius:
- Setting corner radius to high values is allowed. As soon as corners would overlap the stylebox will switch to a relative system. Example:
- [codeblock]
- height = 30
- corner_radius_top_left = 50
- corner_radius_bottom_left = 100
- [/codeblock]
- The relative system now would take the 1:2 ratio of the two left corners to calculate the actual corner width. Both corners added will [b]never[/b] be more than the height. Result:
- [codeblock]
- corner_radius_top_left: 10
- corner_radius_bottom_left: 20
- [/codeblock]
+ - Color
+ - Border width (individual width for each border)
+ - Rounded corners (individual radius for each corner)
+ - Shadow
+ Setting corner radius to high values is allowed. As soon as corners would overlap the stylebox will switch to a relative system. Example:
+ [codeblock]
+ height = 30
+ corner_radius_top_left = 50
+ corner_radius_bottom_left = 100
+ [/codeblock]
+ The relative system now would take the 1:2 ratio of the two left corners to calculate the actual corner width. Both corners added will [b]never[/b] be more than the height. Result:
+ [codeblock]
+ corner_radius_top_left: 10
+ corner_radius_bottom_left: 20
+ [/codeblock]
</description>
<tutorials>
</tutorials>
@@ -117,7 +116,7 @@
<member name="corner_detail" type="int" setter="set_corner_detail" getter="get_corner_detail">
This sets the amount of vertices used for each corner. Higher values result in rounder corners but take more processing power to compute. When choosing a value you should take the corner radius ([method set_corner_radius]) into account.
For corner radius smaller than 10: 4-5 should be enough
- For corner radius smaller than 30: 8-12 should be enough ...
+ For corner radius smaller than 30: 8-12 should be enough
</member>
<member name="corner_radius_bottom_left" type="int" setter="set_corner_radius" getter="get_corner_radius">
The corner radius of the bottom left corner. When set to 0 the corner is not rounded.
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index 4f9ac68e47..2e5d5d81c9 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -149,7 +149,7 @@
<argument index="2" name="transform" type="Transform">
</argument>
<description>
- Append vertices from a given [Mesh] surface onto the current vertex array with specified [Transform].
+ Append vertices from a given [Mesh] surface onto the current vertex array with specified [Transform].
</description>
</method>
<method name="begin">
@@ -187,7 +187,7 @@
<argument index="1" name="surface" type="int">
</argument>
<description>
- Creates a vertex array from an existing [Mesh].
+ Creates a vertex array from an existing [Mesh].
</description>
</method>
<method name="deindex">
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 5acfd6194e..f17c10b31e 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -82,6 +82,7 @@
<return type="int">
</return>
<description>
+ Returns the [code]TabContainer[/code] rearrange group id.
</description>
</method>
<method name="set_popup">
@@ -132,6 +133,7 @@
<argument index="0" name="group_id" type="int">
</argument>
<description>
+ Defines rearrange group id, choose for each [code]TabContainer[/code] the same value to enable tab drag between [code]TabContainer[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code].
</description>
</method>
</methods>
@@ -140,6 +142,7 @@
The current tab index. When set, this index's [Control] node's [code]visible[/code] property is set to [code]true[/code] and all others are set to [code]false[/code].
</member>
<member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ If [code]true[/code], tabs can be rearranged with mouse drag.
</member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="TabContainer.TabAlign">
The alignment of all tabs in the tab container. See the [code]ALIGN_*[/code] constants for details.
@@ -171,10 +174,13 @@
</signals>
<constants>
<constant name="ALIGN_LEFT" value="0" enum="TabAlign">
+ Align the tabs to the left.
</constant>
<constant name="ALIGN_CENTER" value="1" enum="TabAlign">
+ Align the tabs to the center.
</constant>
<constant name="ALIGN_RIGHT" value="2" enum="TabAlign">
+ Align the tabs to the right.
</constant>
</constants>
<theme_items>
diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml
index fc1d0476ed..350b49513d 100644
--- a/doc/classes/Tabs.xml
+++ b/doc/classes/Tabs.xml
@@ -19,6 +19,7 @@
<argument index="1" name="icon" type="Texture" default="null">
</argument>
<description>
+ Adds a new tab.
</description>
</method>
<method name="ensure_tab_visible">
@@ -27,6 +28,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Moves the Scroll view to make the tab visible.
</description>
</method>
<method name="get_offset_buttons_visible" qualifiers="const">
@@ -39,12 +41,14 @@
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if select with right mouse button is enabled.
</description>
</method>
<method name="get_tab_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of tabs.
</description>
</method>
<method name="get_tab_disabled" qualifiers="const">
@@ -53,6 +57,7 @@
<argument index="0" name="tab_idx" type="int">
</argument>
<description>
+ Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled.
</description>
</method>
<method name="get_tab_icon" qualifiers="const">
@@ -61,6 +66,7 @@
<argument index="0" name="tab_idx" type="int">
</argument>
<description>
+ Returns the [Texture] for the tab at index [code]tab_idx[/code] or null if the tab has no [Texture].
</description>
</method>
<method name="get_tab_offset" qualifiers="const">
@@ -84,12 +90,14 @@
<argument index="0" name="tab_idx" type="int">
</argument>
<description>
+ Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
<method name="get_tabs_rearrange_group" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the [code]Tabs[/code] rearrange group id.
</description>
</method>
<method name="move_tab">
@@ -109,6 +117,7 @@
<argument index="0" name="tab_idx" type="int">
</argument>
<description>
+ Removes tab at index [code]tab_idx[/code]
</description>
</method>
<method name="set_select_with_rmb">
@@ -117,6 +126,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ If [code]true[/code] enables selecting a tab with right mouse button.
</description>
</method>
<method name="set_tab_disabled">
@@ -127,6 +137,7 @@
<argument index="1" name="disabled" type="bool">
</argument>
<description>
+ If [code]disabled[/code] is false, hides the tab at index [code]tab_idx[/code]. Note that its title text will remain, unless also removed with [method set_tab_title].
</description>
</method>
<method name="set_tab_icon">
@@ -137,6 +148,7 @@
<argument index="1" name="icon" type="Texture">
</argument>
<description>
+ Sets an icon for the tab at index [code]tab_idx[/code].
</description>
</method>
<method name="set_tab_title">
@@ -147,6 +159,7 @@
<argument index="1" name="title" type="String">
</argument>
<description>
+ Sets a title for the tab at index [code]tab_idx[/code].
</description>
</method>
<method name="set_tabs_rearrange_group">
@@ -155,17 +168,21 @@
<argument index="0" name="group_id" type="int">
</argument>
<description>
+ Defines rearrange group id, choose for each [code]Tabs[/code] the same value to enable tab drag between [code]Tabs[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code].
</description>
</method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
+ Select tab at index [code]tab_idx[/code].
</member>
<member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ If [code]true[/code], tabs can be rearranged with mouse drag.
</member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled">
</member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="Tabs.TabAlign">
+ The alignment of all tabs. See enum [code]TabAlign[/code] constants for details.
</member>
<member name="tab_close_display_policy" type="int" setter="set_tab_close_display_policy" getter="get_tab_close_display_policy" enum="Tabs.CloseButtonDisplayPolicy">
</member>
@@ -210,10 +227,13 @@
</signals>
<constants>
<constant name="ALIGN_LEFT" value="0" enum="TabAlign">
+ Align the tabs to the left.
</constant>
<constant name="ALIGN_CENTER" value="1" enum="TabAlign">
+ Align the tabs to the center.
</constant>
<constant name="ALIGN_RIGHT" value="2" enum="TabAlign">
+ Align the tabs to the right.
</constant>
<constant name="ALIGN_MAX" value="3" enum="TabAlign">
</constant>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 6f8c76d1ea..7034d75473 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -7,7 +7,7 @@
Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles (textures plus optional collision, navigation, and/or occluder shapes) which are used to create grid-based maps.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/using_tilemaps.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/using_tilemaps.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 3f0d6317a6..ffd15e8d8b 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -268,7 +268,7 @@
<argument index="1" name="shape_id" type="int">
</argument>
<description>
- Returns the [Transform2D] of a tile's sahpe.
+ Returns the [Transform2D] of a tile's shape.
</description>
</method>
<method name="tile_get_shapes" qualifiers="const">
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index 09edfe4235..b3168ef129 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -7,8 +7,8 @@
Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a [Basis] "basis" and an [Vector3] "origin". It is similar to a 3x4 matrix.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
- <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Translation.xml b/doc/classes/Translation.xml
index c4e9dbfb70..b57f8f3556 100644
--- a/doc/classes/Translation.xml
+++ b/doc/classes/Translation.xml
@@ -7,6 +7,8 @@
Translations are resources that can be loaded/unloaded on demand. They map a string to another string.
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link>
+ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link>
</tutorials>
<demos>
</demos>
@@ -19,7 +21,7 @@
<argument index="1" name="xlated_message" type="String">
</argument>
<description>
- Add a message for translation.
+ Adds a message if nonexistent, followed by its translation.
</description>
</method>
<method name="erase_message">
@@ -28,7 +30,7 @@
<argument index="0" name="src_message" type="String">
</argument>
<description>
- Erase a message.
+ Erases a message.
</description>
</method>
<method name="get_message" qualifiers="const">
@@ -37,25 +39,27 @@
<argument index="0" name="src_message" type="String">
</argument>
<description>
- Return a message for translation.
+ Returns a message's translation.
</description>
</method>
<method name="get_message_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of existing messages.
</description>
</method>
<method name="get_message_list" qualifiers="const">
<return type="PoolStringArray">
</return>
<description>
- Return all the messages (keys).
+ Returns all the messages (keys).
</description>
</method>
</methods>
<members>
<member name="locale" type="String" setter="set_locale" getter="get_locale">
+ The locale of the translation.
</member>
</members>
<constants>
diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml
index 52d2b2cc47..c2c400ccd5 100644
--- a/doc/classes/TranslationServer.xml
+++ b/doc/classes/TranslationServer.xml
@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TranslationServer" inherits="Object" category="Core" version="3.1">
<brief_description>
- Server that manages all translations. Translations can be set to it and removed from it.
+ Server that manages all translations.
</brief_description>
<description>
+ Server that manages all translations. Translations can be set to it and removed from it.
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/internationalizing_games.html</link>
+ <link>https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html</link>
</tutorials>
<demos>
</demos>
@@ -16,18 +19,21 @@
<argument index="0" name="translation" type="Translation">
</argument>
<description>
+ Adds a [Translation] resource.
</description>
</method>
<method name="clear">
<return type="void">
</return>
<description>
+ Clears the server from all translations.
</description>
</method>
<method name="get_locale" qualifiers="const">
<return type="String">
</return>
<description>
+ Returns the current locale of the game.
</description>
</method>
<method name="get_locale_name" qualifiers="const">
@@ -36,6 +42,7 @@
<argument index="0" name="locale" type="String">
</argument>
<description>
+ Returns a locale's language and its variant (e.g. "en_US" would return "English (United States)").
</description>
</method>
<method name="remove_translation">
@@ -44,6 +51,7 @@
<argument index="0" name="translation" type="Translation">
</argument>
<description>
+ Removes the given translation from the server.
</description>
</method>
<method name="set_locale">
@@ -52,6 +60,7 @@
<argument index="0" name="locale" type="String">
</argument>
<description>
+ Sets the locale of the game.
</description>
</method>
<method name="translate" qualifiers="const">
@@ -60,6 +69,7 @@
<argument index="0" name="message" type="String">
</argument>
<description>
+ Returns the current locale's translation for the given message (key).
</description>
</method>
</methods>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 8051062fc4..14be256d85 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -384,6 +384,8 @@
</theme_item>
<theme_item name="custom_button_pressed" type="StyleBox">
</theme_item>
+ <theme_item name="draw_guides" type="int">
+ </theme_item>
<theme_item name="draw_relationship_lines" type="int">
</theme_item>
<theme_item name="drop_position_color" type="Color">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index a7c5c3f28c..b4227b34be 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -25,7 +25,7 @@
<argument index="4" name="tooltip" type="String" default="&quot;&quot;">
</argument>
<description>
- Adds a button with [Texture] [code]button[/code] at column [code]column[/code]. The [code]button_idx[/code] index is used to identify the button when calling other methods. If not specified, the next available index is used, which may be retrieved by calling [code]get_buton_count()[/code] immediately after this method. Optionally, the button can be [code]disabled[/code] and have a [code]tooltip[/code].
+ Adds a button with [Texture] [code]button[/code] at column [code]column[/code]. The [code]button_idx[/code] index is used to identify the button when calling other methods. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately after this method. Optionally, the button can be [code]disabled[/code] and have a [code]tooltip[/code].
</description>
</method>
<method name="clear_custom_bg_color">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 1dc03ed314..e792835605 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -14,7 +14,7 @@
tween.start()
[/codeblock]
Many methods require a property name, such as "position" above. You can find the correct property name by hovering over the property in the Inspector.
- Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [enum EASE_IN_OUT], and use the one that looks best.
+ Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [code]EASE_IN_OUT[/code], and use the one that looks best.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml
index 0ea5c6e005..93806ac326 100644
--- a/doc/classes/UndoRedo.xml
+++ b/doc/classes/UndoRedo.xml
@@ -102,8 +102,11 @@
<method name="clear_history">
<return type="void">
</return>
+ <argument index="0" name="increase_version" type="bool" default="true">
+ </argument>
<description>
Clear the undo/redo history and associated references.
+ Passing [code]false[/code] to [code]increase_version[/code] will prevent the version number to be increased from this.
</description>
</method>
<method name="commit_action">
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index c323cf6992..006a843c32 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -7,7 +7,7 @@
2-element structure that can be used to represent positions in 2d space or any other pair of numeric values.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -259,10 +259,10 @@
</methods>
<members>
<member name="x" type="float" setter="" getter="">
- The vector's x component.
+ The vector's x component. Also accessible by using the index position [code][0][/code].
</member>
<member name="y" type="float" setter="" getter="">
- The vector's y component.
+ The vector's y component. Also accessible by using the index position [code][1][/code].
</member>
</members>
<constants>
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 8c4e08778d..48856875e7 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -7,7 +7,7 @@
Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link>
</tutorials>
<demos>
</demos>
@@ -260,13 +260,13 @@
</methods>
<members>
<member name="x" type="float" setter="" getter="">
- The vector's x component.
+ The vector's x component. Also accessible by using the index position [code][0][/code].
</member>
<member name="y" type="float" setter="" getter="">
- The vector's y component.
+ The vector's y component. Also accessible by using the index position [code][1][/code].
</member>
<member name="z" type="float" setter="" getter="">
- The vector's z component.
+ The vector's z component. Also accessible by using the index position [code][2][/code].
</member>
</members>
<constants>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 9cd501630e..4706651c6e 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -12,8 +12,8 @@
Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link>
- <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/viewports/index.html</link>
</tutorials>
<demos>
</demos>
@@ -125,6 +125,12 @@
<description>
</description>
</method>
+ <method name="is_input_handled" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="is_size_override_enabled" qualifiers="const">
<return type="bool">
</return>
@@ -147,6 +153,12 @@
<description>
</description>
</method>
+ <method name="set_input_as_handled">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_size_override">
<return type="void">
</return>
@@ -222,6 +234,8 @@
<member name="gui_snap_controls_to_pixels" type="bool" setter="set_snap_controls_to_pixels" getter="is_snap_controls_to_pixels_enabled">
If [code]true[/code] the GUI controls on the viewport will lay pixel perfectly. Default value: [code]true[/code].
</member>
+ <member name="handle_input_locally" type="bool" setter="set_handle_input_locally" getter="is_handling_input_locally">
+ </member>
<member name="hdr" type="bool" setter="set_hdr" getter="get_hdr">
If [code]true[/code] the viewport rendering will receive benefits from High Dynamic Range algorithm. Default value: [code]true[/code].
</member>
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index 225a1f2bba..b24eeaa112 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -239,10 +239,6 @@
</argument>
<argument index="3" name="normal_map" type="RID">
</argument>
- <argument index="4" name="h_frames" type="int">
- </argument>
- <argument index="5" name="v_frames" type="int">
- </argument>
<description>
Adds a particles system to the [CanvasItem]'s draw commands.
</description>
@@ -1117,11 +1113,13 @@
</argument>
<argument index="2" name="depth_begin" type="float">
</argument>
- <argument index="3" name="depth_curve" type="float">
+ <argument index="3" name="depth_end" type="float">
</argument>
- <argument index="4" name="transmit" type="bool">
+ <argument index="4" name="depth_curve" type="float">
</argument>
- <argument index="5" name="transmit_curve" type="float">
+ <argument index="5" name="transmit" type="bool">
+ </argument>
+ <argument index="6" name="transmit_curve" type="float">
</argument>
<description>
</description>
@@ -3613,7 +3611,7 @@
If [code]true[/code] sets the viewport active, else sets it inactive.
</description>
</method>
- <method name="viewport_set_canvas_layer">
+ <method name="viewport_set_canvas_stacking">
<return type="void">
</return>
<argument index="0" name="viewport" type="RID">
@@ -3622,8 +3620,11 @@
</argument>
<argument index="2" name="layer" type="int">
</argument>
+ <argument index="3" name="sublayer" type="int">
+ </argument>
<description>
- Sets the renderlayer for a viewport's canvas.
+ Sets the stacking order for a viewport's canvas.
+ [code]layer[/code] is the actual canvas layer, while [code]sublayer[/code] specifies the stacking order of the canvas among those in the same layer.
</description>
</method>
<method name="viewport_set_canvas_transform">
diff --git a/doc/classes/World.xml b/doc/classes/World.xml
index 540848e40a..4e954acd48 100644
--- a/doc/classes/World.xml
+++ b/doc/classes/World.xml
@@ -7,7 +7,7 @@
Class that has everything pertaining to a world. A physics space, a visual scenario and a sound space. Spatial nodes register their resources into the current world.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/World2D.xml b/doc/classes/World2D.xml
index 780cdd181a..9c3d66dade 100644
--- a/doc/classes/World2D.xml
+++ b/doc/classes/World2D.xml
@@ -7,7 +7,7 @@
Class that has everything pertaining to a 2D world. A physics space, a visual scenario and a sound space. 2D nodes register their resources into the current 2D world.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml
index e68ad2800e..45e1af4e42 100644
--- a/doc/classes/WorldEnvironment.xml
+++ b/doc/classes/WorldEnvironment.xml
@@ -9,7 +9,7 @@
The [code]WorldEnvironment[/code] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link>
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/YSort.xml b/doc/classes/YSort.xml
index 12b9cb623f..45ae8645b1 100644
--- a/doc/classes/YSort.xml
+++ b/doc/classes/YSort.xml
@@ -14,6 +14,7 @@
</methods>
<members>
<member name="sort_enabled" type="bool" setter="set_sort_enabled" getter="is_sort_enabled">
+ If [code]true[/code] child nodes are sorted, otherwise sorting is disabled. Default: [code]true[/code].
</member>
</members>
<constants>
diff --git a/doc/tools/doc_merge.py b/doc/tools/doc_merge.py
index 57ac4bdcdd..496d5dcb74 100644
--- a/doc/tools/doc_merge.py
+++ b/doc/tools/doc_merge.py
@@ -82,7 +82,7 @@ def find_signal_descr(old_class, name):
def find_constant_descr(old_class, name):
- if (old_class == None):
+ if (old_class is None):
return None
constants = old_class.find("constants")
if(constants != None and len(list(constants)) > 0):
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index ab74c0b9d6..4bb4342d5f 100644
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -229,11 +229,11 @@ class ClassStatus:
output['overall'] = (description_progress + items_progress).to_colored_string('{percent}%', '{pad_percent}{s}')
if self.name.startswith('Total'):
- output['url'] = color('url', 'http://docs.godotengine.org/en/latest/classes/')
+ output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/')
if flags['s']:
output['comment'] = color('part_good', 'ALL OK')
else:
- output['url'] = color('url', 'http://docs.godotengine.org/en/latest/classes/class_{name}.html'.format(name=self.name.lower()))
+ output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/class_{name}.html'.format(name=self.name.lower()))
if flags['s'] and not flags['g'] and self.is_ok():
output['comment'] = color('part_good', 'ALL OK')
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index b3d6d32c26..7b7cc52547 100755
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -375,7 +375,7 @@ def make_method(
event=False,
pp=None
):
- if (declare or pp == None):
+ if (declare or pp is None):
t = '- '
else:
t = ""
@@ -405,7 +405,7 @@ def make_method(
t += 'void'
t += ' '
- if declare or pp == None:
+ if declare or pp is None:
s = '**' + method_data.attrib['name'] + '** '
else:
@@ -577,7 +577,7 @@ def make_rst_class(node):
make_method(f, name, m, True, True)
f.write('\n')
d = m.find('description')
- if d == None or d.text.strip() == '':
+ if d is None or d.text.strip() == '':
continue
f.write(rstize_text(d.text.strip(), name))
f.write("\n\n")
@@ -676,7 +676,7 @@ def make_rst_class(node):
make_method(f, name, m, True)
f.write('\n')
d = m.find('description')
- if d == None or d.text.strip() == '':
+ if d is None or d.text.strip() == '':
continue
f.write(rstize_text(d.text.strip(), name))
f.write("\n\n")
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index cf8fb08f76..850b90d59b 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -95,11 +95,6 @@ Error AudioDriverCoreAudio::init() {
result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
ERR_FAIL_COND_V(result != noErr, FAILED);
-
- prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
-
- result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
- ERR_FAIL_COND_V(result != noErr, FAILED);
#endif
AudioStreamBasicDescription strdesc;
@@ -123,26 +118,6 @@ Error AudioDriverCoreAudio::init() {
break;
}
- zeromem(&strdesc, sizeof(strdesc));
- size = sizeof(strdesc);
- result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size);
- ERR_FAIL_COND_V(result != noErr, FAILED);
-
- switch (strdesc.mChannelsPerFrame) {
- case 1: // Mono
- capture_channels = 1;
- break;
-
- case 2: // Stereo
- capture_channels = 2;
- break;
-
- default:
- // Unknown number of channels, default to stereo
- capture_channels = 2;
- break;
- }
-
mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
zeromem(&strdesc, sizeof(strdesc));
@@ -158,11 +133,6 @@ Error AudioDriverCoreAudio::init() {
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc));
ERR_FAIL_COND_V(result != noErr, FAILED);
- strdesc.mChannelsPerFrame = capture_channels;
-
- result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
- ERR_FAIL_COND_V(result != noErr, FAILED);
-
int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
@@ -175,9 +145,6 @@ Error AudioDriverCoreAudio::init() {
unsigned int buffer_size = buffer_frames * channels;
samples_in.resize(buffer_size);
input_buf.resize(buffer_size);
- input_buffer.resize(buffer_size * 8);
- input_position = 0;
- input_size = 0;
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
@@ -189,16 +156,10 @@ Error AudioDriverCoreAudio::init() {
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
ERR_FAIL_COND_V(result != noErr, FAILED);
- zeromem(&callback, sizeof(AURenderCallbackStruct));
- callback.inputProc = &AudioDriverCoreAudio::input_callback;
- callback.inputProcRefCon = this;
- result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
- ERR_FAIL_COND_V(result != noErr, FAILED);
-
result = AudioUnitInitialize(audio_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
- return OK;
+ return capture_init();
}
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
@@ -265,7 +226,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
bufferList.mBuffers[0].mNumberChannels = ad->capture_channels;
bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t);
- OSStatus result = AudioUnitRender(ad->audio_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
+ OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
if (result == noErr) {
for (int i = 0; i < inNumberFrames * ad->capture_channels; i++) {
int32_t sample = ad->input_buf[i] << 16;
@@ -332,6 +293,8 @@ bool AudioDriverCoreAudio::try_lock() {
}
void AudioDriverCoreAudio::finish() {
+ capture_finish();
+
if (audio_unit) {
OSStatus result;
@@ -375,6 +338,7 @@ void AudioDriverCoreAudio::finish() {
ERR_PRINT("AudioComponentInstanceDispose failed");
}
+ audio_unit = NULL;
unlock();
}
@@ -384,20 +348,160 @@ void AudioDriverCoreAudio::finish() {
}
}
-Error AudioDriverCoreAudio::capture_start() {
+Error AudioDriverCoreAudio::capture_init() {
+ AudioComponentDescription desc;
+ zeromem(&desc, sizeof(desc));
+ desc.componentType = kAudioUnitType_Output;
+#ifdef OSX_ENABLED
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+#else
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#endif
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+
+ AudioComponent comp = AudioComponentFindNext(NULL, &desc);
+ ERR_FAIL_COND_V(comp == NULL, FAILED);
+
+ OSStatus result = AudioComponentInstanceNew(comp, &input_unit);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+#ifdef OSX_ENABLED
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+#endif
UInt32 flag = 1;
- OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+ flag = 0;
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ UInt32 size;
+#ifdef OSX_ENABLED
+ AudioDeviceID deviceId;
+ size = sizeof(AudioDeviceID);
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+
+ result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+#endif
+
+ AudioStreamBasicDescription strdesc;
+ zeromem(&strdesc, sizeof(strdesc));
+ size = sizeof(strdesc);
+ result = AudioUnitGetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ switch (strdesc.mChannelsPerFrame) {
+ case 1: // Mono
+ capture_channels = 1;
+ break;
+
+ case 2: // Stereo
+ capture_channels = 2;
+ break;
+
+ default:
+ // Unknown number of channels, default to stereo
+ capture_channels = 2;
+ break;
+ }
+
+ mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
+
+ zeromem(&strdesc, sizeof(strdesc));
+ strdesc.mFormatID = kAudioFormatLinearPCM;
+ strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
+ strdesc.mChannelsPerFrame = capture_channels;
+ strdesc.mSampleRate = mix_rate;
+ strdesc.mFramesPerPacket = 1;
+ strdesc.mBitsPerChannel = 16;
+ strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
+ strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
+
+ result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ AURenderCallbackStruct callback;
+ zeromem(&callback, sizeof(AURenderCallbackStruct));
+ callback.inputProc = &AudioDriverCoreAudio::input_callback;
+ callback.inputProcRefCon = this;
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback));
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ result = AudioUnitInitialize(input_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
return OK;
}
+void AudioDriverCoreAudio::capture_finish() {
+ if (input_unit) {
+ lock();
+
+ AURenderCallbackStruct callback;
+ zeromem(&callback, sizeof(AURenderCallbackStruct));
+ OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
+ if (result != noErr) {
+ ERR_PRINT("AudioUnitSetProperty failed");
+ }
+
+ result = AudioUnitUninitialize(input_unit);
+ if (result != noErr) {
+ ERR_PRINT("AudioUnitUninitialize failed");
+ }
+
+#ifdef OSX_ENABLED
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
+ if (result != noErr) {
+ ERR_PRINT("AudioObjectRemovePropertyListener failed");
+ }
+#endif
+
+ result = AudioComponentInstanceDispose(input_unit);
+ if (result != noErr) {
+ ERR_PRINT("AudioComponentInstanceDispose failed");
+ }
+
+ input_unit = NULL;
+ unlock();
+ }
+}
+
+Error AudioDriverCoreAudio::capture_start() {
+
+ input_buffer_init(buffer_frames);
+
+ OSStatus result = AudioOutputUnitStart(input_unit);
+ if (result != noErr) {
+ ERR_PRINTS("AudioOutputUnitStart failed, code: " + itos(result));
+ }
+
+ return OK;
+}
+
Error AudioDriverCoreAudio::capture_stop() {
- UInt32 flag = 0;
- OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
- ERR_FAIL_COND_V(result != noErr, FAILED);
+ if (input_unit) {
+ OSStatus result = AudioOutputUnitStop(input_unit);
+ if (result != noErr) {
+ ERR_PRINTS("AudioOutputUnitStop failed, code: " + itos(result));
+ }
+ }
return OK;
}
@@ -531,12 +635,14 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
}
if (found) {
- OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID));
+ OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
ERR_FAIL_COND(result != noErr);
- // Reset audio input to keep synchronisation.
- input_position = 0;
- input_size = 0;
+ if (capture) {
+ // Reset audio input to keep synchronisation.
+ input_position = 0;
+ input_size = 0;
+ }
}
}
@@ -580,6 +686,7 @@ String AudioDriverCoreAudio::capture_get_device() {
AudioDriverCoreAudio::AudioDriverCoreAudio() {
audio_unit = NULL;
+ input_unit = NULL;
active = false;
mutex = NULL;
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index d3f7c8d596..474a9e43ae 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -43,6 +43,7 @@
class AudioDriverCoreAudio : public AudioDriver {
AudioComponentInstance audio_unit;
+ AudioComponentInstance input_unit;
bool active;
Mutex *mutex;
@@ -83,6 +84,9 @@ class AudioDriverCoreAudio : public AudioDriver {
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
+ Error capture_init();
+ void capture_finish();
+
public:
const char *get_name() const {
return "CoreAudio";
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index d109ef7b91..7c095e3798 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -74,7 +74,7 @@ public:
void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {}
void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) {}
- void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) {}
+ void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {}
void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {}
bool is_environment(RID p_env) { return false; }
@@ -584,22 +584,12 @@ public:
SelfList<RasterizerScene::InstanceBase>::List instance_list;
- _FORCE_INLINE_ void instance_change_notify() {
+ _FORCE_INLINE_ void instance_change_notify(bool p_aabb = true, bool p_materials = true) {
SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
while (instances) {
- instances->self()->base_changed();
- instances = instances->next();
- }
- }
-
- _FORCE_INLINE_ void instance_material_change_notify() {
-
- SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
- while (instances) {
-
- instances->self()->base_material_changed();
+ instances->self()->base_changed(p_aabb, p_materials);
instances = instances->next();
}
}
@@ -691,6 +681,8 @@ public:
int particles_get_draw_passes(RID p_particles) const { return 0; }
RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { return RID(); }
+ virtual bool particles_is_inactive(RID p_particles) const { return false; }
+
/* RENDER TARGET */
RID render_target_create() { return RID(); }
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp
index 18b5dd3483..9227c04e71 100644
--- a/drivers/gles2/rasterizer_canvas_gles2.cpp
+++ b/drivers/gles2/rasterizer_canvas_gles2.cpp
@@ -1111,6 +1111,7 @@ void RasterizerCanvasGLES2::initialize() {
// polygon buffer
{
uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
poly_size *= 1024;
poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float));
glGenBuffers(1, &data.polygon_buffer);
@@ -1122,6 +1123,7 @@ void RasterizerCanvasGLES2::initialize() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_size_kb", 128);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
index_size *= 1024; // kb
glGenBuffers(1, &data.polygon_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp
index 848ac8b78f..175587b1bb 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -74,6 +74,10 @@
#include <EGL/eglext.h>
#endif
+#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#define strcpy strcpy_s
+#endif
+
#ifndef IPHONE_ENABLED
static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
@@ -84,6 +88,7 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL
return; //these are ultimately annoying, so removing for now
char debSource[256], debType[256], debSev[256];
+
if (source == _EXT_DEBUG_SOURCE_API_ARB)
strcpy(debSource, "OpenGL");
else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB)
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index fbcbebc88c..6eafdb0e1c 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -605,7 +605,6 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
size >>= 1;
int mipmaps = 6;
- int mm_level = mipmaps - 1;
storage->shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false);
storage->shaders.cubemap_filter.bind();
@@ -628,8 +627,6 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst
size >>= 1;
- mm_level--;
-
lod++;
}
@@ -752,13 +749,14 @@ void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, const C
env->fog_sun_amount = p_sun_amount;
}
-void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
+void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->fog_depth_enabled = p_enable;
env->fog_depth_begin = p_depth_begin;
+ env->fog_depth_end = p_depth_end;
env->fog_depth_curve = p_depth_curve;
env->fog_transmit_enabled = p_transmit;
env->fog_transmit_curve = p_transmit_curve;
@@ -1209,6 +1207,8 @@ bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_m
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size);
+ state.current_main_tex = 0;
+
for (int i = 0; i < tc; i++) {
glActiveTexture(GL_TEXTURE0 + i);
@@ -1239,6 +1239,9 @@ bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_m
t = t->get_ptr();
glBindTexture(t->target, t->tex_id);
+ if (i == 0) {
+ state.current_main_tex = t->tex_id;
+ }
}
state.scene_shader.use_material((void *)p_material);
@@ -2054,7 +2057,11 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
if (p_env && !p_shadow && p_env->fog_enabled && (p_env->fog_depth_enabled || p_env->fog_height_enabled)) {
state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, p_env->fog_depth_enabled);
state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, p_env->fog_height_enabled);
- fog_max_distance = p_projection.get_z_far();
+ if (p_env->fog_depth_end > 0) {
+ fog_max_distance = p_env->fog_depth_end;
+ } else {
+ fog_max_distance = p_projection.get_z_far();
+ }
using_fog = true;
}
@@ -2329,7 +2336,6 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
- state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
}
if (rebind_light && light) {
@@ -2455,13 +2461,20 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_ASYM_PANO, asymmetrical);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, !asymmetrical);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, true);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
- storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, true);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false);
storage->shaders.copy.bind();
storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy);
+ if (asymmetrical) {
+ // pack the bits we need from our projection matrix
+ storage->shaders.copy.set_uniform(CopyShaderGLES2::ASYM_PROJ, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1]);
+ ///@TODO I couldn't get mat3 + p_transform.basis to work, that would be better here.
+ storage->shaders.copy.set_uniform(CopyShaderGLES2::PANO_TRANSFORM, p_transform);
+ }
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -2469,6 +2482,8 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C
glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
glBindBuffer(GL_ARRAY_BUFFER, 0);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_ASYM_PANO, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
}
@@ -2479,6 +2494,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
Environment *env = NULL;
int viewport_width, viewport_height;
+ bool probe_interior = false;
if (p_reflection_probe.is_valid()) {
ReflectionProbeInstance *probe = reflection_probe_instance_owner.getornull(p_reflection_probe);
@@ -2496,6 +2512,8 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
viewport_width = probe->probe_ptr->resolution;
viewport_height = probe->probe_ptr->resolution;
+ probe_interior = probe->probe_ptr->interior;
+
} else {
state.render_no_shadows = false;
current_fb = storage->frame.current_rt->fbo;
@@ -2606,6 +2624,10 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
}
}
+ if (probe_interior) {
+ env_radiance_tex = 0; //do not use radiance texture on interiors
+ }
+
// render opaque things first
render_list.sort_by_key(false);
_render_render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, false, false);
@@ -3037,6 +3059,7 @@ void RasterizerSceneGLES2::initialize() {
{
uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater"));
glGenBuffers(1, &state.immediate_buffer);
glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer);
diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h
index 33ac99366d..7d9920158f 100644
--- a/drivers/gles2/rasterizer_scene_gles2.h
+++ b/drivers/gles2/rasterizer_scene_gles2.h
@@ -360,6 +360,7 @@ public:
bool fog_depth_enabled;
float fog_depth_begin;
+ float fog_depth_end;
float fog_depth_curve;
bool fog_transmit_enabled;
float fog_transmit_curve;
@@ -385,6 +386,7 @@ public:
fog_depth_enabled = true;
fog_depth_begin = 10;
+ fog_depth_end = 0;
fog_depth_curve = 1;
fog_transmit_enabled = true;
@@ -422,7 +424,7 @@ public:
virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp);
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount);
- virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve);
+ virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve);
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve);
virtual bool is_environment(RID p_env);
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index d5865064cf..3beb8eac33 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -613,8 +613,72 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer)
return Ref<Image>(img);
#else
- ERR_EXPLAIN("Sorry, It's not possible to obtain images back in OpenGL ES");
- ERR_FAIL_V(Ref<Image>());
+ Image::Format real_format;
+ GLenum gl_format;
+ GLenum gl_internal_format;
+ GLenum gl_type;
+ bool compressed;
+ _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed);
+
+ PoolVector<uint8_t> data;
+
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
+
+ data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
+ PoolVector<uint8_t>::Write wb = data.write();
+
+ GLuint temp_framebuffer;
+ glGenFramebuffers(1, &temp_framebuffer);
+
+ GLuint temp_color_texture;
+ glGenTextures(1, &temp_color_texture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
+
+ glBindTexture(GL_TEXTURE_2D, temp_color_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0);
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_BLEND);
+ glDepthFunc(GL_LEQUAL);
+ glColorMask(1, 1, 1, 1);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture->tex_id);
+
+ glViewport(0, 0, texture->alloc_width, texture->alloc_height);
+
+ shaders.copy.bind();
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ bind_quad_array();
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]);
+
+ glDeleteTextures(1, &temp_color_texture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &temp_framebuffer);
+
+ wb = PoolVector<uint8_t>::Write();
+
+ data.resize(data_size);
+
+ Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data));
+ if (!texture->compressed) {
+ img->convert(real_format);
+ }
+
+ return Ref<Image>(img);
+
#endif
}
@@ -1061,6 +1125,10 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
p_shader->uniforms.clear();
+ if (p_shader->code == String()) {
+ return; //just invalid, but no error
+ }
+
ShaderCompilerGLES2::GeneratedCode gen_code;
ShaderCompilerGLES2::IdentifierActions *actions = NULL;
@@ -1429,8 +1497,9 @@ Variant RasterizerStorageGLES2::material_get_param_default(RID p_material, const
if (material->shader) {
if (material->shader->uniforms.has(p_param)) {
- Vector<ShaderLanguage::ConstantNode::Value> default_value = material->shader->uniforms[p_param].default_value;
- return ShaderLanguage::constant_value_to_variant(default_value, material->shader->uniforms[p_param].type);
+ ShaderLanguage::ShaderNode::Uniform uniform = material->shader->uniforms[p_param];
+ Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint);
}
}
return Variant();
@@ -1538,7 +1607,7 @@ void RasterizerStorageGLES2::_update_material(Material *p_material) {
if (p_material->shader && p_material->shader->mode == VS::SHADER_SPATIAL) {
if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX &&
- (!p_material->shader->spatial.uses_alpha || (p_material->shader->spatial.uses_alpha && p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) {
+ (!p_material->shader->spatial.uses_alpha || p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) {
can_cast_shadow = true;
}
@@ -1559,7 +1628,7 @@ void RasterizerStorageGLES2::_update_material(Material *p_material) {
}
for (Map<RasterizerScene::InstanceBase *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) {
- E->key()->base_material_changed();
+ E->key()->base_changed(false, true);
}
}
}
@@ -1946,7 +2015,7 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
}
mesh->surfaces.push_back(surface);
- mesh->instance_change_notify();
+ mesh->instance_change_notify(true, false);
info.vertex_mem += surface->total_data_size;
}
@@ -2016,7 +2085,7 @@ void RasterizerStorageGLES2::mesh_surface_set_material(RID p_mesh, int p_surface
_material_add_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]);
}
- mesh->instance_material_change_notify();
+ mesh->instance_change_notify(false, true);
}
RID RasterizerStorageGLES2::mesh_surface_get_material(RID p_mesh, int p_surface) const {
@@ -2124,13 +2193,11 @@ void RasterizerStorageGLES2::mesh_remove_surface(RID p_mesh, int p_surface) {
info.vertex_mem -= surface->total_data_size;
- mesh->instance_material_change_notify();
-
memdelete(surface);
mesh->surfaces.remove(p_surface);
- mesh->instance_change_notify();
+ mesh->instance_change_notify(true, true);
}
int RasterizerStorageGLES2::mesh_get_surface_count(RID p_mesh) const {
@@ -2704,7 +2771,7 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() {
multimesh->dirty_aabb = false;
multimesh->dirty_data = false;
- multimesh->instance_change_notify();
+ multimesh->instance_change_notify(true, false);
multimesh_update_list.remove(multimesh_update_list.first());
}
@@ -2809,7 +2876,7 @@ void RasterizerStorageGLES2::immediate_end(RID p_immediate) {
ERR_FAIL_COND(!im->building);
im->building = false;
- im->instance_change_notify();
+ im->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::immediate_clear(RID p_immediate) {
@@ -2818,7 +2885,7 @@ void RasterizerStorageGLES2::immediate_clear(RID p_immediate) {
ERR_FAIL_COND(im->building);
im->chunks.clear();
- im->instance_change_notify();
+ im->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES2::immediate_get_aabb(RID p_immediate) const {
@@ -2832,7 +2899,7 @@ void RasterizerStorageGLES2::immediate_set_material(RID p_immediate, RID p_mater
ERR_FAIL_COND(!im);
im->material = p_material;
- im->instance_material_change_notify();
+ im->instance_change_notify(false, true);
}
RID RasterizerStorageGLES2::immediate_get_material(RID p_immediate) const {
@@ -3043,7 +3110,7 @@ void RasterizerStorageGLES2::update_dirty_skeletons() {
}
for (Set<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) {
- E->get()->base_changed();
+ E->get()->base_changed(true, false);
}
skeleton_update_list.remove(skeleton_update_list.first());
@@ -3108,7 +3175,7 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param
case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS:
case VS::LIGHT_PARAM_SHADOW_BIAS: {
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
} break;
default: {}
}
@@ -3123,7 +3190,7 @@ void RasterizerStorageGLES2::light_set_shadow(RID p_light, bool p_enabled) {
light->shadow = p_enabled;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::light_set_shadow_color(RID p_light, const Color &p_color) {
@@ -3154,7 +3221,7 @@ void RasterizerStorageGLES2::light_set_cull_mask(RID p_light, uint32_t p_mask) {
light->cull_mask = p_mask;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
@@ -3164,7 +3231,7 @@ void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool
light->reverse_cull = p_enabled;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) {
@@ -3174,7 +3241,7 @@ void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOm
light->omni_shadow_mode = p_mode;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
VS::LightOmniShadowMode RasterizerStorageGLES2::light_omni_get_shadow_mode(RID p_light) {
@@ -3191,7 +3258,7 @@ void RasterizerStorageGLES2::light_omni_set_shadow_detail(RID p_light, VS::Light
light->omni_shadow_detail = p_detail;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) {
@@ -3201,7 +3268,7 @@ void RasterizerStorageGLES2::light_directional_set_shadow_mode(RID p_light, VS::
light->directional_shadow_mode = p_mode;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::light_directional_set_blend_splits(RID p_light, bool p_enable) {
@@ -3211,7 +3278,7 @@ void RasterizerStorageGLES2::light_directional_set_blend_splits(RID p_light, boo
light->directional_blend_splits = p_enable;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
bool RasterizerStorageGLES2::light_directional_get_blend_splits(RID p_light) const {
@@ -3330,7 +3397,7 @@ void RasterizerStorageGLES2::reflection_probe_set_update_mode(RID p_probe, VS::R
ERR_FAIL_COND(!reflection_probe);
reflection_probe->update_mode = p_mode;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
@@ -3371,7 +3438,7 @@ void RasterizerStorageGLES2::reflection_probe_set_max_distance(RID p_probe, floa
ERR_FAIL_COND(!reflection_probe);
reflection_probe->max_distance = p_distance;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) {
@@ -3379,7 +3446,7 @@ void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vec
ERR_FAIL_COND(!reflection_probe);
reflection_probe->extents = p_extents;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
@@ -3387,7 +3454,7 @@ void RasterizerStorageGLES2::reflection_probe_set_origin_offset(RID p_probe, con
ERR_FAIL_COND(!reflection_probe);
reflection_probe->origin_offset = p_offset;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
@@ -3411,7 +3478,7 @@ void RasterizerStorageGLES2::reflection_probe_set_enable_shadows(RID p_probe, bo
ERR_FAIL_COND(!reflection_probe);
reflection_probe->enable_shadows = p_enable;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
@@ -3419,7 +3486,7 @@ void RasterizerStorageGLES2::reflection_probe_set_cull_mask(RID p_probe, uint32_
ERR_FAIL_COND(!reflection_probe);
reflection_probe->cull_mask = p_layers;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES2::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
@@ -3603,7 +3670,7 @@ void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AA
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
ERR_FAIL_COND(!capture);
capture->bounds = p_bounds;
- capture->instance_change_notify();
+ capture->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const {
@@ -3624,7 +3691,7 @@ void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const Po
PoolVector<uint8_t>::Read r = p_octree.read();
copymem(w.ptr(), r.ptr(), p_octree.size());
}
- capture->instance_change_notify();
+ capture->instance_change_notify(true, false);
}
PoolVector<uint8_t> RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const {
@@ -3775,6 +3842,10 @@ RID RasterizerStorageGLES2::particles_get_draw_pass_mesh(RID p_particles, int p_
void RasterizerStorageGLES2::update_particles() {
}
+bool RasterizerStorageGLES2::particles_is_inactive(RID p_particles) const {
+ return true;
+}
+
////////
void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
@@ -4394,6 +4465,7 @@ void RasterizerStorageGLES2::initialize() {
}
}
+ config.keep_original_textures = false;
config.shrink_textures_x2 = false;
config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float");
@@ -4541,6 +4613,7 @@ void RasterizerStorageGLES2::initialize() {
#endif
config.force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
+ config.use_fast_texture_filter = GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter");
}
void RasterizerStorageGLES2::finalize() {
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index b42e2dfb1f..5bdc65a0b5 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -43,7 +43,6 @@
/*
#include "shaders/blend_shape.glsl.gen.h"
#include "shaders/canvas.glsl.gen.h"
-#include "shaders/copy.glsl.gen.h"
#include "shaders/particles.glsl.gen.h"
*/
@@ -61,20 +60,12 @@ public:
bool shrink_textures_x2;
bool use_fast_texture_filter;
- // bool use_anisotropic_filter;
-
- bool hdr_supported;
-
- bool use_rgba_2d_shadows;
-
- // float anisotropic_level;
int max_texture_image_units;
int max_texture_size;
- bool generate_wireframes;
-
- bool use_texture_array_environment;
+ // TODO implement wireframe in GLES2
+ // bool generate_wireframes;
Set<String> extensions;
@@ -84,7 +75,6 @@ public:
bool keep_original_textures;
- bool no_depth_prepass;
bool force_vertex_shading;
} config;
@@ -160,20 +150,12 @@ public:
struct Instantiable : public RID_Data {
SelfList<RasterizerScene::InstanceBase>::List instance_list;
- _FORCE_INLINE_ void instance_change_notify() {
- SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
-
- while (instances) {
- instances->self()->base_changed();
- instances = instances->next();
- }
- }
+ _FORCE_INLINE_ void instance_change_notify(bool p_aabb, bool p_materials) {
- _FORCE_INLINE_ void instance_material_change_notify() {
SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
-
while (instances) {
- instances->self()->base_material_changed();
+
+ instances->self()->base_changed(p_aabb, p_materials);
instances = instances->next();
}
}
@@ -273,31 +255,28 @@ public:
void *detect_normal_ud;
Texture() {
- flags = 0;
- width = 0;
- height = 0;
alloc_width = 0;
alloc_height = 0;
- format = Image::FORMAT_L8;
-
target = 0;
- data_size = 0;
- total_data_size = 0;
+ stored_cube_sides = 0;
ignore_mipmaps = false;
-
- compressed = false;
-
- active = false;
-
+ render_target = NULL;
+ flags = width = height = 0;
tex_id = 0;
-
- stored_cube_sides = 0;
-
+ data_size = 0;
+ format = Image::FORMAT_L8;
+ active = false;
+ compressed = false;
+ total_data_size = 0;
+ mipmaps = 0;
+ detect_3d = NULL;
+ detect_3d_ud = NULL;
+ detect_srgb = NULL;
+ detect_srgb_ud = NULL;
+ detect_normal = NULL;
+ detect_normal_ud = NULL;
proxy = NULL;
-
- render_target = NULL;
-
redraw_if_visible = false;
}
@@ -430,6 +409,7 @@ public:
int light_mode;
*/
+
bool uses_screen_texture;
bool uses_screen_uv;
bool uses_time;
@@ -673,7 +653,7 @@ public:
SelfList<MultiMesh> *mm = multimeshes.first();
while (mm) {
- mm->self()->instance_material_change_notify();
+ mm->self()->instance_change_notify(false, true);
mm = mm->next();
}
}
@@ -1110,6 +1090,8 @@ public:
virtual int particles_get_draw_passes(RID p_particles) const;
virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const;
+ virtual bool particles_is_inactive(RID p_particles) const;
+
/* INSTANCE */
virtual void instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance);
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index 082c520480..d52be09cac 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -80,19 +80,17 @@ static String _opstr(SL::Operator p_op) {
static String _mkid(const String &p_id) {
- StringBuffer<> id;
- id += "m_";
- id += p_id;
-
- return id.as_string();
+ String id = "m_" + p_id;
+ return id.replace("__", "_dus_"); //doubleunderscore is reserverd in glsl
}
static String f2sp0(float p_float) {
- if (int(p_float) == p_float)
- return itos(p_float) + ".0";
- else
- return rtoss(p_float);
+ String num = rtoss(p_float);
+ if (num.find(".") == -1 && num.find("e") == -1) {
+ num += ".0";
+ }
+ return num;
}
static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
@@ -364,6 +362,7 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
for (int i = 0; i < snode->functions.size(); i++) {
SL::FunctionNode *fnode = snode->functions[i].function;
+ current_func_name = fnode->name;
function_code[fnode->name] = _dump_node_code(fnode->body, 1, r_gen_code, p_actions, p_default_actions, p_assigning);
}
diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp
index 628a57c06d..c5a67d4e75 100644
--- a/drivers/gles2/shader_gles2.cpp
+++ b/drivers/gles2/shader_gles2.cpp
@@ -196,6 +196,12 @@ static void _display_error_with_code(const String &p_error, const Vector<const c
ERR_PRINTS(p_error);
}
+static String _mkid(const String &p_id) {
+
+ String id = "m_" + p_id;
+ return id.replace("__", "_dus_"); //doubleunderscore is reserverd in glsl
+}
+
ShaderGLES2::Version *ShaderGLES2::get_current_version() {
Version *_v = version_map.getptr(conditional_version);
@@ -492,15 +498,15 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
if (cc) {
// uniforms
for (int i = 0; i < cc->custom_uniforms.size(); i++) {
- StringName native_uniform_name = "m_" + cc->custom_uniforms[i];
- GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data());
+ String native_uniform_name = _mkid(cc->custom_uniforms[i]);
+ GLint location = glGetUniformLocation(v.id, (native_uniform_name).ascii().get_data());
v.custom_uniform_locations[cc->custom_uniforms[i]] = location;
}
// textures
for (int i = 0; i < cc->texture_uniforms.size(); i++) {
- StringName native_uniform_name = "m_" + cc->texture_uniforms[i];
- GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data());
+ String native_uniform_name = _mkid(cc->texture_uniforms[i]);
+ GLint location = glGetUniformLocation(v.id, (native_uniform_name).ascii().get_data());
v.custom_uniform_locations[cc->texture_uniforms[i]] = location;
}
}
diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl
index b990384949..79d4eb2243 100644
--- a/drivers/gles2/shaders/canvas.glsl
+++ b/drivers/gles2/shaders/canvas.glsl
@@ -148,7 +148,10 @@ void main() {
vec4 color = color_interp;
+#if !defined(COLOR_USED)
+ //default behavior, texture by color
color *= texture2D(color_texture, uv_interp);
+#endif
#ifdef SCREEN_UV_USED
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl
index 16bbde196d..0b8da4f875 100644
--- a/drivers/gles2/shaders/copy.glsl
+++ b/drivers/gles2/shaders/copy.glsl
@@ -35,6 +35,8 @@ void main() {
#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
cube_interp = cube_in;
+#elif defined(USE_ASYM_PANO)
+ uv_interp = vertex_attrib.xy;
#else
uv_interp = uv_in;
#endif
@@ -68,6 +70,11 @@ varying vec2 uv_interp;
#endif
/* clang-format on */
+#ifdef USE_ASYM_PANO
+uniform highp mat4 pano_transform;
+uniform highp vec4 asym_proj;
+#endif
+
#ifdef USE_CUBEMAP
uniform samplerCube source_cube; // texunit:0
#else
@@ -108,6 +115,21 @@ void main() {
vec4 color = texturePanorama(source, normalize(cube_interp));
+#elif defined(USE_ASYM_PANO)
+
+ // When an asymmetrical projection matrix is used (applicable for stereoscopic rendering i.e. VR) we need to do this calculation per fragment to get a perspective correct result.
+ // Note that we're ignoring the x-offset for IPD, with Z sufficiently in the distance it becomes neglectible, as a result we could probably just set cube_normal.z to -1.
+ // The Matrix[2][0] (= asym_proj.x) and Matrix[2][1] (= asym_proj.z) values are what provide the right shift in the image.
+
+ vec3 cube_normal;
+ cube_normal.z = -1000000.0;
+ cube_normal.x = (cube_normal.z * (-uv_interp.x - asym_proj.x)) / asym_proj.y;
+ cube_normal.y = (cube_normal.z * (-uv_interp.y - asym_proj.z)) / asym_proj.a;
+ cube_normal = mat3(pano_transform) * cube_normal;
+ cube_normal.z = -cube_normal.z;
+
+ vec4 color = texturePanorama(source, normalize(cube_normal.xyz));
+
#elif defined(USE_CUBEMAP)
vec4 color = textureCube(source_cube, normalize(cube_interp));
#else
diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl
index 7b57f5d497..bc83b69b49 100644
--- a/drivers/gles2/shaders/scene.glsl
+++ b/drivers/gles2/shaders/scene.glsl
@@ -84,7 +84,7 @@ uniform highp mat4 world_transform;
uniform highp float time;
-uniform float normal_mult;
+
#ifdef RENDER_DEPTH
uniform float light_bias;
@@ -247,7 +247,7 @@ void light_compute(
float cLdotH = max(dot(L, H), 0.0);
float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
- blinn *= (shininess + 8.0) / (8.0 * 3.141592654);
+ blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
specular_brdf_NL = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
#endif
@@ -330,11 +330,10 @@ void main() {
#endif
- vec3 normal = normal_attrib * normal_mult;
+ vec3 normal = normal_attrib;
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
vec3 tangent = tangent_attrib.xyz;
- tangent *= normal_mult;
float binormalf = tangent_attrib.a;
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
@@ -359,7 +358,7 @@ void main() {
normal = normalize((world_matrix * vec4(normal, 0.0)).xyz);
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
- tangent = normalize((world_matrix * vec4(tangent, 0.0)), xyz);
+ tangent = normalize((world_matrix * vec4(tangent, 0.0)).xyz);
binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz);
#endif
#endif
@@ -626,7 +625,7 @@ VERTEX_SHADER_CODE
float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex));
- fog_amount = pow(fog_z, fog_depth_curve);
+ fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a;
}
#endif
@@ -1012,9 +1011,7 @@ float G_GGX_2cos(float cos_theta_m, float alpha) {
// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha)
// See Filament docs, Specular G section.
float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) {
- float v = cos_theta_l * (cos_theta_v * (1.0 - alpha) + alpha);
- float l = cos_theta_v * (cos_theta_l * (1.0 - alpha) + alpha);
- return 0.5 / (v + l);
+ return 0.5 / mix(2.0 * cos_theta_l * cos_theta_v, cos_theta_l + cos_theta_v, alpha);
}
float D_GGX(float cos_theta_m, float alpha) {
@@ -1126,6 +1123,18 @@ LIGHT_SHADER_CODE
float NdotV = dot(N, V);
float cNdotV = max(NdotV, 0.0);
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ vec3 H = normalize(V + L);
+#endif
+
+#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ float cNdotH = max(dot(N, H), 0.0);
+#endif
+
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ float cLdotH = max(dot(L, H), 0.0);
+#endif
+
if (metallic < 1.0) {
#if defined(DIFFUSE_OREN_NAYAR)
vec3 diffuse_brdf_NL;
@@ -1160,13 +1169,9 @@ LIGHT_SHADER_CODE
#elif defined(DIFFUSE_BURLEY)
{
-
- vec3 H = normalize(V + L);
- float cLdotH = max(0.0, dot(L, H));
-
- float FD90 = 0.5 + 2.0 * cLdotH * cLdotH * roughness;
- float FdV = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotV);
- float FdL = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotL);
+ float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5;
+ float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV);
+ float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL);
diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL;
/*
float energyBias = mix(roughness, 0.0, 0.5);
@@ -1209,13 +1214,9 @@ LIGHT_SHADER_CODE
#if defined(SPECULAR_BLINN)
//normalized blinn
- vec3 H = normalize(V + L);
- float cNdotH = max(dot(N, H), 0.0);
- float cVdotH = max(dot(V, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
- blinn *= (shininess + 8.0) / (8.0 * 3.141592654);
+ blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
specular_brdf_NL = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
#elif defined(SPECULAR_PHONG)
@@ -1224,7 +1225,7 @@ LIGHT_SHADER_CODE
float cRdotV = max(0.0, dot(R, V));
float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
float phong = pow(cRdotV, shininess);
- phong *= (shininess + 8.0) / (8.0 * 3.141592654);
+ phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
specular_brdf_NL = (phong) / max(4.0 * cNdotV * cNdotL, 0.75);
#elif defined(SPECULAR_TOON)
@@ -1240,11 +1241,6 @@ LIGHT_SHADER_CODE
#elif defined(SPECULAR_SCHLICK_GGX)
// shlick+ggx as default
- vec3 H = normalize(V + L);
-
- float cNdotH = max(dot(N, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
-
#if defined(LIGHT_USE_ANISOTROPY)
float alpha = roughness * roughness;
float aspect = sqrt(1.0 - anisotropy * 0.9);
@@ -1275,24 +1271,18 @@ LIGHT_SHADER_CODE
specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
#if defined(LIGHT_USE_CLEARCOAT)
- if (clearcoat_gloss > 0.0) {
-#if !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_BLINN)
- vec3 H = normalize(V + L);
-#endif
+
#if !defined(SPECULAR_SCHLICK_GGX)
- float cNdotH = max(dot(N, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
- float cLdotH5 = SchlickFresnel(cLdotH);
+ float cLdotH5 = SchlickFresnel(cLdotH);
#endif
- float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
- float Fr = mix(.04, 1.0, cLdotH5);
- //float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
- float Gr = V_GGX(cNdotL, cNdotV, 0.25);
+ float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
+ float Fr = mix(.04, 1.0, cLdotH5);
+ //float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
+ float Gr = V_GGX(cNdotL, cNdotV, 0.25);
- float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
+ float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
- specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
- }
+ specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
#endif
}
@@ -1307,8 +1297,7 @@ LIGHT_SHADER_CODE
#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, texture2D(p_shadow, p_pos).r)
#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, texture2DProj(p_shadow, p_pos).r)
-float sample_shadow(
- highp sampler2D shadow, highp vec4 spos) {
+float sample_shadow(highp sampler2D shadow, highp vec4 spos) {
#ifdef SHADOW_MODE_PCF_13
@@ -1923,12 +1912,12 @@ FRAGMENT_SHADER_CODE
highp vec4 splane = shadow_coord;
splane.xyz /= splane.w;
- float shadow = sample_shadow(light_shadow_atlas, splane.xy, splane.z);
+ float shadow = sample_shadow(light_shadow_atlas, splane);
light_att *= shadow;
}
#endif
-#endif
+#endif // LIGHT_MODE_SPOT
#ifdef USE_VERTEX_LIGHTING
//vertex lighting
@@ -2037,7 +2026,7 @@ FRAGMENT_SHADER_CODE
float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex));
- fog_amount = pow(fog_z, fog_depth_curve);
+ fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a;
if (fog_transmit_enabled) {
vec3 total_light = gl_FragColor.rgb;
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index a9b46baf53..4166cb8361 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -213,12 +213,12 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
} else {
+ texture = texture->get_ptr();
+
if (texture->redraw_if_visible) { //check before proxy, because this is usually used with proxies
VisualServerRaster::redraw_request();
}
- texture = texture->get_ptr();
-
if (texture->render_target)
texture->render_target->used_in_frame = true;
@@ -254,11 +254,12 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
} else {
+ normal_map = normal_map->get_ptr();
+
if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies
VisualServerRaster::redraw_request();
}
- normal_map = normal_map->get_ptr();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normal_map->tex_id);
state.current_normal = p_normal_map;
@@ -976,7 +977,7 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
if (texture) {
- Size2 texpixel_size(1.0 / (texture->width / particles_cmd->h_frames), 1.0 / (texture->height / particles_cmd->v_frames));
+ Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
} else {
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
@@ -993,9 +994,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
}
- state.canvas_shader.set_uniform(CanvasShaderGLES3::H_FRAMES, particles_cmd->h_frames);
- state.canvas_shader.set_uniform(CanvasShaderGLES3::V_FRAMES, particles_cmd->v_frames);
-
glBindVertexArray(data.particle_quad_array); //use particle quad array
glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
@@ -1072,8 +1070,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
glBindVertexArray(0);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
state.using_texture_rect = true;
_set_texture_rect_mode(false);
@@ -1223,8 +1221,6 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
bool rebind_shader = true;
- state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
-
glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_item_ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(CanvasItemUBO), &state.canvas_item_ubo_data, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
@@ -1342,7 +1338,7 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
last_blend_mode = last_blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED ? last_blend_mode : -1;
}
- if (shader_ptr != shader_cache) {
+ if (shader_ptr != shader_cache || rebind_shader) {
if (shader_ptr->canvas_item.uses_time) {
VisualServerRaster::redraw_request();
@@ -1388,12 +1384,12 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
continue;
}
+ t = t->get_ptr();
+
if (t->redraw_if_visible) { //check before proxy, because this is usually used with proxies
VisualServerRaster::redraw_request();
}
- t = t->get_ptr();
-
if (storage->config.srgb_decode_supported && t->using_srgb) {
//no srgb in 2D
glTexParameteri(t->target, _TEXTURE_SRGB_DECODE_EXT, _SKIP_DECODE_EXT);
@@ -1659,6 +1655,14 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
if (current_clip) {
glDisable(GL_SCISSOR_TEST);
}
+ //disable states that may have been used
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHTING, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SHADOWS, false);
}
void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {
@@ -2027,6 +2031,7 @@ void RasterizerCanvasGLES3::initialize() {
{
uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
poly_size *= 1024; //kb
poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float));
glGenBuffers(1, &data.polygon_buffer);
@@ -2074,6 +2079,7 @@ void RasterizerCanvasGLES3::initialize() {
glGenVertexArrays(1, &data.polygon_buffer_pointer_array);
uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
index_size *= 1024; //kb
glGenBuffers(1, &data.polygon_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 2b3be9d0bd..41ce5e7c47 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -73,6 +73,10 @@ RasterizerScene *RasterizerGLES3::get_scene() {
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
#define _EXT_DEBUG_OUTPUT 0x92E0
+#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#define strcpy strcpy_s
+#endif
+
#ifdef GLAD_ENABLED
// Restricting to GLAD as only used in initialize() with GLAD_GL_ARB_debug_output
#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED)
@@ -428,10 +432,10 @@ void RasterizerGLES3::make_current() {
void RasterizerGLES3::register_config() {
- GLOBAL_DEF("rendering/quality/filters/use_nearest_mipmap_filter", false);
GLOBAL_DEF("rendering/quality/filters/anisotropic_filter_level", 4);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/anisotropic_filter_level", PropertyInfo(Variant::INT, "rendering/quality/filters/anisotropic_filter_level", PROPERTY_HINT_RANGE, "1,16,1"));
GLOBAL_DEF("rendering/limits/time/time_rollover_secs", 3600);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/time/time_rollover_secs", PropertyInfo(Variant::REAL, "rendering/limits/time/time_rollover_secs", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"));
}
RasterizerGLES3::RasterizerGLES3() {
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index ed2b39a17d..e0c8d3af52 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -934,13 +934,14 @@ void RasterizerSceneGLES3::environment_set_fog(RID p_env, bool p_enable, const C
env->fog_sun_amount = p_sun_amount;
}
-void RasterizerSceneGLES3::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
+void RasterizerSceneGLES3::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->fog_depth_enabled = p_enable;
env->fog_depth_begin = p_depth_begin;
+ env->fog_depth_end = p_depth_end;
env->fog_depth_curve = p_depth_curve;
env->fog_transmit_enabled = p_transmit;
env->fog_transmit_curve = p_transmit_curve;
@@ -1189,11 +1190,12 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m
if (t) {
+ t = t->get_ptr(); //resolve for proxies
+
if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies
VisualServerRaster::redraw_request();
}
- t = t->get_ptr(); //resolve for proxies
#ifdef TOOLS_ENABLED
if (t->detect_3d) {
t->detect_3d(t->detect_3d_ud);
@@ -1246,14 +1248,11 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m
case ShaderLanguage::TYPE_SAMPLER3D: {
target = GL_TEXTURE_3D;
+ tex = storage->resources.white_tex_3d;
- switch (texture_hints[i]) {
-
- // TODO
- default: {
- tex = storage->resources.white_tex_3d;
- } break;
- }
+ //switch (texture_hints[i]) {
+ // TODO
+ //}
} break;
@@ -1431,7 +1430,16 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
if (particles->draw_order == VS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->particle_valid_histories[1]) {
glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[1]); //modify the buffer, this was used 2 frames ago so it should be good enough for flushing
- RasterizerGLES3Particle *particle_array = (RasterizerGLES3Particle *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 24 * sizeof(float), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
+ RasterizerGLES3Particle *particle_array;
+#ifndef __EMSCRIPTEN__
+ particle_array = static_cast<RasterizerGLES3Particle *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 24 * sizeof(float), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
+#else
+ PoolVector<RasterizerGLES3Particle> particle_vector;
+ particle_vector.resize(particles->amount);
+ PoolVector<RasterizerGLES3Particle>::Write w = particle_vector.write();
+ particle_array = w.ptr();
+ glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(RasterizerGLES3Particle), particle_array);
+#endif
SortArray<RasterizerGLES3Particle, RasterizerGLES3ParticleSort> sorter;
@@ -1443,7 +1451,17 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
sorter.sort(particle_array, particles->amount);
+#ifndef __EMSCRIPTEN__
glUnmapBuffer(GL_ARRAY_BUFFER);
+#else
+ w = PoolVector<RasterizerGLES3Particle>::Write();
+ particle_array = NULL;
+ {
+ PoolVector<RasterizerGLES3Particle>::Read r = particle_vector.read();
+ glBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(RasterizerGLES3Particle), r.ptr());
+ }
+ particle_vector = PoolVector<RasterizerGLES3Particle>();
+#endif
#ifdef DEBUG_ENABLED
if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->instancing_array_wireframe_id) {
glBindVertexArray(s->instancing_array_wireframe_id); // use the wireframe instancing array ID
@@ -2205,7 +2223,6 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
_set_cull(e->sort_key & RenderList::SORT_KEY_MIRROR_FLAG, e->sort_key & RenderList::SORT_KEY_CULL_DISABLED_FLAG, p_reverse_cull);
- state.scene_shader.set_uniform(SceneShaderGLES3::NORMAL_MULT, e->instance->mirror ? -1.0 : 1.0);
state.scene_shader.set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, e->instance->transform);
_render_geometry(e);
@@ -2571,6 +2588,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr
state.ubo_data.fog_color_enabled[1] = linear_fog.g;
state.ubo_data.fog_color_enabled[2] = linear_fog.b;
state.ubo_data.fog_color_enabled[3] = (!p_no_fog && env->fog_enabled) ? 1.0 : 0.0;
+ state.ubo_data.fog_density = linear_fog.a;
Color linear_sun = env->fog_sun_color.to_linear();
state.ubo_data.fog_sun_color_amount[0] = linear_sun.r;
@@ -2579,6 +2597,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr
state.ubo_data.fog_sun_color_amount[3] = env->fog_sun_amount;
state.ubo_data.fog_depth_enabled = env->fog_depth_enabled;
state.ubo_data.fog_depth_begin = env->fog_depth_begin;
+ state.ubo_data.fog_depth_end = env->fog_depth_end;
state.ubo_data.fog_depth_curve = env->fog_depth_curve;
state.ubo_data.fog_transmit_enabled = env->fog_transmit_enabled;
state.ubo_data.fog_transmit_curve = env->fog_transmit_curve;
@@ -3020,20 +3039,17 @@ void RasterizerSceneGLES3::_setup_reflections(RID *p_reflection_probe_cull_resul
reflection_ubo.ambient[3] = rpi->probe_ptr->interior_ambient_probe_contrib;
} else {
Color ambient_linear;
- // FIXME: contrib was retrieved but never used, is it meant to be set as ambient[3]? (GH-20361)
- //float contrib = 0;
if (p_env) {
ambient_linear = p_env->ambient_color.to_linear();
ambient_linear.r *= p_env->ambient_energy;
ambient_linear.g *= p_env->ambient_energy;
ambient_linear.b *= p_env->ambient_energy;
- //contrib = p_env->ambient_sky_contribution;
}
reflection_ubo.ambient[0] = ambient_linear.r;
reflection_ubo.ambient[1] = ambient_linear.g;
reflection_ubo.ambient[2] = ambient_linear.b;
- reflection_ubo.ambient[3] = 0;
+ reflection_ubo.ambient[3] = 0; //not used in exterior mode, since it just blends with regular ambient light
}
int cell_size = reflection_atlas->size / reflection_atlas->subdiv;
@@ -3580,7 +3596,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
- if (!env || storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
+ if (!env || storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT] || storage->frame.current_rt->width < 4 || storage->frame.current_rt->height < 4) { //no post process on small render targets
//no environment or transparent render, simply return and convert to SRGB
glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo);
glActiveTexture(GL_TEXTURE0);
@@ -4278,8 +4294,10 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
glClearBufferfv(GL_COLOR, 0, clear_color.components); // specular
}
+ VS::EnvironmentBG bg_mode = (!env || (probe && env->bg_mode == VS::ENV_BG_CANVAS)) ? VS::ENV_BG_CLEAR_COLOR : env->bg_mode; //if no environment, or canvas while rendering a probe (invalid use case), use color.
+
if (env) {
- switch (env->bg_mode) {
+ switch (bg_mode) {
case VS::ENV_BG_COLOR_SKY:
case VS::ENV_BG_SKY:
@@ -4322,6 +4340,10 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
}
}
+ if (probe && probe->probe_ptr->interior) {
+ env_radiance_tex = 0; //for rendering probe interiors, radiance must not be used.
+ }
+
state.texscreen_copied = false;
glBlendEquation(GL_FUNC_ADD);
@@ -4855,10 +4877,7 @@ void RasterizerSceneGLES3::initialize() {
glBindBuffer(GL_UNIFORM_BUFFER, 0);
render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS);
- if (render_list.max_elements > 1000000)
- render_list.max_elements = 1000000;
- if (render_list.max_elements < 1024)
- render_list.max_elements = 1024;
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/rendering/max_renderable_elements", PropertyInfo(Variant::INT, "rendering/limits/rendering/max_renderable_elements", PROPERTY_HINT_RANGE, "1024,1000000,1"));
{
//quad buffers
@@ -5057,6 +5076,7 @@ void RasterizerSceneGLES3::initialize() {
{
uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater"));
glGenBuffers(1, &state.immediate_buffer);
glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer);
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 4b4a0b9303..f3157d5a46 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -145,6 +145,8 @@ public:
uint32_t fog_depth_enabled;
float fog_depth_begin;
+ float fog_depth_end;
+ float fog_density;
float fog_depth_curve;
uint32_t fog_transmit_enabled;
float fog_transmit_curve;
@@ -438,6 +440,7 @@ public:
bool fog_depth_enabled;
float fog_depth_begin;
+ float fog_depth_end;
float fog_depth_curve;
bool fog_transmit_enabled;
float fog_transmit_curve;
@@ -518,6 +521,7 @@ public:
fog_depth_enabled = true;
fog_depth_begin = 10;
+ fog_depth_end = 0;
fog_depth_curve = 1;
fog_transmit_enabled = true;
@@ -555,7 +559,7 @@ public:
virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp);
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount);
- virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve);
+ virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve);
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve);
virtual bool is_environment(RID p_env);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index a9aa152f10..235c799a55 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -101,6 +101,28 @@
#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+
+void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data) {
+
+ /* clang-format off */
+ EM_ASM({
+ GLctx.getBufferSubData($0, $1, HEAPU8, $2, $3);
+ }, target, offset, data, size);
+ /* clang-format on */
+}
+
+void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data) {
+
+ /* clang-format off */
+ EM_ASM({
+ GLctx.bufferSubData($0, $1, HEAPU8, $2, $3);
+ }, target, offset, data, size);
+ /* clang-format on */
+}
+#endif
+
void glTexStorage2DCustom(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type) {
#ifdef GLES_OVER_GL
@@ -852,8 +874,6 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
int size, ofs;
img->get_mipmap_offset_and_size(i, ofs, size);
- //print_line("mipmap: "+itos(i)+" size: "+itos(size)+" w: "+itos(mm_w)+", h: "+itos(mm_h));
-
if (texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) {
if (texture->compressed) {
@@ -1040,8 +1060,6 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
- //print_line("GET FORMAT: " + Image::get_format_name(texture->format) + " mipmaps: " + itos(texture->mipmaps));
-
for (int i = 0; i < texture->mipmaps; i++) {
int ofs = 0;
@@ -1094,8 +1112,76 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
return Ref<Image>(img);
#else
- ERR_EXPLAIN("Sorry, It's not possible to obtain images back in OpenGL ES");
- ERR_FAIL_V(Ref<Image>());
+ Image::Format real_format;
+ GLenum gl_format;
+ GLenum gl_internal_format;
+ GLenum gl_type;
+ bool compressed;
+ bool srgb;
+ _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb);
+
+ PoolVector<uint8_t> data;
+
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
+
+ data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
+ PoolVector<uint8_t>::Write wb = data.write();
+
+ GLuint temp_framebuffer;
+ glGenFramebuffers(1, &temp_framebuffer);
+
+ GLuint temp_color_texture;
+ glGenTextures(1, &temp_color_texture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
+
+ glBindTexture(GL_TEXTURE_2D, temp_color_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0);
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_BLEND);
+ glDepthFunc(GL_LEQUAL);
+ glColorMask(1, 1, 1, 1);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture->tex_id);
+
+ glViewport(0, 0, texture->alloc_width, texture->alloc_height);
+
+ shaders.copy.bind();
+
+ shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, !srgb);
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBindVertexArray(resources.quadie_array);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glBindVertexArray(0);
+
+ glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]);
+
+ shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, false);
+
+ glDeleteTextures(1, &temp_color_texture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &temp_framebuffer);
+
+ wb = PoolVector<uint8_t>::Write();
+
+ data.resize(data_size);
+
+ Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data));
+ if (!texture->compressed) {
+ img->convert(real_format);
+ }
+
+ return Ref<Image>(img);
#endif
}
@@ -1811,6 +1897,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
p_shader->uniforms.clear();
+ if (p_shader->code == String()) {
+ return; //just invalid, but no error
+ }
+
ShaderCompilerGLES3::GeneratedCode gen_code;
ShaderCompilerGLES3::IdentifierActions *actions = NULL;
@@ -2157,8 +2247,9 @@ Variant RasterizerStorageGLES3::material_get_param_default(RID p_material, const
if (material->shader) {
if (material->shader->uniforms.has(p_param)) {
- Vector<ShaderLanguage::ConstantNode::Value> default_value = material->shader->uniforms[p_param].default_value;
- return ShaderLanguage::constant_value_to_variant(default_value, material->shader->uniforms[p_param].type);
+ ShaderLanguage::ShaderNode::Uniform uniform = material->shader->uniforms[p_param];
+ Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint);
}
}
return Variant();
@@ -2738,7 +2829,7 @@ void RasterizerStorageGLES3::_update_material(Material *material) {
if (material->shader && material->shader->mode == VS::SHADER_SPATIAL) {
if (material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX &&
- (!material->shader->spatial.uses_alpha || (material->shader->spatial.uses_alpha && material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) {
+ (!material->shader->spatial.uses_alpha || material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) {
can_cast_shadow = true;
}
@@ -2759,7 +2850,7 @@ void RasterizerStorageGLES3::_update_material(Material *material) {
}
for (Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.front(); E; E = E->next()) {
- E->key()->base_material_changed();
+ E->key()->base_changed(false, true);
}
}
}
@@ -2795,9 +2886,6 @@ void RasterizerStorageGLES3::_update_material(Material *material) {
if (E->get().order < 0)
continue; // texture, does not go here
- //if (material->shader->mode == VS::SHADER_PARTICLES) {
- // print_line("uniform " + String(E->key()) + " order " + itos(E->get().order) + " offset " + itos(material->shader->ubo_offsets[E->get().order]));
- //}
//regular uniform
uint8_t *data = &local_ubo[material->shader->ubo_offsets[E->get().order]];
@@ -3385,7 +3473,7 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
}
mesh->surfaces.push_back(surface);
- mesh->instance_change_notify();
+ mesh->instance_change_notify(true, false);
info.vertex_mem += surface->total_data_size;
}
@@ -3458,7 +3546,7 @@ void RasterizerStorageGLES3::mesh_surface_set_material(RID p_mesh, int p_surface
_material_add_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]);
}
- mesh->instance_material_change_notify();
+ mesh->instance_change_notify(false, true);
}
RID RasterizerStorageGLES3::mesh_surface_get_material(RID p_mesh, int p_surface) const {
@@ -3494,21 +3582,26 @@ PoolVector<uint8_t> RasterizerStorageGLES3::mesh_surface_get_array(RID p_mesh, i
Surface *surface = mesh->surfaces[p_surface];
- glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id);
- void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, surface->array_byte_size, GL_MAP_READ_BIT);
-
- ERR_FAIL_COND_V(!data, PoolVector<uint8_t>());
-
PoolVector<uint8_t> ret;
ret.resize(surface->array_byte_size);
+ glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id);
+#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ glGetBufferSubData(GL_ARRAY_BUFFER, 0, surface->array_byte_size, w.ptr());
+ }
+#else
+ void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, surface->array_byte_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, PoolVector<uint8_t>());
{
-
PoolVector<uint8_t>::Write w = ret.write();
copymem(w.ptr(), data, surface->array_byte_size);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
return ret;
}
@@ -3521,22 +3614,26 @@ PoolVector<uint8_t> RasterizerStorageGLES3::mesh_surface_get_index_array(RID p_m
ERR_FAIL_COND_V(surface->index_array_len == 0, PoolVector<uint8_t>());
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id);
- void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT);
-
- ERR_FAIL_COND_V(!data, PoolVector<uint8_t>());
-
PoolVector<uint8_t> ret;
ret.resize(surface->index_array_byte_size);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id);
+#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, w.ptr());
+ }
+#else
+ void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT);
+ ERR_FAIL_NULL_V(data, PoolVector<uint8_t>());
{
-
PoolVector<uint8_t>::Write w = ret.write();
copymem(w.ptr(), data, surface->index_array_byte_size);
}
-
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+#endif
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return ret;
}
@@ -3577,23 +3674,26 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap
for (int i = 0; i < mesh->surfaces[p_surface]->blend_shapes.size(); i++) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->surfaces[p_surface]->blend_shapes[i].vertex_id);
- void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, GL_MAP_READ_BIT);
-
- ERR_FAIL_COND_V(!data, Vector<PoolVector<uint8_t> >());
-
PoolVector<uint8_t> ret;
ret.resize(mesh->surfaces[p_surface]->array_byte_size);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->surfaces[p_surface]->blend_shapes[i].vertex_id);
+#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, w.ptr());
+ }
+#else
+ void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, GL_MAP_READ_BIT);
+ ERR_FAIL_COND_V(!data, Vector<PoolVector<uint8_t> >());
{
-
PoolVector<uint8_t>::Write w = ret.write();
copymem(w.ptr(), data, mesh->surfaces[p_surface]->array_byte_size);
}
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+#endif
bsarr.push_back(ret);
-
- glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
}
return bsarr;
@@ -3642,13 +3742,11 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) {
info.vertex_mem -= surface->total_data_size;
- mesh->instance_material_change_notify();
-
memdelete(surface);
mesh->surfaces.remove(p_surface);
- mesh->instance_change_notify();
+ mesh->instance_change_notify(true, true);
}
int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const {
@@ -3664,7 +3762,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb
ERR_FAIL_COND(!mesh);
mesh->custom_aabb = p_aabb;
- mesh->instance_change_notify();
+ mesh->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const {
@@ -3680,12 +3778,14 @@ AABB RasterizerStorageGLES3::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
Mesh *mesh = mesh_owner.get(p_mesh);
ERR_FAIL_COND_V(!mesh, AABB());
- if (mesh->custom_aabb != AABB())
+ if (mesh->custom_aabb != AABB()) {
return mesh->custom_aabb;
+ }
Skeleton *sk = NULL;
- if (p_skeleton.is_valid())
+ if (p_skeleton.is_valid()) {
sk = skeleton_owner.get(p_skeleton);
+ }
AABB aabb;
@@ -3724,6 +3824,7 @@ AABB RasterizerStorageGLES3::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
mtx.origin.y = texture[base_ofs + 3];
AABB baabb = mtx.xform(skbones[j]);
+
if (first) {
laabb = baabb;
first = false;
@@ -4563,7 +4664,7 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
multimesh->dirty_aabb = false;
multimesh->dirty_data = false;
- multimesh->instance_change_notify();
+ multimesh->instance_change_notify(true, false);
multimesh_update_list.remove(multimesh_update_list.first());
}
@@ -4674,7 +4775,7 @@ void RasterizerStorageGLES3::immediate_end(RID p_immediate) {
im->building = false;
- im->instance_change_notify();
+ im->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::immediate_clear(RID p_immediate) {
@@ -4683,7 +4784,7 @@ void RasterizerStorageGLES3::immediate_clear(RID p_immediate) {
ERR_FAIL_COND(im->building);
im->chunks.clear();
- im->instance_change_notify();
+ im->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES3::immediate_get_aabb(RID p_immediate) const {
@@ -4698,7 +4799,7 @@ void RasterizerStorageGLES3::immediate_set_material(RID p_immediate, RID p_mater
Immediate *im = immediate_owner.get(p_immediate);
ERR_FAIL_COND(!im);
im->material = p_material;
- im->instance_material_change_notify();
+ im->instance_change_notify(false, true);
}
RID RasterizerStorageGLES3::immediate_get_material(RID p_immediate) const {
@@ -4904,7 +5005,7 @@ void RasterizerStorageGLES3::update_dirty_skeletons() {
}
for (Set<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) {
- E->get()->base_changed();
+ E->get()->base_changed(true, false);
}
skeleton_update_list.remove(skeleton_update_list.first());
@@ -4970,7 +5071,7 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param
case VS::LIGHT_PARAM_SHADOW_BIAS: {
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
} break;
default: {}
}
@@ -4984,7 +5085,7 @@ void RasterizerStorageGLES3::light_set_shadow(RID p_light, bool p_enabled) {
light->shadow = p_enabled;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::light_set_shadow_color(RID p_light, const Color &p_color) {
@@ -5017,7 +5118,7 @@ void RasterizerStorageGLES3::light_set_cull_mask(RID p_light, uint32_t p_mask) {
light->cull_mask = p_mask;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
@@ -5028,7 +5129,7 @@ void RasterizerStorageGLES3::light_set_reverse_cull_face_mode(RID p_light, bool
light->reverse_cull = p_enabled;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) {
@@ -5039,7 +5140,7 @@ void RasterizerStorageGLES3::light_omni_set_shadow_mode(RID p_light, VS::LightOm
light->omni_shadow_mode = p_mode;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
VS::LightOmniShadowMode RasterizerStorageGLES3::light_omni_get_shadow_mode(RID p_light) {
@@ -5057,7 +5158,7 @@ void RasterizerStorageGLES3::light_omni_set_shadow_detail(RID p_light, VS::Light
light->omni_shadow_detail = p_detail;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) {
@@ -5067,7 +5168,7 @@ void RasterizerStorageGLES3::light_directional_set_shadow_mode(RID p_light, VS::
light->directional_shadow_mode = p_mode;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::light_directional_set_blend_splits(RID p_light, bool p_enable) {
@@ -5077,7 +5178,7 @@ void RasterizerStorageGLES3::light_directional_set_blend_splits(RID p_light, boo
light->directional_blend_splits = p_enable;
light->version++;
- light->instance_change_notify();
+ light->instance_change_notify(true, false);
}
bool RasterizerStorageGLES3::light_directional_get_blend_splits(RID p_light) const {
@@ -5208,7 +5309,7 @@ void RasterizerStorageGLES3::reflection_probe_set_update_mode(RID p_probe, VS::R
ERR_FAIL_COND(!reflection_probe);
reflection_probe->update_mode = p_mode;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
@@ -5249,7 +5350,7 @@ void RasterizerStorageGLES3::reflection_probe_set_max_distance(RID p_probe, floa
ERR_FAIL_COND(!reflection_probe);
reflection_probe->max_distance = p_distance;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) {
@@ -5257,7 +5358,7 @@ void RasterizerStorageGLES3::reflection_probe_set_extents(RID p_probe, const Vec
ERR_FAIL_COND(!reflection_probe);
reflection_probe->extents = p_extents;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
@@ -5265,7 +5366,7 @@ void RasterizerStorageGLES3::reflection_probe_set_origin_offset(RID p_probe, con
ERR_FAIL_COND(!reflection_probe);
reflection_probe->origin_offset = p_offset;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
@@ -5289,7 +5390,7 @@ void RasterizerStorageGLES3::reflection_probe_set_enable_shadows(RID p_probe, bo
ERR_FAIL_COND(!reflection_probe);
reflection_probe->enable_shadows = p_enable;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
@@ -5297,7 +5398,7 @@ void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_
ERR_FAIL_COND(!reflection_probe);
reflection_probe->cull_mask = p_layers;
- reflection_probe->instance_change_notify();
+ reflection_probe->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
@@ -5385,7 +5486,7 @@ void RasterizerStorageGLES3::gi_probe_set_bounds(RID p_probe, const AABB &p_boun
gip->bounds = p_bounds;
gip->version++;
- gip->instance_change_notify();
+ gip->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES3::gi_probe_get_bounds(RID p_probe) const {
@@ -5402,7 +5503,7 @@ void RasterizerStorageGLES3::gi_probe_set_cell_size(RID p_probe, float p_size) {
gip->cell_size = p_size;
gip->version++;
- gip->instance_change_notify();
+ gip->instance_change_notify(true, false);
}
float RasterizerStorageGLES3::gi_probe_get_cell_size(RID p_probe) const {
@@ -5435,7 +5536,7 @@ void RasterizerStorageGLES3::gi_probe_set_dynamic_data(RID p_probe, const PoolVe
gip->dynamic_data = p_data;
gip->version++;
- gip->instance_change_notify();
+ gip->instance_change_notify(true, false);
}
PoolVector<int> RasterizerStorageGLES3::gi_probe_get_dynamic_data(RID p_probe) const {
@@ -5667,7 +5768,7 @@ void RasterizerStorageGLES3::lightmap_capture_set_bounds(RID p_capture, const AA
LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture);
ERR_FAIL_COND(!capture);
capture->bounds = p_bounds;
- capture->instance_change_notify();
+ capture->instance_change_notify(true, false);
}
AABB RasterizerStorageGLES3::lightmap_capture_get_bounds(RID p_capture) const {
@@ -5688,7 +5789,7 @@ void RasterizerStorageGLES3::lightmap_capture_set_octree(RID p_capture, const Po
PoolVector<uint8_t>::Read r = p_octree.read();
copymem(w.ptr(), r.ptr(), p_octree.size());
}
- capture->instance_change_notify();
+ capture->instance_change_notify(true, false);
}
PoolVector<uint8_t> RasterizerStorageGLES3::lightmap_capture_get_octree(RID p_capture) const {
@@ -5911,7 +6012,7 @@ void RasterizerStorageGLES3::particles_set_custom_aabb(RID p_particles, const AA
ERR_FAIL_COND(!particles);
particles->custom_aabb = p_aabb;
_particles_update_histories(particles);
- particles->instance_change_notify();
+ particles->instance_change_notify(true, false);
}
void RasterizerStorageGLES3::particles_set_speed_scale(RID p_particles, float p_scale) {
@@ -6001,9 +6102,21 @@ AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) {
const Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND_V(!particles, AABB());
+ const float *data;
glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]);
- float *data = (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, GL_MAP_READ_BIT);
+#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
+ PoolVector<uint8_t> vector;
+ vector.resize(particles->amount * 16 * 6);
+ {
+ PoolVector<uint8_t>::Write w = vector.write();
+ glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, w.ptr());
+ }
+ PoolVector<uint8_t>::Read r = vector.read();
+ data = reinterpret_cast<const float *>(r.ptr());
+#else
+ data = (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, GL_MAP_READ_BIT);
+#endif
AABB aabb;
Transform inv = particles->emission_transform.affine_inverse();
@@ -6020,7 +6133,13 @@ AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) {
aabb.expand_to(pos);
}
+#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__)
+ r = PoolVector<uint8_t>::Read();
+ vector = PoolVector<uint8_t>();
+#else
glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+
glBindBuffer(GL_ARRAY_BUFFER, 0);
float longest_axis = 0;
@@ -6319,12 +6438,19 @@ void RasterizerStorageGLES3::update_particles() {
particles->particle_valid_histories[0] = true;
}
- particles->instance_change_notify(); //make sure shadows are updated
+ particles->instance_change_notify(true, false); //make sure shadows are updated
}
glDisable(GL_RASTERIZER_DISCARD);
}
+bool RasterizerStorageGLES3::particles_is_inactive(RID p_particles) const {
+
+ const Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND_V(!particles, false);
+ return !particles->emitting && particles->inactive;
+}
+
////////
void RasterizerStorageGLES3::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
@@ -7686,6 +7812,8 @@ void RasterizerStorageGLES3::initialize() {
{
//transform feedback buffers
uint32_t xf_feedback_size = GLOBAL_DEF_RST("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/blend_shape_max_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/blend_shape_max_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater"));
+
for (int i = 0; i < 2; i++) {
glGenBuffers(1, &resources.transform_feedback_buffers[i]);
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 9a4798ac2a..398ffdeb82 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -43,6 +43,12 @@
#include "shaders/cubemap_filter.glsl.gen.h"
#include "shaders/particles.glsl.gen.h"
+// WebGL 2.0 has no MapBufferRange/UnmapBuffer, but offers a non-ES style BufferSubData API instead.
+#ifdef __EMSCRIPTEN__
+void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
+void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
+#endif
+
class RasterizerCanvasGLES3;
class RasterizerSceneGLES3;
@@ -175,22 +181,12 @@ public:
SelfList<RasterizerScene::InstanceBase>::List instance_list;
- _FORCE_INLINE_ void instance_change_notify() {
+ _FORCE_INLINE_ void instance_change_notify(bool p_aabb, bool p_materials) {
SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
while (instances) {
- instances->self()->base_changed();
- instances = instances->next();
- }
- }
-
- _FORCE_INLINE_ void instance_material_change_notify() {
-
- SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first();
- while (instances) {
-
- instances->self()->base_material_changed();
+ instances->self()->base_changed(p_aabb, p_materials);
instances = instances->next();
}
}
@@ -443,6 +439,7 @@ public:
};
int light_mode;
+
bool uses_screen_texture;
bool uses_screen_uv;
bool uses_time;
@@ -658,7 +655,7 @@ public:
bool active;
virtual void material_changed_notify() {
- mesh->instance_material_change_notify();
+ mesh->instance_change_notify(false, true);
mesh->update_multimeshes();
}
@@ -706,7 +703,7 @@ public:
SelfList<MultiMesh> *mm = multimeshes.first();
while (mm) {
- mm->self()->instance_material_change_notify();
+ mm->self()->instance_change_notify(false, true);
mm = mm->next();
}
}
@@ -1264,6 +1261,8 @@ public:
virtual int particles_get_draw_passes(RID p_particles) const;
virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const;
+ virtual bool particles_is_inactive(RID p_particles) const;
+
/* INSTANCE */
virtual void instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance);
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index be36b41417..2372dfb17e 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -133,8 +133,7 @@ static String _interpstr(SL::DataInterpolation p_interp) {
switch (p_interp) {
case SL::INTERPOLATION_FLAT: return "flat ";
- case SL::INTERPOLATION_NO_PERSPECTIVE: return "noperspective ";
- case SL::INTERPOLATION_SMOOTH: return "smooth ";
+ case SL::INTERPOLATION_SMOOTH: return "";
}
return "";
}
@@ -167,15 +166,17 @@ static String _opstr(SL::Operator p_op) {
static String _mkid(const String &p_id) {
- return "m_" + p_id;
+ String id = "m_" + p_id;
+ return id.replace("__", "_dus_"); //doubleunderscore is reserverd in glsl
}
static String f2sp0(float p_float) {
- if (int(p_float) == p_float)
- return itos(p_float) + ".0";
- else
- return rtoss(p_float);
+ String num = rtoss(p_float);
+ if (num.find(".") == -1 && num.find("e") == -1) {
+ num += ".0";
+ }
+ return num;
}
static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
@@ -228,7 +229,7 @@ static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNo
text += ")";
return text;
} break;
- case SL::TYPE_FLOAT: return f2sp0(p_values[0].real) + "f";
+ case SL::TYPE_FLOAT: return f2sp0(p_values[0].real);
case SL::TYPE_VEC2:
case SL::TYPE_VEC3:
case SL::TYPE_VEC4: {
@@ -476,6 +477,7 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
//code for functions
for (int i = 0; i < pnode->functions.size(); i++) {
SL::FunctionNode *fnode = pnode->functions[i].function;
+ current_func_name = fnode->name;
function_code[fnode->name] = _dump_node_code(fnode->body, p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
}
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 799179e8d4..404a9107ab 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -219,20 +219,15 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() {
strings.push_back("#version 300 es\n");
#endif
- int define_line_ofs = 1;
-
for (int i = 0; i < custom_defines.size(); i++) {
strings.push_back(custom_defines[i].get_data());
- define_line_ofs++;
}
for (int j = 0; j < conditional_count; j++) {
bool enable = ((1 << j) & conditional_version.version);
strings.push_back(enable ? conditional_defines[j] : "");
- if (enable)
- define_line_ofs++;
if (enable) {
DEBUG_PRINT(conditional_defines[j]);
@@ -253,7 +248,6 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() {
ERR_FAIL_COND_V(!custom_code_map.has(conditional_version.code_version), NULL);
cc = &custom_code_map[conditional_version.code_version];
v.code_version = cc->version;
- define_line_ofs += 2;
}
/* CREATE PROGRAM */
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 8e8b693eb2..ef2319c332 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -92,11 +92,6 @@ const bool at_light_pass = true;
const bool at_light_pass = false;
#endif
-#ifdef USE_PARTICLES
-uniform int h_frames;
-uniform int v_frames;
-#endif
-
#if defined(USE_MATERIAL)
/* clang-format off */
@@ -146,15 +141,6 @@ void main() {
#ifdef USE_PARTICLES
//scale by texture size
outvec.xy /= color_texpixel_size;
-
- //compute h and v frames and adjust UV interp for animation
- int total_frames = h_frames * v_frames;
- int frame = min(int(float(total_frames) * instance_custom.z), total_frames - 1);
- float frame_w = 1.0 / float(h_frames);
- float frame_h = 1.0 / float(v_frames);
- uv_interp.x = uv_interp.x * frame_w + frame_w * float(frame % h_frames);
- uv_interp.y = uv_interp.y * frame_h + frame_h * float(frame / h_frames);
-
#endif
#define extra_matrix extra_matrix_instance
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 8388b15edb..45802ad3b5 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -42,8 +42,6 @@ layout(location = 4) in vec2 uv_attrib;
layout(location = 5) in vec2 uv2_attrib;
#endif
-uniform float normal_mult;
-
#ifdef USE_SKELETON
layout(location = 6) in uvec4 bone_indices; // attrib:6
layout(location = 7) in vec4 bone_weights; // attrib:7
@@ -98,6 +96,8 @@ layout(std140) uniform SceneData { // ubo:0
bool fog_depth_enabled;
highp float fog_depth_begin;
+ highp float fog_depth_end;
+ mediump float fog_density;
highp float fog_depth_curve;
bool fog_transmit_enabled;
highp float fog_transmit_curve;
@@ -278,11 +278,10 @@ void main() {
}
#endif
- vec3 normal = normal_attrib * normal_mult;
+ vec3 normal = normal_attrib;
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
vec3 tangent = tangent_attrib.xyz;
- tangent *= normal_mult;
float binormalf = tangent_attrib.a;
#endif
@@ -675,6 +674,8 @@ layout(std140) uniform SceneData {
bool fog_depth_enabled;
highp float fog_depth_begin;
+ highp float fog_depth_end;
+ mediump float fog_density;
highp float fog_depth_curve;
bool fog_transmit_enabled;
highp float fog_transmit_curve;
@@ -949,6 +950,18 @@ LIGHT_SHADER_CODE
float NdotV = dot(N, V);
float cNdotV = max(NdotV, 0.0);
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ vec3 H = normalize(V + L);
+#endif
+
+#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ float cNdotH = max(dot(N, H), 0.0);
+#endif
+
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ float cLdotH = max(dot(L, H), 0.0);
+#endif
+
if (metallic < 1.0) {
#if defined(DIFFUSE_OREN_NAYAR)
vec3 diffuse_brdf_NL;
@@ -983,13 +996,9 @@ LIGHT_SHADER_CODE
#elif defined(DIFFUSE_BURLEY)
{
-
- vec3 H = normalize(V + L);
- float cLdotH = max(0.0, dot(L, H));
-
- float FD90 = 0.5 + 2.0 * cLdotH * cLdotH * roughness;
- float FdV = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotV);
- float FdL = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotL);
+ float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5;
+ float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV);
+ float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL);
diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL;
/*
float energyBias = mix(roughness, 0.0, 0.5);
@@ -1026,13 +1035,9 @@ LIGHT_SHADER_CODE
#if defined(SPECULAR_BLINN)
//normalized blinn
- vec3 H = normalize(V + L);
- float cNdotH = max(dot(N, H), 0.0);
- float cVdotH = max(dot(V, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
- blinn *= (shininess + 8.0) / (8.0 * 3.141592654);
+ blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float intensity = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
specular_light += light_color * intensity * specular_blob_intensity * attenuation;
@@ -1043,7 +1048,7 @@ LIGHT_SHADER_CODE
float cRdotV = max(0.0, dot(R, V));
float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25;
float phong = pow(cRdotV, shininess);
- phong *= (shininess + 8.0) / (8.0 * 3.141592654);
+ phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float intensity = (phong) / max(4.0 * cNdotV * cNdotL, 0.75);
specular_light += light_color * intensity * specular_blob_intensity * attenuation;
@@ -1063,11 +1068,6 @@ LIGHT_SHADER_CODE
#elif defined(SPECULAR_SCHLICK_GGX)
// shlick+ggx as default
- vec3 H = normalize(V + L);
-
- float cNdotH = max(dot(N, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
-
#if defined(LIGHT_USE_ANISOTROPY)
float alpha = roughness * roughness;
@@ -1095,23 +1095,17 @@ LIGHT_SHADER_CODE
#endif
#if defined(LIGHT_USE_CLEARCOAT)
- if (clearcoat_gloss > 0.0) {
-#if !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_BLINN)
- vec3 H = normalize(V + L);
-#endif
+
#if !defined(SPECULAR_SCHLICK_GGX)
- float cNdotH = max(dot(N, H), 0.0);
- float cLdotH = max(dot(L, H), 0.0);
- float cLdotH5 = SchlickFresnel(cLdotH);
+ float cLdotH5 = SchlickFresnel(cLdotH);
#endif
- float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
- float Fr = mix(.04, 1.0, cLdotH5);
- float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
+ float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss));
+ float Fr = mix(.04, 1.0, cLdotH5);
+ float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25);
- float specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
+ float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL;
- specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
- }
+ specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
#endif
}
@@ -1591,24 +1585,24 @@ void main() {
float alpha = 1.0;
-#if defined(DO_SIDE_CHECK)
- float side = gl_FrontFacing ? 1.0 : -1.0;
-#else
- float side = 1.0;
-#endif
-
#if defined(ALPHA_SCISSOR_USED)
float alpha_scissor = 0.5;
#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
- vec3 binormal = normalize(binormal_interp) * side;
- vec3 tangent = normalize(tangent_interp) * side;
+ vec3 binormal = normalize(binormal_interp);// * side;
+ vec3 tangent = normalize(tangent_interp);// * side;
#else
vec3 binormal = vec3(0.0);
vec3 tangent = vec3(0.0);
#endif
- vec3 normal = normalize(normal_interp) * side;
+ vec3 normal = normalize(normal_interp);
+
+#if defined(DO_SIDE_CHECK)
+ if (!gl_FrontFacing) {
+ normal = -normal;
+ }
+#endif
#if defined(ENABLE_UV_INTERP)
vec2 uv = uv_interp;
@@ -1664,7 +1658,7 @@ FRAGMENT_SHADER_CODE
normalmap.xy = normalmap.xy * 2.0 - 1.0;
normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc.
- normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side;
+ normal = normalize(mix(normal, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth));
#endif
@@ -2033,10 +2027,11 @@ FRAGMENT_SHADER_CODE
//apply fog
if (fog_depth_enabled) {
+ float fog_far = fog_depth_end > 0 ? fog_depth_end : z_far;
- float fog_z = smoothstep(fog_depth_begin, z_far, length(vertex));
+ float fog_z = smoothstep(fog_depth_begin, fog_far, length(vertex));
- fog_amount = pow(fog_z, fog_depth_curve);
+ fog_amount = pow(fog_z, fog_depth_curve) * fog_density;
if (fog_transmit_enabled) {
vec3 total_light = emission + ambient_light + specular_light + diffuse_light;
float transmit = pow(fog_z, fog_transmit_curve);
@@ -2053,7 +2048,7 @@ FRAGMENT_SHADER_CODE
emission = emission * rev_amount + fog_color * fog_amount;
ambient_light *= rev_amount;
- specular_light *rev_amount;
+ specular_light *= rev_amount;
diffuse_light *= rev_amount;
}
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 9c02549e39..010f7bdb0a 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -346,9 +346,9 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
for (int j = 0; j < ad->pa_map.channels - 1; j++) {
ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
- uint32_t l = ad->samples_in[in_idx++];
- uint32_t r = ad->samples_in[in_idx++];
- ad->samples_out.write[out_idx++] = ((l >> 1) + (r >> 1)) >> 16;
+ uint32_t l = ad->samples_in[in_idx++] >> 16;
+ uint32_t r = ad->samples_in[in_idx++] >> 16;
+ ad->samples_out.write[out_idx++] = (l + r) / 2;
}
}
}
@@ -613,20 +613,18 @@ Error AudioDriverPulseAudio::capture_init_device() {
break;
}
- print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
-
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
spec.channels = pa_rec_map.channels;
spec.rate = mix_rate;
- int latency = 30;
- input_buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
- int buffer_size = input_buffer_frames * spec.channels;
+ int input_latency = 30;
+ int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000);
+ int input_buffer_size = input_buffer_frames * spec.channels;
pa_buffer_attr attr;
- attr.fragsize = buffer_size * sizeof(int16_t);
+ attr.fragsize = input_buffer_size * sizeof(int16_t);
pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);
if (pa_rec_str == NULL) {
@@ -642,9 +640,10 @@ Error AudioDriverPulseAudio::capture_init_device() {
ERR_FAIL_V(ERR_CANT_OPEN);
}
- input_buffer.resize(input_buffer_frames * 8);
- input_position = 0;
- input_size = 0;
+ input_buffer_init(input_buffer_frames);
+
+ print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
+ print_verbose("PulseAudio: input buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms");
return OK;
}
@@ -760,7 +759,6 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
mix_rate = 0;
buffer_frames = 0;
- input_buffer_frames = 0;
pa_buffer_size = 0;
channels = 0;
pa_ready = 0;
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index f8358a452b..d8bab841ff 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -64,7 +64,6 @@ class AudioDriverPulseAudio : public AudioDriver {
unsigned int mix_rate;
unsigned int buffer_frames;
- unsigned int input_buffer_frames;
unsigned int pa_buffer_size;
int channels;
int pa_ready;
diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp
index 10ba0663f2..bc6ceb1e7e 100644
--- a/drivers/rtaudio/audio_driver_rtaudio.cpp
+++ b/drivers/rtaudio/audio_driver_rtaudio.cpp
@@ -114,11 +114,12 @@ Error AudioDriverRtAudio::init() {
unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
print_verbose("Audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
- short int tries = 2;
+ short int tries = 4;
- while (tries >= 0) {
+ while (tries > 0) {
switch (speaker_mode) {
case SPEAKER_MODE_STEREO: parameters.nChannels = 2; break;
+ case SPEAKER_SURROUND_31: parameters.nChannels = 4; break;
case SPEAKER_SURROUND_51: parameters.nChannels = 6; break;
case SPEAKER_SURROUND_71: parameters.nChannels = 8; break;
};
@@ -128,12 +129,14 @@ Error AudioDriverRtAudio::init() {
active = true;
break;
- } catch (RtAudioError &e) {
+ } catch (RtAudioError) {
// try with less channels
ERR_PRINT("Unable to open audio, retrying with fewer channels...");
switch (speaker_mode) {
- case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_MODE_STEREO; break;
+ case SPEAKER_MODE_STEREO: break; // Required to silence unhandled enum value warning.
+ case SPEAKER_SURROUND_31: speaker_mode = SPEAKER_MODE_STEREO; break;
+ case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_SURROUND_31; break;
case SPEAKER_SURROUND_71: speaker_mode = SPEAKER_SURROUND_51; break;
}
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index 2cc2032cbb..833b17f122 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -55,7 +55,7 @@
#include <netinet/tcp.h>
-#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED)
+#if defined(__APPLE__)
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif
@@ -110,7 +110,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
} else { // IPv4 socket
// IPv4 socket with IPv6 address
- ERR_FAIL_COND_V(!p_ip.is_ipv4(), 0);
+ ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
addr4->sin_family = AF_INET;
@@ -122,7 +122,6 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
addr4->sin_addr.s_addr = INADDR_ANY;
}
- copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
return sizeof(sockaddr_in);
}
}
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 7ff27be501..279274734f 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -288,6 +288,11 @@ uint64_t OS_Unix::get_ticks_usec() const {
Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) {
+#ifdef __EMSCRIPTEN__
+ // Don't compile this code at all to avoid undefined references.
+ // Actual virtual call goes to OS_JavaScript.
+ ERR_FAIL_V(ERR_BUG);
+#else
if (p_blocking && r_pipe) {
String argss;
@@ -354,6 +359,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bo
}
return OK;
+#endif
}
Error OS_Unix::kill(const ProcessID &p_pid) {
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index a91e41b008..8665f701b1 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -336,10 +336,7 @@ Error AudioDriverWASAPI::init_capture_device(bool reinit) {
HRESULT hr = audio_input.audio_client->GetBufferSize(&max_frames);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
- // Set the buffer size
- input_buffer.resize(max_frames * CAPTURE_BUFFER_CHANNELS);
- input_position = 0;
- input_size = 0;
+ input_buffer_init(max_frames);
return OK;
}
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index b4492a2022..2582478259 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -114,7 +114,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
path = path + ".tmp";
}
- f = _wfopen(path.c_str(), mode_string);
+ _wfopen_s(&f, path.c_str(), mode_string);
if (f == NULL) {
last_error = ERR_FILE_CANT_OPEN;
@@ -278,7 +278,7 @@ bool FileAccessWindows::file_exists(const String &p_name) {
FILE *g;
//printf("opening file %s\n", p_fname.c_str());
String filename = fix_path(p_name);
- g = _wfopen(filename.c_str(), L"rb");
+ _wfopen_s(&g, filename.c_str(), L"rb");
if (g == NULL) {
return false;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 77be561477..f65825e395 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1253,14 +1253,14 @@ void AnimationTrackEdit::_notification(int p_what) {
float offset = animation->track_get_key_time(track, i) - timeline->get_value();
if (editor->is_key_selected(track, i) && editor->is_moving_selection()) {
- offset += editor->get_moving_selection_offset();
+ offset = editor->snap_time(offset + editor->get_moving_selection_offset());
}
offset = offset * scale + limit;
if (i < animation->track_get_key_count(track) - 1) {
float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value();
if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) {
- offset_n += editor->get_moving_selection_offset();
+ offset_n = editor->snap_time(offset_n + editor->get_moving_selection_offset());
}
offset_n = offset_n * scale + limit;
@@ -1685,15 +1685,10 @@ void AnimationTrackEdit::_zoom_changed() {
}
void AnimationTrackEdit::_path_entered(const String &p_text) {
-
- *block_animation_update_ptr = true;
undo_redo->create_action("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));
undo_redo->commit_action();
- *block_animation_update_ptr = false;
- update();
- path->hide();
}
String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
@@ -3192,7 +3187,8 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo
case Animation::TYPE_ANIMATION: {
value = p_id.value;
} break;
- default: {}
+ default: {
+ }
}
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value);
@@ -3881,9 +3877,7 @@ void AnimationTrackEditor::_move_selection_begin() {
void AnimationTrackEditor::_move_selection(float p_offset) {
moving_selection_offset = p_offset;
- if (snap->is_pressed() && step->get_value() != 0) {
- moving_selection_offset = Math::stepify(moving_selection_offset, step->get_value());
- }
+
for (int i = 0; i < track_edits.size(); i++) {
track_edits[i]->update();
}
@@ -4003,7 +3997,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 2- remove overlapped keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newtime = E->get().pos + motion;
+ float newtime = snap_time(E->get().pos + motion);
int idx = animation->track_find_key(E->key().track, newtime, true);
if (idx == -1)
continue;
@@ -4027,7 +4021,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 3-move the keys (re insert them)
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = E->get().pos + motion;
+ float newpos = snap_time(E->get().pos + motion);
/*
if (newpos<0)
continue; //no add at the beginning
@@ -4038,7 +4032,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 4-(undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = E->get().pos + motion;
+ float newpos = snap_time(E->get().pos + motion);
/*
if (newpos<0)
continue; //no remove what no inserted
@@ -4074,7 +4068,7 @@ void AnimationTrackEditor::_move_selection_commit() {
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
- float newpos = oldpos + motion;
+ float newpos = snap_time(oldpos + motion);
//if (newpos>=0)
undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
@@ -4086,6 +4080,8 @@ void AnimationTrackEditor::_move_selection_commit() {
for (int i = 0; i < track_edits.size(); i++) {
track_edits[i]->update();
}
+
+ _update_key_edit();
}
void AnimationTrackEditor::_move_selection_cancel() {
@@ -4349,7 +4345,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
case Animation::TYPE_METHOD: text += " (Methods)"; break;
case Animation::TYPE_BEZIER: text += " (Bezier)"; break;
case Animation::TYPE_AUDIO: text += " (Audio)"; break;
- default: {};
+ default: {
+ };
}
TreeItem *it = track_copy_select->create_item(troot);
diff --git a/editor/array_property_edit.cpp b/editor/array_property_edit.cpp
index a32a71262f..245c9273ff 100644
--- a/editor/array_property_edit.cpp
+++ b/editor/array_property_edit.cpp
@@ -30,6 +30,7 @@
#include "array_property_edit.h"
+#include "core/io/marshalls.h"
#include "editor_node.h"
#define ITEMS_PER_PAGE 100
@@ -202,6 +203,11 @@ bool ArrayPropertyEdit::_get(const StringName &p_name, Variant &r_ret) const {
int idx = pn.get_slicec('/', 1).to_int();
bool valid;
r_ret = arr.get(idx, &valid);
+
+ if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
+ r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
+ }
+
return valid;
}
}
@@ -232,6 +238,11 @@ void ArrayPropertyEdit::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "indices/" + itos(i + offset) + "_type", PROPERTY_HINT_ENUM, vtypes));
}
+ if (v.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(v)) {
+ p_list->push_back(PropertyInfo(Variant::INT, "indices/" + itos(i + offset), PROPERTY_HINT_OBJECT_ID, "Object"));
+ continue;
+ }
+
if (is_typed || v.get_type() != Variant::NIL) {
PropertyInfo pi(v.get_type(), "indices/" + itos(i + offset));
if (subtype != Variant::NIL) {
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 79c22f667a..2a11f70274 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1039,6 +1039,8 @@ void CodeTextEditor::delete_lines() {
int to_line = text_editor->get_selection_to_line();
int from_line = text_editor->get_selection_from_line();
int count = Math::abs(to_line - from_line) + 1;
+
+ text_editor->cursor_set_line(to_line, false);
while (count) {
text_editor->set_line(text_editor->cursor_get_line(), "");
text_editor->backspace_at_cursor();
@@ -1058,7 +1060,7 @@ void CodeTextEditor::delete_lines() {
text_editor->end_complex_operation();
}
-void CodeTextEditor::code_lines_down() {
+void CodeTextEditor::clone_lines_down() {
int from_line = text_editor->cursor_get_line();
int to_line = text_editor->cursor_get_line();
int column = text_editor->cursor_get_column();
@@ -1070,22 +1072,21 @@ void CodeTextEditor::code_lines_down() {
}
int next_line = to_line + 1;
- if (to_line >= text_editor->get_line_count() - 1) {
- text_editor->set_line(to_line, text_editor->get_line(to_line) + "\n");
- }
-
+ bool caret_at_start = text_editor->cursor_get_line() == from_line;
text_editor->begin_complex_operation();
for (int i = from_line; i <= to_line; i++) {
-
text_editor->unfold_line(i);
- if (i >= text_editor->get_line_count() - 1) {
- text_editor->set_line(i, text_editor->get_line(i) + "\n");
- }
- String line_clone = text_editor->get_line(i);
- text_editor->insert_at(line_clone, next_line);
+ text_editor->set_line(next_line - 1, text_editor->get_line(next_line - 1) + "\n");
+ text_editor->set_line(next_line, text_editor->get_line(i));
next_line++;
}
+ if (caret_at_start) {
+ text_editor->cursor_set_line(to_line + 1);
+ } else {
+ text_editor->cursor_set_line(next_line - 1);
+ }
+
text_editor->cursor_set_column(column);
if (text_editor->is_selection_active()) {
text_editor->select(to_line + 1, text_editor->get_selection_from_column(), next_line - 1, text_editor->get_selection_to_column());
@@ -1129,6 +1130,19 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) {
void CodeTextEditor::set_error(const String &p_error) {
error->set_text(p_error);
+ error->set_tooltip(p_error);
+ error->set_visible(p_error != "");
+}
+
+void CodeTextEditor::set_error_pos(int p_line, int p_column) {
+ error_line = p_line;
+ error_column = p_column;
+}
+
+void CodeTextEditor::_error_pressed() {
+ text_editor->cursor_set_line(error_line);
+ text_editor->cursor_set_column(error_column);
+ text_editor->center_viewport_to_cursor();
}
void CodeTextEditor::_update_font() {
@@ -1190,6 +1204,7 @@ void CodeTextEditor::_bind_methods() {
ClassDB::bind_method("_code_complete_timer_timeout", &CodeTextEditor::_code_complete_timer_timeout);
ClassDB::bind_method("_complete_request", &CodeTextEditor::_complete_request);
ClassDB::bind_method("_font_resize_timeout", &CodeTextEditor::_font_resize_timeout);
+ ClassDB::bind_method("_error_pressed", &CodeTextEditor::_error_pressed);
ADD_SIGNAL(MethodInfo("validate_script"));
ADD_SIGNAL(MethodInfo("load_theme_settings"));
@@ -1238,13 +1253,22 @@ CodeTextEditor::CodeTextEditor() {
code_complete_timer->set_wait_time(EDITOR_DEF("text_editor/completion/code_complete_delay", .3f));
- error = memnew(Label);
- status_bar->add_child(error);
- error->set_autowrap(true);
- error->set_valign(Label::VALIGN_CENTER);
+ error_line = 0;
+ error_column = 0;
+
+ Control *error_box = memnew(Control);
+ status_bar->add_child(error_box);
+ error_box->set_v_size_flags(SIZE_EXPAND_FILL);
+ error_box->set_h_size_flags(SIZE_EXPAND_FILL);
+ error_box->set_clip_contents(true);
+
+ error = memnew(LinkButton);
+ error_box->add_child(error);
+ error->set_anchors_and_margins_preset(Control::PRESET_CENTER_LEFT);
+ error->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
error->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"));
error->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts"));
- error->set_h_size_flags(SIZE_EXPAND_FILL); //required for it to display, given now it's clipping contents, do not touch
+ error->connect("pressed", this, "_error_pressed");
find_replace_bar->connect("error", error, "set_text");
status_bar->add_child(memnew(Label)); //to keep the height if the other labels are not visible
diff --git a/editor/code_editor.h b/editor/code_editor.h
index ee47eff9a8..311b6a959b 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -36,6 +36,7 @@
#include "scene/gui/check_button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/line_edit.h"
+#include "scene/gui/link_button.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/tool_button.h"
#include "scene/main/timer.h"
@@ -157,7 +158,9 @@ class CodeTextEditor : public VBoxContainer {
int font_resize_val;
real_t font_size;
- Label *error;
+ LinkButton *error;
+ int error_line;
+ int error_column;
void _on_settings_change();
@@ -171,6 +174,7 @@ class CodeTextEditor : public VBoxContainer {
void _zoom_out();
void _zoom_changed();
void _reset_zoom();
+ void _error_pressed();
CodeTextEditorCodeCompleteFunc code_complete_func;
void *code_complete_ud;
@@ -203,7 +207,7 @@ public:
void move_lines_up();
void move_lines_down();
void delete_lines();
- void code_lines_down();
+ void clone_lines_down();
void goto_line(int p_line);
void goto_line_selection(int p_line, int p_begin, int p_end);
@@ -213,6 +217,7 @@ public:
void update_editor_settings();
void set_error(const String &p_error);
+ void set_error_pos(int p_line, int p_column);
void update_line_and_column() { _line_col_changed(); }
TextEdit *get_text_edit() { return text_editor; }
FindReplaceBar *get_find_replace_bar() { return find_replace_bar; }
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index c4516c1f17..926fa37040 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -118,8 +118,10 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) {
if (enable_rl) {
search_options->add_constant_override("draw_relationship_lines", 1);
search_options->add_color_override("relationship_line_color", rl_color);
+ search_options->add_constant_override("draw_guides", 0);
} else {
search_options->add_constant_override("draw_relationship_lines", 0);
+ search_options->add_constant_override("draw_guides", 1);
}
is_replace_mode = p_replace_mode;
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index d64b02a605..99b6955160 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -557,8 +557,9 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
//////////////
-void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> &report) {
+void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Vector<String> &report) {
+ mode = p_mode;
for_file = p_for_file;
set_title(TTR("Error loading:") + " " + p_for_file.get_file());
files->clear();
@@ -584,7 +585,14 @@ void DependencyErrorDialog::show(const String &p_for_file, const Vector<String>
void DependencyErrorDialog::ok_pressed() {
- EditorNode::get_singleton()->load_scene(for_file, true);
+ switch (mode) {
+ case MODE_SCENE:
+ EditorNode::get_singleton()->load_scene(for_file, true);
+ break;
+ case MODE_RESOURCE:
+ EditorNode::get_singleton()->load_resource(for_file, true);
+ break;
+ }
}
void DependencyErrorDialog::custom_action(const String &) {
@@ -599,7 +607,7 @@ DependencyErrorDialog::DependencyErrorDialog() {
files = memnew(Tree);
files->set_hide_root(true);
- vb->add_margin_child(TTR("Scene failed to load due to missing dependencies:"), files, true);
+ vb->add_margin_child(TTR("Load failed due to missing dependencies:"), files, true);
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_custom_minimum_size(Size2(1, 200));
get_ok()->set_text(TTR("Open Anyway"));
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index 4f268de748..e46df4c837 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -134,7 +134,15 @@ public:
class DependencyErrorDialog : public ConfirmationDialog {
GDCLASS(DependencyErrorDialog, ConfirmationDialog);
+public:
+ enum Mode {
+ MODE_SCENE,
+ MODE_RESOURCE,
+ };
+
+private:
String for_file;
+ Mode mode;
Button *fdep;
Label *text;
Tree *files;
@@ -142,7 +150,7 @@ class DependencyErrorDialog : public ConfirmationDialog {
void custom_action(const String &);
public:
- void show(const String &p_for_file, const Vector<String> &report);
+ void show(Mode p_mode, const String &p_for_file, const Vector<String> &report);
DependencyErrorDialog();
};
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index cdf0e4b829..14abaa835c 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -47,7 +47,9 @@ void EditorAbout::_notification(int p_what) {
Control *base = EditorNode::get_singleton()->get_gui_base();
Ref<Font> font = base->get_font("source", "EditorFonts");
_tpl_text->add_font_override("normal_font", font);
+ _tpl_text->add_constant_override("line_separation", 6 * EDSCALE);
_license_text->add_font_override("normal_font", font);
+ _license_text->add_constant_override("line_separation", 6 * EDSCALE);
_logo->set_texture(base->get_icon("Logo", "EditorIcons"));
} break;
}
@@ -207,7 +209,6 @@ EditorAbout::EditorAbout() {
TreeItem *tpl_ti_lc = _tpl_tree->create_item(root);
tpl_ti_lc->set_text(0, TTR("Licenses"));
tpl_ti_lc->set_selectable(0, false);
- int read_idx = 0;
String long_text = "";
for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) {
@@ -232,7 +233,6 @@ EditorAbout::EditorAbout() {
String license = "\n License: " + String(part.license) + "\n";
text += license;
long_text += license + "\n";
- read_idx++;
}
ti->set_metadata(0, text);
}
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 1374c8c9aa..64742ff74c 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -185,6 +185,7 @@ void EditorAutoloadSettings::_autoload_edited() {
if (path.begins_with("*"))
path = path.substr(1, path.length());
+ // Singleton autoloads are represented with a leading "*" in their path.
if (checked)
path = "*" + path;
@@ -651,6 +652,7 @@ void EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Add AutoLoad"));
+ // Singleton autoloads are represented with a leading "*" in their path.
undo_redo->add_do_property(ProjectSettings::get_singleton(), name, "*" + path);
if (ProjectSettings::get_singleton()->has_setting(name)) {
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 9420452da1..bd6ce797b5 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -946,16 +946,18 @@ void EditorData::script_class_save_icon_paths() {
void EditorData::script_class_load_icon_paths() {
script_class_clear_icon_paths();
- Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons");
- List<Variant> keys;
- d.get_key_list(&keys);
+ if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) {
+ Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons");
+ List<Variant> keys;
+ d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- String name = E->get().operator String();
- _script_class_icon_paths[name] = d[name];
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ String name = E->get().operator String();
+ _script_class_icon_paths[name] = d[name];
- String path = ScriptServer::get_global_class_path(name);
- script_class_set_name(path, name);
+ String path = ScriptServer::get_global_class_path(name);
+ script_class_set_name(path, name);
+ }
}
}
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 1a6188862f..209a006a06 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -144,6 +144,17 @@ String EditorExportPreset::get_include_filter() const {
return include_filter;
}
+void EditorExportPreset::set_export_path(const String &p_path) {
+
+ export_path = p_path;
+ EditorExport::singleton->save_presets();
+}
+
+String EditorExportPreset::get_export_path() const {
+
+ return export_path;
+}
+
void EditorExportPreset::set_exclude_filter(const String &p_exclude) {
exclude_filter = p_exclude;
@@ -213,6 +224,7 @@ String EditorExportPreset::get_custom_features() const {
EditorExportPreset::EditorExportPreset() {
+ export_path = "";
export_filter = EXPORT_ALL_RESOURCES;
runnable = false;
}
@@ -1034,6 +1046,7 @@ void EditorExport::_save() {
}
config->set_value(section, "include_filter", preset->get_include_filter());
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
+ config->set_value(section, "export_path", preset->get_export_path());
config->set_value(section, "patch_list", preset->get_patches());
String option_section = "preset." + itos(i) + ".options";
@@ -1189,6 +1202,7 @@ void EditorExport::load_config() {
preset->set_include_filter(config->get_value(section, "include_filter"));
preset->set_exclude_filter(config->get_value(section, "exclude_filter"));
+ preset->set_export_path(config->get_value(section, "export_path", ""));
Vector<String> patch_list = config->get_value(section, "patch_list");
@@ -1337,18 +1351,21 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset,
return valid;
}
-String EditorExportPlatformPC::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const {
+List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
for (Map<String, String>::Element *E = extensions.front(); E; E = E->next()) {
if (p_preset->get(E->key())) {
- return extensions[E->key()];
+ list.push_back(extensions[E->key()]);
+ return list;
}
}
if (extensions.has("default")) {
- return extensions["default"];
+ list.push_back(extensions["default"]);
+ return list;
}
- return "";
+ return list;
}
Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
diff --git a/editor/editor_export.h b/editor/editor_export.h
index b4ee5b89e7..380b33cd17 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -57,6 +57,7 @@ private:
ExportFilter export_filter;
String include_filter;
String exclude_filter;
+ String export_path;
String exporter;
Set<String> selected_files;
@@ -114,6 +115,9 @@ public:
void set_custom_features(const String &p_custom_features);
String get_custom_features() const;
+ void set_export_path(const String &p_path);
+ String get_export_path() const;
+
const List<PropertyInfo> &get_properties() const { return properties; }
EditorExportPreset();
@@ -241,7 +245,7 @@ public:
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0;
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const = 0;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0;
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
@@ -388,7 +392,7 @@ public:
virtual Ref<Texture> get_logo() const;
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
void set_extension(const String &p_extension, const String &p_feature_key = "default");
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index f425d0a995..b39f910182 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -580,7 +580,7 @@ void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p
if (single_item_selected) {
item_menu->add_separator();
Dictionary item_meta = item_list->get_item_metadata(p_item);
- String item_text = item_meta["dir"] ? TTR("Open In File Manager") : TTR("Show In File Manager");
+ String item_text = item_meta["dir"] ? TTR("Open in File Manager") : TTR("Show in File Manager");
item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), item_text, ITEM_MENU_SHOW_IN_EXPLORER);
}
@@ -605,7 +605,7 @@ void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) {
}
item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5);
item_menu->add_separator();
- item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
+ item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
item_menu->set_position(item_list->get_global_position() + p_pos);
item_menu->popup();
@@ -1135,14 +1135,10 @@ void EditorFileDialog::_update_drives() {
}
void EditorFileDialog::_favorite_selected(int p_idx) {
-
- Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
- ERR_FAIL_INDEX(p_idx, favorited.size());
-
- dir_access->change_dir(favorited[p_idx]);
+ dir_access->change_dir(favorites->get_item_metadata(p_idx));
file->set_text("");
- invalidate();
update_dir();
+ invalidate();
_push_history();
}
@@ -1192,7 +1188,7 @@ void EditorFileDialog::_update_favorites() {
bool res = access == ACCESS_RESOURCES;
String current = get_current_dir();
- Ref<Texture> star = get_icon("Favorites", "EditorIcons");
+ Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons");
favorites->clear();
favorite->set_pressed(false);
@@ -1203,16 +1199,23 @@ void EditorFileDialog::_update_favorites() {
if (cres != res)
continue;
String name = favorited[i];
-
- bool setthis = name == current;
+ bool setthis = false;
if (res && name == "res://") {
+ if (name == current)
+ setthis = true;
name = "/";
+ } else if (name.ends_with("/")) {
+ if (name == current)
+ setthis = true;
+ name = name.substr(0, name.length() - 1);
+ name = name.get_file();
+
+ favorites->add_item(name, folder_icon);
} else {
- name = name.get_file() + "/";
+ continue; // We don't handle favorite files here
}
- favorites->add_item(name, star);
favorites->set_item_metadata(favorites->get_item_count() - 1, favorited[i]);
if (setthis) {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index d73bf86f64..94eb1a3399 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -466,6 +466,7 @@ bool EditorFileSystem::_update_scan_actions() {
bool fs_changed = false;
Vector<String> reimports;
+ Vector<String> reloads;
for (List<ItemAction>::Element *E = scan_actions.front(); E; E = E->next()) {
@@ -545,12 +546,25 @@ bool EditorFileSystem::_update_scan_actions() {
fs_changed = true;
} break;
+ case ItemAction::ACTION_FILE_RELOAD: {
+
+ int idx = ia.dir->find_file_index(ia.file);
+ ERR_CONTINUE(idx == -1);
+ String full_path = ia.dir->get_file_path(idx);
+
+ reloads.push_back(full_path);
+
+ } break;
}
}
if (reimports.size()) {
reimport_files(reimports);
}
+
+ if (reloads.size()) {
+ emit_signal("resources_reload", reloads);
+ }
scan_actions.clear();
return fs_changed;
@@ -905,11 +919,11 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
continue;
}
+ String path = cd.plus_file(p_dir->files[i]->file);
+
if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) {
//check here if file must be imported or not
- String path = cd.plus_file(p_dir->files[i]->file);
-
uint64_t mt = FileAccess::get_modified_time(path);
bool reimport = false;
@@ -936,6 +950,20 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
ia.file = p_dir->files[i]->file;
scan_actions.push_back(ia);
}
+ } else if (ResourceCache::has(path)) { //test for potential reload
+
+ uint64_t mt = FileAccess::get_modified_time(path);
+
+ if (mt != p_dir->files[i]->modified_time) {
+
+ p_dir->files[i]->modified_time = mt; //save new time, but test for reload
+
+ ItemAction ia;
+ ia.action = ItemAction::ACTION_FILE_RELOAD;
+ ia.dir = p_dir;
+ ia.file = p_dir->files[i]->file;
+ scan_actions.push_back(ia);
+ }
}
}
@@ -1305,11 +1333,6 @@ void EditorFileSystem::_save_late_updated_files() {
}
}
-void EditorFileSystem::_resource_saved(const String &p_path) {
-
- EditorFileSystem::get_singleton()->update_file(p_path);
-}
-
Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
List<String> deps;
@@ -1731,6 +1754,7 @@ void EditorFileSystem::_bind_methods() {
ADD_SIGNAL(MethodInfo("filesystem_changed"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
ADD_SIGNAL(MethodInfo("resources_reimported", PropertyInfo(Variant::POOL_STRING_ARRAY, "resources")));
+ ADD_SIGNAL(MethodInfo("resources_reload", PropertyInfo(Variant::POOL_STRING_ARRAY, "resources")));
}
void EditorFileSystem::_update_extensions() {
@@ -1772,7 +1796,6 @@ EditorFileSystem::EditorFileSystem() {
abort_scan = false;
scanning_changes = false;
scanning_changes_done = false;
- ResourceSaver::set_save_callback(_resource_saved);
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->change_dir("res://.import") != OK) {
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 47077425a1..51b3fd38f0 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -116,7 +116,8 @@ class EditorFileSystem : public Node {
ACTION_DIR_REMOVE,
ACTION_FILE_ADD,
ACTION_FILE_REMOVE,
- ACTION_FILE_TEST_REIMPORT
+ ACTION_FILE_TEST_REIMPORT,
+ ACTION_FILE_RELOAD
};
Action action;
@@ -204,8 +205,6 @@ class EditorFileSystem : public Node {
bool _update_scan_actions();
- static void _resource_saved(const String &p_path);
-
void _update_extensions();
void _reimport_file(const String &p_file);
diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp
new file mode 100644
index 0000000000..011fe20564
--- /dev/null
+++ b/editor/editor_folding.cpp
@@ -0,0 +1,175 @@
+#include "editor_folding.h"
+
+#include "core/os/file_access.h"
+#include "editor_settings.h"
+
+PoolVector<String> EditorFolding::_get_unfolds(const Object *p_object) {
+
+ PoolVector<String> sections;
+ sections.resize(p_object->editor_get_section_folding().size());
+ if (sections.size()) {
+ PoolVector<String>::Write w = sections.write();
+ int idx = 0;
+ for (const Set<String>::Element *E = p_object->editor_get_section_folding().front(); E; E = E->next()) {
+ w[idx++] = E->get();
+ }
+ }
+
+ return sections;
+}
+
+void EditorFolding::save_resource_folding(const RES &p_resource, const String &p_path) {
+ Ref<ConfigFile> config;
+ config.instance();
+ PoolVector<String> unfolds = _get_unfolds(p_resource.ptr());
+ config->set_value("folding", "sections_unfolded", unfolds);
+
+ String path = EditorSettings::get_singleton()->get_project_settings_dir();
+ String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
+ file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file);
+ config->save(file);
+}
+
+void EditorFolding::_set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds) {
+
+ int uc = p_unfolds.size();
+ PoolVector<String>::Read r = p_unfolds.read();
+ p_object->editor_clear_section_folding();
+ for (int i = 0; i < uc; i++) {
+ p_object->editor_set_section_unfold(r[i], true);
+ }
+}
+
+void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) {
+
+ Ref<ConfigFile> config;
+ config.instance();
+
+ String path = EditorSettings::get_singleton()->get_project_settings_dir();
+ String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
+ file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file);
+
+ if (config->load(file) != OK) {
+ return;
+ }
+
+ PoolVector<String> unfolds;
+
+ if (config->has_section_key("folding", "sections_unfolded")) {
+ unfolds = config->get_value("folding", "sections_unfolded");
+ }
+ _set_unfolds(p_resource.ptr(), unfolds);
+}
+
+void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) {
+ if (p_root != p_node) {
+ if (!p_node->get_owner()) {
+ return; //not owned, bye
+ }
+ if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) {
+ return;
+ }
+ }
+
+ PoolVector<String> unfolds = _get_unfolds(p_node);
+
+ if (unfolds.size()) {
+ p_folds.push_back(p_root->get_path_to(p_node));
+ p_folds.push_back(unfolds);
+ }
+
+ List<PropertyInfo> plist;
+ p_node->get_property_list(&plist);
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ if (E->get().type == Variant::OBJECT) {
+ RES res = p_node->get(E->get().name);
+ if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) {
+
+ PoolVector<String> res_unfolds = _get_unfolds(res.ptr());
+ resource_folds.push_back(res->get_path());
+ resource_folds.push_back(res_unfolds);
+ resources.insert(res);
+ }
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources);
+ }
+}
+void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) {
+
+ Ref<ConfigFile> config;
+ config.instance();
+
+ Array unfolds, res_unfolds;
+ Set<RES> resources;
+ _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources);
+
+ config->set_value("folding", "node_unfolds", unfolds);
+ config->set_value("folding", "resource_unfolds", res_unfolds);
+
+ String path = EditorSettings::get_singleton()->get_project_settings_dir();
+ String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
+ file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file);
+ config->save(file);
+}
+void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) {
+
+ Ref<ConfigFile> config;
+ config.instance();
+
+ String path = EditorSettings::get_singleton()->get_project_settings_dir();
+ String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
+ file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file);
+
+ if (config->load(file) != OK) {
+ return;
+ }
+
+ Array unfolds;
+ if (config->has_section_key("folding", "node_unfolds")) {
+ unfolds = config->get_value("folding", "node_unfolds");
+ }
+ Array res_unfolds;
+ if (config->has_section_key("folding", "resource_unfolds")) {
+ res_unfolds = config->get_value("folding", "resource_unfolds");
+ }
+
+ ERR_FAIL_COND(unfolds.size() & 1);
+ ERR_FAIL_COND(res_unfolds.size() & 1);
+
+ for (int i = 0; i < unfolds.size(); i += 2) {
+ NodePath path = unfolds[i];
+ PoolVector<String> un = unfolds[i + 1];
+ Node *node = p_scene->get_node(path);
+ if (!node) {
+ continue;
+ }
+ _set_unfolds(node, un);
+ }
+
+ for (int i = 0; i < res_unfolds.size(); i += 2) {
+ String path = res_unfolds[i];
+ RES res;
+ if (ResourceCache::has(path)) {
+ res = RES(ResourceCache::get(path));
+ }
+ if (res.is_null()) {
+ continue;
+ }
+
+ PoolVector<String> unfolds = res_unfolds[i + 1];
+ _set_unfolds(res.ptr(), unfolds);
+ }
+}
+
+bool EditorFolding::has_folding_data(const String &p_path) {
+
+ String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
+ file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file);
+ return FileAccess::exists(file);
+}
+
+EditorFolding::EditorFolding() {
+}
diff --git a/editor/editor_folding.h b/editor/editor_folding.h
new file mode 100644
index 0000000000..cfd4b5466d
--- /dev/null
+++ b/editor/editor_folding.h
@@ -0,0 +1,25 @@
+#ifndef EDITOR_FOLDING_H
+#define EDITOR_FOLDING_H
+
+#include "scene/main/node.h"
+
+class EditorFolding {
+
+ PoolVector<String> _get_unfolds(const Object *p_object);
+ void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds);
+
+ void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources);
+
+public:
+ void save_resource_folding(const RES &p_resource, const String &p_path);
+ void load_resource_folding(RES p_resource, const String &p_path);
+
+ void save_scene_folding(const Node *p_scene, const String &p_path);
+ void load_scene_folding(Node *p_scene, const String &p_path);
+
+ bool has_folding_data(const String &p_path);
+
+ EditorFolding();
+};
+
+#endif // EDITOR_FOLDING_H
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 8b1818b595..a54ad7894a 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -93,12 +93,14 @@ void editor_register_fonts(Ref<Theme> p_theme) {
/* Custom font */
+ bool font_antialiased = (bool)EditorSettings::get_singleton()->get("interface/editor/main_font_antialiased");
DynamicFontData::Hinting font_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/main_font_hinting");
String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
Ref<DynamicFontData> CustomFont;
if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
CustomFont.instance();
+ CustomFont->set_antialiased(font_antialiased);
CustomFont->set_hinting(font_hinting);
CustomFont->set_font_path(custom_font_path);
CustomFont->set_force_autohinter(true); //just looks better..i think?
@@ -112,6 +114,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
Ref<DynamicFontData> CustomFontBold;
if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
CustomFontBold.instance();
+ CustomFontBold->set_antialiased(font_antialiased);
CustomFontBold->set_hinting(font_hinting);
CustomFontBold->set_font_path(custom_font_path_bold);
CustomFontBold->set_force_autohinter(true); //just looks better..i think?
@@ -122,10 +125,12 @@ void editor_register_fonts(Ref<Theme> p_theme) {
/* Custom source code font */
String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
+ bool font_source_antialiased = (bool)EditorSettings::get_singleton()->get("interface/editor/code_font_antialiased");
DynamicFontData::Hinting font_source_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/code_font_hinting");
Ref<DynamicFontData> CustomFontSource;
if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) {
CustomFontSource.instance();
+ CustomFontSource->set_antialiased(font_source_antialiased);
CustomFontSource->set_hinting(font_source_hinting);
CustomFontSource->set_font_path(custom_font_path_source);
} else {
@@ -138,48 +143,56 @@ void editor_register_fonts(Ref<Theme> p_theme) {
Ref<DynamicFontData> DefaultFont;
DefaultFont.instance();
+ DefaultFont->set_antialiased(font_antialiased);
DefaultFont->set_hinting(font_hinting);
DefaultFont->set_font_ptr(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size);
DefaultFont->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> DefaultFontBold;
DefaultFontBold.instance();
- DefaultFont->set_hinting(font_hinting);
+ DefaultFontBold->set_antialiased(font_antialiased);
+ DefaultFontBold->set_hinting(font_hinting);
DefaultFontBold->set_font_ptr(_font_NotoSansUI_Bold, _font_NotoSansUI_Bold_size);
DefaultFontBold->set_force_autohinter(true); // just looks better..i think?
Ref<DynamicFontData> FontFallback;
FontFallback.instance();
+ FontFallback->set_antialiased(font_antialiased);
FontFallback->set_hinting(font_hinting);
FontFallback->set_font_ptr(_font_DroidSansFallback, _font_DroidSansFallback_size);
FontFallback->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> FontJapanese;
FontJapanese.instance();
+ FontJapanese->set_antialiased(font_antialiased);
FontJapanese->set_hinting(font_hinting);
FontJapanese->set_font_ptr(_font_DroidSansJapanese, _font_DroidSansJapanese_size);
FontJapanese->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> FontArabic;
FontArabic.instance();
+ FontArabic->set_antialiased(font_antialiased);
FontArabic->set_hinting(font_hinting);
FontArabic->set_font_ptr(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
FontArabic->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> FontHebrew;
FontHebrew.instance();
+ FontHebrew->set_antialiased(font_antialiased);
FontHebrew->set_hinting(font_hinting);
FontHebrew->set_font_ptr(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size);
FontHebrew->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> FontThai;
FontThai.instance();
+ FontThai->set_antialiased(font_antialiased);
FontThai->set_hinting(font_hinting);
FontThai->set_font_ptr(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
FontThai->set_force_autohinter(true); //just looks better..i think?
Ref<DynamicFontData> FontHindi;
FontHindi.instance();
+ FontHindi->set_antialiased(font_antialiased);
FontHindi->set_hinting(font_hinting);
FontHindi->set_font_ptr(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size);
FontHindi->set_force_autohinter(true); //just looks better..i think?
@@ -188,6 +201,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
Ref<DynamicFontData> dfmono;
dfmono.instance();
+ dfmono->set_antialiased(font_source_antialiased);
dfmono->set_hinting(font_source_hinting);
dfmono->set_font_ptr(_font_Hack_Regular, _font_Hack_Regular_size);
//dfd->set_force_autohinter(true); //just looks better..i think?
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 80dd5aa114..f502f918df 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -36,482 +36,10 @@
#include "editor_node.h"
#include "editor_settings.h"
-#define CONTRIBUTE_URL "http://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html"
+#define CONTRIBUTE_URL "https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html"
#define CONTRIBUTE2_URL "https://github.com/godotengine/godot-docs"
#define REQUEST_URL "https://github.com/godotengine/godot-docs/issues/new"
-void EditorHelpSearch::popup_dialog() {
-
- popup_centered(Size2(700, 600) * EDSCALE);
- if (search_box->get_text() != "") {
- search_box->select_all();
- _update_search();
- }
- search_box->grab_focus();
-}
-
-void EditorHelpSearch::popup_dialog(const String &p_term) {
-
- popup_centered(Size2(700, 600) * EDSCALE);
- if (p_term != "") {
- search_box->set_text(p_term);
- search_box->select_all();
- _update_search();
- } else {
- search_box->clear();
- }
- search_box->grab_focus();
-}
-
-void EditorHelpSearch::_text_changed(const String &p_newtext) {
-
- _update_search();
-}
-
-void EditorHelpSearch::_sbox_input(const Ref<InputEvent> &p_ie) {
-
- Ref<InputEventKey> k = p_ie;
-
- if (k.is_valid() && (k->get_scancode() == KEY_UP ||
- k->get_scancode() == KEY_DOWN ||
- k->get_scancode() == KEY_PAGEUP ||
- k->get_scancode() == KEY_PAGEDOWN)) {
-
- search_options->call("_gui_input", k);
- search_box->accept_event();
- }
-}
-
-void EditorHelpSearch::IncrementalSearch::phase1(Map<String, DocData::ClassDoc>::Element *E) {
-
- if (E->key().findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_name:" + E->key());
- item->set_text(0, E->key() + " (Class)");
- Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node");
- item->set_icon(0, icon);
- }
-}
-
-void EditorHelpSearch::IncrementalSearch::phase2(Map<String, DocData::ClassDoc>::Element *E) {
-
- DocData::ClassDoc &c = E->get();
-
- Ref<Texture> cicon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node");
-
- for (int i = 0; i < c.methods.size(); i++) {
- if ((term.begins_with(".") && c.methods[i].name.begins_with(term.right(1))) || (term.ends_with("(") && c.methods[i].name.ends_with(term.left(term.length() - 1).strip_edges())) || (term.begins_with(".") && term.ends_with("(") && c.methods[i].name == term.substr(1, term.length() - 2).strip_edges()) || c.methods[i].name.findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_method:" + E->key() + ":" + c.methods[i].name);
- item->set_text(0, E->key() + "." + c.methods[i].name + " (Method)");
- item->set_icon(0, cicon);
- }
- }
-
- for (int i = 0; i < c.signals.size(); i++) {
-
- if (c.signals[i].name.findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_signal:" + E->key() + ":" + c.signals[i].name);
- item->set_text(0, E->key() + "." + c.signals[i].name + " (Signal)");
- item->set_icon(0, cicon);
- }
- }
-
- for (int i = 0; i < c.constants.size(); i++) {
-
- if (c.constants[i].name.findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_constant:" + E->key() + ":" + c.constants[i].name);
- item->set_text(0, E->key() + "." + c.constants[i].name + " (Constant)");
- item->set_icon(0, cicon);
- }
- }
-
- for (int i = 0; i < c.properties.size(); i++) {
-
- if (c.properties[i].name.findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_property:" + E->key() + ":" + c.properties[i].name);
- item->set_text(0, E->key() + "." + c.properties[i].name + " (Property)");
- item->set_icon(0, cicon);
- }
- }
-
- for (int i = 0; i < c.theme_properties.size(); i++) {
-
- if (c.theme_properties[i].name.findn(term) != -1) {
-
- TreeItem *item = search_options->create_item(root);
- item->set_metadata(0, "class_theme_item:" + E->key() + ":" + c.theme_properties[i].name);
- item->set_text(0, E->key() + "." + c.theme_properties[i].name + " (Theme Item)");
- item->set_icon(0, cicon);
- }
- }
-}
-
-bool EditorHelpSearch::IncrementalSearch::slice() {
-
- if (phase > 2)
- return true;
-
- if (iterator) {
-
- switch (phase) {
-
- case 1: {
- phase1(iterator);
- } break;
- case 2: {
- phase2(iterator);
- } break;
- default: {
- WARN_PRINT("illegal phase in IncrementalSearch");
- return true;
- }
- }
-
- iterator = iterator->next();
- } else {
-
- phase += 1;
- iterator = doc->class_list.front();
- }
-
- return false;
-}
-
-EditorHelpSearch::IncrementalSearch::IncrementalSearch(EditorHelpSearch *p_search, Tree *p_search_options, const String &p_term) :
- search(p_search),
- search_options(p_search_options) {
-
- def_icon = search->get_icon("Node", "EditorIcons");
- doc = EditorHelp::get_doc_data();
-
- term = p_term;
-
- root = search_options->create_item();
- phase = 0;
- iterator = 0;
-}
-
-bool EditorHelpSearch::IncrementalSearch::empty() const {
-
- return root->get_children() == NULL;
-}
-
-bool EditorHelpSearch::IncrementalSearch::work(uint64_t slot) {
-
- const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
-
- while (!slice()) {
-
- if (OS::get_singleton()->get_ticks_usec() > until)
- return false;
- }
-
- return true;
-}
-
-void EditorHelpSearch::_update_search() {
- search_options->clear();
-
- String term = search_box->get_text();
- if (term.length() < 2)
- return;
-
- search = Ref<IncrementalSearch>(memnew(IncrementalSearch(this, search_options, term)));
- set_process(true);
-}
-
-void EditorHelpSearch::_confirmed() {
-
- TreeItem *ti = search_options->get_selected();
- if (!ti)
- return;
-
- String mdata = ti->get_metadata(0);
- EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
- emit_signal("go_to_help", mdata);
- // go to that
- hide();
-}
-
-void EditorHelpSearch::_notification(int p_what) {
-
- if (p_what == NOTIFICATION_ENTER_TREE) {
-
- //_update_icons
- search_box->set_right_icon(get_icon("Search", "EditorIcons"));
- search_box->set_clear_button_enabled(true);
-
- connect("confirmed", this, "_confirmed");
- _update_search();
- } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
-
- if (is_visible_in_tree()) {
-
- search_box->call_deferred("grab_focus"); // still not visible
- search_box->select_all();
- }
- } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
-
- //_update_icons
- search_box->set_right_icon(get_icon("Search", "EditorIcons"));
- search_box->set_clear_button_enabled(true);
- } else if (p_what == NOTIFICATION_PROCESS) {
-
- if (search.is_valid()) {
-
- if (search->work()) {
-
- get_ok()->set_disabled(search->empty());
- search = Ref<IncrementalSearch>();
- set_process(false);
- }
- } else {
-
- set_process(false);
- }
- }
-}
-
-void EditorHelpSearch::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("_text_changed"), &EditorHelpSearch::_text_changed);
- ClassDB::bind_method(D_METHOD("_confirmed"), &EditorHelpSearch::_confirmed);
- ClassDB::bind_method(D_METHOD("_sbox_input"), &EditorHelpSearch::_sbox_input);
- ClassDB::bind_method(D_METHOD("_update_search"), &EditorHelpSearch::_update_search);
-
- ADD_SIGNAL(MethodInfo("go_to_help"));
-}
-
-EditorHelpSearch::EditorHelpSearch() {
-
- VBoxContainer *vbc = memnew(VBoxContainer);
- add_child(vbc);
-
- search_box = memnew(LineEdit);
- vbc->add_child(search_box);
- search_box->connect("text_changed", this, "_text_changed");
- search_box->connect("gui_input", this, "_sbox_input");
- search_options = memnew(Tree);
- search_options->set_hide_root(true);
- vbc->add_margin_child(TTR("Matches:"), search_options, true);
- get_ok()->set_text(TTR("Open"));
- get_ok()->set_disabled(true);
- register_text_enter(search_box);
- set_hide_on_ok(false);
- search_options->connect("item_activated", this, "_confirmed");
- set_title(TTR("Search Help"));
-}
-
-/////////////////////////////////
-
-void EditorHelpIndex::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root) {
-
- if (p_types.has(p_type))
- return;
-
- String inherits = EditorHelp::get_doc_data()->class_list[p_type].inherits;
-
- TreeItem *parent = p_root;
-
- if (inherits.length()) {
-
- if (!p_types.has(inherits)) {
-
- add_type(inherits, p_types, p_root);
- }
-
- if (p_types.has(inherits))
- parent = p_types[inherits];
- }
-
- TreeItem *item = class_list->create_item(parent);
- item->set_metadata(0, p_type);
- item->set_tooltip(0, EditorHelp::get_doc_data()->class_list[p_type].brief_description);
- item->set_text(0, p_type);
-
- Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(p_type);
- item->set_icon(0, icon);
-
- p_types[p_type] = item;
-}
-
-void EditorHelpIndex::_tree_item_selected() {
-
- TreeItem *s = class_list->get_selected();
- if (!s)
- return;
-
- EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
- emit_signal("open_class", s->get_text(0));
- hide();
-}
-
-void EditorHelpIndex::select_class(const String &p_class) {
-
- if (!tree_item_map.has(p_class))
- return;
- tree_item_map[p_class]->select(0);
- class_list->ensure_cursor_is_visible();
-}
-
-void EditorHelpIndex::popup_dialog() {
-
- popup_centered(Size2(500, 600) * EDSCALE);
-
- search_box->set_text("");
- _update_class_list();
-}
-
-void EditorHelpIndex::_notification(int p_what) {
-
- if (p_what == NOTIFICATION_ENTER_TREE) {
-
- //_update_icons
- search_box->set_right_icon(get_icon("Search", "EditorIcons"));
- search_box->set_clear_button_enabled(true);
- _update_class_list();
-
- connect("confirmed", this, "_tree_item_selected");
-
- } else if (p_what == NOTIFICATION_POST_POPUP) {
-
- search_box->call_deferred("grab_focus");
- } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
-
- //_update_icons
- search_box->set_right_icon(get_icon("Search", "EditorIcons"));
- search_box->set_clear_button_enabled(true);
-
- bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
- Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
-
- if (enable_rl) {
- class_list->add_constant_override("draw_relationship_lines", 1);
- class_list->add_color_override("relationship_line_color", rl_color);
- } else {
- class_list->add_constant_override("draw_relationship_lines", 0);
- }
- }
-}
-
-void EditorHelpIndex::_text_changed(const String &p_text) {
-
- _update_class_list();
-}
-
-void EditorHelpIndex::_update_class_list() {
-
- class_list->clear();
- tree_item_map.clear();
- TreeItem *root = class_list->create_item();
-
- String filter = search_box->get_text().strip_edges();
- String to_select = "";
-
- for (Map<String, DocData::ClassDoc>::Element *E = EditorHelp::get_doc_data()->class_list.front(); E; E = E->next()) {
-
- if (filter == "") {
- add_type(E->key(), tree_item_map, root);
- } else {
-
- bool found = false;
- String type = E->key();
-
- while (type != "") {
- if (filter.is_subsequence_ofi(type)) {
-
- if (to_select.empty() || type.length() < to_select.length()) {
- to_select = type;
- }
-
- found = true;
- }
-
- type = EditorHelp::get_doc_data()->class_list[type].inherits;
- }
-
- if (found) {
- add_type(E->key(), tree_item_map, root);
- }
- }
- }
-
- if (tree_item_map.has(filter)) {
- select_class(filter);
- } else if (to_select != "") {
- select_class(to_select);
- }
-}
-
-void EditorHelpIndex::_sbox_input(const Ref<InputEvent> &p_ie) {
-
- Ref<InputEventKey> k = p_ie;
-
- if (k.is_valid() && (k->get_scancode() == KEY_UP ||
- k->get_scancode() == KEY_DOWN ||
- k->get_scancode() == KEY_PAGEUP ||
- k->get_scancode() == KEY_PAGEDOWN)) {
-
- class_list->call("_gui_input", k);
- search_box->accept_event();
- }
-}
-
-void EditorHelpIndex::_bind_methods() {
-
- ClassDB::bind_method("_tree_item_selected", &EditorHelpIndex::_tree_item_selected);
- ClassDB::bind_method("_text_changed", &EditorHelpIndex::_text_changed);
- ClassDB::bind_method("_sbox_input", &EditorHelpIndex::_sbox_input);
- ClassDB::bind_method("select_class", &EditorHelpIndex::select_class);
- ADD_SIGNAL(MethodInfo("open_class"));
-}
-
-EditorHelpIndex::EditorHelpIndex() {
-
- VBoxContainer *vbc = memnew(VBoxContainer);
- add_child(vbc);
-
- search_box = memnew(LineEdit);
- vbc->add_child(search_box);
- search_box->set_h_size_flags(SIZE_EXPAND_FILL);
-
- register_text_enter(search_box);
-
- search_box->connect("text_changed", this, "_text_changed");
- search_box->connect("gui_input", this, "_sbox_input");
-
- class_list = memnew(Tree);
- vbc->add_margin_child(TTR("Class List:") + " ", class_list, true);
- class_list->set_hide_root(true);
- class_list->set_v_size_flags(SIZE_EXPAND_FILL);
-
- class_list->connect("item_activated", this, "_tree_item_selected");
-
- bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
- Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
-
- if (enable_rl) {
- class_list->add_constant_override("draw_relationship_lines", 1);
- class_list->add_color_override("relationship_line_color", rl_color);
- } else {
- class_list->add_constant_override("draw_relationship_lines", 0);
- }
-
- get_ok()->set_text(TTR("Open"));
- set_title(TTR("Search Classes"));
-}
-
-/////////////////////////////////
-
DocData *EditorHelp::doc = NULL;
void EditorHelp::_init_colors() {
@@ -1919,8 +1447,6 @@ EditorHelp::EditorHelp() {
EditorHelp::~EditorHelp() {
}
-/////////////
-
void EditorHelpBit::_go_to_help(String p_what) {
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 25db68b42d..205778ec11 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -31,6 +31,8 @@
#ifndef EDITOR_HELP_H
#define EDITOR_HELP_H
+#include "editor/code_editor.h"
+#include "editor/doc/doc_data.h"
#include "editor/editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
@@ -38,95 +40,9 @@
#include "scene/gui/split_container.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/text_edit.h"
-#include "scene/gui/tree.h"
-
-#include "editor/code_editor.h"
-#include "editor/doc/doc_data.h"
#include "scene/main/timer.h"
-class EditorNode;
-
-class EditorHelpSearch : public ConfirmationDialog {
-
- GDCLASS(EditorHelpSearch, ConfirmationDialog)
-
- LineEdit *search_box;
- Tree *search_options;
- String base_type;
-
- class IncrementalSearch : public Reference {
- String term;
- TreeItem *root;
-
- EditorHelpSearch *search;
- Tree *search_options;
-
- DocData *doc;
- Ref<Texture> def_icon;
-
- int phase;
- Map<String, DocData::ClassDoc>::Element *iterator;
-
- void phase1(Map<String, DocData::ClassDoc>::Element *E);
- void phase2(Map<String, DocData::ClassDoc>::Element *E);
- bool slice();
-
- public:
- IncrementalSearch(EditorHelpSearch *p_search, Tree *p_search_options, const String &p_term);
-
- bool empty() const;
- bool work(uint64_t slot = 1000000 / 10);
- };
-
- Ref<IncrementalSearch> search;
-
- void _update_search();
-
- void _sbox_input(const Ref<InputEvent> &p_ie);
-
- void _confirmed();
- void _text_changed(const String &p_newtext);
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void popup_dialog();
- void popup_dialog(const String &p_term);
-
- EditorHelpSearch();
-};
-
-class EditorHelpIndex : public ConfirmationDialog {
- GDCLASS(EditorHelpIndex, ConfirmationDialog);
-
- LineEdit *search_box;
- Tree *class_list;
- HashMap<String, TreeItem *> tree_item_map;
-
- void _tree_item_selected();
- void _text_changed(const String &p_text);
- void _sbox_input(const Ref<InputEvent> &p_ie);
-
- void _update_class_list();
-
- void add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root);
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void select_class(const String &p_class);
-
- void popup_dialog();
-
- EditorHelpIndex();
-};
-
class FindBar : public HBoxContainer {
-
GDCLASS(FindBar, HBoxContainer);
LineEdit *search_text;
@@ -172,6 +88,7 @@ public:
};
class EditorHelp : public VBoxContainer {
+
GDCLASS(EditorHelp, VBoxContainer);
enum Page {
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
new file mode 100644
index 0000000000..f5717ddf2a
--- /dev/null
+++ b/editor/editor_help_search.cpp
@@ -0,0 +1,591 @@
+/*************************************************************************/
+/* editor_help_search.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_help_search.h"
+
+#include "core/os/keyboard.h"
+#include "editor_node.h"
+
+void EditorHelpSearch::_update_icons() {
+
+ search_box->set_right_icon(get_icon("Search", "EditorIcons"));
+ search_box->set_clear_button_enabled(true);
+ search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+ case_sensitive_button->set_icon(get_icon("MatchCase", "EditorIcons"));
+ hierarchy_button->set_icon(get_icon("ClassList", "EditorIcons"));
+
+ if (is_visible_in_tree())
+ _update_results();
+}
+
+void EditorHelpSearch::_load_settings() {
+
+ bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
+ Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
+
+ if (enable_rl) {
+ results_tree->add_constant_override("draw_relationship_lines", 1);
+ results_tree->add_color_override("relationship_line_color", rl_color);
+ } else {
+ results_tree->add_constant_override("draw_relationship_lines", 0);
+ results_tree->add_constant_override("draw_guides", 1);
+ }
+}
+
+void EditorHelpSearch::_update_results() {
+
+ String term = search_box->get_text();
+
+ int search_flags = filter_combo->get_selected_id();
+ if (case_sensitive_button->is_pressed())
+ search_flags |= SEARCH_CASE_SENSITIVE;
+ if (hierarchy_button->is_pressed())
+ search_flags |= SEARCH_SHOW_HIERARCHY;
+
+ search = Ref<Runner>(memnew(Runner(this, results_tree, term, search_flags)));
+ set_process(true);
+}
+
+void EditorHelpSearch::_search_box_gui_input(const Ref<InputEvent> &p_ie) {
+
+ // Redirect up and down navigational key events to the results list.
+ Ref<InputEventKey> k = p_ie;
+ if (k.is_valid()) {
+ switch (k->get_scancode()) {
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN: {
+ results_tree->call("_gui_input", k);
+ search_box->accept_event();
+ } break;
+ }
+ }
+}
+
+void EditorHelpSearch::_search_box_text_changed(const String &p_text) {
+
+ _update_results();
+}
+
+void EditorHelpSearch::_case_sensitive_button_pressed() {
+
+ search_box->grab_focus();
+ _update_results();
+}
+
+void EditorHelpSearch::_hierarchy_button_pressed() {
+
+ search_box->grab_focus();
+ _update_results();
+}
+
+void EditorHelpSearch::_filter_combo_item_selected(int p_option) {
+
+ _update_results();
+}
+
+void EditorHelpSearch::_confirmed() {
+
+ // Activate the script editor and emit the signal with the documentation link to display.
+ EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
+
+ TreeItem *item = results_tree->get_selected();
+ emit_signal("go_to_help", item->get_metadata(0));
+
+ hide();
+}
+
+void EditorHelpSearch::_notification(int p_what) {
+
+ switch (p_what) {
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+
+ _load_settings();
+ _update_icons();
+ } break;
+ case NOTIFICATION_ENTER_TREE: {
+
+ connect("confirmed", this, "_confirmed");
+ _update_icons();
+ } break;
+ case NOTIFICATION_POPUP_HIDE: {
+
+ results_tree->clear();
+ EditorSettings::get_singleton()->set("interface/dialogs/search_help_bounds", get_rect());
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+ // Update background search.
+ if (search.is_valid()) {
+ if (search->work()) {
+ // Search done.
+ get_ok()->set_disabled(results_tree->get_root()->get_children() == NULL);
+ search = Ref<Runner>();
+ set_process(false);
+ }
+ } else {
+ set_process(false);
+ }
+ } break;
+ }
+}
+
+void EditorHelpSearch::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_search_box_gui_input"), &EditorHelpSearch::_search_box_gui_input);
+ ClassDB::bind_method(D_METHOD("_search_box_text_changed"), &EditorHelpSearch::_search_box_text_changed);
+ ClassDB::bind_method(D_METHOD("_case_sensitive_button_pressed"), &EditorHelpSearch::_case_sensitive_button_pressed);
+ ClassDB::bind_method(D_METHOD("_hierarchy_button_pressed"), &EditorHelpSearch::_hierarchy_button_pressed);
+ ClassDB::bind_method(D_METHOD("_filter_combo_item_selected"), &EditorHelpSearch::_filter_combo_item_selected);
+ ClassDB::bind_method(D_METHOD("_confirmed"), &EditorHelpSearch::_confirmed);
+ ADD_SIGNAL(MethodInfo("go_to_help"));
+}
+
+void EditorHelpSearch::popup_dialog() {
+
+ popup_dialog(search_box->get_text());
+}
+
+void EditorHelpSearch::popup_dialog(const String &p_term) {
+
+ // Restore valid window bounds or pop up at default size.
+ if (EditorSettings::get_singleton()->has_setting("interface/dialogs/search_help_bounds"))
+ popup(EditorSettings::get_singleton()->get("interface/dialogs/search_help_bounds"));
+ else
+ popup_centered_ratio(0.5F);
+
+ if (p_term == "") {
+ search_box->clear();
+ } else {
+ search_box->set_text(p_term);
+ search_box->select_all();
+ }
+ search_box->grab_focus();
+ _update_results();
+}
+
+EditorHelpSearch::EditorHelpSearch() {
+
+ set_hide_on_ok(false);
+ set_resizable(true);
+ set_title(TTR("Search Help"));
+
+ get_ok()->set_disabled(true);
+ get_ok()->set_text(TTR("Open"));
+
+ // Split search and results area.
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ add_child(vbox);
+
+ // Create the search box and filter controls (at the top).
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ vbox->add_child(hbox);
+
+ search_box = memnew(LineEdit);
+ search_box->set_custom_minimum_size(Size2(200, 0));
+ search_box->set_h_size_flags(SIZE_EXPAND_FILL);
+ search_box->connect("gui_input", this, "_search_box_gui_input");
+ search_box->connect("text_changed", this, "_search_box_text_changed");
+ register_text_enter(search_box);
+ hbox->add_child(search_box);
+
+ case_sensitive_button = memnew(ToolButton);
+ case_sensitive_button->set_tooltip("Case Sensitive");
+ case_sensitive_button->connect("pressed", this, "_case_sensitive_button_pressed");
+ case_sensitive_button->set_toggle_mode(true);
+ hbox->add_child(case_sensitive_button);
+
+ hierarchy_button = memnew(ToolButton);
+ hierarchy_button->set_tooltip("Show Hierarchy");
+ hierarchy_button->connect("pressed", this, "_hierarchy_button_pressed");
+ hierarchy_button->set_toggle_mode(true);
+ hierarchy_button->set_pressed(true);
+ hbox->add_child(hierarchy_button);
+
+ filter_combo = memnew(OptionButton);
+ filter_combo->set_custom_minimum_size(Size2(200, 0));
+ filter_combo->set_stretch_ratio(0); // Fixed width.
+ filter_combo->add_item(TTR("Display All"), SEARCH_ALL);
+ filter_combo->add_separator();
+ filter_combo->add_item(TTR("Classes Only"), SEARCH_CLASSES);
+ filter_combo->add_item(TTR("Methods Only"), SEARCH_METHODS);
+ filter_combo->add_item(TTR("Signals Only"), SEARCH_SIGNALS);
+ filter_combo->add_item(TTR("Constants Only"), SEARCH_CONSTANTS);
+ filter_combo->add_item(TTR("Properties Only"), SEARCH_PROPERTIES);
+ filter_combo->add_item(TTR("Theme Properties Only"), SEARCH_THEME_ITEMS);
+ filter_combo->connect("item_selected", this, "_filter_combo_item_selected");
+ hbox->add_child(filter_combo);
+
+ // Create the results tree.
+ results_tree = memnew(Tree);
+ results_tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ results_tree->set_columns(2);
+ results_tree->set_column_title(0, TTR("Name"));
+ results_tree->set_column_title(1, TTR("Member Type"));
+ results_tree->set_column_expand(1, false);
+ results_tree->set_column_min_width(1, 150);
+ results_tree->set_custom_minimum_size(Size2(0, 100));
+ results_tree->set_hide_root(true);
+ results_tree->set_select_mode(Tree::SELECT_ROW);
+ results_tree->connect("item_activated", this, "_confirmed");
+ vbox->add_child(results_tree, true);
+
+ _load_settings();
+}
+
+bool EditorHelpSearch::Runner::_slice() {
+
+ bool phase_done = false;
+ switch (phase) {
+ case PHASE_MATCH_CLASSES_INIT:
+ phase_done = _phase_match_classes_init();
+ break;
+ case PHASE_MATCH_CLASSES:
+ phase_done = _phase_match_classes();
+ break;
+ case PHASE_CLASS_ITEMS_INIT:
+ phase_done = _phase_class_items_init();
+ break;
+ case PHASE_CLASS_ITEMS:
+ phase_done = _phase_class_items();
+ break;
+ case PHASE_MEMBER_ITEMS_INIT:
+ phase_done = _phase_member_items_init();
+ break;
+ case PHASE_MEMBER_ITEMS:
+ phase_done = _phase_member_items();
+ break;
+ case PHASE_SELECT_MATCH:
+ phase_done = _phase_select_match();
+ break;
+ case PHASE_MAX:
+ return true;
+ default:
+ WARN_PRINTS("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
+ return true;
+ };
+
+ if (phase_done)
+ phase++;
+ return false;
+}
+
+bool EditorHelpSearch::Runner::_phase_match_classes_init() {
+
+ iterator_doc = EditorHelp::get_doc_data()->class_list.front();
+ matches.clear();
+ matched_item = NULL;
+
+ return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_match_classes() {
+
+ DocData::ClassDoc &class_doc = iterator_doc->value();
+
+ matches[class_doc.name] = ClassMatch();
+ ClassMatch &match = matches[class_doc.name];
+
+ match.doc = &class_doc;
+
+ // Match class name.
+ if (search_flags & SEARCH_CLASSES)
+ match.name = term == "" || _match_string(term, class_doc.name);
+
+ // Match members if the term is long enough.
+ if (term.length() > 1) {
+ if (search_flags & SEARCH_METHODS)
+ for (int i = 0; i < class_doc.methods.size(); i++) {
+ String method_name = search_flags & SEARCH_CASE_SENSITIVE ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ (term.begins_with(".") && method_name.begins_with(term.right(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges()))
+ match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
+ }
+ if (search_flags & SEARCH_SIGNALS)
+ for (int i = 0; i < class_doc.signals.size(); i++)
+ if (_match_string(term, class_doc.signals[i].name))
+ match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
+ if (search_flags & SEARCH_CONSTANTS)
+ for (int i = 0; i < class_doc.constants.size(); i++)
+ if (_match_string(term, class_doc.constants[i].name))
+ match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
+ if (search_flags & SEARCH_PROPERTIES)
+ for (int i = 0; i < class_doc.properties.size(); i++)
+ if (_match_string(term, class_doc.properties[i].name))
+ match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
+ if (search_flags & SEARCH_THEME_ITEMS)
+ for (int i = 0; i < class_doc.theme_properties.size(); i++)
+ if (_match_string(term, class_doc.theme_properties[i].name))
+ match.theme_properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.theme_properties[i]));
+ }
+
+ iterator_doc = iterator_doc->next();
+ return !iterator_doc;
+}
+
+bool EditorHelpSearch::Runner::_phase_class_items_init() {
+
+ iterator_match = matches.front();
+
+ results_tree->clear();
+ root_item = results_tree->create_item();
+ class_items.clear();
+
+ return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_class_items() {
+
+ ClassMatch &match = iterator_match->value();
+
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ if (match.required())
+ _create_class_hierarchy(match);
+ } else {
+ if (match.name)
+ _create_class_item(root_item, match.doc, false);
+ }
+
+ iterator_match = iterator_match->next();
+ return !iterator_match;
+}
+
+bool EditorHelpSearch::Runner::_phase_member_items_init() {
+
+ iterator_match = matches.front();
+
+ return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_member_items() {
+
+ ClassMatch &match = iterator_match->value();
+
+ TreeItem *parent = search_flags & SEARCH_SHOW_HIERARCHY ? class_items[match.doc->name] : root_item;
+ for (int i = 0; i < match.methods.size(); i++)
+ _create_method_item(parent, match.doc, match.methods[i]);
+ for (int i = 0; i < match.signals.size(); i++)
+ _create_signal_item(parent, match.doc, match.signals[i]);
+ for (int i = 0; i < match.constants.size(); i++)
+ _create_constant_item(parent, match.doc, match.constants[i]);
+ for (int i = 0; i < match.properties.size(); i++)
+ _create_property_item(parent, match.doc, match.properties[i]);
+ for (int i = 0; i < match.theme_properties.size(); i++)
+ _create_theme_property_item(parent, match.doc, match.theme_properties[i]);
+
+ iterator_match = iterator_match->next();
+ return !iterator_match;
+}
+
+bool EditorHelpSearch::Runner::_phase_select_match() {
+
+ if (matched_item)
+ matched_item->select(0);
+ results_tree->ensure_cursor_is_visible();
+
+ return true;
+}
+
+bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String &p_string) const {
+
+ if (search_flags & SEARCH_CASE_SENSITIVE)
+ return p_string.find(p_term) > -1;
+ else
+ return p_string.findn(p_term) > -1;
+}
+
+void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_text) {
+
+ if (!matched_item) {
+ if (search_flags & SEARCH_CASE_SENSITIVE) {
+ if (p_text.casecmp_to(term) == 0)
+ matched_item = p_item;
+ } else {
+ if (p_text.nocasecmp_to(term) == 0)
+ matched_item = p_item;
+ }
+ }
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) {
+
+ if (class_items.has(p_match.doc->name))
+ return class_items[p_match.doc->name];
+
+ // Ensure parent nodes are created first.
+ TreeItem *parent = root_item;
+ if (p_match.doc->inherits != "") {
+ if (class_items.has(p_match.doc->inherits)) {
+ parent = class_items[p_match.doc->inherits];
+ } else {
+ ClassMatch &base_match = matches[p_match.doc->inherits];
+ parent = _create_class_hierarchy(base_match);
+ }
+ }
+
+ TreeItem *class_item = _create_class_item(parent, p_match.doc, !p_match.name);
+ class_items[p_match.doc->name] = class_item;
+ return class_item;
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
+
+ Ref<Texture> icon = empty_icon;
+ if (ui_service->has_icon(p_doc->name, "EditorIcons"))
+ icon = ui_service->get_icon(p_doc->name, "EditorIcons");
+ else if (ClassDB::is_parent_class(p_doc->name, "Object"))
+ icon = ui_service->get_icon("Object", "EditorIcons");
+ String tooltip = p_doc->brief_description.strip_edges();
+
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, p_doc->name);
+ item->set_text(1, TTR("Class"));
+ item->set_tooltip(0, tooltip);
+ item->set_tooltip(1, tooltip);
+ item->set_metadata(0, "class_name:" + p_doc->name);
+ if (p_gray) {
+ item->set_custom_color(0, disabled_color);
+ item->set_custom_color(1, disabled_color);
+ }
+
+ _match_item(item, p_doc->name);
+
+ return item;
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "")
+ tooltip += " = " + arg.default_value;
+ if (i < p_doc->arguments.size() - 1)
+ tooltip += ", ";
+ }
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, "Method", "method", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "")
+ tooltip += " = " + arg.default_value;
+ if (i < p_doc->arguments.size() - 1)
+ tooltip += ", ";
+ }
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, "Signal", "signal", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
+
+ String tooltip = p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, "Constant", "constant", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, "Property", "property", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, "Theme Property", "theme_item", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_type, const String &p_metatype, const String &p_tooltip) {
+
+ Ref<Texture> icon;
+ String text;
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ icon = ui_service->get_icon(p_icon, "EditorIcons");
+ text = p_name;
+ } else {
+ icon = ui_service->get_icon(p_icon, "EditorIcons");
+ /*// In flat mode, show the class icon.
+ if (ui_service->has_icon(p_class_name, "EditorIcons"))
+ icon = ui_service->get_icon(p_class_name, "EditorIcons");
+ else if (ClassDB::is_parent_class(p_class_name, "Object"))
+ icon = ui_service->get_icon("Object", "EditorIcons");*/
+ text = p_class_name + "." + p_name;
+ }
+
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, text);
+ item->set_text(1, TTR(p_type));
+ item->set_tooltip(0, p_tooltip);
+ item->set_tooltip(1, p_tooltip);
+ item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);
+
+ _match_item(item, p_name);
+
+ return item;
+}
+
+bool EditorHelpSearch::Runner::work(uint64_t slot) {
+
+ // Return true when the search has been completed, otherwise false.
+ const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
+ while (!_slice())
+ if (OS::get_singleton()->get_ticks_usec() > until)
+ return false;
+ return true;
+}
+
+EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags) {
+
+ ui_service = p_icon_service;
+ results_tree = p_results_tree;
+ term = p_term.strip_edges();
+ search_flags = p_search_flags;
+
+ if ((search_flags & SEARCH_CASE_SENSITIVE) == 0)
+ term = term.to_lower();
+
+ empty_icon = ui_service->get_icon("ArrowRight", "EditorIcons");
+ disabled_color = ui_service->get_color("disabled_font_color", "Editor");
+
+ phase = 0;
+}
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
new file mode 100644
index 0000000000..1016675dd2
--- /dev/null
+++ b/editor/editor_help_search.h
@@ -0,0 +1,156 @@
+/*************************************************************************/
+/* editor_help_search.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITOR_HELP_SEARCH_H
+#define EDITOR_HELP_SEARCH_H
+
+#include "core/ordered_hash_map.h"
+#include "editor/code_editor.h"
+#include "editor/editor_help.h"
+#include "editor/editor_plugin.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/tree.h"
+
+class EditorHelpSearch : public ConfirmationDialog {
+ GDCLASS(EditorHelpSearch, ConfirmationDialog);
+
+ enum SearchFlags {
+ SEARCH_CLASSES = 1 << 0,
+ SEARCH_METHODS = 1 << 1,
+ SEARCH_SIGNALS = 1 << 2,
+ SEARCH_CONSTANTS = 1 << 3,
+ SEARCH_PROPERTIES = 1 << 4,
+ SEARCH_THEME_ITEMS = 1 << 5,
+ SEARCH_ALL = SEARCH_CLASSES | SEARCH_METHODS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+ SEARCH_CASE_SENSITIVE = 1 << 29,
+ SEARCH_SHOW_HIERARCHY = 1 << 30
+ };
+
+ LineEdit *search_box;
+ ToolButton *case_sensitive_button;
+ ToolButton *hierarchy_button;
+ OptionButton *filter_combo;
+ Tree *results_tree;
+
+ class Runner;
+ Ref<Runner> search;
+
+ void _update_icons();
+ void _load_settings();
+ void _update_results();
+
+ void _search_box_gui_input(const Ref<InputEvent> &p_ie);
+ void _search_box_text_changed(const String &p_text);
+ void _case_sensitive_button_pressed();
+ void _hierarchy_button_pressed();
+ void _filter_combo_item_selected(int p_option);
+ void _confirmed();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void popup_dialog();
+ void popup_dialog(const String &p_term);
+
+ EditorHelpSearch();
+};
+
+class EditorHelpSearch::Runner : public Reference {
+
+ enum Phase {
+ PHASE_MATCH_CLASSES_INIT,
+ PHASE_MATCH_CLASSES,
+ PHASE_CLASS_ITEMS_INIT,
+ PHASE_CLASS_ITEMS,
+ PHASE_MEMBER_ITEMS_INIT,
+ PHASE_MEMBER_ITEMS,
+ PHASE_SELECT_MATCH,
+ PHASE_MAX
+ };
+ int phase;
+
+ struct ClassMatch {
+ DocData::ClassDoc *doc;
+ bool name;
+ Vector<DocData::MethodDoc *> methods;
+ Vector<DocData::MethodDoc *> signals;
+ Vector<DocData::ConstantDoc *> constants;
+ Vector<DocData::PropertyDoc *> properties;
+ Vector<DocData::PropertyDoc *> theme_properties;
+
+ bool required() {
+ return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
+ }
+ };
+
+ Control *ui_service;
+ Tree *results_tree;
+ String term;
+ int search_flags;
+
+ Ref<Texture> empty_icon;
+ Color disabled_color;
+
+ Map<String, DocData::ClassDoc>::Element *iterator_doc;
+ Map<String, ClassMatch> matches;
+ Map<String, ClassMatch>::Element *iterator_match;
+ TreeItem *root_item;
+ Map<String, TreeItem *> class_items;
+ TreeItem *matched_item;
+
+ bool _slice();
+ bool _phase_match_classes_init();
+ bool _phase_match_classes();
+ bool _phase_class_items_init();
+ bool _phase_class_items();
+ bool _phase_member_items_init();
+ bool _phase_member_items();
+ bool _phase_select_match();
+
+ bool _match_string(const String &p_term, const String &p_string) const;
+ void _match_item(TreeItem *p_item, const String &p_text);
+ TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
+ TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
+ TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
+ TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+ TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+ TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_type, const String &p_metatype, const String &p_tooltip);
+
+public:
+ bool work(uint64_t slot = 100000);
+
+ Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags);
+};
+
+#endif // EDITOR_HELP_SEARCH_H
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index a90f15004a..a564a2a113 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -374,14 +374,24 @@ bool EditorProperty::_get_instanced_node_original_property(const StringName &p_p
node = node->get_owner();
}
+ if (!found) {
+ //if not found, try default class value
+ Variant attempt = ClassDB::class_get_default_property_value(object->get_class_name(), property);
+ if (attempt.get_type() != Variant::NIL) {
+ found = true;
+ value = attempt;
+ }
+ }
+
return found;
}
-bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) {
+bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig) {
// this is a pretty difficult function, because a property may not be saved but may have
// the flag to not save if one or if zero
+ //make sure there is an actual state
{
Node *node = Object::cast_to<Node>(object);
if (!node)
@@ -415,15 +425,6 @@ bool EditorProperty::_is_property_different(const Variant &p_current, const Vari
return false; //pointless to check if we are not comparing against anything.
}
- if (p_orig.get_type() == Variant::NIL) {
- // not found (was not saved)
- // check if it was not saved due to being zero or one
- if (p_current.is_zero() && property_usage & PROPERTY_USAGE_STORE_IF_NONZERO)
- return false;
- if (p_current.is_one() && property_usage & PROPERTY_USAGE_STORE_IF_NONONE)
- return false;
- }
-
if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) {
float a = p_current;
float b = p_orig;
@@ -434,23 +435,6 @@ bool EditorProperty::_is_property_different(const Variant &p_current, const Vari
return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig));
}
-bool EditorProperty::_is_instanced_node_with_original_property_different() {
-
- bool mbi = _might_be_in_instance();
- if (mbi) {
- Variant vorig;
- int usage = property_usage & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO);
- if (_get_instanced_node_original_property(property, vorig) || usage) {
- Variant v = object->get(property);
-
- if (_is_property_different(v, vorig, usage)) {
- return true;
- }
- }
- }
- return false;
-}
-
void EditorProperty::update_reload_status() {
if (property == StringName())
@@ -458,8 +442,22 @@ void EditorProperty::update_reload_status() {
bool has_reload = false;
- if (_is_instanced_node_with_original_property_different()) {
- has_reload = true;
+ if (_might_be_in_instance()) {
+ //check for difference including instantiation
+ Variant vorig;
+ if (_get_instanced_node_original_property(property, vorig)) {
+ Variant v = object->get(property);
+
+ if (_is_property_different(v, vorig)) {
+ has_reload = true;
+ }
+ }
+ } else {
+ //check for difference against default class value instead
+ Variant default_value = ClassDB::class_get_default_property_value(object->get_class_name(), property);
+ if (default_value != Variant() && default_value != object->get(property)) {
+ has_reload = true;
+ }
}
if (object->call("property_can_revert", property).operator bool()) {
@@ -628,7 +626,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
}
if (keying_rect.has_point(mb->get_position())) {
- emit_signal("property_keyed", property);
+ emit_signal("property_keyed", property, use_keying_next());
if (use_keying_next()) {
call_deferred("emit_signal", "property_changed", property, object->get(property).operator int64_t() + 1);
@@ -651,6 +649,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
Variant rev = object->call("property_get_revert", property);
emit_signal("property_changed", property, rev);
update_property();
+ return;
}
if (!object->get_script().is_null()) {
@@ -659,8 +658,16 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
if (scr->get_property_default_value(property, orig_value)) {
emit_signal("property_changed", property, orig_value);
update_property();
+ return;
}
}
+
+ Variant default_value = ClassDB::class_get_default_property_value(object->get_class_name(), property);
+ if (default_value != Variant()) {
+ emit_signal("property_changed", property, default_value);
+ update_property();
+ return;
+ }
}
if (check_rect.has_point(mb->get_position())) {
checked = !checked;
@@ -738,9 +745,9 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
tooltip_text = p_text;
EditorHelpBit *help_bit = memnew(EditorHelpBit);
help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
- help_bit->get_rich_text()->set_fixed_size_to_width(300);
+ help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
- String text = TTR("Property: ") + "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n";
+ String text = TTR("Property:") + " [u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n";
text += p_text.get_slice("::", 1).strip_edges();
help_bit->set_text(text);
help_bit->call_deferred("set_text", text); //hack so it uses proper theme once inside scene
@@ -960,7 +967,7 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons
tooltip_text = p_text;
EditorHelpBit *help_bit = memnew(EditorHelpBit);
help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
- help_bit->get_rich_text()->set_fixed_size_to_width(300);
+ help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
String text = "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n";
text += p_text.get_slice("::", 1).strip_edges();
@@ -1082,10 +1089,8 @@ void EditorInspectorSection::_notification(int p_what) {
Color color = get_color("font_color", "Tree");
draw_string(font, Point2(hs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
- int ofs = 0;
if (arrow.is_valid()) {
draw_texture(arrow, Point2(get_size().width - arrow->get_width(), (h - arrow->get_height()) / 2).floor());
- ofs += hs + arrow->get_width();
}
}
}
@@ -1148,6 +1153,11 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ Ref<Font> font = get_font("font", "Tree");
+ if (mb->get_position().y > font->get_height()) { //clicked outside
+ return;
+ }
+
_test_unfold();
bool unfold = !object->editor_is_section_unfolded(section);
@@ -1370,6 +1380,28 @@ void EditorInspector::update_tree() {
// TreeItem *current_category = NULL;
+ bool unfold_if_edited = false;
+
+ if (use_folding && auto_unfold_edited && get_tree()->get_edited_scene_root()) {
+ String path;
+ Node *node = Object::cast_to<Node>(object);
+ if (node) {
+ path = get_tree()->get_edited_scene_root()->get_filename();
+ }
+ Resource *res = Object::cast_to<Resource>(object);
+ if (res) {
+ if (res->get_path().is_resource_file()) {
+ path = res->get_path();
+ } else if (res->get_path().begins_with("res://")) { //internal resource
+ path = get_tree()->get_edited_scene_root()->get_filename();
+ }
+ }
+
+ if (!EditorNode::get_singleton()->get_editor_folding().has_folding_data(path)) {
+ unfold_if_edited = true;
+ }
+ }
+
String filter = search_box ? search_box->get_text() : "";
String group;
String group_base;
@@ -1380,6 +1412,8 @@ void EditorInspector::update_tree() {
object->get_property_list(&plist, true);
HashMap<String, VBoxContainer *> item_path;
+ Map<VBoxContainer *, EditorInspectorSection *> section_map;
+
item_path[""] = main_vbox;
Color sscolor = get_color("prop_subsection", "Editor");
@@ -1542,7 +1576,9 @@ void EditorInspector::update_tree() {
c.a /= level;
section->setup(acc_path, path_name, object, c, use_folding);
- item_path[acc_path] = section->get_vbox();
+ VBoxContainer *vb = section->get_vbox();
+ item_path[acc_path] = vb;
+ section_map[vb] = section;
}
current_vbox = item_path[acc_path];
level = (MIN(level + 1, 4));
@@ -1685,6 +1721,13 @@ void EditorInspector::update_tree() {
if (current_selected && ep->property == current_selected) {
ep->select(current_focusable);
}
+
+ if (unfold_if_edited && ep->can_revert_to_default()) {
+ //if edited and there is a parent section, unfold it.
+ if (current_vbox && section_map.has(current_vbox)) {
+ section_map[current_vbox]->unfold();
+ }
+ }
}
}
@@ -1992,20 +2035,20 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array
changing--;
}
-void EditorInspector::_property_keyed(const String &p_path) {
+void EditorInspector::_property_keyed(const String &p_path, bool p_advance) {
if (!object)
return;
- emit_signal("property_keyed", p_path, object->get(p_path), true); //second param is deprecated
+ emit_signal("property_keyed", p_path, object->get(p_path), p_advance); //second param is deprecated
}
-void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) {
+void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance) {
if (!object)
return;
- emit_signal("property_keyed", p_path, p_value, false); //second param is deprecated
+ emit_signal("property_keyed", p_path, p_value, p_advance); //second param is deprecated
}
void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
@@ -2181,6 +2224,10 @@ String EditorInspector::get_object_class() const {
return object_class;
}
+void EditorInspector::set_auto_unfold_edited(bool p_enable) {
+ auto_unfold_edited = p_enable;
+}
+
void EditorInspector::_bind_methods() {
ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(false));
@@ -2205,6 +2252,7 @@ void EditorInspector::_bind_methods() {
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked")));
ADD_SIGNAL(MethodInfo("restart_requested"));
}
@@ -2236,6 +2284,7 @@ EditorInspector::EditorInspector() {
set_process(true);
property_focusable = -1;
use_sub_inspector_bg = false;
+ auto_unfold_edited = false;
get_v_scrollbar()->connect("value_changed", this, "_vscroll_changed");
update_scroll_request = -1;
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index dccbdb9a73..00841178bd 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -71,8 +71,7 @@ private:
bool draw_top_bg;
bool _might_be_in_instance();
- bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage);
- bool _is_instanced_node_with_original_property_different();
+ bool _is_property_different(const Variant &p_current, const Variant &p_orig);
bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value);
void _focusable_focused(int p_index);
@@ -152,6 +151,8 @@ public:
void set_draw_top_bg(bool p_draw) { draw_top_bg = p_draw; }
+ bool can_revert_to_default() const { return can_revert; }
+
EditorProperty();
};
@@ -271,6 +272,7 @@ class EditorInspector : public ScrollContainer {
bool read_only;
bool keying;
bool use_sub_inspector_bg;
+ bool auto_unfold_edited;
float refresh_countdown;
bool update_tree_pending;
@@ -293,8 +295,8 @@ class EditorInspector : public ScrollContainer {
void _property_changed(const String &p_path, const Variant &p_value, bool changing = false);
void _property_changed_update_all(const String &p_path, const Variant &p_value);
void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
- void _property_keyed(const String &p_path);
- void _property_keyed_with_value(const String &p_path, const Variant &p_value);
+ void _property_keyed(const String &p_path, bool p_advance);
+ void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance);
void _property_checked(const String &p_path, bool p_checked);
@@ -365,6 +367,7 @@ public:
String get_object_class() const;
void set_use_sub_inspector_bg(bool p_enable);
+ void set_auto_unfold_edited(bool p_enable);
EditorInspector();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 18dd85617b..3129ef3508 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -384,7 +384,6 @@ void EditorNode::_notification(int p_what) {
update_menu->set_icon(gui_base->get_icon("Progress1", "EditorIcons"));
PopupMenu *p = help_menu->get_popup();
- p->set_item_icon(p->get_item_index(HELP_CLASSES), gui_base->get_icon("ClassList", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
@@ -410,51 +409,50 @@ void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_nam
push_item(script.operator->());
}
-void EditorNode::_fs_changed() {
+void EditorNode::_resources_changed(const PoolVector<String> &p_resources) {
- for (Set<FileDialog *>::Element *E = file_dialogs.front(); E; E = E->next()) {
+ List<Ref<Resource> > changed;
- E->get()->invalidate();
- }
+ int rc = p_resources.size();
+ for (int i = 0; i < rc; i++) {
- for (Set<EditorFileDialog *>::Element *E = editor_file_dialogs.front(); E; E = E->next()) {
+ Ref<Resource> res(ResourceCache::get(p_resources.get(i)));
+ if (res.is_null()) {
+ continue;
+ }
- E->get()->invalidate();
- }
+ if (!res->editor_can_reload_from_file())
+ continue;
+ if (!res->get_path().is_resource_file() && !res->get_path().is_abs_path())
+ continue;
+ if (!FileAccess::exists(res->get_path()))
+ continue;
- {
- //reload changed resources
- List<Ref<Resource> > changed;
+ if (res->get_import_path() != String()) {
+ //this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback
+ continue;
+ }
- List<Ref<Resource> > cached;
- ResourceCache::get_cached_resources(&cached);
- // FIXME: This should be done in a thread.
- for (List<Ref<Resource> >::Element *E = cached.front(); E; E = E->next()) {
+ changed.push_back(res);
+ }
- if (!E->get()->editor_can_reload_from_file())
- continue;
- if (!E->get()->get_path().is_resource_file() && !E->get()->get_path().is_abs_path())
- continue;
- if (!FileAccess::exists(E->get()->get_path()))
- continue;
+ if (changed.size()) {
+ for (List<Ref<Resource> >::Element *E = changed.front(); E; E = E->next()) {
+ E->get()->reload_from_file();
+ }
+ }
+}
- if (E->get()->get_import_path() != String()) {
- //this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback
- continue;
- }
+void EditorNode::_fs_changed() {
- uint64_t mt = FileAccess::get_modified_time(E->get()->get_path());
+ for (Set<FileDialog *>::Element *E = file_dialogs.front(); E; E = E->next()) {
- if (mt != E->get()->get_last_modified_time()) {
- changed.push_back(E->get());
- }
- }
+ E->get()->invalidate();
+ }
- if (changed.size()) {
- for (List<Ref<Resource> >::Element *E = changed.front(); E; E = E->next()) {
- E->get()->reload_from_file();
- }
- }
+ for (Set<EditorFileDialog *>::Element *E = editor_file_dialogs.front(); E; E = E->next()) {
+
+ E->get()->invalidate();
}
_mark_unsaved_scenes();
@@ -574,13 +572,29 @@ void EditorNode::_editor_select_prev() {
_editor_select(editor);
}
-Error EditorNode::load_resource(const String &p_scene) {
+Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) {
- RES res = ResourceLoader::load(p_scene);
+ dependency_errors.clear();
+
+ Error err;
+ RES res = ResourceLoader::load(p_resource, "", false, &err);
ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN);
- inspector_dock->edit_resource(res);
+ if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) {
+
+ //current_option = -1;
+ Vector<String> errors;
+ for (Set<String>::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) {
+ errors.push_back(E->get());
+ }
+ dependency_error->show(DependencyErrorDialog::MODE_RESOURCE, p_resource, errors);
+ dependency_errors.erase(p_resource);
+
+ return ERR_FILE_MISSING_DEPENDENCIES;
+ }
+
+ inspector_dock->edit_resource(res);
return OK;
}
@@ -621,6 +635,7 @@ void EditorNode::save_resource(const Ref<Resource> &p_resource) {
void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) {
file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ saving_resource = p_resource;
current_option = RESOURCE_SAVE_AS;
List<String> extensions;
@@ -1061,6 +1076,9 @@ void EditorNode::_save_scene(String p_file, int idx) {
set_current_version(editor_data.get_undo_redo().get_version());
else
editor_data.set_edited_scene_version(0, idx);
+
+ editor_folding.save_scene_folding(scene, p_file);
+
_update_title();
_update_scene_tabs();
} else {
@@ -1244,15 +1262,13 @@ void EditorNode::_dialog_action(String p_file) {
case RESOURCE_SAVE:
case RESOURCE_SAVE_AS: {
- uint32_t current = editor_history.get_current();
+ ERR_FAIL_COND(saving_resource.is_null())
+ save_resource_in_path(saving_resource, p_file);
+ saving_resource = Ref<Resource>();
+ ObjectID current = editor_history.get_current();
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
-
- ERR_FAIL_COND(!Object::cast_to<Resource>(current_obj))
-
- RES current_res = RES(Object::cast_to<Resource>(current_obj));
-
- save_resource_in_path(current_res, p_file);
-
+ ERR_FAIL_COND(!current_obj);
+ current_obj->_change_notify();
} break;
case SETTINGS_LAYOUT_SAVE: {
@@ -1984,7 +2000,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (err != OK)
ERR_PRINT("Failed to load scene");
editor_data.move_edited_scene_to_index(cur_idx);
- get_undo_redo()->clear_history();
+ get_undo_redo()->clear_history(false);
scene_tabs->set_current_tab(cur_idx);
} break;
@@ -2244,14 +2260,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
file->popup_centered_ratio();
} break;
- case HELP_CLASSES: {
- emit_signal("request_help_index", "");
- } break;
case HELP_SEARCH: {
emit_signal("request_help_search", "");
} break;
case HELP_DOCS: {
- OS::get_singleton()->shell_open("http://docs.godotengine.org/");
+ OS::get_singleton()->shell_open("https://docs.godotengine.org/");
} break;
case HELP_QA: {
OS::get_singleton()->shell_open("https://godotengine.org/qa/");
@@ -2551,6 +2564,12 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled)
return;
}
+ //errors in the script cause the base_type to be ""
+ if (String(script->get_instance_base_type()) == "") {
+ show_warning(vformat(TTR("Unable to load addon script from path: '%s' There seems to be an error in the code, please check the syntax."), path));
+ return;
+ }
+
//could check inheritance..
if (String(script->get_instance_base_type()) != "EditorPlugin") {
show_warning(vformat(TTR("Unable to load addon script from path: '%s' Base type is not EditorPlugin."), path));
@@ -2594,7 +2613,7 @@ void EditorNode::_remove_edited_scene() {
}
_scene_tab_changed(new_index);
editor_data.remove_scene(old_index);
- editor_data.get_undo_redo().clear_history();
+ editor_data.get_undo_redo().clear_history(false);
_update_title();
_update_scene_tabs();
}
@@ -2842,7 +2861,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
errors.push_back(E->get());
}
- dependency_error->show(lpath, errors);
+ dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors);
opening_prev = false;
if (prev != -1) {
@@ -2907,6 +2926,8 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
_update_scene_tabs();
_add_to_recent_scenes(lpath);
+ editor_folding.load_scene_folding(new_scene, lpath);
+
prev_scene->set_disabled(previous_scenes.size() == 0);
opening_prev = false;
@@ -3129,34 +3150,33 @@ Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p
script = p_object;
}
- StringName name;
- String icon_path;
if (script.is_valid()) {
- name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
- icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
- name = script->get_instance_base_type();
+ StringName name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
+ String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
+ if (icon_path.length())
+ return ResourceLoader::load(icon_path);
+
+ // should probably be deprecated in 4.x
+ StringName base = script->get_instance_base_type();
+ if (base != StringName()) {
+ const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types();
+ for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) {
+ const Vector<EditorData::CustomType> &ct = E->value();
+ for (int i = 0; i < ct.size(); ++i) {
+ if (ct[i].name == base && ct[i].icon.is_valid()) {
+ return ct[i].icon;
+ }
+ }
+ }
+ }
}
- if (gui_base->has_icon(p_object->get_class(), "EditorIcons"))
- return gui_base->get_icon(p_object->get_class(), "EditorIcons");
-
- if (icon_path.length())
- return ResourceLoader::load(icon_path);
-
+ // should probably be deprecated in 4.x
if (p_object->has_meta("_editor_icon"))
return p_object->get_meta("_editor_icon");
- if (name != StringName()) {
- const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types();
- for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) {
- const Vector<EditorData::CustomType> &ct = E->value();
- for (int i = 0; i < ct.size(); ++i) {
- if (ct[i].name == name && ct[i].icon.is_valid()) {
- return ct[i].icon;
- }
- }
- }
- }
+ if (gui_base->has_icon(p_object->get_class(), "EditorIcons"))
+ return gui_base->get_icon(p_object->get_class(), "EditorIcons");
if (p_fallback.length())
return gui_base->get_icon(p_fallback, "EditorIcons");
@@ -3892,7 +3912,9 @@ void EditorNode::_scene_tab_hover(int p_tab) {
tab_preview_panel->hide();
} else {
String path = editor_data.get_scene_path(p_tab);
- EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab);
+ if (path != String()) {
+ EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab);
+ }
}
}
@@ -3928,7 +3950,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES);
if (scene_tabs->get_hovered_tab() >= 0) {
scene_tabs_context_menu->add_separator();
- scene_tabs_context_menu->add_item(TTR("Show in filesystem"), FILE_SHOW_IN_FILESYSTEM);
+ scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE);
scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE);
}
@@ -4542,6 +4564,19 @@ void EditorNode::_video_driver_selected(int p_which) {
_update_video_driver_color();
}
+void EditorNode::_resource_saved(RES p_resource, const String &p_path) {
+ if (EditorFileSystem::get_singleton()) {
+ EditorFileSystem::get_singleton()->update_file(p_path);
+ }
+
+ singleton->editor_folding.save_resource_folding(p_resource, p_path);
+}
+
+void EditorNode::_resource_loaded(RES p_resource, const String &p_path) {
+
+ singleton->editor_folding.load_resource_folding(p_resource, p_path);
+}
+
void EditorNode::_bind_methods() {
ClassDB::bind_method("_menu_option", &EditorNode::_menu_option);
@@ -4616,11 +4651,12 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected);
+ ClassDB::bind_method(D_METHOD("_resources_changed"), &EditorNode::_resources_changed);
+
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("pause_pressed"));
ADD_SIGNAL(MethodInfo("stop_pressed"));
ADD_SIGNAL(MethodInfo("request_help_search"));
- ADD_SIGNAL(MethodInfo("request_help_index"));
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::POOL_STRING_ARRAY, "args")));
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
}
@@ -4842,6 +4878,7 @@ EditorNode::EditorNode() {
EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
EDITOR_DEF_RST("interface/inspector/capitalize_properties", true);
EDITOR_DEF_RST("interface/inspector/disable_folding", false);
+ EDITOR_DEF_RST("interface/inspector/auto_unfold_edited", true);
EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true);
EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
@@ -4981,7 +5018,7 @@ EditorNode::EditorNode() {
dock_select_rect_over = -1;
dock_popup_selected = -1;
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- dock_slot[i]->set_custom_minimum_size(Size2(230, 220) * EDSCALE);
+ dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL);
dock_slot[i]->set_popup(dock_select_popup);
dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i));
@@ -5158,7 +5195,7 @@ EditorNode::EditorNode() {
p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save all Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES);
+ p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_W), FILE_CLOSE);
p->add_separator();
@@ -5249,10 +5286,13 @@ EditorNode::EditorNode() {
p->add_check_item(TTR("Visible Navigation"), RUN_DEBUG_NAVIGATION);
p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on."));
p->add_separator();
+ //those are now on by default, since they are harmless
p->add_check_item(TTR("Sync Scene Changes"), RUN_LIVE_DEBUG);
p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
+ p->set_item_checked(p->get_item_count() - 1, true);
p->add_check_item(TTR("Sync Script Changes"), RUN_RELOAD_SCRIPTS);
p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
+ p->set_item_checked(p->get_item_count() - 1, true);
p->connect("id_pressed", this, "_menu_option");
menu_hb->add_spacer();
@@ -5302,8 +5342,7 @@ EditorNode::EditorNode() {
p = help_menu->get_popup();
p->set_hide_on_window_lose_focus(true);
p->connect("id_pressed", this, "_menu_option");
- p->add_icon_item(gui_base->get_icon("ClassList", "EditorIcons"), TTR("Classes"), HELP_CLASSES);
- p->add_icon_item(gui_base->get_icon("HelpSearch", "EditorIcons"), TTR("Search"), HELP_SEARCH);
+ p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_F4), HELP_SEARCH);
p->add_separator();
p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Online Docs"), HELP_DOCS);
p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Q&A"), HELP_QA);
@@ -5497,8 +5536,8 @@ EditorNode::EditorNode() {
right_r_vsplit->hide();
// Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize
- left_r_hsplit->set_split_offset(40 * EDSCALE);
- main_hsplit->set_split_offset(-40 * EDSCALE);
+ left_r_hsplit->set_split_offset(70 * EDSCALE);
+ main_hsplit->set_split_offset(-70 * EDSCALE);
// Define corresponding default layout
@@ -5513,8 +5552,8 @@ EditorNode::EditorNode() {
for (int i = 0; i < vsplits.size(); i++)
default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0);
default_layout->set_value(docks_section, "dock_hsplit_1", 0);
- default_layout->set_value(docks_section, "dock_hsplit_2", 40 * EDSCALE);
- default_layout->set_value(docks_section, "dock_hsplit_3", -40 * EDSCALE);
+ default_layout->set_value(docks_section, "dock_hsplit_2", 70 * EDSCALE);
+ default_layout->set_value(docks_section, "dock_hsplit_3", -70 * EDSCALE);
default_layout->set_value(docks_section, "dock_hsplit_4", 0);
_update_layouts_menu();
@@ -5738,6 +5777,7 @@ EditorNode::EditorNode() {
_edit_current();
current = NULL;
+ saving_resource = Ref<Resource>();
reference_resource_mem = true;
save_external_resources_mem = true;
@@ -5780,6 +5820,7 @@ EditorNode::EditorNode() {
EditorFileSystem::get_singleton()->connect("sources_changed", this, "_sources_changed");
EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_fs_changed");
EditorFileSystem::get_singleton()->connect("resources_reimported", this, "_resources_reimported");
+ EditorFileSystem::get_singleton()->connect("resources_reload", this, "_resources_changed");
_build_icon_type_cache();
@@ -5827,6 +5868,9 @@ EditorNode::EditorNode() {
print_handler.userdata = this;
add_print_handler(&print_handler);
+ ResourceSaver::set_save_callback(_resource_saved);
+ ResourceLoader::set_load_callback(_resource_loaded);
+
#ifdef OSX_ENABLED
ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1);
ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2);
@@ -5855,6 +5899,7 @@ EditorNode::~EditorNode() {
memdelete(editor_plugins_force_input_forwarding);
memdelete(file_server);
memdelete(progress_hb);
+
EditorSettings::destroy();
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 0096748ed1..e220daf8a9 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -38,6 +38,7 @@
#include "editor/editor_about.h"
#include "editor/editor_data.h"
#include "editor/editor_export.h"
+#include "editor/editor_folding.h"
#include "editor/editor_inspector.h"
#include "editor/editor_log.h"
#include "editor/editor_name_dialog.h"
@@ -179,7 +180,6 @@ private:
SETTINGS_HELP,
SCENE_TAB_CLOSE,
- HELP_CLASSES,
HELP_SEARCH,
HELP_DOCS,
HELP_QA,
@@ -355,6 +355,7 @@ private:
EditorExport *editor_export;
Object *current;
+ Ref<Resource> saving_resource;
bool _playing_edited;
String run_custom_filename;
@@ -385,6 +386,7 @@ private:
EditorSelection *editor_selection;
ProjectExportDialog *project_export;
EditorResourcePreview *resource_preview;
+ EditorFolding editor_folding;
EditorFileServer *file_server;
@@ -600,6 +602,11 @@ private:
PrintHandlerList print_handler;
static void _print_handler(void *p_this, const String &p_string, bool p_error);
+ static void _resource_saved(RES p_resource, const String &p_path);
+ static void _resource_loaded(RES p_resource, const String &p_path);
+
+ void _resources_changed(const PoolVector<String> &p_resources);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -682,7 +689,7 @@ public:
void fix_dependencies(const String &p_for_file);
void clear_scene() { _cleanup_scene(); }
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false);
- Error load_resource(const String &p_scene);
+ Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
bool is_scene_open(const String &p_path);
@@ -690,6 +697,7 @@ public:
void set_current_scene(int p_idx);
static EditorData &get_editor_data() { return singleton->editor_data; }
+ static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
EditorHistory *get_editor_history() { return &editor_history; }
static VSplitContainer *get_top_split() { return singleton->top_split; }
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index dd3a8aa307..86b2db877e 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -475,7 +475,6 @@ void EditorPlugin::set_force_draw_over_forwarding_enabled() {
}
void EditorPlugin::notify_scene_changed(const Node *scn_root) {
- if (scn_root == NULL) return;
emit_signal("scene_changed", scn_root);
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index d078cfcc91..57c6fa3547 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -477,33 +477,16 @@ EditorPropertyCheck::EditorPropertyCheck() {
void EditorPropertyEnum::_option_selected(int p_which) {
- String text = options->get_item_text(p_which);
- Vector<String> text_split = text.split(":");
- if (text_split.size() == 1) {
- emit_signal("property_changed", get_edited_property(), p_which);
- return;
- }
- String name = text_split[1];
- emit_signal("property_changed", get_edited_property(), name.to_int());
+ int val = options->get_item_metadata(p_which);
+ emit_signal("property_changed", get_edited_property(), val);
}
void EditorPropertyEnum::update_property() {
int which = get_edited_object()->get(get_edited_property());
- if (which == 0) {
- options->select(which);
- return;
- }
for (int i = 0; i < options->get_item_count(); i++) {
- String text = options->get_item_text(i);
- Vector<String> text_split = text.split(":");
- if (text_split.size() == 1) {
- options->select(which);
- return;
- }
- String name = text_split[1];
- if (itos(which) == name) {
+ if (which == (int)options->get_item_metadata(i)) {
options->select(i);
return;
}
@@ -511,8 +494,15 @@ void EditorPropertyEnum::update_property() {
}
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
+
+ int current_val = 0;
for (int i = 0; i < p_options.size(); i++) {
- options->add_item(p_options[i], i);
+ Vector<String> text_split = p_options[i].split(":");
+ if (text_split.size() != 1)
+ current_val = text_split[1].to_int();
+ options->add_item(text_split[0]);
+ options->set_item_metadata(i, current_val);
+ current_val += 1;
}
}
@@ -1812,9 +1802,26 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
NodePath path = p_path;
Node *base_node = Object::cast_to<Node>(get_edited_object());
- if (base_node == NULL && get_edited_object()->has_method("get_root_path")) {
+ if (!base_node) {
+ //try a base node within history
+ if (EditorNode::get_singleton()->get_editor_history()->get_path_size() > 0) {
+ Object *base = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_history()->get_path_object(0));
+ if (base) {
+ base_node = Object::cast_to<Node>(base);
+ }
+ }
+ }
+
+ if (!base_node && get_edited_object()->has_method("get_root_path")) {
base_node = get_edited_object()->call("get_root_path");
}
+
+ if (!base_node && Object::cast_to<Reference>(get_edited_object())) {
+ Node *to_node = get_node(p_path);
+ ERR_FAIL_COND(!to_node);
+ path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
+ }
+
if (base_node) { // for AnimationTrackKeyEdit
path = base_node->get_path().rel_path_to(p_path);
}
@@ -1870,6 +1877,12 @@ void EditorPropertyNodePath::update_property() {
Node *target_node = base_node->get_node(p);
ERR_FAIL_COND(!target_node);
+ if (String(target_node->get_name()).find("@") != -1) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(p);
+ return;
+ }
+
assign->set_text(target_node->get_name());
assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));
}
@@ -2015,6 +2028,13 @@ void EditorPropertyResource::_menu_option(int p_which) {
} break;
+ case OBJ_MENU_SAVE: {
+ RES res = get_edited_object()->get(get_edited_property());
+ if (res.is_null())
+ return;
+ EditorNode::get_singleton()->save_resource(res);
+ } break;
+
case OBJ_MENU_COPY: {
RES res = get_edited_object()->get(get_edited_property());
@@ -2068,8 +2088,22 @@ void EditorPropertyResource::_menu_option(int p_which) {
if (intype == "ViewportTexture") {
+ Resource *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."));
+ return;
+ }
+
+ if (r && !r->is_local_to_scene()) {
+ EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on this resource because it's not set as local to scene.\nPlease switch on the 'local to scene' property on it (and all resources containing it up to a node)."));
+ return;
+ }
+
if (!scene_tree) {
scene_tree = memnew(SceneTreeDialog);
+ Vector<StringName> valid_types;
+ valid_types.push_back("Viewport");
+ scene_tree->get_scene_tree()->set_valid_types(valid_types);
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
add_child(scene_tree);
scene_tree->connect("selected", this, "_viewport_selected");
@@ -2130,7 +2164,8 @@ void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<T
}
}
-void EditorPropertyResource::_update_menu() {
+void EditorPropertyResource::_update_menu_items() {
+
//////////////////// UPDATE MENU //////////////////////////
RES res = get_edited_object()->get(get_edited_property());
@@ -2217,10 +2252,11 @@ void EditorPropertyResource::_update_menu() {
menu->add_icon_item(get_icon("Edit", "EditorIcons"), TTR("Edit"), OBJ_MENU_EDIT);
menu->add_icon_item(get_icon("Clear", "EditorIcons"), TTR("Clear"), OBJ_MENU_CLEAR);
menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
+ menu->add_icon_item(get_icon("Save", "EditorIcons"), TTR("Save"), OBJ_MENU_SAVE);
RES r = res;
if (r.is_valid() && r->get_path().is_resource_file()) {
menu->add_separator();
- menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
+ menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
}
} else {
}
@@ -2272,6 +2308,11 @@ void EditorPropertyResource::_update_menu() {
menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i);
}
}
+}
+
+void EditorPropertyResource::_update_menu() {
+
+ _update_menu_items();
Rect2 gt = edit->get_global_rect();
menu->set_as_minsize();
@@ -2296,6 +2337,20 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
emit_signal("object_id_selected", get_edited_property(), p_id);
}
+void EditorPropertyResource::_button_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ _update_menu_items();
+ Vector2 pos = mb->get_global_position();
+ //pos = assign->get_global_transform().xform(pos);
+ menu->set_as_minsize();
+ menu->set_global_position(pos);
+ menu->popup();
+ }
+ }
+}
+
void EditorPropertyResource::_open_editor_pressed() {
RES res = get_edited_object()->get(get_edited_property());
if (res.is_valid()) {
@@ -2583,6 +2638,7 @@ void EditorPropertyResource::_bind_methods() {
ClassDB::bind_method(D_METHOD("drop_data_fw"), &EditorPropertyResource::drop_data_fw);
ClassDB::bind_method(D_METHOD("_button_draw"), &EditorPropertyResource::_button_draw);
ClassDB::bind_method(D_METHOD("_open_editor_pressed"), &EditorPropertyResource::_open_editor_pressed);
+ ClassDB::bind_method(D_METHOD("_button_input"), &EditorPropertyResource::_button_input);
}
EditorPropertyResource::EditorPropertyResource() {
@@ -2609,6 +2665,7 @@ EditorPropertyResource::EditorPropertyResource() {
preview->set_margin(MARGIN_BOTTOM, -1);
preview->set_margin(MARGIN_RIGHT, -1);
assign->add_child(preview);
+ assign->connect("gui_input", this, "_button_input");
menu = memnew(PopupMenu);
add_child(menu);
@@ -2617,6 +2674,7 @@ EditorPropertyResource::EditorPropertyResource() {
menu->connect("id_pressed", this, "_menu_option");
edit->connect("pressed", this, "_update_menu");
hbc->add_child(edit);
+ edit->connect("gui_input", this, "_button_input");
file = NULL;
scene_tree = NULL;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 35d8f4d306..b82ef977ef 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -522,10 +522,11 @@ class EditorPropertyResource : public EditorProperty {
OBJ_MENU_EDIT = 1,
OBJ_MENU_CLEAR = 2,
OBJ_MENU_MAKE_UNIQUE = 3,
- OBJ_MENU_COPY = 4,
- OBJ_MENU_PASTE = 5,
- OBJ_MENU_NEW_SCRIPT = 6,
- OBJ_MENU_SHOW_IN_FILE_SYSTEM = 7,
+ OBJ_MENU_SAVE = 4,
+ OBJ_MENU_COPY = 5,
+ OBJ_MENU_PASTE = 6,
+ OBJ_MENU_NEW_SCRIPT = 7,
+ OBJ_MENU_SHOW_IN_FILE_SYSTEM = 8,
TYPE_BASE_ID = 100,
CONVERT_BASE_ID = 1000
@@ -552,6 +553,8 @@ class EditorPropertyResource : public EditorProperty {
void _resource_selected();
void _viewport_selected(const NodePath &p_path);
+ void _update_menu_items();
+
void _update_menu();
void _sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool);
@@ -564,6 +567,7 @@ class EditorPropertyResource : public EditorProperty {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ void _button_input(const Ref<InputEvent> &p_event);
void _open_editor_pressed();
protected:
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 24360813a2..431f608f46 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -29,9 +29,9 @@
/*************************************************************************/
#include "editor_properties_array_dict.h"
+#include "core/io/marshalls.h"
#include "editor/editor_scale.h"
#include "editor_properties.h"
-
bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) {
String pn = p_name;
@@ -54,6 +54,10 @@ bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) c
int idx = pn.get_slicec('/', 1).to_int();
bool valid;
r_ret = array.get(idx, &valid);
+ if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
+ r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
+ }
+
return valid;
}
@@ -120,6 +124,10 @@ bool EditorPropertyDictionaryObject::_get(const StringName &p_name, Variant &r_r
int idx = pn.get_slicec('/', 1).to_int();
Variant key = dict.get_key_at_index(idx);
r_ret = dict[key];
+ if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
+ r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
+ }
+
return true;
}
@@ -198,6 +206,10 @@ void EditorPropertyArray::_change_type_menu(int p_index) {
update_property();
}
+void EditorPropertyArray::_object_id_selected(const String &p_property, ObjectID p_id) {
+ emit_signal("object_id_selected", p_property, p_id);
+}
+
void EditorPropertyArray::update_property() {
Variant array = get_edited_object()->get(get_edited_property());
@@ -431,9 +443,19 @@ void EditorPropertyArray::update_property() {
} break;
case Variant::OBJECT: {
- EditorPropertyResource *editor = memnew(EditorPropertyResource);
- editor->setup("Resource");
- prop = editor;
+
+ if (Object::cast_to<EncodedObjectAsID>(value)) {
+
+ EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
+ editor->setup("Object");
+ prop = editor;
+
+ } else {
+
+ EditorPropertyResource *editor = memnew(EditorPropertyResource);
+ editor->setup("Resource");
+ prop = editor;
+ }
} break;
case Variant::DICTIONARY: {
@@ -497,6 +519,7 @@ void EditorPropertyArray::update_property() {
prop->set_label(itos(i + offset));
prop->set_selectable(false);
prop->connect("property_changed", this, "_property_changed");
+ prop->connect("object_id_selected", this, "_object_id_selected");
if (array.get_type() == Variant::ARRAY) {
HBoxContainer *hb = memnew(HBoxContainer);
vbox->add_child(hb);
@@ -578,6 +601,7 @@ void EditorPropertyArray::_bind_methods() {
ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed, DEFVAL(false));
ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type);
ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu);
+ ClassDB::bind_method("_object_id_selected", &EditorPropertyArray::_object_id_selected);
}
EditorPropertyArray::EditorPropertyArray() {
@@ -893,9 +917,19 @@ void EditorPropertyDictionary::update_property() {
} break;
case Variant::OBJECT: {
- EditorPropertyResource *editor = memnew(EditorPropertyResource);
- editor->setup("Resource");
- prop = editor;
+
+ if (Object::cast_to<EncodedObjectAsID>(value)) {
+
+ EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
+ editor->setup("Object");
+ prop = editor;
+
+ } else {
+
+ EditorPropertyResource *editor = memnew(EditorPropertyResource);
+ editor->setup("Resource");
+ prop = editor;
+ }
} break;
case Variant::DICTIONARY: {
@@ -986,6 +1020,7 @@ void EditorPropertyDictionary::update_property() {
prop->set_selectable(false);
prop->connect("property_changed", this, "_property_changed");
+ prop->connect("object_id_selected", this, "_object_id_selected");
HBoxContainer *hb = memnew(HBoxContainer);
if (add_vbox) {
@@ -1022,6 +1057,10 @@ void EditorPropertyDictionary::update_property() {
#endif
}
+void EditorPropertyDictionary::_object_id_selected(const String &p_property, ObjectID p_id) {
+ emit_signal("object_id_selected", p_property, p_id);
+}
+
void EditorPropertyDictionary::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
@@ -1055,6 +1094,7 @@ void EditorPropertyDictionary::_bind_methods() {
ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type);
ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu);
ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value);
+ ClassDB::bind_method("_object_id_selected", &EditorPropertyDictionary::_object_id_selected);
}
EditorPropertyDictionary::EditorPropertyDictionary() {
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index d2bd849f30..d5eecd9106 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -101,6 +101,8 @@ class EditorPropertyArray : public EditorProperty {
void _change_type(Object *p_button, int p_index);
void _change_type_menu(int p_index);
+ void _object_id_selected(const String &p_property, ObjectID p_id);
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -134,6 +136,7 @@ class EditorPropertyDictionary : public EditorProperty {
void _change_type_menu(int p_index);
void _add_key_value();
+ void _object_id_selected(const String &p_property, ObjectID p_id);
protected:
static void _bind_methods();
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 1e97920f7e..fdfa094ba2 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -300,6 +300,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "10,40,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/code_font_size", 14);
hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT);
+ _initial_set("interface/editor/main_font_antialiased", true);
+ _initial_set("interface/editor/code_font_antialiased", true);
_initial_set("interface/editor/main_font_hinting", 2);
hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/code_font_hinting", 2);
@@ -357,7 +359,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/theme/color_theme", "Adaptive");
hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom");
- _initial_set("text_editor/theme/line_spacing", 4);
+ _initial_set("text_editor/theme/line_spacing", 6);
_initial_set("text_editor/theme/selection_color", Color::html("40808080"));
_load_default_text_editor_theme();
@@ -410,7 +412,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/completion/add_type_hints", false);
_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
- _initial_set("docks/scene_tree/draw_relationship_lines", false);
+ _initial_set("docks/scene_tree/draw_relationship_lines", true);
_initial_set("docks/scene_tree/relationship_line_color", Color::html("464646"));
_initial_set("editors/grid_map/pick_distance", 5000.0);
@@ -433,7 +435,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// navigation
_initial_set("editors/3d/navigation/navigation_scheme", 0);
- _initial_set("editors/3d/navigation/invert_y-axis", false);
+ _initial_set("editors/3d/navigation/invert_y_axis", false);
hints["editors/3d/navigation/navigation_scheme"] = PropertyInfo(Variant::INT, "editors/3d/navigation/navigation_scheme", PROPERTY_HINT_ENUM, "Godot,Maya,Modo");
_initial_set("editors/3d/navigation/zoom_style", 0);
hints["editors/3d/navigation/zoom_style"] = PropertyInfo(Variant::INT, "editors/3d/navigation/zoom_style", PROPERTY_HINT_ENUM, "Vertical, Horizontal");
diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp
index 27fe716855..306f047860 100644
--- a/editor/fileserver/editor_file_server.cpp
+++ b/editor/fileserver/editor_file_server.cpp
@@ -150,13 +150,13 @@ void EditorFileServer::_subthread_start(void *s) {
s.parse_utf8(fileutf8.ptr());
if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) {
- DEBUG_PRINT("FILE EXISTS: " + s);
+ print_verbose("FILE EXISTS: " + s);
}
if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) {
- DEBUG_PRINT("MOD TIME: " + s);
+ print_verbose("MOD TIME: " + s);
}
if (cmd == FileAccessNetwork::COMMAND_OPEN_FILE) {
- DEBUG_PRINT("OPEN: " + s);
+ print_verbose("OPEN: " + s);
}
if (!s.begins_with("res://")) {
@@ -243,7 +243,7 @@ void EditorFileServer::_subthread_start(void *s) {
int read = cd->files[id]->get_buffer(buf.ptrw(), blocklen);
ERR_CONTINUE(read < 0);
- DEBUG_PRINT("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen));
+ print_verbose("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen));
//not found, continue
encode_uint32(id, buf4);
@@ -259,7 +259,7 @@ void EditorFileServer::_subthread_start(void *s) {
} break;
case FileAccessNetwork::COMMAND_CLOSE: {
- DEBUG_PRINT("CLOSED");
+ print_verbose("CLOSED");
ERR_CONTINUE(!cd->files.has(id));
memdelete(cd->files[id]);
cd->files.erase(id);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 4d386c1af6..828e608fa4 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -225,16 +225,11 @@ void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool
updating_tree = false;
}
-void FileSystemDock::_update_display_mode() {
+void FileSystemDock::_update_display_mode(bool p_force) {
// Compute the new display mode
- DisplayMode new_display_mode;
- if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) {
- new_display_mode = file_list_view ? DISPLAY_MODE_FILE_LIST_ONLY : DISPLAY_MODE_TREE_ONLY;
- } else {
- new_display_mode = DISPLAY_MODE_SPLIT;
- }
+ DisplayMode new_display_mode = (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) ? DISPLAY_MODE_TREE_ONLY : DISPLAY_MODE_SPLIT;
- if (new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) {
+ if (p_force || new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) {
display_mode = new_display_mode;
old_display_mode_setting = display_mode_setting;
button_toggle_display_mode->set_pressed(display_mode_setting == DISPLAY_MODE_SETTING_SPLIT ? true : false);
@@ -252,19 +247,9 @@ void FileSystemDock::_update_display_mode() {
file_list_vb->hide();
break;
- case DISPLAY_MODE_FILE_LIST_ONLY:
- tree->hide();
- tree_search_box->hide();
- button_tree->show();
-
- file_list_vb->show();
- _update_file_list(true);
- break;
-
case DISPLAY_MODE_SPLIT:
tree->show();
tree->set_v_size_flags(SIZE_EXPAND_FILL);
- button_tree->hide();
tree->ensure_cursor_is_visible();
tree_search_box->hide();
_update_tree(_compute_uncollapsed_paths());
@@ -295,7 +280,6 @@ void FileSystemDock::_notification(int p_what) {
String ei = "EditorIcons";
button_reload->set_icon(get_icon("Reload", ei));
button_toggle_display_mode->set_icon(get_icon("Panels2", ei));
- button_tree->set_icon(get_icon("Filesystem", ei));
_update_file_list_display_mode_button();
button_file_list_display_mode->connect("pressed", this, "_change_file_display");
@@ -312,7 +296,6 @@ void FileSystemDock::_notification(int p_what) {
file_list_popup->connect("id_pressed", this, "_file_list_rmb_option");
tree_popup->connect("id_pressed", this, "_tree_rmb_option");
- button_tree->connect("pressed", this, "_go_to_tree", varray(), CONNECT_DEFERRED);
current_path->connect("text_entered", this, "navigate_to_path");
display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode")));
@@ -352,14 +335,23 @@ void FileSystemDock::_notification(int p_what) {
tree->set_drop_mode_flags(0);
} break;
+ case NOTIFICATION_THEME_CHANGED: {
+ if (is_visible_in_tree()) {
+ _update_display_mode(true);
+ }
+ } break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
// Update icons
String ei = "EditorIcons";
button_reload->set_icon(get_icon("Reload", ei));
button_toggle_display_mode->set_icon(get_icon("Panels2", ei));
- button_tree->set_icon(get_icon("Filesystem", ei));
button_hist_next->set_icon(get_icon("Forward", ei));
button_hist_prev->set_icon(get_icon("Back", ei));
+ if (button_file_list_display_mode->is_pressed()) {
+ button_file_list_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons"));
+ } else {
+ button_file_list_display_mode->set_icon(get_icon("FileList", "EditorIcons"));
+ }
tree_search_box->set_right_icon(get_icon("Search", ei));
tree_search_box->set_clear_button_enabled(true);
@@ -412,7 +404,7 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
return;
TreeItem *favorites_item = tree->get_root()->get_children();
- if (selected->get_parent() == favorites_item) {
+ if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) {
// Go to the favorites if we click in the favorites and the path has changed
path = "Favorites";
} else {
@@ -474,20 +466,9 @@ void FileSystemDock::navigate_to_path(const String &p_path) {
_set_current_path_text(path);
_push_to_history();
+ _update_tree(_compute_uncollapsed_paths());
if (display_mode == DISPLAY_MODE_SPLIT) {
- if (path.ends_with("/") || path == "Favorites") {
- _go_to_file_list();
- }
- _update_tree(_compute_uncollapsed_paths());
_update_file_list(false);
- } else if (display_mode == DISPLAY_MODE_TREE_ONLY) {
- if (path.ends_with("/") || path == "Favorites") {
- _go_to_file_list();
- } else {
- _update_tree(_compute_uncollapsed_paths());
- }
- } else { // DISPLAY_MODE_FILE_LIST_ONLY
- _update_file_list(true);
}
String file_name = p_path.get_file();
@@ -701,7 +682,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
_search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128);
} else {
- if ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders) {
+ if (display_mode == DISPLAY_MODE_TREE_ONLY || always_show_folders) {
// Display folders in the list
if (directory != "res://") {
@@ -829,35 +810,16 @@ void FileSystemDock::_tree_activate_file() {
TreeItem *selected = tree->get_selected();
if (selected) {
call_deferred("_select_file", selected->get_metadata(0));
- }
-}
-void FileSystemDock::_file_list_activate_file(int p_idx) {
- _select_file(files->get_item_metadata(p_idx));
-}
-
-void FileSystemDock::_go_to_file_list() {
-
- if (display_mode == DISPLAY_MODE_TREE_ONLY) {
-
- file_list_view = true;
- _update_display_mode();
- } else {
- TreeItem *selected = tree->get_selected();
- if (selected) {
+ if (path.ends_with("/") || path == "Favorites") {
bool collapsed = selected->is_collapsed();
selected->set_collapsed(!collapsed);
}
- _update_file_list(false);
}
}
-void FileSystemDock::_go_to_tree() {
-
- file_list_view = false;
- tree->grab_focus();
- _update_display_mode();
- tree->ensure_cursor_is_visible();
+void FileSystemDock::_file_list_activate_file(int p_idx) {
+ _select_file(files->get_item_metadata(p_idx));
}
void FileSystemDock::_preview_invalidated(const String &p_path) {
@@ -1172,6 +1134,23 @@ void FileSystemDock::_update_project_settings_after_move(const Map<String, Strin
}
};
}
+
+ // Also search for the file in autoload, as they are stored differently from normal files.
+ List<PropertyInfo> property_list;
+ ProjectSettings::get_singleton()->get_property_list(&property_list);
+ for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("autoload/")) {
+ // If the autoload resource paths has a leading "*", it indicates that it is a Singleton,
+ // so we have to handle both cases when updating.
+ String autoload = GLOBAL_GET(E->get().name);
+ String autoload_singleton = autoload.substr(1, autoload.length());
+ if (p_renames.has(autoload)) {
+ ProjectSettings::get_singleton()->set_setting(E->get().name, p_renames[autoload]);
+ } else if (autoload.begins_with("*") && p_renames.has(autoload_singleton)) {
+ ProjectSettings::get_singleton()->set_setting(E->get().name, "*" + p_renames[autoload_singleton]);
+ }
+ }
+ }
ProjectSettings::get_singleton()->save();
}
@@ -1200,7 +1179,8 @@ void FileSystemDock::_make_dir_confirm() {
if (dir_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided"));
return;
- } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) {
+ } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.find("*") != -1 ||
+ dir_name.find("|") != -1 || dir_name.find(">") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) {
EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters"));
return;
}
@@ -1623,7 +1603,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected)
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
- make_script_dialog_text->config("Node", fpath + "new_script.gd", false);
+ make_script_dialog_text->config("Node", fpath.plus_file("new_script.gd"), false);
make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE);
} break;
@@ -1676,9 +1656,6 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from
tree_search_box->set_text(searched_string);
switch (display_mode) {
- case DISPLAY_MODE_FILE_LIST_ONLY: {
- _update_file_list(false);
- } break;
case DISPLAY_MODE_TREE_ONLY: {
_update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>());
} break;
@@ -1707,12 +1684,6 @@ void FileSystemDock::fix_dependencies(const String &p_for_file) {
void FileSystemDock::focus_on_filter() {
- if (display_mode == DISPLAY_MODE_FILE_LIST_ONLY && tree->is_visible()) {
- // Tree mode, switch to files list with search box
- tree->hide();
- file_list_vb->show();
- }
-
file_list_search_box->grab_focus();
}
@@ -1997,7 +1968,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori
return;
}
-void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths) {
+void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options) {
// Add options for files and folders
ERR_FAIL_COND(p_paths.empty())
@@ -2084,12 +2055,14 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
if (p_paths.size() == 1) {
p_popup->add_separator();
- p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
- p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
- p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE);
+ if (p_display_path_dependent_options) {
+ p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
+ p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
+ p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE);
+ }
String fpath = p_paths[0];
- String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager");
+ String item_text = fpath.ends_with("/") ? TTR("Open in File Manager") : TTR("Show in File Manager");
p_popup->add_item(item_text, FILE_SHOW_IN_EXPLORER);
}
}
@@ -2100,8 +2073,8 @@ void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos) {
if (paths.size() == 1) {
if (paths[0].ends_with("/")) {
- tree_popup->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL);
- tree_popup->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL);
+ tree_popup->add_item(TTR("Expand All"), FOLDER_EXPAND_ALL);
+ tree_popup->add_item(TTR("Collapse All"), FOLDER_COLLAPSE_ALL);
tree_popup->add_separator();
}
}
@@ -2133,7 +2106,7 @@ void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) {
if (!paths.empty()) {
file_list_popup->clear();
file_list_popup->set_size(Size2(1, 1));
- _file_and_folders_fill_popup(file_list_popup, paths);
+ _file_and_folders_fill_popup(file_list_popup, paths, searched_string.length() == 0);
file_list_popup->set_position(files->get_global_position() + p_pos);
file_list_popup->popup();
}
@@ -2141,13 +2114,16 @@ void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) {
void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) {
// Right click on empty space for file list
+ if (searched_string.length() > 0)
+ return;
+
file_list_popup->clear();
file_list_popup->set_size(Size2(1, 1));
file_list_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
file_list_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
file_list_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE);
- file_list_popup->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
+ file_list_popup->add_item(TTR("Show in File Manager"), FILE_SHOW_IN_EXPLORER);
file_list_popup->set_position(files->get_global_position() + p_pos);
file_list_popup->popup();
}
@@ -2302,7 +2278,6 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_file_list_activate_file"), &FileSystemDock::_file_list_activate_file);
ClassDB::bind_method(D_METHOD("_tree_activate_file"), &FileSystemDock::_tree_activate_file);
ClassDB::bind_method(D_METHOD("_select_file"), &FileSystemDock::_select_file);
- ClassDB::bind_method(D_METHOD("_go_to_tree"), &FileSystemDock::_go_to_tree);
ClassDB::bind_method(D_METHOD("navigate_to_path"), &FileSystemDock::navigate_to_path);
ClassDB::bind_method(D_METHOD("_change_file_display"), &FileSystemDock::_change_file_display);
ClassDB::bind_method(D_METHOD("_fw_history"), &FileSystemDock::_fw_history);
@@ -2398,7 +2373,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
/*
button_open = memnew( Button );
button_open->set_flat(true);
- button_open->connect("pressed",this,"_go_to_file_list");
+ button_open->connect("pressed",this,"_tree_toggle_collapsed");
toolbar_hbc->add_child(button_open);
button_open->hide();
button_open->set_focus_mode(FOCUS_NONE);
@@ -2448,11 +2423,6 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
path_hb = memnew(HBoxContainer);
file_list_vb->add_child(path_hb);
- button_tree = memnew(ToolButton);
- button_tree->set_tooltip(TTR("Enter tree-view."));
- button_tree->hide();
- path_hb->add_child(button_tree);
-
file_list_search_box = memnew(LineEdit);
file_list_search_box->set_h_size_flags(SIZE_EXPAND_FILL);
file_list_search_box->set_placeholder(TTR("Search files"));
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index d964515572..df6fa5f9d2 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -73,7 +73,6 @@ private:
enum DisplayMode {
DISPLAY_MODE_TREE_ONLY,
- DISPLAY_MODE_FILE_LIST_ONLY,
DISPLAY_MODE_SPLIT,
};
@@ -109,7 +108,6 @@ private:
Button *button_toggle_display_mode;
Button *button_reload;
- Button *button_tree;
Button *button_file_list_display_mode;
Button *button_hist_next;
Button *button_hist_prev;
@@ -127,7 +125,6 @@ private:
DisplayMode display_mode;
DisplayModeSetting display_mode_setting;
DisplayModeSetting old_display_mode_setting;
- bool file_list_view;
PopupMenu *file_list_popup;
PopupMenu *tree_popup;
@@ -193,8 +190,7 @@ private:
void _change_file_display();
void _fs_changed();
- void _go_to_tree();
- void _go_to_file_list();
+ void _tree_toggle_collapsed();
void _select_file(const String p_path);
void _tree_activate_file();
@@ -237,7 +233,7 @@ private:
void _search_changed(const String &p_text, const Control *p_from);
- void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths);
+ void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true);
void _tree_rmb_select(const Vector2 &p_pos);
void _file_list_rmb_select(int p_item, const Vector2 &p_pos);
void _file_list_rmb_pressed(const Vector2 &p_pos);
@@ -268,7 +264,7 @@ private:
void _file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata);
void _tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata);
- void _update_display_mode();
+ void _update_display_mode(bool p_force = false);
Vector<String> _tree_get_selected(bool remove_self_inclusion = true);
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index 0ccaa95fb7..705bb1d9c5 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "find_in_files.h"
+
#include "core/os/dir_access.h"
#include "core/os/os.h"
#include "editor_node.h"
@@ -89,8 +90,6 @@ static bool find_next(const String &line, String pattern, int from, bool match_c
//--------------------------------------------------------------------------------
FindInFiles::FindInFiles() {
_root_prefix = ROOT_PREFIX;
- _extension_filter.insert("gd");
- _extension_filter.insert("cs");
_searching = false;
_whole_words = true;
_match_case = true;
@@ -301,8 +300,7 @@ const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested";
FindInFilesDialog::FindInFilesDialog() {
- set_custom_minimum_size(Size2(400, 190) * EDSCALE);
- set_resizable(true);
+ set_custom_minimum_size(Size2(500 * EDSCALE, 0));
set_title(TTR("Find in Files"));
VBoxContainer *vbc = memnew(VBoxContainer);
@@ -317,7 +315,7 @@ FindInFilesDialog::FindInFilesDialog() {
vbc->add_child(gc);
Label *find_label = memnew(Label);
- find_label->set_text(TTR("Find: "));
+ find_label->set_text(TTR("Find:"));
gc->add_child(find_label);
_search_text_line_edit = memnew(LineEdit);
@@ -326,10 +324,7 @@ FindInFilesDialog::FindInFilesDialog() {
_search_text_line_edit->connect("text_entered", this, "_on_search_text_entered");
gc->add_child(_search_text_line_edit);
- {
- Control *placeholder = memnew(Control);
- gc->add_child(placeholder);
- }
+ gc->add_child(memnew(Control)); // Space to mantain the grid aligned.
{
HBoxContainer *hbc = memnew(HBoxContainer);
@@ -346,7 +341,7 @@ FindInFilesDialog::FindInFilesDialog() {
}
Label *folder_label = memnew(Label);
- folder_label->set_text(TTR("Folder: "));
+ folder_label->set_text(TTR("Folder:"));
gc->add_child(folder_label);
{
@@ -374,7 +369,7 @@ FindInFilesDialog::FindInFilesDialog() {
}
Label *filter_label = memnew(Label);
- filter_label->set_text(TTR("Filter: "));
+ filter_label->set_text(TTR("Filters:"));
gc->add_child(filter_label);
{
@@ -382,7 +377,8 @@ FindInFilesDialog::FindInFilesDialog() {
Vector<String> exts;
exts.push_back("gd");
- exts.push_back("cs");
+ if (Engine::get_singleton()->has_singleton("GodotSharp"))
+ exts.push_back("cs");
for (int i = 0; i < exts.size(); ++i) {
CheckBox *cb = memnew(CheckBox);
@@ -395,39 +391,14 @@ FindInFilesDialog::FindInFilesDialog() {
gc->add_child(hbc);
}
- {
- Control *placeholder = memnew(Control);
- placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16));
- vbc->add_child(placeholder);
- }
-
- {
- HBoxContainer *hbc = memnew(HBoxContainer);
- hbc->set_alignment(HBoxContainer::ALIGN_CENTER);
-
- _find_button = add_button(TTR("Find..."), false, "find");
- _find_button->set_disabled(true);
-
- {
- Control *placeholder = memnew(Control);
- placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
- hbc->add_child(placeholder);
- }
-
- _replace_button = add_button(TTR("Replace..."), false, "replace");
- _replace_button->set_disabled(true);
+ _find_button = add_button(TTR("Find..."), false, "find");
+ _find_button->set_disabled(true);
- {
- Control *placeholder = memnew(Control);
- placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
- hbc->add_child(placeholder);
- }
-
- Button *cancel_button = get_ok();
- cancel_button->set_text(TTR("Cancel"));
+ _replace_button = add_button(TTR("Replace..."), false, "replace");
+ _replace_button->set_disabled(true);
- vbc->add_child(hbc);
- }
+ Button *cancel_button = get_ok();
+ cancel_button->set_text(TTR("Cancel"));
}
void FindInFilesDialog::set_search_text(String text) {
diff --git a/editor/icons/icon_GUI_viewport_hdiagsplitter.svg b/editor/icons/icon_GUI_viewport_hdiagsplitter.svg
index 36769768fd..90a0f56c43 100644
--- a/editor/icons/icon_GUI_viewport_hdiagsplitter.svg
+++ b/editor/icons/icon_GUI_viewport_hdiagsplitter.svg
@@ -1,64 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="64"
- height="34"
- version="1.1"
- viewBox="0 0 64 34"
- id="svg6"
- sodipodi:docname="icon_GUI_vsplitter1.svg"
- inkscape:version="0.92.2 2405546, 2018-03-11">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1366"
- inkscape:window-height="714"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="5.6568543"
- inkscape:cx="37.006499"
- inkscape:cy="15.680715"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg6" />
- <g
- transform="translate(0,-1018.4)"
- id="g4" />
- <g
- transform="rotate(90,541.2,539.2)"
- id="g4-3">
- <path
- id="path2-6"
- style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-opacity:0.39216003"
- d="M 4.0306826,1048.4 H 34 m -30,30 v -60"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- </g>
+<svg width="64" height="34" version="1.1" viewBox="0 0 64 34" xmlns="http://www.w3.org/2000/svg">
+<g transform="rotate(90,541.2,539.2)">
+<path d="m4.0307 1048.4h29.969m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/>
+</g>
</svg>
diff --git a/editor/icons/icon_GUI_viewport_vdiagsplitter.svg b/editor/icons/icon_GUI_viewport_vdiagsplitter.svg
index f23b4a0a74..481f895d46 100644
--- a/editor/icons/icon_GUI_viewport_vdiagsplitter.svg
+++ b/editor/icons/icon_GUI_viewport_vdiagsplitter.svg
@@ -1,68 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="34"
- height="64"
- version="1.1"
- viewBox="0 0 34 64"
- id="svg6"
- sodipodi:docname="icon_GUI_vsplitter2.svg"
- inkscape:version="0.92.2 2405546, 2018-03-11">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1366"
- inkscape:window-height="714"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4"
- inkscape:cx="32.245723"
- inkscape:cy="44.255214"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg6" />
- <g
- transform="translate(0,-988.4)"
- id="g4" />
- <g
- id="g839"
- transform="rotate(90,32.003536,32.003535)">
- <g
- id="g4-3"
- transform="rotate(90,526.2,554.2)">
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- d="M 4.0306826,1048.4 H 34 m -30,30 v -60"
- style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-opacity:0.39216003"
- id="path2-6" />
- </g>
- </g>
+<svg width="34" height="64" version="1.1" viewBox="0 0 34 64" xmlns="http://www.w3.org/2000/svg">
+<g transform="rotate(90 32.004 32.004)">
+<g transform="rotate(90,526.2,554.2)">
+<path d="m4.0307 1048.4h29.969m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/>
+</g>
+</g>
</svg>
diff --git a/editor/icons/icon_GUI_viewport_vhsplitter.svg b/editor/icons/icon_GUI_viewport_vhsplitter.svg
index 429cf909ae..52d7d8f0b7 100644
--- a/editor/icons/icon_GUI_viewport_vhsplitter.svg
+++ b/editor/icons/icon_GUI_viewport_vhsplitter.svg
@@ -1,63 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="64"
- height="64"
- version="1.1"
- viewBox="0 0 64 64"
- id="svg6"
- sodipodi:docname="icon_GUI_vsplitter.svg"
- inkscape:version="0.92.2 2405546, 2018-03-11">
- <metadata
- id="metadata12">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs10" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1366"
- inkscape:window-height="714"
- id="namedview8"
- showgrid="false"
- inkscape:zoom="4.65625"
- inkscape:cx="9.8488117"
- inkscape:cy="20.04653"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg6" />
- <g
- transform="translate(0,-988.4)"
- id="g4" />
- <g
- transform="rotate(90,526.2,554.2)"
- id="g4-3">
- <path
- id="path2-6"
- style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-opacity:0.39216003"
- d="m -26,1048.4 h 60 m -30,30 v -60"
- inkscape:connector-curvature="0" />
- </g>
+<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
+<g transform="rotate(90,526.2,554.2)">
+<path d="m-26 1048.4h60m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/>
+</g>
</svg>
diff --git a/editor/icons/icon_script_extend.svg b/editor/icons/icon_script_extend.svg
new file mode 100644
index 0000000000..ef3d48af9f
--- /dev/null
+++ b/editor/icons/icon_script_extend.svg
@@ -0,0 +1,8 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m6 1v1c-0.55228 0-1 0.44772-1 1v10h-1v-2h-2v2c2.826e-4 0.35698 0.19084 0.68674 0.5 0.86523 0.15194 0.088045 0.32439 0.13452 0.5 0.13477v1h7c0.73866 0 1.3763-0.40437 1.7227-1h-3.7227v-4h4v-5h3v-2c0-1.1046-0.89543-2-2-2z" fill="#e0e0e0"/>
+<path transform="translate(0 1036.4)" d="m6 1c-1.1046 0-2 0.89543-2 2v7h-2-1v1 2c0 1.1046 0.89543 2 2 2s2-0.89543 2-2v-10c0-0.55228 0.44772-1 1-1s1 0.44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-0.89543-2-2-2zm-4 10h2v2c0 0.55228-0.44772 1-1 1s-1-0.44772-1-1v-2z" fill="#b4b4b4"/>
+<circle cx="3" cy="1048.4" rx="1" ry="1" fill="#e0e0e0"/>
+<path d="m16 1048.4-3-3v2h-4v2h4v2z" fill="#68b6ff" fill-rule="evenodd"/>
+</g>
+</svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index e8bb772a64..b133efead7 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -771,7 +771,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
int binormal_pos = (binormal_src->stride ? binormal_src->stride : 3) * p.indices[src + binormal_ofs];
ERR_FAIL_INDEX_V(binormal_pos, binormal_src->array.size(), ERR_INVALID_DATA);
- Vector3 binormal = Vector3(binormal_src->array[binormal_pos + 0], binormal_src->array[binormal_pos + 1], binormal_src->array[binormal_pos + 2]);
+ Vector3 binormal = Vector3(-binormal_src->array[binormal_pos + 0], -binormal_src->array[binormal_pos + 1], -binormal_src->array[binormal_pos + 2]); // Due to Godots face order it seems we need to flip our binormal!
int tangent_pos = (tangent_src->stride ? tangent_src->stride : 3) * p.indices[src + tangent_ofs];
ERR_FAIL_INDEX_V(tangent_pos, tangent_src->array.size(), ERR_INVALID_DATA);
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index cb15be35f2..f47252d388 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -899,7 +899,16 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) {
array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true);
}
if (a.has("TANGENT")) {
- array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(state, a["TANGENT"], true);
+ PoolVector<float> tans = _decode_accessor_as_floats(state, a["TANGENT"], true);
+ { // we need our binormals inversed, so flip our w component.
+ int ts = tans.size();
+ PoolVector<float>::Write w = tans.write();
+
+ for (int j = 3; j < ts; j += 4) {
+ w[j] *= -1.0;
+ }
+ }
+ array[Mesh::ARRAY_TANGENT] = tans;
}
if (a.has("TEXCOORD_0")) {
array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(state, a["TEXCOORD_0"], true);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 5eb1a42f9f..b38f4c4f14 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -216,7 +216,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT));
- r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.1"), 1.0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
}
void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal) {
@@ -235,7 +235,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
if (p_streamable)
format |= StreamTexture::FORMAT_BIT_STREAM;
- if (p_mipmaps || p_compress_mode == COMPRESS_VIDEO_RAM) //VRAM always uses mipmaps
+ if (p_mipmaps)
format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
if (p_detect_3d)
format |= StreamTexture::FORMAT_BIT_DETECT_3D;
@@ -310,7 +310,9 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
case COMPRESS_VIDEO_RAM: {
Ref<Image> image = p_image->duplicate();
- image->generate_mipmaps(p_force_normal);
+ if (p_mipmaps) {
+ image->generate_mipmaps(p_force_normal);
+ }
if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBE9995);
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 31eb193461..2e68609a56 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -38,11 +38,17 @@ public:
List<PropertyInfo> properties;
Ref<ResourceImporter> importer;
Vector<String> paths;
+ Set<StringName> checked;
+ bool checking;
bool _set(const StringName &p_name, const Variant &p_value) {
if (values.has(p_name)) {
values[p_name] = p_value;
+ if (checking) {
+ checked.insert(p_name);
+ _change_notify(String(p_name).utf8().get_data());
+ }
return true;
}
@@ -63,13 +69,24 @@ public:
for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
if (!importer->get_option_visibility(E->get().name, values))
continue;
- p_list->push_back(E->get());
+ PropertyInfo pi = E->get();
+ if (checking) {
+ pi.usage |= PROPERTY_USAGE_CHECKABLE;
+ if (checked.has(E->get().name)) {
+ pi.usage |= PROPERTY_USAGE_CHECKED;
+ }
+ }
+ p_list->push_back(pi);
}
}
void update() {
_change_notify();
}
+
+ ImportDockParameters() {
+ checking = false;
+ }
};
void ImportDock::set_edit_path(const String &p_path) {
@@ -125,6 +142,8 @@ void ImportDock::_update_options(const Ref<ConfigFile> &p_config) {
params->properties.clear();
params->values.clear();
+ params->checking = false;
+ params->checked.clear();
for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
@@ -205,6 +224,8 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
params->properties.clear();
params->values.clear();
+ params->checking = true;
+ params->checked.clear();
for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
@@ -360,11 +381,21 @@ void ImportDock::_reimport() {
Error err = config->load(params->paths[i] + ".import");
ERR_CONTINUE(err != OK);
- config->set_value("remap", "importer", params->importer->get_importer_name());
- config->erase_section("params");
+ if (params->checking) {
+ //update only what edited (checkboxes)
+ for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) {
+ if (params->checked.has(E->get().name)) {
+ config->set_value("params", E->get().name, params->values[E->get().name]);
+ }
+ }
+ } else {
+ //override entirely
+ config->set_value("remap", "importer", params->importer->get_importer_name());
+ config->erase_section("params");
- for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) {
- config->set_value("params", E->get().name, params->values[E->get().name]);
+ for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) {
+ config->set_value("params", E->get().name, params->values[E->get().name]);
+ }
}
config->save(params->paths[i] + ".import");
@@ -388,11 +419,20 @@ void ImportDock::_notification(int p_what) {
} break;
}
}
+
+void ImportDock::_property_toggled(const StringName &p_prop, bool p_checked) {
+ if (p_checked) {
+ params->checked.insert(p_prop);
+ } else {
+ params->checked.erase(p_prop);
+ }
+}
void ImportDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_reimport"), &ImportDock::_reimport);
ClassDB::bind_method(D_METHOD("_preset_selected"), &ImportDock::_preset_selected);
ClassDB::bind_method(D_METHOD("_importer_selected"), &ImportDock::_importer_selected);
+ ClassDB::bind_method(D_METHOD("_property_toggled"), &ImportDock::_property_toggled);
}
void ImportDock::initialize_import_options() const {
@@ -423,6 +463,7 @@ ImportDock::ImportDock() {
import_opts = memnew(EditorInspector);
add_child(import_opts);
import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
+ import_opts->connect("property_toggled", this, "_property_toggled");
hb = memnew(HBoxContainer);
add_child(hb);
diff --git a/editor/import_dock.h b/editor/import_dock.h
index 41c7298d9a..632fd39700 100644
--- a/editor/import_dock.h
+++ b/editor/import_dock.h
@@ -60,6 +60,7 @@ class ImportDock : public VBoxContainer {
void _importer_selected(int i_idx);
void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>());
+ void _property_toggled(const StringName &p_prop, bool p_checked);
void _reimport();
enum {
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 750fca2852..2a8885c0a4 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -593,6 +593,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
inspector->set_undo_redo(&editor_data->get_undo_redo());
inspector->set_use_filter(true); // TODO: check me
+ inspector->set_auto_unfold_edited(bool(EDITOR_GET("interface/inspector/auto_unfold_edited")));
inspector->connect("resource_selected", this, "_resource_selected");
inspector->connect("property_keyed", this, "_property_keyed");
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index be4e752d55..420d8ad3cf 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -52,7 +52,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("MultiNode Set") + " " + String(name));
+ ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS);
for (const List<NodePath>::Element *E = nodes.front(); E; E = E->next()) {
if (!es->has_node(E->get()))
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index a334f79f5a..8e626e7111 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -180,7 +180,7 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(desc_lb);
desc_edit = memnew(TextEdit);
- desc_edit->set_custom_minimum_size(Size2(400.0f, 50.0f));
+ desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
grid->add_child(desc_edit);
Label *author_lb = memnew(Label);
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index f7e59e2beb..16423decc4 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -132,8 +132,8 @@ Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const {
void AbstractPolygon2DEditor::_commit_action() {
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
}
@@ -218,7 +218,7 @@ void AbstractPolygon2DEditor::_node_removed(Node *p_node) {
edit(NULL);
hide();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
}
@@ -229,6 +229,18 @@ void AbstractPolygon2DEditor::_wip_changed() {
}
}
+void AbstractPolygon2DEditor::_wip_cancel() {
+
+ wip.clear();
+ wip_active = false;
+
+ edited_point = PosVertex();
+ hover_point = Vertex();
+ selected_point = Vertex();
+
+ canvas_item_editor->update_viewport();
+}
+
void AbstractPolygon2DEditor::_wip_close() {
if (!wip_active)
return;
@@ -334,7 +346,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
edited_point = PosVertex(closest, xform.affine_inverse().xform(closest.pos));
selected_point = closest;
edge_point = PosVertex();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
} else {
@@ -403,7 +415,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
wip_active = true;
_wip_changed();
edited_point = PosVertex(-1, 1, cpoint);
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
hover_point = Vertex();
selected_point = Vertex(0);
edge_point = PosVertex();
@@ -424,12 +436,12 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
_wip_changed();
edited_point = PosVertex(-1, wip.size(), cpoint);
selected_point = Vertex(wip.size() - 1);
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
}
} else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) {
- _wip_close();
+ _wip_cancel();
}
}
}
@@ -453,7 +465,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
_set_polygon(edited_point.polygon, vertices);
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} else if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
const PosVertex onEdgeVertex = closest_edge_point(gpoint);
@@ -462,20 +474,20 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
hover_point = Vertex();
edge_point = onEdgeVertex;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} else {
if (edge_point.valid()) {
edge_point = PosVertex();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
const PosVertex new_hover_point = closest_point(gpoint);
if (hover_point != new_hover_point) {
hover_point = new_hover_point;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
}
}
@@ -494,7 +506,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
wip.remove(selected_point.vertex);
_wip_changed();
selected_point = wip.size() - 1;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
} else {
@@ -510,6 +522,8 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
} else if (wip_active && k->get_scancode() == KEY_ENTER) {
_wip_close();
+ } else if (wip_active && k->get_scancode() == KEY_ESCAPE) {
+ _wip_cancel();
}
}
@@ -627,7 +641,7 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) {
hover_point = Vertex();
selected_point = Vertex();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} else {
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 00634ba5b8..c03670f254 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -103,6 +103,7 @@ protected:
virtual void _menu_option(int p_option);
void _wip_changed();
void _wip_close();
+ void _wip_cancel();
bool _delete_point(const Vector2 &p_gpoint);
void _notification(int p_what);
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 394b888d0e..e2fe9a91d8 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -607,6 +607,8 @@ void AnimationNodeBlendSpace2DEditor::_update_space() {
auto_triangles->set_pressed(blend_space->get_auto_triangles());
+ interpolation->select(blend_space->get_blend_mode());
+
max_x_value->set_value(blend_space->get_max_space().x);
max_y_value->set_value(blend_space->get_max_space().y);
@@ -636,6 +638,8 @@ void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());
undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value()));
undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
+ undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());
+ undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());
undo_redo->add_do_method(this, "_update_space");
undo_redo->add_undo_method(this, "_update_space");
undo_redo->commit_action();
@@ -752,6 +756,10 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
snap->set_icon(get_icon("SnapGrid", "EditorIcons"));
open_editor->set_icon(get_icon("Edit", "EditorIcons"));
auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons"));
+ interpolation->clear();
+ interpolation->add_icon_item(get_icon("TrackContinuous", "EditorIcons"), "", 0);
+ interpolation->add_icon_item(get_icon("TrackDiscrete", "EditorIcons"), "", 1);
+ interpolation->add_icon_item(get_icon("TrackCapture", "EditorIcons"), "", 2);
}
if (p_what == NOTIFICATION_PROCESS) {
@@ -914,6 +922,13 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
snap_y->set_step(0.01);
snap_y->set_max(1000);
+ top_hb->add_child(memnew(VSeparator));
+
+ top_hb->add_child(memnew(Label(TTR("Blend:"))));
+ interpolation = memnew(OptionButton);
+ top_hb->add_child(interpolation);
+ interpolation->connect("item_selected", this, "_config_changed");
+
edit_hb = memnew(HBoxContainer);
top_hb->add_child(edit_hb);
edit_hb->add_child(memnew(VSeparator));
diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h
index 613289e4d8..603fa1cd19 100644
--- a/editor/plugins/animation_blend_space_2d_editor.h
+++ b/editor/plugins/animation_blend_space_2d_editor.h
@@ -60,6 +60,7 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
ToolButton *snap;
SpinBox *snap_x;
SpinBox *snap_y;
+ OptionButton *interpolation;
ToolButton *auto_triangles;
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 205458fb1d..eb3c432ee7 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -692,7 +692,9 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
Ref<Animation> anim = player->get_animation(an->get_animation());
if (anim.is_valid()) {
E->get()->set_max(anim->get_length());
- E->get()->set_value(an->get_playback_time());
+ //StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E->get().input_node;
+ StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->key()) + "/time";
+ E->get()->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path));
}
}
}
@@ -833,8 +835,6 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) {
if (p_node.is_valid()) {
blend_tree = p_node;
- } else {
- blend_tree.unref();
}
if (blend_tree.is_null()) {
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 66770d98e5..138b8a491c 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -962,6 +962,9 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int
HBoxContainer *hbc = memnew(HBoxContainer);
+ if (p_page_count < 2)
+ return hbc;
+
//do the mario
int from = p_page - 5;
if (from < 0)
diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp
index e65a697857..40e3bb5be2 100644
--- a/editor/plugins/baked_lightmap_editor_plugin.cpp
+++ b/editor/plugins/baked_lightmap_editor_plugin.cpp
@@ -108,7 +108,7 @@ void BakedLightmapEditorPlugin::_bind_methods() {
BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) {
editor = p_node;
- bake = memnew(Button);
+ bake = memnew(ToolButton);
bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
bake->set_text(TTR("Bake Lightmaps"));
bake->hide();
diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h
index a32b573851..8d3b5b1dd6 100644
--- a/editor/plugins/baked_lightmap_editor_plugin.h
+++ b/editor/plugins/baked_lightmap_editor_plugin.h
@@ -42,7 +42,7 @@ class BakedLightmapEditorPlugin : public EditorPlugin {
BakedLightmap *lightmap;
- Button *bake;
+ ToolButton *bake;
EditorNode *editor;
static EditorProgress *tmp_progress;
diff --git a/editor/plugins/camera_editor_plugin.cpp b/editor/plugins/camera_editor_plugin.cpp
index 37fbb54c30..3d8b24ccc7 100644
--- a/editor/plugins/camera_editor_plugin.cpp
+++ b/editor/plugins/camera_editor_plugin.cpp
@@ -32,18 +32,6 @@
#include "spatial_editor_plugin.h"
-void CameraEditor::_notification(int p_what) {
-
- switch (p_what) {
-
- /* case NOTIFICATION_PROCESS: {
-
- if (preview->is_pressed() && node)
- node->call("make_current");
-
- } break;*/
- }
-}
void CameraEditor::_node_removed(Node *p_node) {
if (p_node == node) {
diff --git a/editor/plugins/camera_editor_plugin.h b/editor/plugins/camera_editor_plugin.h
index 275624beeb..0340808c9a 100644
--- a/editor/plugins/camera_editor_plugin.h
+++ b/editor/plugins/camera_editor_plugin.h
@@ -50,7 +50,6 @@ class CameraEditor : public Control {
void _pressed();
protected:
- void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a9bc5e77ac..ee2283a035 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -179,8 +179,21 @@ void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap,
}
}
-bool CanvasItemEditor::_is_node_editable(const Node *p_node) {
- return (!(p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_")) && !(ClassDB::is_parent_class(p_node->get_parent()->get_class_name(), "Container") && ClassDB::is_parent_class(p_node->get_class_name(), "Control")));
+bool CanvasItemEditor::_is_node_locked(const Node *p_node) {
+ return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
+}
+
+bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning) {
+ if (_is_node_locked(p_node)) {
+ return false;
+ }
+ if (Object::cast_to<Control>(p_node) && Object::cast_to<Container>(p_node->get_parent())) {
+ if (p_popup_warning) {
+ _popup_warning_temporarily(warning_child_of_container, 3.0);
+ }
+ return false;
+ }
+ return true;
}
void CanvasItemEditor::_snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation, float p_radius) {
@@ -415,7 +428,7 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c
}
}
- if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_editable(canvas_item))) {
+ if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_locked(canvas_item))) {
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
Rect2 rect = canvas_item->_edit_get_rect();
if (r_first) {
@@ -437,7 +450,7 @@ Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) {
return rect;
}
-void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
@@ -449,16 +462,14 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
if (canvas_item) {
if (!canvas_item->is_set_as_toplevel()) {
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
} else {
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, canvas_item->get_transform(), p_canvas_xform);
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, canvas_item->get_transform(), p_canvas_xform);
}
} else {
CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform);
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, Transform2D(), cl ? cl->get_transform() : p_canvas_xform);
}
- if (p_limit != 0 && r_items.size() >= p_limit)
- return;
}
if (canvas_item && canvas_item->is_visible_in_tree()) {
@@ -478,11 +489,11 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
return;
}
-void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) {
+void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) {
Node *scene = editor->get_edited_scene();
- _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit);
+ _find_canvas_items_at_pos(p_pos, scene, r_items);
//Remove invalid results
for (int i = 0; i < r_items.size(); i++) {
@@ -513,7 +524,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
}
//Remove the item if invalid
- if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || !_is_node_editable(canvas_item)) {
+ if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || _is_node_locked(canvas_item)) {
r_items.remove(i);
i--;
} else {
@@ -614,7 +625,7 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n
bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner());
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
- bool locked = !_is_node_editable(p_node);
+ bool locked = _is_node_locked(p_node);
if (!lock_children || !editable) {
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
@@ -681,7 +692,7 @@ List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_lock
List<CanvasItem *> selection;
for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
- if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || _is_node_editable(canvas_item))) {
+ if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || !_is_node_locked(canvas_item))) {
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (se) {
selection.push_back(canvas_item);
@@ -1025,8 +1036,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
// Scroll or pan down
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- _update_scrollbars();
- viewport->update();
+ update_viewport();
} else {
_zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position());
}
@@ -1037,8 +1047,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
// Scroll or pan up
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- _update_scrollbars();
- viewport->update();
+ update_viewport();
} else {
_zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position());
}
@@ -1049,8 +1058,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
// Pan left
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- _update_scrollbars();
- viewport->update();
+ update_viewport();
return true;
}
}
@@ -1059,8 +1067,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
// Pan right
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- _update_scrollbars();
- viewport->update();
+ update_viewport();
return true;
}
}
@@ -1112,8 +1119,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
}
view_offset.x -= relative.x / zoom;
view_offset.y -= relative.y / zoom;
- _update_scrollbars();
- viewport->update();
+ update_viewport();
return true;
}
}
@@ -1131,8 +1137,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta();
view_offset.x += delta.x;
view_offset.y += delta.y;
- _update_scrollbars();
- viewport->update();
+ update_viewport();
return true;
}
@@ -1293,18 +1298,28 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
// Start rotation
if (drag_type == DRAG_NONE) {
if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
- drag_selection = _get_edited_canvas_items();
- if (drag_selection.size() > 0 && ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) {
- drag_type = DRAG_ROTATE;
- drag_from = transform.affine_inverse().xform(b->get_position());
- CanvasItem *canvas_item = drag_selection[0];
- if (canvas_item->_edit_use_pivot()) {
- drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot());
- } else {
- drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin();
+ if ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+
+ // Remove not movable nodes
+ for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+ if (!_is_node_movable(E->get(), true))
+ selection.erase(E);
+ }
+
+ drag_selection = selection;
+ if (drag_selection.size() > 0) {
+ drag_type = DRAG_ROTATE;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ CanvasItem *canvas_item = drag_selection[0];
+ if (canvas_item->_edit_use_pivot()) {
+ drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot());
+ } else {
+ drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin();
+ }
+ _save_canvas_item_state(drag_selection);
+ return true;
}
- _save_canvas_item_state(drag_selection);
- return true;
}
}
}
@@ -1367,7 +1382,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
Control *control = Object::cast_to<Control>(selection[0]);
- if (control && !Object::cast_to<Container>(control->get_parent())) {
+ if (control && _is_node_movable(control)) {
Vector2 anchor_pos[4];
anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP));
anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP));
@@ -1486,7 +1501,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
- if (canvas_item->_edit_use_rect()) {
+ if (canvas_item->_edit_use_rect() && _is_node_movable(canvas_item)) {
Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
@@ -1654,27 +1669,30 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
- Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
- Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
- Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+ if (_is_node_movable(canvas_item)) {
- drag_type = DRAG_SCALE_BOTH;
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+ Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+ Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
- Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
- Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
- if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
- drag_type = DRAG_SCALE_X;
- }
- Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
- if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
- drag_type = DRAG_SCALE_Y;
- }
+ drag_type = DRAG_SCALE_BOTH;
- drag_from = transform.affine_inverse().xform(b->get_position());
- drag_selection = List<CanvasItem *>();
- drag_selection.push_back(canvas_item);
- _save_canvas_item_state(drag_selection);
- return true;
+ Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
+ Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+ if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
+ drag_type = DRAG_SCALE_X;
+ }
+ Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+ if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
+ drag_type = DRAG_SCALE_Y;
+ }
+
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = List<CanvasItem *>();
+ drag_selection.push_back(canvas_item);
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
}
}
}
@@ -1702,13 +1720,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_SCALE_BOTH) {
Size2 scale_factor = drag_to_local / drag_from_local;
if (uniform) {
- if (ABS(offset.x) > ABS(offset.y)) {
- scale.x *= scale_factor.x;
- scale.y = scale.x * ratio;
- } else {
- scale.y *= scale_factor.y;
- scale.x = scale.y / ratio;
- }
+ scale *= (scale_factor.x + scale_factor.y) / 2.0;
} else {
scale *= scale_factor;
}
@@ -1759,12 +1771,22 @@ 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() == BUTTON_LEFT && b->is_pressed()) {
- List<CanvasItem *> selection = _get_edited_canvas_items();
- if (((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) && selection.size() > 0) {
- drag_type = DRAG_MOVE;
- drag_from = transform.affine_inverse().xform(b->get_position());
- drag_selection = selection;
- _save_canvas_item_state(drag_selection);
+ if ((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+
+ // Remove not movable nodes
+ for (int i = 0; i < selection.size(); i++) {
+ if (!_is_node_movable(selection[i], true)) {
+ selection.erase(selection[i]);
+ }
+ }
+
+ if (selection.size() > 0) {
+ drag_type = DRAG_MOVE;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = selection;
+ _save_canvas_item_state(drag_selection);
+ }
return true;
}
}
@@ -2014,7 +2036,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
Vector<_SelectResult> selection;
// Retrieve the items
- _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0);
+ _get_canvas_items_at_pos(click, selection);
// Retrieve the bones
_get_bones_at_pos(click, selection);
@@ -2042,10 +2064,19 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Drag the node(s) if requested
List<CanvasItem *> selection = _get_edited_canvas_items();
- drag_type = DRAG_MOVE;
- drag_selection = selection;
- drag_from = click;
- _save_canvas_item_state(drag_selection);
+ // Remove not movable nodes
+ for (int i = 0; i < selection.size(); i++) {
+ if (!_is_node_movable(selection[i], true)) {
+ selection.erase(selection[i]);
+ }
+ }
+
+ if (selection.size() > 0) {
+ drag_type = DRAG_MOVE;
+ drag_selection = selection;
+ drag_from = click;
+ _save_canvas_item_state(drag_selection);
+ }
}
// Select the item
return true;
@@ -2178,6 +2209,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
//printf("Zoom or pan\n");
} else if ((accepted = _gui_input_select(p_event))) {
//printf("Selection\n");
+ } else {
+ //printf("Not accepted\n");
}
if (accepted)
@@ -2708,12 +2741,12 @@ void CanvasItemEditor::_draw_selection() {
// Draw control-related helpers
Control *control = Object::cast_to<Control>(canvas_item);
- if (control) {
+ if (control && _is_node_movable(control)) {
_draw_control_helpers(control);
}
// Draw the resize handles
- if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) {
+ if (tool == TOOL_SELECT && canvas_item->_edit_use_rect() && _is_node_movable(canvas_item)) {
Rect2 rect = canvas_item->_edit_get_rect();
Vector2 endpoints[4] = {
xform.xform(rect.position),
@@ -2741,40 +2774,41 @@ void CanvasItemEditor::_draw_selection() {
bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT);
if ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
-
- Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
- Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
-
- Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
- bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom;
-
- if (drag_type == DRAG_SCALE_X) {
- scale_factor.x += offset.x;
- if (uniform) {
- scale_factor.y += offset.x;
- }
- } else if (drag_type == DRAG_SCALE_Y) {
- scale_factor.y -= offset.y;
- if (uniform) {
- scale_factor.x -= offset.y;
+ if (_is_node_movable(canvas_item)) {
+ Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+ Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+
+ Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
+ bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom;
+
+ if (drag_type == DRAG_SCALE_X) {
+ scale_factor.x += offset.x;
+ if (uniform) {
+ scale_factor.y += offset.x;
+ }
+ } else if (drag_type == DRAG_SCALE_Y) {
+ scale_factor.y -= offset.y;
+ if (uniform) {
+ scale_factor.x -= offset.y;
+ }
}
- }
- //scale_factor *= zoom;
+ //scale_factor *= zoom;
- viewport->draw_set_transform_matrix(simple_xform);
- Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
- Color x_axis_color(1.0, 0.4, 0.4, 0.6);
- viewport->draw_rect(x_handle_rect, x_axis_color);
- viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color);
+ viewport->draw_set_transform_matrix(simple_xform);
+ Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+ Color x_axis_color(1.0, 0.4, 0.4, 0.6);
+ viewport->draw_rect(x_handle_rect, x_axis_color);
+ viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color);
- Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
- Color y_axis_color(0.4, 1.0, 0.4, 0.6);
- viewport->draw_rect(y_handle_rect, y_axis_color);
- viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color);
+ Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+ Color y_axis_color(0.4, 1.0, 0.4, 0.6);
+ viewport->draw_rect(y_handle_rect, y_axis_color);
+ viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color);
- viewport->draw_set_transform_matrix(viewport->get_transform());
+ viewport->draw_set_transform_matrix(viewport->get_transform());
+ }
}
}
}
@@ -2950,7 +2984,7 @@ void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Trans
_draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform);
}
- if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || !_is_node_editable(canvas_item))) {
+ if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || _is_node_locked(canvas_item))) {
Transform2D xform = transform * canvas_xform * parent_xform;
// Draw the node's position
@@ -3139,6 +3173,8 @@ void CanvasItemEditor::_draw_viewport() {
group_button->set_disabled(selection.empty());
ungroup_button->set_visible(all_group);
+ info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
+
_draw_grid();
_draw_selection();
_draw_axis();
@@ -3168,6 +3204,11 @@ void CanvasItemEditor::_draw_viewport() {
_draw_hover();
}
+void CanvasItemEditor::update_viewport() {
+ _update_scrollbars();
+ viewport->update();
+}
+
void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
@@ -3362,11 +3403,14 @@ void CanvasItemEditor::_notification(int p_what) {
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
- drag_type = DRAG_NONE;
+ Array selection = editor_selection->get_selected_nodes();
+ if (selection.size() != 1 || (Node *)selection[0] != p_canvas_item) {
+ drag_type = DRAG_NONE;
- // Clear the selection
- editor_selection->clear(); //_clear_canvas_items();
- editor_selection->add_node(p_canvas_item);
+ // Clear the selection
+ editor_selection->clear(); //_clear_canvas_items();
+ editor_selection->add_node(p_canvas_item);
+ }
}
void CanvasItemEditor::_queue_update_bone_list() {
@@ -3498,6 +3542,35 @@ void CanvasItemEditor::_update_scrollbars() {
updating_scroll = false;
}
+void CanvasItemEditor::_popup_warning_depop(Control *p_control) {
+ ERR_FAIL_COND(!popup_temporarily_timers.has(p_control));
+
+ Timer *timer = popup_temporarily_timers[p_control];
+ p_control->hide();
+ remove_child(timer);
+ popup_temporarily_timers.erase(p_control);
+ memdelete(timer);
+ info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
+}
+
+void CanvasItemEditor::_popup_warning_temporarily(Control *p_control, const float p_duration) {
+ Timer *timer;
+ if (!popup_temporarily_timers.has(p_control)) {
+ timer = memnew(Timer);
+ timer->connect("timeout", this, "_popup_warning_depop", varray(p_control));
+ timer->set_one_shot(true);
+ add_child(timer);
+
+ popup_temporarily_timers[p_control] = timer;
+ } else {
+ timer = popup_temporarily_timers[p_control];
+ }
+
+ timer->start(p_duration);
+ p_control->show();
+ info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
+}
+
void CanvasItemEditor::_update_scroll(float) {
if (updating_scroll)
@@ -3574,8 +3647,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
view_offset.x = Math::round(view_offset.x + ofs.x);
view_offset.y = Math::round(view_offset.y + ofs.y);
- _update_scrollbars();
- viewport->update();
+ update_viewport();
}
void CanvasItemEditor::_button_zoom_minus() {
@@ -4172,8 +4244,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center);
view_offset.x -= offset.x / zoom;
view_offset.y -= offset.y / zoom;
- _update_scrollbars();
- viewport->update();
+ update_viewport();
} else { // VIEW_FRAME_TO_SELECTION
@@ -4206,10 +4277,11 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
-
+ ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop);
ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
+ ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
ADD_SIGNAL(MethodInfo("item_group_status_changed"));
@@ -4391,7 +4463,22 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
viewport->update();
}
+void CanvasItemEditor::add_control_to_info_overlay(Control *p_control) {
+ ERR_FAIL_COND(!p_control);
+
+ p_control->set_h_size_flags(p_control->get_h_size_flags() & ~Control::SIZE_EXPAND_FILL);
+ info_overlay->add_child(p_control);
+ info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
+}
+
+void CanvasItemEditor::remove_control_from_info_overlay(Control *p_control) {
+
+ info_overlay->remove_child(p_control);
+ info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
+}
+
void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) {
+ ERR_FAIL_COND(!p_control);
hb->add_child(p_control);
}
@@ -4417,6 +4504,46 @@ void CanvasItemEditor::focus_selection() {
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
+ key_pos = true;
+ key_rot = true;
+ key_scale = false;
+
+ show_grid = false;
+ show_origin = true;
+ show_viewport = true;
+ show_helpers = false;
+ show_rulers = true;
+ show_guides = true;
+ show_edit_locks = true;
+ zoom = 1;
+ view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH);
+ previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen
+ grid_offset = Point2();
+ grid_step = Point2(10, 10);
+ grid_step_multiplier = 0;
+ snap_rotation_offset = 0;
+ snap_rotation_step = 15 / (180 / Math_PI);
+ snap_active = false;
+ snap_node_parent = true;
+ snap_node_anchors = true;
+ snap_node_sides = true;
+ snap_node_center = true;
+ snap_other_nodes = true;
+ snap_grid = true;
+ snap_guides = true;
+ snap_rotation = false;
+ snap_pixel = false;
+
+ skeleton_show_bones = true;
+
+ drag_type = DRAG_NONE;
+ drag_from = Vector2();
+ drag_to = Vector2();
+ dragged_guide_pos = Point2();
+ dragged_guide_index = -1;
+
+ bone_last_frame = 0;
+
bone_list_dirty = false;
tool = TOOL_SELECT;
undo_redo = p_editor->get_undo_redo();
@@ -4460,6 +4587,28 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->connect("draw", this, "_draw_viewport");
viewport->connect("gui_input", this, "_gui_input_viewport");
+ info_overlay = memnew(VBoxContainer);
+ info_overlay->set_anchors_and_margins_preset(Control::PRESET_BOTTOM_LEFT);
+ info_overlay->set_margin(MARGIN_LEFT, 10);
+ info_overlay->set_margin(MARGIN_BOTTOM, -15);
+ info_overlay->set_v_grow_direction(Control::GROW_DIRECTION_BEGIN);
+ info_overlay->add_constant_override("separation", 10);
+ viewport_scrollable->add_child(info_overlay);
+
+ Theme *info_overlay_theme = memnew(Theme);
+ info_overlay_theme->copy_default_theme();
+ info_overlay->set_theme(info_overlay_theme);
+
+ StyleBoxFlat *info_overlay_label_stylebox = memnew(StyleBoxFlat);
+ info_overlay_label_stylebox->set_bg_color(Color(0.0, 0.0, 0.0, 0.2));
+ info_overlay_label_stylebox->set_expand_margin_size_all(4);
+ info_overlay_theme->set_stylebox("normal", "Label", info_overlay_label_stylebox);
+
+ warning_child_of_container = memnew(Label);
+ warning_child_of_container->hide();
+ warning_child_of_container->set_text("Warning: Children of a container get their position and size determined only by their parent");
+ add_control_to_info_overlay(warning_child_of_container);
+
h_scroll = memnew(HScrollBar);
viewport->add_child(h_scroll);
h_scroll->connect("value_changed", this, "_update_scroll");
@@ -4608,6 +4757,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
skeleton_menu = memnew(MenuButton);
hb->add_child(skeleton_menu);
+ skeleton_menu->set_tooltip(TTR("Skeleton Options"));
p = skeleton_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
@@ -4716,49 +4866,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY);
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE);
- key_pos = true;
- key_rot = true;
- key_scale = false;
-
- show_grid = false;
- show_origin = true;
- show_viewport = true;
- show_helpers = false;
- show_rulers = true;
- show_guides = true;
- show_edit_locks = true;
- zoom = 1;
- view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH);
- previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen
- grid_offset = Point2();
- grid_step = Point2(10, 10);
- grid_step_multiplier = 0;
- snap_rotation_offset = 0;
- snap_rotation_step = 15 / (180 / Math_PI);
- snap_active = false;
- snap_node_parent = true;
- snap_node_anchors = true;
- snap_node_sides = true;
- snap_node_center = true;
- snap_other_nodes = true;
- snap_grid = true;
- snap_guides = true;
- snap_rotation = false;
- snap_pixel = false;
- skeleton_show_bones = true;
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
singleton = this;
set_process_unhandled_key_input(true);
- drag_type = DRAG_NONE;
- drag_from = Vector2();
- drag_to = Vector2();
- dragged_guide_pos = Point2();
- dragged_guide_index = -1;
-
- bone_last_frame = 0;
-
// Update the menus' checkboxes
call_deferred("set_state", get_state());
}
@@ -4958,19 +5070,13 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
editor_data->get_undo_redo().add_do_property(child, "polygon", list);
}
- // locate at preview position
- Point2 pos = Point2(0, 0);
- if (parent && parent->has_method("get_global_position")) {
- pos = parent->call("get_global_position");
- }
- Transform2D trans = canvas->get_canvas_transform();
- Point2 target_position = (p_point - trans.get_origin()) / trans.get_scale().x - pos;
- if (default_type == "Polygon2D" || default_type == "TouchScreenButton" || default_type == "TextureRect" || default_type == "NinePatchRect") {
- target_position -= texture_size / 2;
- }
+ // Compute the global position
+ Transform2D xform = canvas_item_editor->get_canvas_transform();
+ Point2 target_position = xform.affine_inverse().xform(p_point);
+
// there's nothing to be used as source position so snapping will work as absolute if enabled
- target_position = canvas->snap_point(target_position);
- editor_data->get_undo_redo().add_do_method(child, "set_position", target_position);
+ target_position = canvas_item_editor->snap_point(target_position);
+ editor_data->get_undo_redo().add_do_method(child, "set_global_position", target_position);
}
bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
@@ -5005,8 +5111,8 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
if (parent_ci) {
- Vector2 target_pos = canvas->get_canvas_transform().affine_inverse().xform(p_point);
- target_pos = canvas->snap_point(target_pos);
+ Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point);
+ target_pos = canvas_item_editor->snap_point(target_pos);
target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos);
editor_data->get_undo_redo().add_do_method(instanced_scene, "set_position", target_pos);
}
@@ -5126,7 +5232,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
if (!preview_node->get_parent()) { // create preview only once
_create_preview(files);
}
- Transform2D trans = canvas->get_canvas_transform();
+ Transform2D trans = canvas_item_editor->get_canvas_transform();
preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
label->set_text(vformat(TTR("Adding %s..."), default_type));
}
@@ -5221,7 +5327,7 @@ void CanvasItemEditorViewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_mouse_exit"), &CanvasItemEditorViewport::_on_mouse_exit);
}
-CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas) {
+CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas_item_editor) {
default_type = "Sprite";
// Node2D
types.push_back("Sprite");
@@ -5236,7 +5342,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
target_node = NULL;
editor = p_node;
editor_data = editor->get_scene_tree_dock()->get_editor_data();
- canvas = p_canvas;
+ canvas_item_editor = p_canvas_item_editor;
preview_node = memnew(Node2D);
accept = memnew(AcceptDialog);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 4f8cc6ab5e..207e57dbe2 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -64,7 +64,10 @@ public:
Dictionary undo_state;
- CanvasItemEditorSelectedItem() { prev_rot = 0; }
+ CanvasItemEditorSelectedItem() :
+ prev_anchors() {
+ prev_rot = 0;
+ }
};
class CanvasItemEditor : public VBoxContainer {
@@ -220,6 +223,11 @@ private:
ToolButton *zoom_reset;
ToolButton *zoom_plus;
+ Map<Control *, Timer *> popup_temporarily_timers;
+
+ Label *warning_child_of_container;
+ VBoxContainer *info_overlay;
+
Transform2D transform;
bool show_grid;
bool show_rulers;
@@ -369,9 +377,10 @@ private:
Ref<ShortCut> multiply_grid_step_shortcut;
Ref<ShortCut> divide_grid_step_shortcut;
- bool _is_node_editable(const Node *p_node);
- void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0);
+ bool _is_node_locked(const Node *p_node);
+ bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
+ void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items);
void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items);
void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
@@ -477,6 +486,9 @@ private:
void _update_bone_list();
void _tree_changed(Node *);
+ void _popup_warning_temporarily(Control *p_control, const float p_duration);
+ void _popup_warning_depop(Control *p_control);
+
friend class CanvasItemEditorPlugin;
protected:
@@ -542,11 +554,16 @@ public:
void add_control_to_menu_panel(Control *p_control);
void remove_control_from_menu_panel(Control *p_control);
+ void add_control_to_info_overlay(Control *p_control);
+ void remove_control_from_info_overlay(Control *p_control);
+
HSplitContainer *get_palette_split();
VSplitContainer *get_bottom_split();
Control *get_viewport_control() { return viewport; }
+ void update_viewport();
+
Tool get_current_tool() { return tool; }
void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
@@ -591,7 +608,7 @@ class CanvasItemEditorViewport : public Control {
EditorNode *editor;
EditorData *editor_data;
- CanvasItemEditor *canvas;
+ CanvasItemEditor *canvas_item_editor;
Node2D *preview_node;
AcceptDialog *accept;
WindowDialog *selector;
@@ -625,7 +642,7 @@ public:
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);
- CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas);
+ CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas_item_editor);
~CanvasItemEditorViewport();
};
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index d1a94f5b49..313ba1ee6b 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -129,7 +129,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
capsule->set_height(parameter * 2 - capsule->get_radius() * 2);
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
} break;
@@ -138,7 +138,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
Ref<CircleShape2D> circle = node->get_shape();
circle->set_radius(p_point.length());
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} break;
@@ -160,7 +160,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
line->set_normal(p_point.normalized());
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
} break;
@@ -170,7 +170,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
ray->set_length(Math::abs(p_point.y));
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} break;
@@ -183,7 +183,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
rect->set_extents(extents.abs());
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
} break;
@@ -198,16 +198,16 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
seg->set_b(p_point);
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
} break;
}
+ node->get_shape()->_change_notify();
}
void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
- Control *c = canvas_item_editor->get_viewport_control();
undo_redo->create_action(TTR("Set Handle"));
switch (shape_type) {
@@ -216,14 +216,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
if (idx == 0) {
undo_redo->add_do_method(capsule.ptr(), "set_radius", capsule->get_radius());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(capsule.ptr(), "set_radius", p_org);
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
} else if (idx == 1) {
undo_redo->add_do_method(capsule.ptr(), "set_height", capsule->get_height());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(capsule.ptr(), "set_height", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
@@ -232,9 +232,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<CircleShape2D> circle = node->get_shape();
undo_redo->add_do_method(circle.ptr(), "set_radius", circle->get_radius());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(circle.ptr(), "set_radius", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -251,14 +251,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
if (idx == 0) {
undo_redo->add_do_method(line.ptr(), "set_d", line->get_d());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(line.ptr(), "set_d", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else {
undo_redo->add_do_method(line.ptr(), "set_normal", line->get_normal());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(line.ptr(), "set_normal", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
@@ -267,9 +267,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<RayShape2D> ray = node->get_shape();
undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -277,9 +277,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<RectangleShape2D> rect = node->get_shape();
undo_redo->add_do_method(rect.ptr(), "set_extents", rect->get_extents());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(rect.ptr(), "set_extents", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} break;
@@ -287,14 +287,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
Ref<SegmentShape2D> seg = node->get_shape();
if (idx == 0) {
undo_redo->add_do_method(seg.ptr(), "set_a", seg->get_a());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(seg.ptr(), "set_a", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else if (idx == 1) {
undo_redo->add_do_method(seg.ptr(), "set_b", seg->get_b());
- undo_redo->add_do_method(c, "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
undo_redo->add_undo_method(seg.ptr(), "set_b", p_org);
- undo_redo->add_undo_method(c, "update");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
} break;
@@ -411,7 +411,7 @@ void CollisionShape2DEditor::_get_current_shape_type() {
shape_type = -1;
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
@@ -538,7 +538,7 @@ void CollisionShape2DEditor::edit(Node *p_node) {
node = NULL;
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
void CollisionShape2DEditor::_bind_methods() {
diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp
index 06da64b181..5fc5cad1ef 100644
--- a/editor/plugins/gi_probe_editor_plugin.cpp
+++ b/editor/plugins/gi_probe_editor_plugin.cpp
@@ -90,7 +90,7 @@ void GIProbeEditorPlugin::_bind_methods() {
GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) {
editor = p_node;
- bake = memnew(Button);
+ bake = memnew(ToolButton);
bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
bake->set_text(TTR("Bake GI Probe"));
bake->hide();
diff --git a/editor/plugins/gi_probe_editor_plugin.h b/editor/plugins/gi_probe_editor_plugin.h
index 017e9bd743..1b3b63f227 100644
--- a/editor/plugins/gi_probe_editor_plugin.h
+++ b/editor/plugins/gi_probe_editor_plugin.h
@@ -42,7 +42,7 @@ class GIProbeEditorPlugin : public EditorPlugin {
GIProbe *gi_probe;
- Button *bake;
+ ToolButton *bake;
EditorNode *editor;
static EditorProgress *tmp_progress;
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 186e66f980..8df40232b0 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -350,8 +350,6 @@ ItemListEditor::ItemListEditor() {
selected_idx = -1;
- add_child(memnew(VSeparator));
-
toolbar_button = memnew(ToolButton);
toolbar_button->set_text(TTR("Items"));
add_child(toolbar_button);
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp
index 2f2e1dae81..6a16cf0989 100644
--- a/editor/plugins/light_occluder_2d_editor_plugin.cpp
+++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -57,7 +57,7 @@ void LightOccluder2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = NULL;
hide();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
}
@@ -88,8 +88,8 @@ void LightOccluder2DEditor::_wip_close(bool p_closed) {
undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_closed", node->get_occluder_polygon()->is_closed());
undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_closed", p_closed);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
wip.clear();
wip_active = false;
@@ -139,7 +139,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
wip.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
edited_point = 1;
return true;
} else {
@@ -158,7 +158,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
wip.push_back(cpoint);
edited_point = wip.size();
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
//add wip point
@@ -183,8 +183,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", poly);
poly.push_back(cpoint);
undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return true;
}
@@ -217,7 +217,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
edited_point = closest_idx + 1;
edited_point_pos = xform.affine_inverse().xform(closest_pos);
node->get_occluder_polygon()->set_polygon(Variant(poly));
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
} else {
@@ -244,7 +244,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
pre_move_edit = poly;
edited_point = closest_idx;
edited_point_pos = xform.affine_inverse().xform(closest_pos);
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
}
@@ -259,8 +259,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly);
undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", pre_move_edit);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
edited_point = -1;
@@ -290,8 +290,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", poly);
poly.remove(closest_idx);
undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return true;
}
@@ -312,7 +312,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
cpoint = canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
}
@@ -369,7 +369,7 @@ void LightOccluder2DEditor::edit(Node *p_collision_polygon) {
wip.clear();
wip_active = false;
edited_point = -1;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
} else {
node = NULL;
}
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index 5dcbca2ed6..ab94258c44 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "canvas_item_editor_plugin.h"
#include "core/io/image_loader.h"
+#include "scene/2d/cpu_particles_2d.h"
#include "scene/gui/separator.h"
#include "scene/resources/particles_material.h"
@@ -82,6 +83,25 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) {
emission_mask->popup_centered_minsize();
} break;
+ case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: {
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+
+ CPUParticles2D *cpu_particles = memnew(CPUParticles2D);
+ cpu_particles->convert_from_particles(particles);
+ cpu_particles->set_name(particles->get_name());
+ cpu_particles->set_transform(particles->get_transform());
+ cpu_particles->set_visible(particles->is_visible());
+ cpu_particles->set_pause_mode(particles->get_pause_mode());
+
+ undo_redo->create_action("Replace Particles by CPUParticles");
+ undo_redo->add_do_method(particles, "replace_by", cpu_particles);
+ undo_redo->add_undo_method(cpu_particles, "replace_by", particles);
+ undo_redo->add_do_reference(cpu_particles);
+ undo_redo->add_undo_reference(particles);
+ undo_redo->commit_action();
+
+ } break;
}
}
@@ -355,6 +375,8 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
menu->get_popup()->add_separator();
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
// menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
+ menu->get_popup()->add_separator();
+ menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES);
menu->set_text(TTR("Particles"));
toolbar->add_child(menu);
diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h
index 71ca8ef499..eaa96d84e9 100644
--- a/editor/plugins/particles_2d_editor_plugin.h
+++ b/editor/plugins/particles_2d_editor_plugin.h
@@ -46,7 +46,8 @@ class Particles2DEditorPlugin : public EditorPlugin {
MENU_GENERATE_VISIBILITY_RECT,
MENU_LOAD_EMISSION_MASK,
- MENU_CLEAR_EMISSION_MASK
+ MENU_CLEAR_EMISSION_MASK,
+ MENU_OPTION_CONVERT_TO_CPU_PARTICLES
};
enum EmissionMode {
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index 6b41946918..f2dfae7a9f 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -453,6 +453,7 @@ void ParticlesEditor::_bind_methods() {
ParticlesEditor::ParticlesEditor() {
+ node = NULL;
particles_editor_hb = memnew(HBoxContainer);
SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
options = memnew(MenuButton);
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 88b3194490..c67c96798a 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -130,8 +130,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Remove Point from Curve"));
undo_redo->add_do_method(curve.ptr(), "remove_point", i);
undo_redo->add_undo_method(curve.ptr(), "add_point", curve->get_point_position(i), curve->get_point_in(i), curve->get_point_out(i), i);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return true;
} else if (dist_to_p_out < grab_threshold) {
@@ -139,8 +139,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Remove Out-Control from Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_out", i, Vector2());
undo_redo->add_undo_method(curve.ptr(), "set_point_out", i, curve->get_point_out(i));
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return true;
} else if (dist_to_p_in < grab_threshold) {
@@ -148,8 +148,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Remove In-Control from Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_in", i, Vector2());
undo_redo->add_undo_method(curve.ptr(), "set_point_in", i, curve->get_point_in(i));
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return true;
}
@@ -165,8 +165,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
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());
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
action = ACTION_MOVING_POINT;
@@ -174,7 +174,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
moving_from = curve->get_point_position(action_point);
moving_screen_from = gpoint;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
@@ -196,8 +196,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move Point in Curve"));
undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from);
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
} break;
@@ -212,8 +212,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
}
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
} break;
@@ -228,8 +228,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
}
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
} break;
@@ -280,7 +280,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} break;
}
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
return true;
}
}
@@ -331,7 +331,7 @@ void Path2DEditor::_node_visibility_changed() {
if (!node)
return;
- canvas_item_editor->get_viewport_control()->update();
+ canvas_item_editor->update_viewport();
}
void Path2DEditor::edit(Node *p_path2d) {
@@ -406,8 +406,8 @@ void Path2DEditor::_mode_selected(int p_mode) {
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());
- undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
- undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
+ undo_redo->add_do_method(canvas_item_editor, "update_viewport");
+ undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
undo_redo->commit_action();
return;
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 7e11c4a253..ae6fa8e6bb 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -50,8 +50,7 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("name_changed"));
ADD_SIGNAL(MethodInfo("edited_script_changed"));
- ADD_SIGNAL(MethodInfo("request_help_search", PropertyInfo(Variant::STRING, "topic")));
- ADD_SIGNAL(MethodInfo("request_help_index"));
+ ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic")));
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")));
@@ -210,6 +209,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) {
search_box->set_right_icon(get_icon("Search", "EditorIcons"));
search_box->set_clear_button_enabled(true);
} break;
+ case NOTIFICATION_EXIT_TREE: {
+ disconnect("confirmed", this, "_confirmed");
+ } break;
}
}
@@ -490,7 +492,7 @@ void ScriptEditor::_open_recent_script(int p_idx) {
edit(text_file, true);
return;
}
- // if it's a path then its most likely a deleted file not help
+ // if it's a path then it's most likely a deleted file not help
} else if (path.find("::") != -1) {
// built-in script
Ref<Script> script = ResourceLoader::load(path);
@@ -972,13 +974,9 @@ void ScriptEditor::_menu_option(int p_option) {
help_search_dialog->popup_dialog();
} break;
- case SEARCH_CLASSES: {
-
- help_index->popup_dialog();
- } break;
case SEARCH_WEBSITE: {
- OS::get_singleton()->shell_open("http://docs.godotengine.org/");
+ OS::get_singleton()->shell_open("https://docs.godotengine.org/");
} break;
case WINDOW_NEXT: {
@@ -1203,12 +1201,6 @@ void ScriptEditor::_menu_option(int p_option) {
if (help) {
switch (p_option) {
-
- case SEARCH_CLASSES: {
-
- help_index->popup_dialog();
- help_index->call_deferred("select_class", help->get_class());
- } break;
case HELP_SEARCH_FIND: {
help->popup_search();
} break;
@@ -1315,7 +1307,6 @@ void ScriptEditor::_notification(int p_what) {
EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");
help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
site_search->set_icon(get_icon("Instance", "EditorIcons"));
- class_search->set_icon(get_icon("ClassList", "EditorIcons"));
script_forward->set_icon(get_icon("Forward", "EditorIcons"));
script_back->set_icon(get_icon("Back", "EditorIcons"));
@@ -1327,7 +1318,6 @@ void ScriptEditor::_notification(int p_what) {
get_tree()->connect("tree_changed", this, "_tree_changed");
editor->get_inspector_dock()->connect("request_help", this, "_request_help");
editor->connect("request_help_search", this, "_help_search");
- editor->connect("request_help_index", this, "_help_index");
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -1347,7 +1337,6 @@ void ScriptEditor::_notification(int p_what) {
help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
site_search->set_icon(get_icon("Instance", "EditorIcons"));
- class_search->set_icon(get_icon("ClassList", "EditorIcons"));
script_forward->set_icon(get_icon("Forward", "EditorIcons"));
script_back->set_icon(get_icon("Back", "EditorIcons"));
@@ -1363,7 +1352,9 @@ void ScriptEditor::_notification(int p_what) {
if (is_visible()) {
find_in_files_button->show();
} else {
- find_in_files->hide();
+ if (find_in_files->is_visible_in_tree()) {
+ editor->hide_bottom_panel();
+ }
find_in_files_button->hide();
}
@@ -2001,7 +1992,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
_save_layout();
se->connect("name_changed", this, "_update_script_names");
se->connect("edited_script_changed", this, "_script_changed");
- se->connect("request_help_search", this, "_help_search");
+ se->connect("request_help", this, "_help_search");
se->connect("request_open_script_at_line", this, "_goto_script_line");
se->connect("go_to_help", this, "_help_class_goto");
se->connect("request_save_history", this, "_save_history");
@@ -2728,10 +2719,6 @@ void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
auto_reload_running_scripts = p_enabled;
}
-void ScriptEditor::_help_index(String p_text) {
- help_index->popup_dialog();
-}
-
void ScriptEditor::_help_search(String p_text) {
help_search_dialog->popup_dialog(p_text);
}
@@ -2806,8 +2793,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
find_in_files->set_with_replace(with_replace);
find_in_files->start_search();
- find_in_files_button->set_pressed(true);
- find_in_files->show();
+ editor->make_bottom_panel_item_visible(find_in_files);
}
void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
@@ -2838,7 +2824,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line);
ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2);
ClassDB::bind_method("_help_search", &ScriptEditor::_help_search);
- ClassDB::bind_method("_help_index", &ScriptEditor::_help_index);
ClassDB::bind_method("_save_history", &ScriptEditor::_save_history);
ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
@@ -2891,7 +2876,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
restoring_layout = false;
waiting_update_names = false;
pending_auto_reload = false;
- auto_reload_running_scripts = false;
+ auto_reload_running_scripts = true;
members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/show_members_overview");
help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
editor = p_editor;
@@ -2912,7 +2897,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_list = memnew(ItemList);
list_split->add_child(script_list);
- script_list->set_custom_minimum_size(Size2(150 * EDSCALE, 90)); //need to give a bit of limit to avoid it from disappearing
+ script_list->set_custom_minimum_size(Size2(150, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
script_list->set_v_size_flags(SIZE_EXPAND_FILL);
script_split->set_split_offset(140);
_sort_list_on_update = true;
@@ -2951,7 +2936,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
overview_vbox->add_child(members_overview);
members_overview->set_allow_reselect(true);
- members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
+ members_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
members_overview->set_allow_rmb_select(true);
members_overview->set_drag_forwarding(this);
@@ -2959,12 +2944,12 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
help_overview = memnew(ItemList);
overview_vbox->add_child(help_overview);
help_overview->set_allow_reselect(true);
- help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
+ help_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
help_overview->set_v_size_flags(SIZE_EXPAND_FILL);
tab_container = memnew(TabContainer);
tab_container->set_tabs_visible(false);
- tab_container->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
+ tab_container->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
script_split->add_child(tab_container);
tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -2997,7 +2982,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show In File System")), SHOW_IN_FILE_SYSTEM);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
@@ -3073,12 +3058,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
menu_hb->add_child(site_search);
site_search->set_tooltip(TTR("Open Godot online documentation"));
- class_search = memnew(ToolButton);
- class_search->set_text(TTR("Classes"));
- class_search->connect("pressed", this, "_menu_option", varray(SEARCH_CLASSES));
- menu_hb->add_child(class_search);
- class_search->set_tooltip(TTR("Search the class hierarchy."));
-
help_search = memnew(ToolButton);
help_search->set_text(TTR("Search Help"));
help_search->connect("pressed", this, "_menu_option", varray(SEARCH_HELP));
@@ -3164,17 +3143,12 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(help_search_dialog);
help_search_dialog->connect("go_to_help", this, "_help_class_goto");
- help_index = memnew(EditorHelpIndex);
- add_child(help_index);
- help_index->connect("open_class", this, "_help_class_open");
-
find_in_files_dialog = memnew(FindInFilesDialog);
find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
add_child(find_in_files_dialog);
find_in_files = memnew(FindInFilesPanel);
find_in_files_button = editor->add_bottom_panel_item(TTR("Search Results"), find_in_files);
- find_in_files_button->set_tooltip(TTR("Search in files"));
find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 28c07393f7..4be5345aaa 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -34,6 +34,7 @@
#include "core/script_language.h"
#include "editor/code_editor.h"
#include "editor/editor_help.h"
+#include "editor/editor_help_search.h"
#include "editor/editor_plugin.h"
#include "editor/script_create_dialog.h"
#include "scene/gui/item_list.h"
@@ -155,7 +156,6 @@ class ScriptEditor : public PanelContainer {
DEBUG_SHOW_KEEP_OPEN,
DEBUG_WITH_EXTERNAL_EDITOR,
SEARCH_HELP,
- SEARCH_CLASSES,
SEARCH_WEBSITE,
HELP_SEARCH_FIND,
HELP_SEARCH_FIND_NEXT,
@@ -200,7 +200,6 @@ class ScriptEditor : public PanelContainer {
Button *help_search;
Button *site_search;
- Button *class_search;
EditorHelpSearch *help_search_dialog;
ItemList *script_list;
@@ -254,7 +253,7 @@ class ScriptEditor : public PanelContainer {
Vector<ScriptHistory> history;
int history_pos;
- EditorHelpIndex *help_index;
+ Vector<String> previous_scripts;
void _tab_changed(int p_which);
void _menu_option(int p_option);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 9b968c3523..e6bb8b24a9 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -179,7 +179,7 @@ void ScriptTextEditor::_load_theme_settings() {
text_edit->add_color_override("search_result_border_color", search_result_border_color);
text_edit->add_color_override("symbol_color", symbol_color);
- text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4));
+ text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6));
colors_cache.symbol_color = symbol_color;
colors_cache.keyword_color = keyword_color;
@@ -443,6 +443,7 @@ void ScriptTextEditor::_validate_script() {
if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc, &warnings, &safe_lines)) {
String error_text = "error(" + itos(line) + "," + itos(col) + "): " + errortxt;
code_editor->set_error(error_text);
+ code_editor->set_error_pos(line - 1, col - 1);
} else {
code_editor->set_error("");
line = -1;
@@ -773,7 +774,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case EDIT_CLONE_DOWN: {
- code_editor->code_lines_down();
+ code_editor->clone_lines_down();
} break;
case EDIT_TOGGLE_FOLD_LINE: {
@@ -817,6 +818,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (tx->get_selection_to_column() == 0)
end -= 1;
+ int col_to = tx->get_selection_to_column();
+ int cursor_pos = tx->cursor_get_column();
+
// Check if all lines in the selected block are commented
bool is_commented = true;
for (int i = begin; i <= end; i++) {
@@ -839,19 +843,42 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
tx->set_line(i, line_text);
}
+
+ // Adjust selection & cursor position.
+ int offset = is_commented ? -1 : 1;
+ int col_from = tx->get_selection_from_column() > 0 ? tx->get_selection_from_column() + offset : 0;
+
+ if (is_commented && tx->cursor_get_column() == tx->get_line(tx->cursor_get_line()).length() + 1)
+ cursor_pos += 1;
+
+ if (tx->get_selection_to_column() != 0 && col_to != tx->get_line(tx->get_selection_to_line()).length() + 1)
+ col_to += offset;
+
+ if (tx->cursor_get_column() != 0)
+ cursor_pos += offset;
+
+ tx->select(begin, col_from, tx->get_selection_to_line(), col_to);
+ tx->cursor_set_column(cursor_pos);
+
} else {
int begin = tx->cursor_get_line();
String line_text = tx->get_line(begin);
- if (line_text.begins_with(delimiter))
+ int col = tx->cursor_get_column();
+ if (line_text.begins_with(delimiter)) {
line_text = line_text.substr(delimiter.length(), line_text.length());
- else
+ col -= 1;
+ } else {
line_text = delimiter + line_text;
+ col += 1;
+ }
+
tx->set_line(begin, line_text);
+ tx->cursor_set_column(col);
}
tx->end_complex_operation();
tx->update();
- //tx->deselect();
+
} break;
case EDIT_COMPLETE: {
@@ -1023,7 +1050,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (text == "")
text = tx->get_word_under_cursor();
if (text != "") {
- emit_signal("request_help_search", text);
+ emit_signal("request_help", text);
}
} break;
case LOOKUP_SYMBOL: {
@@ -1557,8 +1584,8 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE);
#endif
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T);
- ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent To Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y);
- ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent To Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I);
ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I);
#ifdef OSX_ENABLED
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 17f93b55a1..6bc5c77df2 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -193,6 +193,7 @@ void ShaderTextEditor::_validate_script() {
if (err != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
set_error(error_text);
+ set_error_pos(sl.get_error_line() - 1, 0);
for (int i = 0; i < get_text_edit()->get_line_count(); i++)
get_text_edit()->set_line_as_marked(i, false);
get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true);
@@ -263,7 +264,7 @@ void ShaderEditor::_menu_option(int p_option) {
shader_editor->delete_lines();
} break;
case EDIT_CLONE_DOWN: {
- shader_editor->code_lines_down();
+ shader_editor->clone_lines_down();
} break;
case EDIT_TOGGLE_COMMENT: {
diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp
index e7d9f1b702..8b0beefb3e 100644
--- a/editor/plugins/skeleton_editor_plugin.cpp
+++ b/editor/plugins/skeleton_editor_plugin.cpp
@@ -151,6 +151,7 @@ void SkeletonEditor::_bind_methods() {
}
SkeletonEditor::SkeletonEditor() {
+ skeleton = NULL;
options = memnew(MenuButton);
SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index f57614b3b3..a5563c0497 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -952,8 +952,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (b->is_pressed()) {
int mod = _get_key_modifier(b);
- if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) {
- set_freelook_active(true);
+ if (!orthogonal) {
+ if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) {
+ set_freelook_active(true);
+ }
}
} else {
set_freelook_active(false);
@@ -1134,7 +1136,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (clicked) {
_select_clicked(clicked_wants_append, true);
- //clickd processing was deferred
+ // Processing was deferred.
clicked = 0;
}
@@ -1241,7 +1243,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!clicked_includes_current) {
_select_clicked(clicked_wants_append, true);
- //clickd processing was deferred
+ // Processing was deferred.
}
_compute_edit(_edit.mouse_pos);
@@ -1339,7 +1341,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
List<Node *> &selection = editor_selection->get_selected_node_list();
- bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
+ // Disable local transformation for TRANSFORM_VIEW
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
float snap = 0;
if (_edit.snap || spatial_editor->is_snap_enabled()) {
@@ -1468,7 +1471,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
List<Node *> &selection = editor_selection->get_selected_node_list();
- bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
+ // Disable local transformation for TRANSFORM_VIEW
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
float snap = 0;
if (_edit.snap || spatial_editor->is_snap_enabled()) {
@@ -1643,6 +1647,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
nav_mode = NAVIGATION_ZOOM;
} else if (freelook_active) {
nav_mode = NAVIGATION_LOOK;
+ } else if (orthogonal) {
+ nav_mode = NAVIGATION_PAN;
}
} else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) {
@@ -1793,7 +1799,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) {
_menu_option(VIEW_CENTER_TO_SELECTION);
}
- if (ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) {
+ // Orthgonal mode doesn't work in freelook.
+ if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) {
_menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL);
_update_name();
}
@@ -1823,7 +1830,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
set_message(TTR("Animation Key Inserted."));
}
- if (ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) {
+ // Freelook doesn't work in orthogonal mode.
+ if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) {
set_freelook_active(!is_freelook_active());
} else if (k->get_scancode() == KEY_ESCAPE) {
@@ -1893,7 +1901,7 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
- bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis");
+ bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
if (invert_y_axis) {
cursor.x_rot -= p_relative.y * radians_per_pixel;
@@ -1911,37 +1919,38 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con
void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
- // Freelook only works properly in perspective.
- // It could technically work in ortho, but it's terrible for a user due to FOV being a fixed width.
- if (!orthogonal) {
- real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
- real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
- bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis");
-
- // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
- Transform prev_camera_transform = to_camera_transform(cursor);
+ if (orthogonal) {
+ _nav_pan(p_event, p_relative);
+ return;
+ }
- if (invert_y_axis) {
- cursor.x_rot -= p_relative.y * radians_per_pixel;
- } else {
- cursor.x_rot += p_relative.y * radians_per_pixel;
- }
- cursor.y_rot += p_relative.x * radians_per_pixel;
- if (cursor.x_rot > Math_PI / 2.0)
- cursor.x_rot = Math_PI / 2.0;
- if (cursor.x_rot < -Math_PI / 2.0)
- cursor.x_rot = -Math_PI / 2.0;
+ real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
+ real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+ bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
- // Look is like the opposite of Orbit: the focus point rotates around the camera
- Transform camera_transform = to_camera_transform(cursor);
- Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
- Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
- Vector3 diff = prev_pos - pos;
- cursor.pos += diff;
+ // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
+ Transform prev_camera_transform = to_camera_transform(cursor);
- name = "";
- _update_name();
+ if (invert_y_axis) {
+ cursor.x_rot -= p_relative.y * radians_per_pixel;
+ } else {
+ cursor.x_rot += p_relative.y * radians_per_pixel;
}
+ cursor.y_rot += p_relative.x * radians_per_pixel;
+ if (cursor.x_rot > Math_PI / 2.0)
+ cursor.x_rot = Math_PI / 2.0;
+ if (cursor.x_rot < -Math_PI / 2.0)
+ cursor.x_rot = -Math_PI / 2.0;
+
+ // Look is like the opposite of Orbit: the focus point rotates around the camera
+ Transform camera_transform = to_camera_transform(cursor);
+ Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
+ Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
+ Vector3 diff = prev_pos - pos;
+ cursor.pos += diff;
+
+ name = "";
+ _update_name();
}
void SpatialEditorViewport::set_freelook_active(bool active_now) {
@@ -2222,8 +2231,21 @@ void SpatialEditorViewport::_notification(int p_what) {
bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
info_label->set_visible(show_info);
+ Camera *current_camera;
+
+ if (previewing) {
+ current_camera = previewing;
+ } else {
+ current_camera = camera;
+ }
+
if (show_info) {
String text;
+ text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n";
+ text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n";
+ text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n";
+ text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n";
+ text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n";
text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n";
text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n";
text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n";
@@ -2250,6 +2272,11 @@ void SpatialEditorViewport::_notification(int p_what) {
float cinema_half_width = cinema_label->get_size().width / 2.0f;
cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width);
}
+
+ if (lock_rotation) {
+ float locked_half_width = locked_label->get_size().width / 2.0f;
+ locked_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -locked_half_width);
+ }
}
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -2260,27 +2287,36 @@ void SpatialEditorViewport::_notification(int p_what) {
surface->connect("mouse_exited", this, "_surface_mouse_exit");
surface->connect("focus_entered", this, "_surface_focus_enter");
surface->connect("focus_exited", this, "_surface_focus_exit");
- view_menu->set_flat(false);
- view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- view_menu->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- view_menu->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- view_menu->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- view_menu->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
- preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
+
_init_gizmo_instance(index);
}
+
if (p_what == NOTIFICATION_EXIT_TREE) {
_finish_gizmo_instances();
}
- if (p_what == NOTIFICATION_MOUSE_ENTER) {
- }
+ if (p_what == NOTIFICATION_THEME_CHANGED) {
- if (p_what == NOTIFICATION_DRAW) {
+ view_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
+ preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
+
+ view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ view_menu->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ view_menu->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ view_menu->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ view_menu->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+
+ preview_camera->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ preview_camera->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ preview_camera->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ preview_camera->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ preview_camera->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+
+ info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ locked_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
}
}
@@ -2521,8 +2557,14 @@ void SpatialEditorViewport::_menu_option(int p_option) {
if (!se)
continue;
- Transform xform = camera_transform;
- xform.scale_basis(sp->get_scale());
+ Transform xform;
+ if (orthogonal) {
+ xform = sp->get_global_transform();
+ xform.basis.set_euler(camera_transform.basis.get_euler());
+ } else {
+ xform = camera_transform;
+ xform.scale_basis(sp->get_scale());
+ }
undo_redo->add_do_method(sp, "set_global_transform", xform);
undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
@@ -2570,9 +2612,9 @@ void SpatialEditorViewport::_menu_option(int p_option) {
lock_rotation = !current;
view_menu->get_popup()->set_item_checked(idx, !current);
if (lock_rotation) {
- view_menu->set_icon(get_icon("Lock", "EditorIcons"));
+ locked_label->show();
} else {
- view_menu->set_icon(Ref<Texture>());
+ locked_label->hide();
}
} break;
@@ -2642,11 +2684,6 @@ void SpatialEditorViewport::_menu_option(int p_option) {
bool current = view_menu->get_popup()->is_item_checked(idx);
view_menu->get_popup()->set_item_checked(idx, !current);
- if (current)
- preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
- else
- preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 15 * EDSCALE + fps_label->get_size().height);
-
} break;
case VIEW_DISPLAY_NORMAL: {
@@ -2760,7 +2797,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore
if (!preview)
preview_camera->hide();
- view_menu->show();
+ view_menu->set_disabled(false);
surface->update();
} else {
@@ -2768,7 +2805,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
previewing = preview;
previewing->connect("tree_exiting", this, "_preview_exited_scene");
VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), preview->get_camera()); //replace
- view_menu->hide();
+ view_menu->set_disabled(true);
surface->update();
}
}
@@ -3425,8 +3462,10 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
surface->set_focus_mode(FOCUS_ALL);
view_menu = memnew(MenuButton);
+ view_menu->set_flat(false);
surface->add_child(view_menu);
- view_menu->set_position(Point2(4, 4) * EDSCALE);
+ view_menu->set_position(Point2(10, 10) * EDSCALE);
+
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT);
@@ -3477,12 +3516,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q);
ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT);
- preview_camera = memnew(Button);
- preview_camera->set_toggle_mode(true);
- preview_camera->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
- preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
- preview_camera->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
- preview_camera->set_h_grow_direction(GROW_DIRECTION_BEGIN);
+ preview_camera = memnew(CheckBox);
+ preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton.
preview_camera->set_text(TTR("Preview"));
surface->add_child(preview_camera);
preview_camera->hide();
@@ -3502,7 +3537,6 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
surface->add_child(info_label);
info_label->hide();
- // FPS Counter.
fps_label = memnew(Label);
fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
@@ -3520,6 +3554,16 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
cinema_label->hide();
previewing_cinema = false;
+ locked_label = memnew(Label);
+ locked_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE);
+ locked_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE);
+ locked_label->set_h_grow_direction(GROW_DIRECTION_END);
+ locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN);
+ locked_label->set_align(Label::ALIGN_CENTER);
+ surface->add_child(locked_label);
+ locked_label->set_text(TTR("View Rotation Locked"));
+ locked_label->hide();
+
accept = NULL;
freelook_active = false;
@@ -4160,10 +4204,10 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
gizmo_plugins.write[j]->set_state(state);
switch (state) {
- case EditorSpatialGizmoPlugin::ON_TOP:
+ case EditorSpatialGizmoPlugin::VISIBLE:
gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible"));
break;
- case EditorSpatialGizmoPlugin::VISIBLE:
+ case EditorSpatialGizmoPlugin::ON_TOP:
gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray"));
break;
case EditorSpatialGizmoPlugin::HIDDEN:
@@ -4273,10 +4317,10 @@ void SpatialEditor::_menu_gizmo_toggled(int p_option) {
// Change icon
const int state = gizmos_menu->get_item_state(idx);
switch (state) {
- case EditorSpatialGizmoPlugin::ON_TOP:
+ case EditorSpatialGizmoPlugin::VISIBLE:
gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible"));
break;
- case EditorSpatialGizmoPlugin::VISIBLE:
+ case EditorSpatialGizmoPlugin::ON_TOP:
gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray"));
break;
case EditorSpatialGizmoPlugin::HIDDEN:
@@ -4795,7 +4839,7 @@ void SpatialEditor::_init_gizmos_menu() {
for (int i = 0; i < gizmo_plugins.size(); ++i) {
if (!gizmo_plugins[i]->can_be_hidden()) continue;
String plugin_name = gizmo_plugins[i]->get_name();
- gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::ON_TOP, i);
+ gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::VISIBLE, i);
gizmos_menu->set_item_icon(gizmos_menu->get_item_index(i), gizmos_menu->get_icon("visibility_visible"));
}
}
@@ -5443,7 +5487,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
view_menu = memnew(MenuButton);
view_menu->set_text(TTR("View"));
- view_menu->set_position(Point2(212, 0));
hbc_menu->add_child(view_menu);
p = view_menu->get_popup();
@@ -5876,7 +5919,7 @@ void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) {
}
EditorSpatialGizmoPlugin::EditorSpatialGizmoPlugin() {
- current_state = ON_TOP;
+ current_state = VISIBLE;
}
EditorSpatialGizmoPlugin::~EditorSpatialGizmoPlugin() {
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index c552f21e39..c515a4aaf9 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -192,7 +192,7 @@ private:
EditorSelection *editor_selection;
UndoRedo *undo_redo;
- Button *preview_camera;
+ CheckBox *preview_camera;
ViewportContainer *viewport_container;
MenuButton *view_menu;
@@ -211,6 +211,7 @@ private:
Label *info_label;
Label *fps_label;
Label *cinema_label;
+ Label *locked_label;
struct _RayResult {
@@ -753,9 +754,9 @@ class EditorSpatialGizmoPlugin : public Resource {
GDCLASS(EditorSpatialGizmoPlugin, Resource);
public:
- static const int ON_TOP = 0;
- static const int VISIBLE = 1;
- static const int HIDDEN = 2;
+ static const int VISIBLE = 0;
+ static const int HIDDEN = 1;
+ static const int ON_TOP = 2;
private:
int current_state;
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 30246147c2..40781908fd 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -548,7 +548,6 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) {
} else {
hide();
- //set_physics_process(false);
}
}
@@ -816,16 +815,26 @@ SpriteFramesEditor::SpriteFramesEditor() {
void SpriteFramesEditorPlugin::edit(Object *p_object) {
frames_editor->set_undo_redo(&get_undo_redo());
- SpriteFrames *s = Object::cast_to<SpriteFrames>(p_object);
- if (!s)
- return;
+
+ SpriteFrames *s;
+ AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object);
+ if (animated_sprite) {
+ s = *animated_sprite->get_sprite_frames();
+ } else {
+ s = Object::cast_to<SpriteFrames>(p_object);
+ }
frames_editor->edit(s);
}
bool SpriteFramesEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("SpriteFrames");
+ AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object);
+ if (animated_sprite && *animated_sprite->get_sprite_frames()) {
+ return true;
+ } else {
+ return p_object->is_class("SpriteFrames");
+ }
}
void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
@@ -833,14 +842,11 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
button->show();
editor->make_bottom_panel_item_visible(frames_editor);
- //frames_editor->set_process(true);
} else {
button->hide();
if (frames_editor->is_visible_in_tree())
editor->hide_bottom_panel();
-
- //frames_editor->set_process(false);
}
}
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 4ff7046a35..4a8eae1ba4 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -131,7 +131,7 @@ void TextEditor::_load_theme_settings() {
text_edit->add_color_override("search_result_border_color", search_result_border_color);
text_edit->add_color_override("symbol_color", symbol_color);
- text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4));
+ text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6));
colors_cache.font_color = text_color;
colors_cache.symbol_color = symbol_color;
@@ -360,7 +360,7 @@ void TextEditor::_edit_option(int p_op) {
} break;
case EDIT_CLONE_DOWN: {
- code_editor->code_lines_down();
+ code_editor->clone_lines_down();
} break;
case EDIT_TOGGLE_FOLD_LINE: {
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 33e1f7c6a3..7c3e524d89 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -846,7 +846,8 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
p->set_item_checked(0, true);
p->connect("id_pressed", this, "_set_snap_mode");
hb_grid = memnew(HBoxContainer);
- hb_tools->add_child(hb_grid);
+ //hb_tools->add_child(hb_grid);
+ main_vb->add_child(hb_grid);
hb_grid->add_child(memnew(VSeparator));
hb_grid->add_child(memnew(Label(TTR("Offset:"))));
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 27c3ff960b..213cd2ce1a 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "tile_map_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
+#include "core/math/math_funcs.h"
#include "core/os/input.h"
#include "core/os/keyboard.h"
#include "editor/editor_scale.h"
@@ -43,8 +44,8 @@ void TileMapEditor::_notification(int p_what) {
case NOTIFICATION_PROCESS: {
- if (bucket_queue.size() && canvas_item_editor_viewport) {
- canvas_item_editor_viewport->update();
+ if (bucket_queue.size()) {
+ CanvasItemEditor::get_singleton()->update_viewport();
}
} break;
@@ -65,13 +66,11 @@ void TileMapEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
- transp->set_icon(get_icon("Transpose", "EditorIcons"));
- mirror_x->set_icon(get_icon("MirrorX", "EditorIcons"));
- mirror_y->set_icon(get_icon("MirrorY", "EditorIcons"));
- rotate_0->set_icon(get_icon("Rotate0", "EditorIcons"));
- rotate_90->set_icon(get_icon("Rotate90", "EditorIcons"));
- rotate_180->set_icon(get_icon("Rotate180", "EditorIcons"));
- rotate_270->set_icon(get_icon("Rotate270", "EditorIcons"));
+ rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons"));
+ rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons"));
+ flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons"));
+ flip_vertical_button->set_icon(get_icon("MirrorY", "EditorIcons"));
+ clear_transform_button->set_icon(get_icon("Clear", "EditorIcons"));
search_box->set_right_icon(get_icon("Search", "EditorIcons"));
search_box->set_clear_button_enabled(true);
@@ -97,27 +96,27 @@ void TileMapEditor::_menu_option(int p_option) {
// immediately without pressing the left mouse button first
tool = TOOL_NONE;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} break;
case OPTION_BUCKET: {
tool = TOOL_BUCKET;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} break;
case OPTION_PICK_TILE: {
tool = TOOL_PICKING;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} break;
case OPTION_SELECT: {
tool = TOOL_SELECTING;
selection_active = false;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} break;
case OPTION_COPY: {
@@ -126,7 +125,7 @@ void TileMapEditor::_menu_option(int p_option) {
if (selection_active) {
tool = TOOL_PASTING;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
} break;
case OPTION_ERASE_SELECTION: {
@@ -141,7 +140,7 @@ void TileMapEditor::_menu_option(int p_option) {
selection_active = false;
copydata.clear();
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} break;
case OPTION_FIX_INVALID: {
@@ -165,7 +164,7 @@ void TileMapEditor::_menu_option(int p_option) {
tool = TOOL_PASTING;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
} break;
}
@@ -182,13 +181,13 @@ void TileMapEditor::_palette_multi_selected(int index, bool selected) {
void TileMapEditor::_canvas_mouse_enter() {
mouse_over = true;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
void TileMapEditor::_canvas_mouse_exit() {
mouse_over = false;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
Vector<int> TileMapEditor::get_selected_tiles() const {
@@ -301,7 +300,7 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p
}
node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose);
- if (manual_autotile || node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE) {
+ if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) {
if (current != -1) {
node->set_cell_autotile_coord(p_pos.x, p_pos.y, position);
}
@@ -357,6 +356,10 @@ void TileMapEditor::_update_palette() {
if (!node)
return;
+ // Update the clear button
+ clear_transform_button->set_disabled(!flip_h && !flip_v && !transpose);
+
+ // Update the palette
Vector<int> selected = get_selected_tiles();
palette->clear();
manual_palette->clear();
@@ -429,9 +432,6 @@ void TileMapEditor::_update_palette() {
Ref<Texture> tex = tileset->tile_get_texture(entries[i].id);
if (tex.is_valid()) {
- Color color = tileset->tile_get_modulate(entries[i].id);
- palette->set_item_icon_modulate(palette->get_item_count() - 1, color);
-
Rect2 region = tileset->tile_get_region(entries[i].id);
if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) {
@@ -440,10 +440,25 @@ void TileMapEditor::_update_palette() {
region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id);
}
- if (!region.has_no_area())
+ // Transpose and flip
+ palette->set_item_icon_transposed(palette->get_item_count() - 1, transpose);
+ if (flip_h) {
+ region.size.x = -region.size.x;
+ }
+ if (flip_v) {
+ region.size.y = -region.size.y;
+ }
+
+ // Set region
+ if (region.size != Size2())
palette->set_item_icon_region(palette->get_item_count() - 1, region);
+ // Set icon
palette->set_item_icon(palette->get_item_count() - 1, tex);
+
+ // Modulation
+ Color color = tileset->tile_get_modulate(entries[i].id);
+ palette->set_item_icon_modulate(palette->get_item_count() - 1, color);
}
palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
@@ -519,12 +534,12 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
selected.push_back(id);
set_selected_tiles(selected);
- mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y));
- mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y));
- transp->set_pressed(node->is_cell_transposed(p_pos.x, p_pos.y));
+ flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
+ flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
+ transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
- _update_transform_buttons();
- canvas_item_editor_viewport->update();
+ _update_palette();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) {
@@ -671,7 +686,7 @@ void TileMapEditor::_select(const Point2i &p_from, const Point2i &p_to) {
rectangle.position = begin;
rectangle.size = end - begin;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
void TileMapEditor::_erase_selection() {
@@ -978,7 +993,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
paint_undo.clear();
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
} else if (tool == TOOL_RECTANGLE_PAINT) {
@@ -995,7 +1010,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
_finish_undo();
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
} else if (tool == TOOL_PASTING) {
@@ -1011,12 +1026,12 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
_finish_undo();
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true; // We want to keep the Pasting tool
} else if (tool == TOOL_SELECTING) {
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
} else if (tool == TOOL_BUCKET) {
@@ -1055,7 +1070,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_NONE;
selection_active = false;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1065,7 +1080,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_NONE;
copydata.clear();
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1106,7 +1121,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
_finish_undo();
if (tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
tool = TOOL_NONE;
@@ -1149,7 +1164,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (new_over_tile != over_tile) {
over_tile = new_over_tile;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
if (show_tile_info) {
@@ -1235,7 +1250,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
_set_cell(points[i], invalid_cell);
}
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
return true;
@@ -1294,7 +1309,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_NONE;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1308,13 +1323,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
// NOTE: We do not set tool = TOOL_PAINTING as this begins painting
// immediately without pressing the left mouse button first
tool = TOOL_NONE;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) {
tool = TOOL_BUCKET;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1327,7 +1342,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_SELECTING;
selection_active = false;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1337,7 +1352,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (selection_active) {
tool = TOOL_PASTING;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
@@ -1354,7 +1369,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_PASTING;
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
}
@@ -1366,23 +1381,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
flip_h = !flip_h;
- mirror_x->set_pressed(flip_h);
- _update_transform_buttons();
- canvas_item_editor_viewport->update();
+ _update_palette();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
flip_v = !flip_v;
- mirror_y->set_pressed(flip_v);
- _update_transform_buttons();
- canvas_item_editor_viewport->update();
+ _update_palette();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
transpose = !transpose;
- transp->set_pressed(transpose);
- _update_transform_buttons();
- canvas_item_editor_viewport->update();
+ _update_palette();
+ CanvasItemEditor::get_singleton()->update_viewport();
return true;
}
}
@@ -1643,9 +1655,7 @@ void TileMapEditor::edit(Node *p_tile_map) {
void TileMapEditor::_tileset_settings_changed() {
_update_palette();
-
- if (canvas_item_editor_viewport)
- canvas_item_editor_viewport->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
}
void TileMapEditor::_icon_size_changed(float p_value) {
@@ -1666,7 +1676,10 @@ void TileMapEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter);
ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit);
ClassDB::bind_method(D_METHOD("_tileset_settings_changed"), &TileMapEditor::_tileset_settings_changed);
- ClassDB::bind_method(D_METHOD("_update_transform_buttons"), &TileMapEditor::_update_transform_buttons);
+ ClassDB::bind_method(D_METHOD("_rotate"), &TileMapEditor::_rotate);
+ ClassDB::bind_method(D_METHOD("_flip_horizontal"), &TileMapEditor::_flip_horizontal);
+ ClassDB::bind_method(D_METHOD("_flip_vertical"), &TileMapEditor::_flip_vertical);
+ ClassDB::bind_method(D_METHOD("_clear_transform"), &TileMapEditor::_clear_transform);
ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected);
ClassDB::bind_method(D_METHOD("_palette_multi_selected"), &TileMapEditor::_palette_multi_selected);
@@ -1691,37 +1704,67 @@ TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) {
return op;
}
-void TileMapEditor::_update_transform_buttons(Object *p_button) {
- //ERR_FAIL_NULL(p_button);
- ToolButton *b = Object::cast_to<ToolButton>(p_button);
- //ERR_FAIL_COND(!b);
-
- if (b == rotate_0) {
- mirror_x->set_pressed(false);
- mirror_y->set_pressed(false);
- transp->set_pressed(false);
- } else if (b == rotate_90) {
- mirror_x->set_pressed(true);
- mirror_y->set_pressed(false);
- transp->set_pressed(true);
- } else if (b == rotate_180) {
- mirror_x->set_pressed(true);
- mirror_y->set_pressed(true);
- transp->set_pressed(false);
- } else if (b == rotate_270) {
- mirror_x->set_pressed(false);
- mirror_y->set_pressed(true);
- transp->set_pressed(true);
+void TileMapEditor::_rotate(int steps) {
+ const bool normal_rotation_matrix[][3] = {
+ { false, false, false },
+ { true, true, false },
+ { false, true, true },
+ { true, false, true }
+ };
+
+ const bool mirrored_rotation_matrix[][3] = {
+ { false, true, false },
+ { true, true, true },
+ { false, false, true },
+ { true, false, false }
+ };
+
+ if (transpose ^ flip_h ^ flip_v) {
+ // Odd number of flags activated = mirrored rotation
+ for (int i = 0; i < 4; i++) {
+ if (transpose == mirrored_rotation_matrix[i][0] &&
+ flip_h == mirrored_rotation_matrix[i][1] &&
+ flip_v == mirrored_rotation_matrix[i][2]) {
+ int new_id = Math::wrapi(i + steps, 0, 4);
+ transpose = mirrored_rotation_matrix[new_id][0];
+ flip_h = mirrored_rotation_matrix[new_id][1];
+ flip_v = mirrored_rotation_matrix[new_id][2];
+ break;
+ }
+ }
+ } else {
+ // Even number of flags activated = normal rotation
+ for (int i = 0; i < 4; i++) {
+ if (transpose == normal_rotation_matrix[i][0] &&
+ flip_h == normal_rotation_matrix[i][1] &&
+ flip_v == normal_rotation_matrix[i][2]) {
+ int new_id = Math::wrapi(i + steps, 0, 4);
+ transpose = normal_rotation_matrix[new_id][0];
+ flip_h = normal_rotation_matrix[new_id][1];
+ flip_v = normal_rotation_matrix[new_id][2];
+ break;
+ }
+ }
}
- flip_h = mirror_x->is_pressed();
- flip_v = mirror_y->is_pressed();
- transpose = transp->is_pressed();
+ _update_palette();
+}
+
+void TileMapEditor::_flip_horizontal() {
+ flip_h = !flip_h;
+ _update_palette();
+}
+
+void TileMapEditor::_flip_vertical() {
+ flip_v = !flip_v;
+ _update_palette();
+}
- rotate_0->set_pressed(!flip_h && !flip_v && !transpose);
- rotate_90->set_pressed(flip_h && !flip_v && transpose);
- rotate_180->set_pressed(flip_h && flip_v && !transpose);
- rotate_270->set_pressed(!flip_h && flip_v && transpose);
+void TileMapEditor::_clear_transform() {
+ transpose = false;
+ flip_h = false;
+ flip_v = false;
+ _update_palette();
}
TileMapEditor::TileMapEditor(EditorNode *p_editor) {
@@ -1754,10 +1797,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A);
ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S);
- HBoxContainer *tool_hb1 = memnew(HBoxContainer);
- add_child(tool_hb1);
- HBoxContainer *tool_hb2 = memnew(HBoxContainer);
- add_child(tool_hb2);
+ HBoxContainer *tool_hb = memnew(HBoxContainer);
+ add_child(tool_hb);
manual_button = memnew(CheckBox);
manual_button->set_text("Disable Autotile");
@@ -1842,52 +1883,37 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
p->connect("id_pressed", this, "_menu_option");
toolbar->add_child(options);
-
- transp = memnew(ToolButton);
- transp->set_toggle_mode(true);
- transp->set_tooltip(TTR("Transpose") + " (" + ED_GET_SHORTCUT("tile_map_editor/transpose")->get_as_text() + ")");
- transp->set_focus_mode(FOCUS_NONE);
- transp->connect("pressed", this, "_update_transform_buttons", make_binds(transp));
- tool_hb1->add_child(transp);
- mirror_x = memnew(ToolButton);
- mirror_x->set_toggle_mode(true);
- mirror_x->set_tooltip(TTR("Mirror X") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_x")->get_as_text() + ")");
- mirror_x->set_focus_mode(FOCUS_NONE);
- mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x));
- tool_hb1->add_child(mirror_x);
- mirror_y = memnew(ToolButton);
- mirror_y->set_toggle_mode(true);
- mirror_y->set_tooltip(TTR("Mirror Y") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_y")->get_as_text() + ")");
- mirror_y->set_focus_mode(FOCUS_NONE);
- mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y));
- tool_hb1->add_child(mirror_y);
-
- rotate_0 = memnew(ToolButton);
- rotate_0->set_toggle_mode(true);
- rotate_0->set_tooltip(TTR("Rotate 0 degrees"));
- rotate_0->set_focus_mode(FOCUS_NONE);
- rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0));
- tool_hb2->add_child(rotate_0);
- rotate_90 = memnew(ToolButton);
- rotate_90->set_toggle_mode(true);
- rotate_90->set_tooltip(TTR("Rotate 90 degrees"));
- rotate_90->set_focus_mode(FOCUS_NONE);
- rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90));
- tool_hb2->add_child(rotate_90);
- rotate_180 = memnew(ToolButton);
- rotate_180->set_toggle_mode(true);
- rotate_180->set_tooltip(TTR("Rotate 180 degrees"));
- rotate_180->set_focus_mode(FOCUS_NONE);
- rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180));
- tool_hb2->add_child(rotate_180);
- rotate_270 = memnew(ToolButton);
- rotate_270->set_toggle_mode(true);
- rotate_270->set_tooltip(TTR("Rotate 270 degrees"));
- rotate_270->set_focus_mode(FOCUS_NONE);
- rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270));
- tool_hb2->add_child(rotate_270);
-
- rotate_0->set_pressed(true);
+ rotate_left_button = memnew(ToolButton);
+ rotate_left_button->set_tooltip(TTR("Rotate left"));
+ rotate_left_button->set_focus_mode(FOCUS_NONE);
+ rotate_left_button->connect("pressed", this, "_rotate", varray(-1));
+ tool_hb->add_child(rotate_left_button);
+
+ rotate_right_button = memnew(ToolButton);
+ rotate_right_button->set_tooltip(TTR("Rotate right"));
+ rotate_right_button->set_focus_mode(FOCUS_NONE);
+ rotate_right_button->connect("pressed", this, "_rotate", varray(1));
+ tool_hb->add_child(rotate_right_button);
+
+ flip_horizontal_button = memnew(ToolButton);
+ flip_horizontal_button->set_tooltip(TTR("Flip horizontally"));
+ flip_horizontal_button->set_focus_mode(FOCUS_NONE);
+ flip_horizontal_button->connect("pressed", this, "_flip_horizontal");
+ tool_hb->add_child(flip_horizontal_button);
+
+ flip_vertical_button = memnew(ToolButton);
+ flip_vertical_button->set_tooltip(TTR("Flip vertically"));
+ flip_vertical_button->set_focus_mode(FOCUS_NONE);
+ flip_vertical_button->connect("pressed", this, "_flip_vertical");
+ tool_hb->add_child(flip_vertical_button);
+
+ clear_transform_button = memnew(ToolButton);
+ clear_transform_button->set_tooltip(TTR("Clear transform"));
+ clear_transform_button->set_focus_mode(FOCUS_NONE);
+ clear_transform_button->connect("pressed", this, "_clear_transform");
+ tool_hb->add_child(clear_transform_button);
+
+ clear_transform_button->set_disabled(true);
}
TileMapEditor::~TileMapEditor() {
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 3d44647a1b..68e5806ee5 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -93,13 +93,13 @@ class TileMapEditor : public VBoxContainer {
Label *tile_info;
MenuButton *options;
- ToolButton *transp;
- ToolButton *mirror_x;
- ToolButton *mirror_y;
- ToolButton *rotate_0;
- ToolButton *rotate_90;
- ToolButton *rotate_180;
- ToolButton *rotate_270;
+
+ ToolButton *flip_horizontal_button;
+ ToolButton *flip_vertical_button;
+ ToolButton *rotate_left_button;
+ ToolButton *rotate_right_button;
+ ToolButton *clear_transform_button;
+
CheckBox *manual_button;
Tool tool;
@@ -196,11 +196,15 @@ class TileMapEditor : public VBoxContainer {
void _tileset_settings_changed();
void _icon_size_changed(float p_value);
+ void _clear_transform();
+ void _flip_horizontal();
+ void _flip_vertical();
+ void _rotate(int steps);
+
protected:
void _notification(int p_what);
static void _bind_methods();
CellOp _get_op_from_cell(const Point2i &p_pos);
- void _update_transform_buttons(Object *p_button = NULL);
public:
HBoxContainer *get_toolbar() const { return toolbar; }
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 3de2284cea..d0e0b3e006 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -483,6 +483,11 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
//---------------
helper = memnew(TilesetEditorContext(this));
tile_names_opacity = 0;
+
+ // config scale
+ max_scale = 10.0f;
+ min_scale = 0.1f;
+ scale_ratio = 1.2f;
}
TileSetEditor::~TileSetEditor() {
@@ -569,6 +574,10 @@ void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) {
int invalid_count = 0;
for (int i = 0; i < p_paths.size(); i++) {
Ref<Texture> t = Ref<Texture>(ResourceLoader::load(p_paths[i]));
+
+ ERR_EXPLAIN("'" + p_paths[i] + "' is not a valid texture.");
+ ERR_CONTINUE(!t.is_valid());
+
if (texture_map.has(t->get_rid())) {
invalid_count++;
} else {
@@ -577,9 +586,13 @@ void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) {
texture_list->set_item_metadata(texture_list->get_item_count() - 1, t->get_rid());
}
}
- update_texture_list_icon();
- texture_list->select(texture_list->get_item_count() - 1);
- _on_texture_list_selected(texture_list->get_item_count() - 1);
+
+ if (texture_list->get_item_count() > 0) {
+ update_texture_list_icon();
+ texture_list->select(texture_list->get_item_count() - 1);
+ _on_texture_list_selected(texture_list->get_item_count() - 1);
+ }
+
if (invalid_count > 0) {
err_dialog->set_text(vformat(TTR("%s file(s) were not added because was already on the list."), String::num(invalid_count, 0)));
err_dialog->popup_centered(Size2(300, 60));
@@ -945,7 +958,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
Ref<InputEventMouseMotion> mm = p_ie;
if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) {
if (!current_tile_region.has_point(mb->get_position())) {
List<int> *tiles = new List<int>();
tileset->get_tile_list(tiles);
@@ -964,6 +977,15 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
}
+
+ // Mouse Wheel Event
+ const int _mouse_button_index = mb->get_button_index();
+ if (_mouse_button_index == BUTTON_WHEEL_UP && mb->get_control()) {
+ _zoom_in();
+
+ } else if (_mouse_button_index == BUTTON_WHEEL_DOWN && mb->get_control()) {
+ _zoom_out();
+ }
}
// Drag Middle Mouse
if (mm.is_valid()) {
@@ -1440,23 +1462,11 @@ void TileSetEditor::_on_tool_clicked(int p_tool) {
}
}
} else if (p_tool == ZOOM_OUT) {
- float scale = workspace->get_scale().x;
- if (scale > 0.1) {
- scale /= 2;
- workspace->set_scale(Vector2(scale, scale));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
- }
+ _zoom_out();
} else if (p_tool == ZOOM_1) {
- workspace->set_scale(Vector2(1, 1));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size);
+ _reset_zoom();
} else if (p_tool == ZOOM_IN) {
- float scale = workspace->get_scale().x;
- scale *= 2;
- workspace->set_scale(Vector2(scale, scale));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
+ _zoom_in();
} else if (p_tool == TOOL_SELECT) {
if (creating_shape) {
// Cancel Creation
@@ -1495,6 +1505,31 @@ void TileSetEditor::_set_snap_sep(Vector2 p_val) {
workspace->update();
}
+void TileSetEditor::_zoom_in() {
+ float scale = workspace->get_scale().x;
+ if (scale < max_scale) {
+ scale *= scale_ratio;
+ workspace->set_scale(Vector2(scale, scale));
+ workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
+ workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
+ }
+}
+void TileSetEditor::_zoom_out() {
+
+ float scale = workspace->get_scale().x;
+ if (scale > min_scale) {
+ scale /= scale_ratio;
+ workspace->set_scale(Vector2(scale, scale));
+ workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
+ workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
+ }
+}
+void TileSetEditor::_reset_zoom() {
+ workspace->set_scale(Vector2(1, 1));
+ workspace_container->set_custom_minimum_size(workspace->get_rect().size);
+ workspace_overlay->set_custom_minimum_size(workspace->get_rect().size);
+}
+
void TileSetEditor::draw_highlight_current_tile() {
if (get_current_tile() >= 0) {
@@ -2076,13 +2111,24 @@ void TileSetEditor::update_texture_list() {
List<int> ids;
tileset->get_tile_list(&ids);
+ Vector<int> ids_to_remove;
for (List<int>::Element *E = ids.front(); E; E = E->next()) {
+ // Clear tiles referencing gone textures (user has been already given the chance to fix broken deps)
+ if (!tileset->tile_get_texture(E->get()).is_valid()) {
+ ids_to_remove.push_back(E->get());
+ ERR_CONTINUE(!tileset->tile_get_texture(E->get()).is_valid());
+ }
+
if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) {
texture_list->add_item(tileset->tile_get_texture(E->get())->get_path().get_file());
texture_map.insert(tileset->tile_get_texture(E->get())->get_rid(), tileset->tile_get_texture(E->get()));
texture_list->set_item_metadata(texture_list->get_item_count() - 1, tileset->tile_get_texture(E->get())->get_rid());
}
}
+ for (int i = 0; i < ids_to_remove.size(); i++) {
+ tileset->remove_tile(ids_to_remove[i]);
+ }
+
if (texture_list->get_item_count() > 0 && selected_texture.is_valid()) {
texture_list->select(texture_list->find_metadata(selected_texture->get_rid()));
if (texture_list->get_selected_items().size() > 0)
diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
index 23bf68b90f..bd8a2ddb98 100644
--- a/editor/plugins/tile_set_editor_plugin.h
+++ b/editor/plugins/tile_set_editor_plugin.h
@@ -141,6 +141,10 @@ class TileSetEditor : public Control {
EditMode edit_mode;
int current_tile;
+ float max_scale;
+ float min_scale;
+ float scale_ratio;
+
void update_texture_list();
void update_texture_list_icon();
@@ -178,6 +182,10 @@ private:
void _set_snap_off(Vector2 p_val);
void _set_snap_sep(Vector2 p_val);
+ void _zoom_in();
+ void _zoom_out();
+ void _reset_zoom();
+
void draw_highlight_current_tile();
void draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>());
void draw_tile_subdivision(int p_id, Color p_color) const;
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index 29a780961e..4a98aa411d 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -39,7 +39,7 @@ void BackgroundProgress::_add_task(const String &p_task, const String &p_label,
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(tasks.has(p_task));
- Task t;
+ BackgroundProgress::Task t;
t.hb = memnew(HBoxContainer);
Label *l = memnew(Label);
l->set_text(p_label + " ");
@@ -112,7 +112,7 @@ void BackgroundProgress::add_task(const String &p_task, const String &p_label, i
void BackgroundProgress::task_step(const String &p_task, int p_step) {
//this code is weird, but it prevents deadlock.
- bool no_updates;
+ bool no_updates = true;
{
_THREAD_SAFE_METHOD_
no_updates = updates.empty();
@@ -167,7 +167,7 @@ void ProgressDialog::_popup() {
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
ERR_FAIL_COND(tasks.has(p_task));
- Task t;
+ ProgressDialog::Task t;
t.vb = memnew(VBoxContainer);
VBoxContainer *vb2 = memnew(VBoxContainer);
t.vb->add_margin_child(p_label, vb2);
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 3ec49e187f..f8ba6fd4e3 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -50,6 +50,7 @@ void ProjectExportDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
+ duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons"));
delete_preset->set_icon(get_icon("Remove", "EditorIcons"));
connect("confirmed", this, "_export_pck_zip");
custom_feature_display->get_parent_control()->add_style_override("panel", get_stylebox("bg", "Tree"));
@@ -58,6 +59,7 @@ void ProjectExportDialog::_notification(int p_what) {
EditorSettings::get_singleton()->set("interface/dialogs/export_bounds", get_rect());
} break;
case NOTIFICATION_THEME_CHANGED: {
+ duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons"));
delete_preset->set_icon(get_icon("Remove", "EditorIcons"));
Control *panel = custom_feature_display->get_parent_control();
if (panel)
@@ -163,14 +165,37 @@ void ProjectExportDialog::_update_presets() {
updating = false;
}
+void ProjectExportDialog::_update_export_all() {
+
+ bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0 ? true : false;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
+ bool needs_templates;
+ String error;
+ if (preset->get_export_path() == "" || !preset->get_platform()->can_export(preset, error, needs_templates)) {
+ can_export = false;
+ break;
+ }
+ }
+
+ if (can_export) {
+ export_all_button->set_disabled(false);
+ } else {
+ export_all_button->set_disabled(true);
+ }
+}
+
void ProjectExportDialog::_edit_preset(int p_index) {
if (p_index < 0 || p_index >= presets->get_item_count()) {
name->set_text("");
name->set_editable(false);
+ export_path->set_editable(false);
runnable->set_disabled(true);
parameters->edit(NULL);
presets->unselect_all();
+ duplicate_preset->set_disabled(true);
delete_preset->set_disabled(true);
sections->hide();
patches->clear();
@@ -188,8 +213,11 @@ void ProjectExportDialog::_edit_preset(int p_index) {
sections->show();
name->set_editable(true);
+ export_path->set_editable(true);
+ duplicate_preset->set_disabled(false);
delete_preset->set_disabled(false);
name->set_text(current->get_name());
+ export_path->set_text(current->get_export_path());
runnable->set_disabled(false);
runnable->set_pressed(current->is_runnable());
parameters->edit(current.ptr());
@@ -260,6 +288,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
custom_features->set_text(current->get_custom_features());
_update_feature_list();
+ _update_export_all();
updating = false;
}
@@ -428,6 +457,73 @@ void ProjectExportDialog::_name_changed(const String &p_string) {
_update_presets();
}
+void ProjectExportDialog::_export_path_changed(const String &p_string) {
+
+ if (updating)
+ return;
+
+ Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current());
+ ERR_FAIL_COND(current.is_null());
+
+ current->set_export_path(p_string);
+ _update_presets();
+}
+
+void ProjectExportDialog::_duplicate_preset() {
+
+ Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current());
+ if (current.is_null())
+ return;
+
+ Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
+ ERR_FAIL_COND(!preset.is_valid());
+
+ String name = current->get_name() + "" + itos(1);
+ bool make_runnable = true;
+ int attempt = 2;
+ while (true) {
+
+ bool valid = true;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
+ if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
+ make_runnable = false;
+ }
+ if (p->get_name() == name) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid)
+ break;
+
+ attempt++;
+ name = current->get_name() + " " + itos(attempt);
+ }
+
+ preset->set_name(name);
+ if (make_runnable)
+ preset->set_runnable(make_runnable);
+ preset->set_export_filter(current->get_export_filter());
+ preset->set_include_filter(current->get_include_filter());
+ preset->set_exclude_filter(current->get_exclude_filter());
+ Vector<String> list = current->get_patches();
+ for (int i = 0; i < list.size(); i++) {
+ preset->add_patch(list[i]);
+ }
+ preset->set_custom_features(current->get_custom_features());
+
+ for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) {
+ preset->set(E->get().name, current->get(E->get().name));
+ }
+
+ EditorExport::get_singleton()->add_export_preset(preset);
+ _update_presets();
+ _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
+}
+
void ProjectExportDialog::_delete_preset() {
Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current());
@@ -745,12 +841,19 @@ void ProjectExportDialog::_export_project() {
export_project->set_access(FileDialog::ACCESS_FILESYSTEM);
export_project->clear_filters();
- String extension = platform->get_binary_extension(current);
- if (extension != String()) {
- export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export");
- export_project->set_current_file(default_filename + "." + extension);
+ List<String> extension_list = platform->get_binary_extensions(current);
+ for (int i = 0; i < extension_list.size(); i++) {
+ export_project->add_filter("*." + extension_list[i] + " ; " + platform->get_name() + " Export");
+ }
+
+ if (current->get_export_path() != "") {
+ export_project->set_current_path(current->get_export_path());
} else {
- export_project->set_current_file(default_filename);
+ if (extension_list.size() >= 1) {
+ export_project->set_current_file(default_filename + "." + extension_list[0]);
+ } else {
+ export_project->set_current_file(default_filename);
+ }
}
// Ensure that signal is connected if previous attempt left it disconnected with _validate_export_path
@@ -772,6 +875,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
ERR_FAIL_COND(current.is_null());
Ref<EditorExportPlatform> platform = current->get_platform();
ERR_FAIL_COND(platform.is_null());
+ current->set_export_path(p_path);
Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0);
if (err != OK) {
@@ -782,6 +886,42 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
}
}
+void ProjectExportDialog::_export_all_dialog() {
+
+ export_all_dialog->show();
+ export_all_dialog->popup_centered_minsize(Size2(300, 80));
+}
+
+void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
+
+ export_all_dialog->hide();
+
+ _export_all(p_str == "release" ? false : true);
+}
+
+void ProjectExportDialog::_export_all(bool p_debug) {
+
+ String mode = p_debug ? TTR("Debug") : TTR("Release");
+ EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count());
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
+ ERR_FAIL_COND(preset.is_null());
+ Ref<EditorExportPlatform> platform = preset->get_platform();
+ ERR_FAIL_COND(platform.is_null());
+
+ ep.step(preset->get_name(), i);
+
+ Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
+ if (err != OK) {
+ error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name());
+ error_dialog->show();
+ error_dialog->popup_centered_minsize(Size2(300, 80));
+ ERR_PRINT("Failed to export project");
+ }
+ }
+}
+
void ProjectExportDialog::_bind_methods() {
ClassDB::bind_method("_add_preset", &ProjectExportDialog::_add_preset);
@@ -789,6 +929,7 @@ void ProjectExportDialog::_bind_methods() {
ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters);
ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed);
ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed);
+ ClassDB::bind_method("_duplicate_preset", &ProjectExportDialog::_duplicate_preset);
ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset);
ClassDB::bind_method("_delete_preset_confirm", &ProjectExportDialog::_delete_preset_confirm);
ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw);
@@ -805,8 +946,12 @@ void ProjectExportDialog::_bind_methods() {
ClassDB::bind_method("_export_pck_zip_selected", &ProjectExportDialog::_export_pck_zip_selected);
ClassDB::bind_method("_open_export_template_manager", &ProjectExportDialog::_open_export_template_manager);
ClassDB::bind_method("_validate_export_path", &ProjectExportDialog::_validate_export_path);
+ ClassDB::bind_method("_export_path_changed", &ProjectExportDialog::_export_path_changed);
ClassDB::bind_method("_export_project", &ProjectExportDialog::_export_project);
ClassDB::bind_method("_export_project_to_path", &ProjectExportDialog::_export_project_to_path);
+ ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
+ ClassDB::bind_method("_export_all_dialog", &ProjectExportDialog::_export_all_dialog);
+ ClassDB::bind_method("_export_all_dialog_action", &ProjectExportDialog::_export_all_dialog_action);
ClassDB::bind_method("_custom_features_changed", &ProjectExportDialog::_custom_features_changed);
ClassDB::bind_method("_tab_changed", &ProjectExportDialog::_tab_changed);
}
@@ -842,6 +987,9 @@ ProjectExportDialog::ProjectExportDialog() {
presets->set_drag_forwarding(this);
mc->add_child(presets);
presets->connect("item_selected", this, "_edit_preset");
+ duplicate_preset = memnew(ToolButton);
+ preset_hb->add_child(duplicate_preset);
+ duplicate_preset->connect("pressed", this, "_duplicate_preset");
delete_preset = memnew(ToolButton);
preset_hb->add_child(delete_preset);
delete_preset->connect("pressed", this, "_delete_preset");
@@ -858,6 +1006,10 @@ ProjectExportDialog::ProjectExportDialog() {
runnable->connect("pressed", this, "_runnable_pressed");
settings_vb->add_child(runnable);
+ export_path = memnew(LineEdit);
+ settings_vb->add_margin_child(TTR("Export Path:"), export_path);
+ export_path->connect("text_changed", this, "_export_path_changed");
+
sections = memnew(TabContainer);
sections->set_tab_align(TabContainer::ALIGN_LEFT);
settings_vb->add_child(sections);
@@ -948,7 +1100,9 @@ ProjectExportDialog::ProjectExportDialog() {
//disable by default
name->set_editable(false);
+ export_path->set_editable(false);
runnable->set_disabled(true);
+ duplicate_preset->set_disabled(true);
delete_preset->set_disabled(true);
sections->hide();
parameters->edit(NULL);
@@ -967,6 +1121,19 @@ ProjectExportDialog::ProjectExportDialog() {
// Disable initially before we select a valid preset
export_button->set_disabled(true);
+ export_all_dialog = memnew(ConfirmationDialog);
+ add_child(export_all_dialog);
+ export_all_dialog->set_title("Export All");
+ export_all_dialog->set_text(TTR("Export mode?"));
+ export_all_dialog->get_ok()->hide();
+ export_all_dialog->add_button(TTR("Debug"), true, "debug");
+ export_all_dialog->add_button(TTR("Release"), true, "release");
+ export_all_dialog->connect("custom_action", this, "_export_all_dialog_action");
+
+ export_all_button = add_button(TTR("Export All"), !OS::get_singleton()->get_swap_ok_cancel(), "export");
+ export_all_button->connect("pressed", this, "_export_all_dialog");
+ export_all_button->set_disabled(true);
+
export_pck_zip = memnew(FileDialog);
export_pck_zip->add_filter("*.zip ; ZIP File");
export_pck_zip->add_filter("*.pck ; Godot Game Pack");
@@ -1030,6 +1197,8 @@ ProjectExportDialog::ProjectExportDialog() {
default_filename = "UnnamedProject";
}
}
+
+ _update_export_all();
}
ProjectExportDialog::~ProjectExportDialog() {
diff --git a/editor/project_export.h b/editor/project_export.h
index 552c6d7faf..7009968138 100644
--- a/editor/project_export.h
+++ b/editor/project_export.h
@@ -61,10 +61,12 @@ private:
TabContainer *sections;
MenuButton *add_preset;
+ Button *duplicate_preset;
Button *delete_preset;
ItemList *presets;
LineEdit *name;
+ LineEdit *export_path;
EditorInspector *parameters;
CheckButton *runnable;
@@ -91,6 +93,8 @@ private:
ConfirmationDialog *patch_erase;
Button *export_button;
+ Button *export_all_button;
+ AcceptDialog *export_all_dialog;
LineEdit *custom_features;
RichTextLabel *custom_feature_display;
@@ -106,10 +110,13 @@ private:
void _runnable_pressed();
void _update_parameters(const String &p_edited_property);
void _name_changed(const String &p_string);
+ void _export_path_changed(const String &p_string);
void _add_preset(int p_platform);
void _edit_preset(int p_index);
+ void _duplicate_preset();
void _delete_preset();
void _delete_preset_confirm();
+ void _update_export_all();
void _update_presets();
@@ -139,6 +146,9 @@ private:
void _validate_export_path(const String &p_path);
void _export_project();
void _export_project_to_path(const String &p_path);
+ void _export_all_dialog();
+ void _export_all_dialog_action(const String &p_str);
+ void _export_all(bool p_debug);
void _update_feature_list();
void _custom_features_changed(const String &p_text);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 83de5a646a..8c906e5f0b 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1312,7 +1312,7 @@ void ProjectManager::_load_recent_projects() {
show->set_modulate(Color(1, 1, 1, 0.5));
path_hb->add_child(show);
show->connect("pressed", this, "_show_project", varray(path));
- show->set_tooltip(TTR("Show In File Manager"));
+ show->set_tooltip(TTR("Show in File Manager"));
Label *fpath = memnew(Label(path));
fpath->set_name("path");
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index c611327a31..8da75b7b3f 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -132,7 +132,7 @@ void CustomPropertyEditor::_menu_option(int p_which) {
emit_signal("variant_changed");
} else if (hint == PROPERTY_HINT_ENUM) {
- v = p_which;
+ v = menu->get_item_metadata(p_which);
emit_signal("variant_changed");
}
} break;
@@ -427,12 +427,14 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
} else if (hint == PROPERTY_HINT_ENUM) {
Vector<String> options = hint_text.split(",");
+ int current_val = 0;
for (int i = 0; i < options.size(); i++) {
- if (options[i].find(":") != -1) {
- menu->add_item(options[i].get_slicec(':', 0), options[i].get_slicec(':', 1).to_int());
- } else {
- menu->add_item(options[i], i);
- }
+ Vector<String> text_split = options[i].split(":");
+ if (text_split.size() != 1)
+ current_val = text_split[1].to_int();
+ menu->add_item(text_split[0]);
+ menu->set_item_metadata(i, current_val);
+ current_val += 1;
}
menu->set_position(get_position());
menu->popup();
@@ -966,7 +968,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
RES r = v;
if (r.is_valid() && r->get_path().is_resource_file()) {
menu->add_separator();
- menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
+ menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
}
} else {
}
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 9042bdc7c1..a8c97be936 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -394,6 +394,8 @@ void PropertySelector::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
connect("confirmed", this, "_confirmed");
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ disconnect("confirmed", this, "_confirmed");
}
}
diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp
index e48a0022e8..8dacc3c142 100644
--- a/editor/quick_open.cpp
+++ b/editor/quick_open.cpp
@@ -261,6 +261,8 @@ void EditorQuickOpen::_notification(int p_what) {
search_box->set_right_icon(get_icon("Search", "EditorIcons"));
search_box->set_clear_button_enabled(true);
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ disconnect("confirmed", this, "_confirmed");
}
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index a6a014f3bd..fe438236c9 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -53,6 +53,13 @@ void SceneTreeDock::_nodes_drag_begin() {
}
}
+void SceneTreeDock::_quick_open() {
+ Vector<String> files = quick_open->get_selected_files();
+ for (int i = 0; i < files.size(); i++) {
+ instance(files[i]);
+ }
+}
+
void SceneTreeDock::_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
@@ -319,16 +326,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
- file->clear_filters();
- for (int i = 0; i < extensions.size(); i++) {
-
- file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
- }
-
- file->popup_centered_ratio();
+ quick_open->popup_dialog("PackedScene", true);
+ quick_open->set_title(TTR("Instance Child Scene"));
} break;
case TOOL_REPLACE: {
@@ -359,15 +358,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
ScriptLanguage *l = ScriptServer::get_language(i);
if (l->get_type() == existing->get_class()) {
String name = l->get_global_class_name(existing->get_path());
- if (ScriptServer::is_global_class(name)) {
- if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
- inherits = editor->get_editor_data().script_class_get_base(name);
- } else if (l->can_inherit_from_file()) {
- inherits = "\"" + existing->get_path() + "\"";
- }
- } else {
+ if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
+ inherits = name;
+ } else if (l->can_inherit_from_file()) {
inherits = "\"" + existing->get_path() + "\"";
}
+ break;
}
}
}
@@ -574,6 +570,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node);
editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename());
editor_data->get_undo_redo().add_do_method(root, "set_filename", String());
+ editor_data->get_undo_redo().add_do_method(node, "set_owner", (Object *)NULL);
+ editor_data->get_undo_redo().add_do_method(root, "set_owner", node);
_node_replace_owner(root, root, node, MODE_DO);
editor_data->get_undo_redo().add_undo_method(root, "set_filename", root->get_filename());
@@ -581,6 +579,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
editor_data->get_undo_redo().add_undo_method(node, "remove_child", root);
editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root);
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node);
+ editor_data->get_undo_redo().add_undo_method(root, "set_owner", (Object *)NULL);
+ editor_data->get_undo_redo().add_undo_method(node, "set_owner", root);
+
_node_replace_owner(root, root, root, MODE_UNDO);
editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree");
@@ -982,7 +983,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->get_owner()) {
+ if (p_node->get_owner() == p_base && p_node != p_root) {
UndoRedo *undo_redo = &editor_data->get_undo_redo();
switch (p_mode) {
case MODE_BIDI: {
@@ -1302,6 +1303,13 @@ bool SceneTreeDock::_validate_no_foreign() {
return false;
}
+ // When edited_scene inherits from another one the root Node will be the parent Scene,
+ // we don't want to consider that Node a foreign one otherwise we would not be able to
+ // delete it
+ if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene == E->get()) {
+ continue;
+ }
+
if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get())) >= 0) {
accept->set_text(TTR("Can't operate on nodes the current scene inherits from!"));
@@ -1492,6 +1500,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
editor_data->get_undo_redo().commit_action();
editor->push_item(p_script.operator->());
+ _update_script_button();
}
void SceneTreeDock::_toggle_editable_children() {
@@ -1607,9 +1616,17 @@ void SceneTreeDock::_delete_confirm() {
void SceneTreeDock::_update_script_button() {
if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) {
- button_create_script->show();
+ Node *n = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0];
+ if (n->get_script().is_null()) {
+ button_create_script->show();
+ button_clear_script->hide();
+ } else {
+ button_create_script->hide();
+ button_clear_script->show();
+ }
} else {
- button_create_script->hide();
+ button_create_script->show();
+ button_clear_script->hide();
}
}
@@ -2054,9 +2071,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
existing_script = selected->get_script();
}
- menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
+ if (!existing_script.is_valid()) {
+ menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT);
+ }
if (selection.size() > 1 || existing_script.is_valid()) {
menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT);
+ menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT);
}
menu->add_separator();
@@ -2283,6 +2303,7 @@ void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_new_scene_from"), &SceneTreeDock::_new_scene_from);
ClassDB::bind_method(D_METHOD("_nodes_dragged"), &SceneTreeDock::_nodes_dragged);
ClassDB::bind_method(D_METHOD("_files_dropped"), &SceneTreeDock::_files_dropped);
+ ClassDB::bind_method(D_METHOD("_quick_open"), &SceneTreeDock::_quick_open);
ClassDB::bind_method(D_METHOD("_script_dropped"), &SceneTreeDock::_script_dropped);
ClassDB::bind_method(D_METHOD("_tree_rmb"), &SceneTreeDock::_tree_rmb);
ClassDB::bind_method(D_METHOD("_filter_changed"), &SceneTreeDock::_filter_changed);
@@ -2319,6 +2340,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene"));
ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type"));
ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script"));
+ ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script"));
ED_SHORTCUT("scene_tree/clear_script", TTR("Clear Script"));
ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KEY_MASK_CMD | KEY_UP);
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN);
@@ -2350,6 +2372,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter->set_h_size_flags(SIZE_EXPAND_FILL);
filter->set_placeholder(TTR("Filter nodes"));
filter_hbc->add_child(filter);
+ filter->add_constant_override("minimum_spaces", 0);
filter->connect("text_changed", this, "_filter_changed");
tb = memnew(ToolButton);
@@ -2433,9 +2456,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
accept = memnew(AcceptDialog);
add_child(accept);
- file = memnew(EditorFileDialog);
- add_child(file);
- file->connect("file_selected", this, "instance");
+ quick_open = memnew(EditorQuickOpen);
+ add_child(quick_open);
+ quick_open->connect("quick_open", this, "_quick_open");
set_process_unhandled_key_input(true);
delete_dialog = memnew(ConfirmationDialog);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index eea34a3ec5..3939f4f361 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -36,6 +36,7 @@
#include "editor/editor_data.h"
#include "editor/editor_sub_scene.h"
#include "editor/groups_editor.h"
+#include "editor/quick_open.h"
#include "editor/rename_dialog.h"
#include "editor/reparent_dialog.h"
#include "editor/script_create_dialog.h"
@@ -125,7 +126,7 @@ class SceneTreeDock : public VBoxContainer {
ConfirmationDialog *editable_instance_remove_dialog;
ReparentDialog *reparent_dialog;
- EditorFileDialog *file;
+ EditorQuickOpen *quick_open;
EditorSubScene *import_subscene_dialog;
EditorFileDialog *new_scene_from_dialog;
@@ -194,6 +195,7 @@ class SceneTreeDock : public VBoxContainer {
void _nodes_dragged(Array p_nodes, NodePath p_to, int p_type);
void _files_dropped(Vector<String> p_files, NodePath p_to, int p_type);
void _script_dropped(String p_file, NodePath p_to);
+ void _quick_open();
void _tree_rmb(const Vector2 &p_menu_pos);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 07670bb420..95f0c4870e 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -73,7 +73,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
undo_redo->create_action(TTR("Toggle Visible"));
_toggle_visible(n);
List<Node *> selection = editor_selection->get_selected_node_list();
- if (selection.size() > 1) {
+ if (selection.size() > 1 && selection.find(n) != NULL) {
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
Node *nv = E->get();
ERR_FAIL_COND(!nv);
@@ -665,6 +665,13 @@ void SceneTreeEditor::_renamed() {
Node *n = get_node(np);
ERR_FAIL_COND(!n);
+ // Empty node names are not allowed, so resets it to previous text and show warning
+ if (which->get_text(0).strip_edges().empty()) {
+ which->set_text(0, n->get_name());
+ EditorNode::get_singleton()->show_warning(TTR("No name provided"));
+ return;
+ }
+
String new_name = which->get_text(0);
if (!Node::_validate_node_name(new_name)) {
@@ -973,8 +980,11 @@ void SceneTreeEditor::_editor_settings_changed() {
if (enable_rl) {
tree->add_constant_override("draw_relationship_lines", 1);
tree->add_color_override("relationship_line_color", rl_color);
- } else
+ tree->add_constant_override("draw_guides", 0);
+ } else {
tree->add_constant_override("draw_relationship_lines", 0);
+ tree->add_constant_override("draw_guides", 1);
+ }
}
void SceneTreeEditor::_bind_methods() {
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index 6fbac0d4b5..559ab32505 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -123,8 +123,8 @@ protected:
if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/"))
return false;
- emit_signal("value_edited", p_name, p_value);
prop_values[p_name] = p_value;
+ emit_signal("value_edited", p_name, p_value);
return true;
}
@@ -355,7 +355,7 @@ void ScriptEditorDebugger::_video_mem_request() {
Size2 ScriptEditorDebugger::get_minimum_size() const {
Size2 ms = Control::get_minimum_size();
- ms.y = MAX(ms.y, 250);
+ ms.y = MAX(ms.y, 250 * EDSCALE);
return ms;
}
void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) {
@@ -467,7 +467,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
String type = p_data[1];
Array properties = p_data[2];
- bool is_new_object = false;
if (remote_objects.has(id)) {
debugObj = remote_objects[id];
} else {
@@ -475,10 +474,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
debugObj->remote_object_id = id;
debugObj->type_name = type;
remote_objects[id] = debugObj;
- is_new_object = true;
debugObj->connect("value_edited", this, "_scene_tree_property_value_edited");
}
+ int old_prop_size = debugObj->prop_list.size();
+
+ debugObj->prop_list.clear();
+ int new_props_added = 0;
+ Set<String> changed;
for (int i = 0; i < properties.size(); i++) {
Array prop = properties[i];
@@ -511,18 +514,34 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
}
}
- if (is_new_object) {
- //don't update.. it's the same, instead refresh
- debugObj->prop_list.push_back(pinfo);
- }
+ //always add the property, since props may have been added or removed
+ debugObj->prop_list.push_back(pinfo);
- debugObj->prop_values[pinfo.name] = var;
+ if (!debugObj->prop_values.has(pinfo.name)) {
+ new_props_added++;
+ debugObj->prop_values[pinfo.name] = var;
+ } else {
+
+ if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) {
+ debugObj->prop_values[pinfo.name] = var;
+ changed.insert(pinfo.name);
+ }
+ }
}
if (editor->get_editor_history()->get_current() != debugObj->get_instance_id()) {
editor->push_item(debugObj, "");
} else {
- debugObj->update();
+
+ if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) {
+ //only some may have changed, if so, then update those, if exist
+ for (Set<String>::Element *E = changed.front(); E; E = E->next()) {
+ EditorNode::get_singleton()->get_inspector()->update_property(E->get());
+ }
+ } else {
+ //full update, because props were added or removed
+ debugObj->update();
+ }
}
} else if (p_msg == "message:video_mem") {
@@ -583,6 +602,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
String n = p_data[ofs + i * 2 + 0];
Variant v = p_data[ofs + i * 2 + 1];
+
PropertyHint h = PROPERTY_HINT_NONE;
String hs = String();
@@ -1022,8 +1042,11 @@ void ScriptEditorDebugger::_notification(int p_what) {
if (enable_rl) {
inspect_scene_tree->add_constant_override("draw_relationship_lines", 1);
inspect_scene_tree->add_color_override("relationship_line_color", rl_color);
- } else
+ inspect_scene_tree->add_constant_override("draw_guides", 0);
+ } else {
inspect_scene_tree->add_constant_override("draw_relationship_lines", 0);
+ inspect_scene_tree->add_constant_override("draw_guides", 1);
+ }
} break;
case NOTIFICATION_PROCESS: {
@@ -1272,7 +1295,7 @@ void ScriptEditorDebugger::stop() {
breaked = false;
server->stop();
-
+ _clear_remote_objects();
ppeer->set_stream_peer(Ref<StreamPeer>());
if (connection.is_valid()) {
@@ -2234,7 +2257,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this);
p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this);
- live_debug = false;
+ live_debug = true;
last_path_id = false;
error_count = 0;
warning_count = 0;
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index a321cb16c5..881f20cecb 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -3454,10 +3454,9 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
if (points.size() > 3) {
- QuickHull qh;
Vector<Vector3> varr = Variant(points);
Geometry::MeshData md;
- Error err = qh.build(varr, md);
+ Error err = QuickHull::build(varr, md);
if (err == OK) {
Vector<Vector3> points;
points.resize(md.edges.size() * 2);
diff --git a/gles_builders.py b/gles_builders.py
index 1e63a53f1a..e56ccc4431 100644
--- a/gles_builders.py
+++ b/gles_builders.py
@@ -59,11 +59,11 @@ def include_file_in_legacygl_header(filename, header_data, depth):
included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
- if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None:
+ if include_file_in_legacygl_header(included_file, header_data, depth + 1) is None:
print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
- if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None:
+ if include_file_in_legacygl_header(included_file, header_data, depth + 1) is None:
print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
line = fs.readline()
@@ -231,9 +231,9 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
fd.write("\t_FORCE_INLINE_ void set_conditional(Conditionals p_conditional,bool p_enable) { _set_conditional(p_conditional,p_enable); }\n\n")
fd.write("\t#ifdef DEBUG_ENABLED\n ")
fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; if (!is_version_valid()) return; ERR_FAIL_COND( get_active()!=this ); \n\n ")
- fd.write("\t#else\n ")
+ fd.write("\t#else\n ")
fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; \n\n ")
- fd.write("\t#endif\n")
+ fd.write("\t#endif\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, double p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint8_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n")
diff --git a/main/input_default.cpp b/main/input_default.cpp
index 913c143025..b1084900d6 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -261,6 +261,13 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
+ // Notes on mouse-touch emulation:
+ // - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects
+ // as true mouse events. The only difference is the situation is flagged as emulated so they are not
+ // emulated back to touch events in an endless loop.
+ // - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't
+ // require additional handling by this class.
+
_THREAD_SAFE_METHOD_
Ref<InputEventKey> k = p_event;
@@ -316,11 +323,21 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool
}
}
- if (emulate_mouse_from_touch) {
+ Ref<InputEventScreenTouch> st = p_event;
+
+ if (st.is_valid()) {
+
+ if (st->is_pressed()) {
+ SpeedTrack &track = touch_speed_track[st->get_index()];
+ track.reset();
+ } else {
+ // Since a pointer index may not occur again (OSs may or may not reuse them),
+ // imperatively remove it from the map to keep no fossil entries in it
+ touch_speed_track.erase(st->get_index());
+ }
- Ref<InputEventScreenTouch> st = p_event;
+ if (emulate_mouse_from_touch) {
- if (st.is_valid()) {
bool translate = false;
if (st->is_pressed()) {
if (mouse_from_touch_index == -1) {
@@ -351,10 +368,18 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool
_parse_input_event_impl(button_event, true);
}
}
+ }
+
+ Ref<InputEventScreenDrag> sd = p_event;
+
+ if (sd.is_valid()) {
+
+ SpeedTrack &track = touch_speed_track[sd->get_index()];
+ track.update(sd->get_relative());
+ sd->set_speed(track.speed);
- Ref<InputEventScreenDrag> sd = p_event;
+ if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) {
- if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) {
Ref<InputEventMouseMotion> motion_event;
motion_event.instance();
@@ -404,6 +429,7 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = p_event->is_action_pressed(E->key());
+ action.strength = 0.f;
action_state[E->key()] = action;
}
action_state[E->key()].strength = p_event->get_action_strength(E->key());
@@ -530,13 +556,14 @@ Point2i InputDefault::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_moti
void InputDefault::iteration(float p_step) {
}
-void InputDefault::action_press(const StringName &p_action) {
+void InputDefault::action_press(const StringName &p_action, float p_strength) {
Action action;
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = true;
+ action.strength = p_strength;
action_state[p_action] = action;
}
@@ -548,6 +575,7 @@ void InputDefault::action_release(const StringName &p_action) {
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
action.pressed = false;
+ action.strength = 0.f;
action_state[p_action] = action;
}
diff --git a/main/input_default.h b/main/input_default.h
index b420ec124b..b6767669f0 100644
--- a/main/input_default.h
+++ b/main/input_default.h
@@ -117,6 +117,7 @@ class InputDefault : public Input {
};
SpeedTrack mouse_speed_track;
+ Map<int, SpeedTrack> touch_speed_track;
Map<int, Joypad> joy_names;
int fallback_mapping;
@@ -226,7 +227,7 @@ public:
void set_main_loop(MainLoop *p_main_loop);
void set_mouse_position(const Point2 &p_posf);
- void action_press(const StringName &p_action);
+ void action_press(const StringName &p_action, float p_strength = 1.f);
void action_release(const StringName &p_action);
void iteration(float p_step);
diff --git a/main/main.cpp b/main/main.cpp
index 41ae368087..ca1b03392a 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -722,6 +722,29 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
I = N;
}
+ // Network file system needs to be configured before globals, since globals are based on the
+ // 'project.godot' file which will only be available through the network if this is enabled
+ FileAccessNetwork::configure();
+ if (remotefs != "") {
+
+ file_access_network_client = memnew(FileAccessNetworkClient);
+ int port;
+ if (remotefs.find(":") != -1) {
+ port = remotefs.get_slicec(':', 1).to_int();
+ remotefs = remotefs.get_slicec(':', 0);
+ } else {
+ port = 6010;
+ }
+
+ Error err = file_access_network_client->connect(remotefs, port, remotefs_pass);
+ if (err) {
+ OS::get_singleton()->printerr("Could not connect to remotefs: %s:%i.\n", remotefs.utf8().get_data(), port);
+ goto error;
+ }
+
+ FileAccess::make_default<FileAccessNetwork>(FileAccess::ACCESS_RESOURCES);
+ }
+
if (globals->setup(project_path, main_pack, upwards) == OK) {
found_project = true;
} else {
@@ -729,16 +752,22 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#ifdef TOOLS_ENABLED
editor = false;
#else
- OS::get_singleton()->print("Error: Could not load game path '%s'.\n", project_path.ascii().get_data());
+ String error_msg = "Error: Could not load game data at path '" + project_path + "'. Is the .pck file missing?\n";
+ OS::get_singleton()->print(error_msg.ascii().get_data());
+ OS::get_singleton()->alert(error_msg);
goto error;
#endif
}
GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
+ ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes
GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048);
+ ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater"));
GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10);
+ ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_messages_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_messages_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater"));
GLOBAL_DEF("network/limits/debugger_stdout/max_errors_per_frame", 10);
+ ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_errors_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_errors_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater"));
if (debug_mode == "remote") {
@@ -762,28 +791,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
script_debugger = memnew(ScriptDebuggerLocal);
OS::get_singleton()->initialize_debugging();
}
-
- FileAccessNetwork::configure();
-
- if (remotefs != "") {
-
- file_access_network_client = memnew(FileAccessNetworkClient);
- int port;
- if (remotefs.find(":") != -1) {
- port = remotefs.get_slicec(':', 1).to_int();
- remotefs = remotefs.get_slicec(':', 0);
- } else {
- port = 6010;
- }
-
- Error err = file_access_network_client->connect(remotefs, port, remotefs_pass);
- if (err) {
- OS::get_singleton()->printerr("Could not connect to remotefs: %s:%i.\n", remotefs.utf8().get_data(), port);
- goto error;
- }
-
- FileAccess::make_default<FileAccessNetwork>(FileAccess::ACCESS_RESOURCES);
- }
if (script_debugger) {
//there is a debugger, parse breakpoints
@@ -811,6 +818,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("logging/file_logging/enable_file_logging", false);
GLOBAL_DEF("logging/file_logging/log_path", "user://logs/log.txt");
GLOBAL_DEF("logging/file_logging/max_log_files", 10);
+ ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers
if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) {
String base_path = GLOBAL_GET("logging/file_logging/log_path");
int max_files = GLOBAL_GET("logging/file_logging/max_log_files");
@@ -1001,6 +1009,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
+ ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,120,1,or_greater"));
GLOBAL_DEF("debug/settings/stdout/print_fps", false);
@@ -1009,10 +1018,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF("application/run/frame_delay_msec", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("application/run/frame_delay_msec", PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater")); // No negative numbers
}
OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false));
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(GLOBAL_DEF("application/run/low_processor_mode_sleep_usec", 8000));
+ ProjectSettings::get_singleton()->set_custom_property_info("application/run/low_processor_mode_sleep_usec", PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater")); // No negative numbers
Engine::get_singleton()->set_frame_delay(frame_delay);
@@ -1641,7 +1652,7 @@ bool Main::start() {
GLOBAL_DEF("display/window/stretch/aspect", "ignore");
ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"));
GLOBAL_DEF("display/window/stretch/shrink", 1);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::STRING, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1"));
+ ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::REAL, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1"));
sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true));
sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true));
GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);
diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp
index 26e728d3aa..deaba285cf 100644
--- a/main/tests/test_oa_hash_map.cpp
+++ b/main/tests/test_oa_hash_map.cpp
@@ -92,6 +92,35 @@ MainLoop *test() {
}
}
+ // stress test / test for issue #22928
+ {
+ OAHashMap<int, int> map;
+ int dummy;
+ const int N = 1000;
+ uint32_t *keys = new uint32_t[N];
+
+ Math::seed(0);
+
+ // insert a couple of random keys (with a dummy value, which is ignored)
+ for (int i = 0; i < N; i++) {
+ keys[i] = Math::rand();
+ map.set(keys[i], dummy);
+
+ if (!map.lookup(keys[i], dummy))
+ OS::get_singleton()->print("could not find 0x%X despite it was just inserted!\n", unsigned(keys[i]));
+ }
+
+ // check whether the keys are still present
+ for (int i = 0; i < N; i++) {
+ if (!map.lookup(keys[i], dummy)) {
+ OS::get_singleton()->print("could not find 0x%X despite it has been inserted previously! (not checking the other keys, breaking...)\n", unsigned(keys[i]));
+ break;
+ }
+ }
+
+ delete[] keys;
+ }
+
return NULL;
}
} // namespace TestOAHashMap
diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp
index 74157d63c9..7e19c7bf3e 100644
--- a/main/tests/test_string.cpp
+++ b/main/tests/test_string.cpp
@@ -480,7 +480,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish % frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
//////// INTS
@@ -491,7 +491,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 5 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Int left padded with zeroes.
format = "fish %05d frog";
@@ -500,7 +500,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 00005 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Int left padded with spaces.
format = "fish %5d frog";
@@ -509,7 +509,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 5 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Int right padded with spaces.
format = "fish %-5d frog";
@@ -518,7 +518,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 5 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Int with sign (positive).
format = "fish %+d frog";
@@ -527,7 +527,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish +5 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Negative int.
format = "fish %d frog";
@@ -536,7 +536,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish -5 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Hex (lower)
format = "fish %x frog";
@@ -545,7 +545,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 2d frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Hex (upper)
format = "fish %X frog";
@@ -554,7 +554,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 2D frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Octal
format = "fish %o frog";
@@ -563,7 +563,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 143 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
////// REALS
@@ -574,7 +574,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.990000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real left-padded
format = "fish %11f frog";
@@ -583,7 +583,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.990000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real right-padded
format = "fish %-11f frog";
@@ -592,7 +592,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.990000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real given int.
format = "fish %f frog";
@@ -601,7 +601,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.000000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real with sign (positive).
format = "fish %+f frog";
@@ -610,7 +610,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish +99.990000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real with 1 decimals.
format = "fish %.1f frog";
@@ -619,7 +619,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 100.0 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real with 12 decimals.
format = "fish %.12f frog";
@@ -628,7 +628,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.990000000000 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Real with no decimals.
format = "fish %.f frog";
@@ -637,7 +637,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 100 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
/////// Strings.
@@ -648,7 +648,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish cheese frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// String left-padded
format = "fish %10s frog";
@@ -657,7 +657,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish cheese frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// String right-padded
format = "fish %-10s frog";
@@ -666,7 +666,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish cheese frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
///// Characters
@@ -677,7 +677,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish A frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Character as int.
format = "fish %c frog";
@@ -686,7 +686,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish A frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
///// Dynamic width
@@ -698,7 +698,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish cheese frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Int dynamic width
format = "fish %*d frog";
@@ -708,7 +708,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Float dynamic width
format = "fish %*.*f frog";
@@ -719,7 +719,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == String("fish 99.990 frog") && !error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
///// Errors
@@ -730,7 +730,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "not enough arguments for format string" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// More arguments than formats.
format = "fish %s frog";
@@ -740,7 +740,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "not all arguments converted during string formatting" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Incomplete format.
format = "fish %10";
@@ -749,7 +749,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "incomplete format" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Bad character in format string
format = "fish %&f frog";
@@ -758,7 +758,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "unsupported format character" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Too many decimals.
format = "fish %2.2.2f frog";
@@ -767,7 +767,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "too many decimal points in format" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// * not a number
format = "fish %*f frog";
@@ -777,7 +777,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "* wants number" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Character too long.
format = "fish %c frog";
@@ -786,7 +786,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "%c requires number or single-character string" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
// Character bad type.
format = "fish %c frog";
@@ -795,7 +795,7 @@ bool test_28() {
output = format.sprintf(args, &error);
success = (output == "%c requires number or single-character string" && error);
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
return state;
}
@@ -819,37 +819,127 @@ bool test_29() {
String ip4 = "192.168.0.1";
bool success = ip4.is_valid_ip_address();
OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
ip4 = "192.368.0.1";
success = (!ip4.is_valid_ip_address());
OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
success = ip6.is_valid_ip_address();
OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334";
success = (!ip6.is_valid_ip_address());
OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334";
success = (!ip6.is_valid_ip_address());
OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
ip6 = "2001:0db8::0:8a2e:370:7334";
success = (ip6.is_valid_ip_address());
OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
ip6 = "::ffff:192.168.0.1";
success = (ip6.is_valid_ip_address());
OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL");
- if (!success) state = false;
+ state = state && success;
+
+ return state;
+};
+
+bool test_30() {
+ bool state = true;
+ bool success = true;
+ String input = "bytes2var";
+ String output = "Bytes 2 Var";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "linear2db";
+ output = "Linear 2 Db";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "vector3";
+ output = "Vector 3";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "sha256";
+ output = "Sha 256";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "2db";
+ output = "2 Db";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "PascalCase";
+ output = "Pascal Case";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "PascalPascalCase";
+ output = "Pascal Pascal Case";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "snake_case";
+ output = "Snake Case";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "snake_snake_case";
+ output = "Snake Snake Case";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "sha256sum";
+ output = "Sha 256 Sum";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "cat2dog";
+ output = "Cat 2 Dog";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "function(name)";
+ output = "Function(name)";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "snake_case_function(snake_case_arg)";
+ output = "Snake Case Function(snake Case Arg)";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
+
+ input = "snake_case_function( snake_case_arg )";
+ output = "Snake Case Function( Snake Case Arg )";
+ success = (input.capitalize() == output);
+ state = state && success;
+ OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL");
return state;
};
@@ -887,6 +977,7 @@ TestFunc test_funcs[] = {
test_27,
test_28,
test_29,
+ test_30,
0
};
diff --git a/methods.py b/methods.py
index 0cf05751a8..5fcbc94298 100644
--- a/methods.py
+++ b/methods.py
@@ -330,7 +330,7 @@ def split_lib(self, libname, src_list = None, env_lib = None):
list = []
lib_list = []
- if src_list == None:
+ if src_list is None:
src_list = getattr(env, libname + "_sources")
if type(env_lib) == type(None):
@@ -591,7 +591,7 @@ def generate_vs_project(env, num_jobs):
release_debug_targets = ['bin\\godot.windows.opt.tools.32.exe'] + ['bin\\godot.windows.opt.tools.64.exe']
targets = debug_targets + release_targets + release_debug_targets
if not env.get('MSVS'):
- env['MSVS']['PROJECTSUFFIX'] = '.vcxproj'
+ env['MSVS']['PROJECTSUFFIX'] = '.vcxproj'
env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
env.MSVSProject(
target=['#godot' + env['MSVSPROJECTSUFFIX']],
@@ -628,3 +628,27 @@ def CommandNoCache(env, target, sources, command, **args):
result = env.Command(target, sources, command, **args)
env.NoCache(result)
return result
+
+def detect_darwin_sdk_path(platform, env):
+ sdk_name = ''
+ if platform == 'osx':
+ sdk_name = 'macosx'
+ var_name = 'MACOS_SDK_PATH'
+ elif platform == 'iphone':
+ sdk_name = 'iphoneos'
+ var_name = 'IPHONESDK'
+ elif platform == 'iphonesimulator':
+ sdk_name = 'iphonesimulator'
+ var_name = 'IPHONESDK'
+ else:
+ raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")
+
+ if not env[var_name]:
+ try:
+ sdk_path = subprocess.check_output(['xcrun', '--sdk', sdk_name, '--show-sdk-path']).strip()
+ if sdk_path:
+ env[var_name] = sdk_path
+ except (subprocess.CalledProcessError, OSError) as e:
+ print("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
+ raise
+
diff --git a/misc/dist/document_icon.svg b/misc/dist/document_icon.svg
new file mode 100644
index 0000000000..2652110fa5
--- /dev/null
+++ b/misc/dist/document_icon.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024">
+<path d="M159.143 69.856v884.288h705.714v-665.51L646.08 69.856z" fill="#fff" stroke="#d2d2d2" stroke-width="20"/>
+<g stroke-width=".32">
+ <path d="M723.128 594.284s-.701-4.304-1.111-4.265l-78.083 7.534a12.691 12.691 0 0 0-11.474 11.78l-2.145 30.747-60.408 4.31-4.11-27.866c-.914-6.198-6.326-10.87-12.591-10.87h-82.412c-6.263 0-11.675 4.672-12.59 10.87l-4.111 27.865-60.408-4.31-2.145-30.745a12.692 12.692 0 0 0-11.475-11.783l-78.12-7.532c-.404-.039-.7 4.269-1.104 4.269l-.105 16.897 66.161 10.67 2.167 31.02c.438 6.28 5.505 11.357 11.79 11.808l83.194 5.935c.315.022.626.035.937.035 6.252 0 11.655-4.675 12.57-10.873l4.228-28.671h60.436l4.228 28.671c.913 6.196 6.323 10.87 12.583 10.87.307 0 .613-.01.913-.032l83.206-5.935c6.282-.45 11.351-5.528 11.79-11.808l2.164-31.02 66.133-10.717z" fill="#fff"/>
+ <path d="M300.84 466.87v127.413c.233.003.466.011.697.033l78.113 7.531a8.41 8.41 0 0 1 7.583 7.789l2.409 34.481 68.138 4.862 4.694-31.825a8.41 8.41 0 0 1 8.321-7.184h82.412a8.41 8.41 0 0 1 8.32 7.184l4.693 31.825 68.14-4.862 2.407-34.481a8.414 8.414 0 0 1 7.583-7.789l78.082-7.531c.231-.022.462-.03.695-.033v-10.166l.033-.01V466.87h.289c10.348-13.207 19.923-27.124 29.105-41.972-12.195-20.76-27.137-39.313-43.109-56.502-14.813 7.456-29.2 15.903-42.79 24.891-6.8-6.759-14.458-12.288-21.981-18.067-7.392-5.937-15.722-10.29-23.622-15.361 2.352-17.517 3.515-34.762 3.983-52.76-20.385-10.26-42.123-17.062-64.111-21.947-8.779 14.754-16.807 30.732-23.799 46.352-8.291-1.385-16.621-1.899-24.962-1.998v-.013c-.058 0-.112.013-.162.013-.052 0-.106-.013-.157-.013v.013c-8.356.1-16.68.613-24.973 1.998-6.988-15.62-15.012-31.598-23.804-46.352-21.977 4.885-43.717 11.688-64.1 21.947.466 17.998 1.63 35.243 3.988 52.76-7.916 5.071-16.235 9.424-23.629 15.36-7.512 5.78-15.184 11.31-21.986 18.068-13.589-8.988-27.972-17.435-42.79-24.89-15.971 17.188-30.905 35.74-43.104 56.501 9.178 14.848 18.76 28.765 29.105 41.972z" fill="#478cbf"/>
+ <path d="M653.052 617.91l-2.42 34.664a8.413 8.413 0 0 1-7.792 7.803l-83.204 5.937a8.412 8.412 0 0 1-8.92-7.165l-4.77-32.357h-67.89l-4.772 32.358c-.64 4.354-4.534 7.486-8.92 7.164l-83.203-5.937a8.413 8.413 0 0 1-7.792-7.803l-2.42-34.665-70.238-6.772c.033 7.55.13 15.819.13 17.465 0 74.181 94.102 109.836 211.016 110.246h.287c116.915-.41 210.984-36.065 210.984-110.246 0-1.676.101-9.912.136-17.465z" fill="#478cbf"/>
+ <path d="M448.344 518.591c0 26.01-21.074 47.078-47.074 47.078-25.987 0-47.067-21.069-47.067-47.078 0-25.991 21.08-47.05 47.067-47.05 26 0 47.074 21.059 47.074 47.05" fill="#fff"/>
+ <path d="M437.017 521.384c0 17.251-13.982 31.233-31.246 31.233-17.256 0-31.247-13.982-31.247-31.233 0-17.252 13.99-31.247 31.247-31.247 17.264 0 31.246 13.995 31.246 31.247" fill="#414042"/>
+ <path d="M511.997 569.952c-8.37 0-15.152-6.168-15.152-13.77v-43.336c0-7.596 6.783-13.77 15.152-13.77 8.368 0 15.166 6.174 15.166 13.77v43.336c0 7.602-6.798 13.77-15.166 13.77M575.657 518.591c0 26.01 21.074 47.078 47.078 47.078 25.985 0 47.063-21.069 47.063-47.078 0-25.991-21.078-47.05-47.063-47.05-26.004 0-47.078 21.059-47.078 47.05" fill="#fff"/>
+ <path d="M586.987 521.384c0 17.251 13.978 31.233 31.23 31.233 17.268 0 31.245-13.982 31.245-31.233 0-17.252-13.977-31.247-31.246-31.247-17.251 0-31.23 13.995-31.23 31.247" fill="#414042"/>
+</g>
+</svg> \ No newline at end of file
diff --git a/misc/dist/linux/org.godotengine.Godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml
index 9f3f9e34af..c11317cd35 100644
--- a/misc/dist/linux/org.godotengine.Godot.appdata.xml
+++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml
@@ -27,8 +27,8 @@
</screenshots>
<url type="homepage">https://godotengine.org</url>
<url type="bugtracker">https://github.com/godotengine/godot/issues</url>
- <url type="faq">http://docs.godotengine.org/en/latest/about/faq.html</url>
- <url type="help">http://docs.godotengine.org</url>
+ <url type="faq">https://docs.godotengine.org/en/latest/about/faq.html</url>
+ <url type="help">https://docs.godotengine.org</url>
<url type="donation">https://godotengine.org/donate</url>
<url type="translate">https://hosted.weblate.org/projects/godot-engine/godot</url>
<developer_name>The Godot Engine Community</developer_name>
diff --git a/misc/dist/osx_tools.app/Contents/Info.plist b/misc/dist/osx_tools.app/Contents/Info.plist
index 828a40206d..faa929b818 100755
--- a/misc/dist/osx_tools.app/Contents/Info.plist
+++ b/misc/dist/osx_tools.app/Contents/Info.plist
@@ -38,31 +38,82 @@
<key>CFBundleDocumentTypes</key>
<array>
<dict>
- <key>CFBundleTypeExtensions</key>
- <array>
- <string>scn</string>
- <string>SCN</string>
- </array>
- <key>CFBundleTypeIconFile</key>
- <string>Godot.icns</string>
- <key>CFBundleTypeMIMETypes</key>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>LSItemContentTypes</key>
<array>
- <string>scene/x-scn</string>
+ <string>public.tscn</string>
</array>
- <key>CFBundleTypeName</key>
- <string>Godot Scene</string>
- <key>CFBundleTypeOSTypes</key>
+ <key>NSExportableTypes</key>
<array>
- <string>SCN </string>
+ <string>public.tscn</string>
</array>
+ </dict>
+ <dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
- <key>LSIsAppleDefaultForType</key>
- <true/>
<key>LSItemContentTypes</key>
<array>
- <string>org.godotengine.scn</string>
+ <string>public.godot</string>
+ </array>
+ <key>NSExportableTypes</key>
+ <array>
+ <string>public.godot</string>
+ </array>
+ </dict>
+ </array>
+ <key>UTExportedTypeDeclarations</key>
+ <array>
+ <dict>
+ <key>UTTypeIdentifier</key>
+ <string>public.tscn</string>
+ <key>UTTypeReferenceURL</key>
+ <string></string>
+ <key>UTTypeDescription</key>
+ <string>Godot Scene</string>
+ <key>UTTypeIconFile</key>
+ <string>Document.icns</string>
+ <key>UTTypeConformsTo</key>
+ <array>
+ <string>public.data</string>
+ </array>
+ <key>UTTypeTagSpecification</key>
+ <dict>
+ <key>com.apple.ostype</key>
+ <string>TSCN</string>
+ <key>public.filename-extension</key>
+ <array>
+ <string>scn</string>
+ <string>tscn</string>
+ </array>
+ <key>public.mime-type</key>
+ <string>scene/x-scn</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>UTTypeIdentifier</key>
+ <string>public.godot</string>
+ <key>UTTypeReferenceURL</key>
+ <string></string>
+ <key>UTTypeDescription</key>
+ <string>Godot Project</string>
+ <key>UTTypeIconFile</key>
+ <string>Document.icns</string>
+ <key>UTTypeConformsTo</key>
+ <array>
+ <string>public.data</string>
</array>
+ <key>UTTypeTagSpecification</key>
+ <dict>
+ <key>com.apple.ostype</key>
+ <string>GODP</string>
+ <key>public.filename-extension</key>
+ <array>
+ <string>godot</string>
+ </array>
+ <key>public.mime-type</key>
+ <string>project/x-godot</string>
+ </dict>
</dict>
</array>
</dict>
diff --git a/misc/dist/osx_tools.app/Contents/Resources/Document.icns b/misc/dist/osx_tools.app/Contents/Resources/Document.icns
new file mode 100644
index 0000000000..06d7c65298
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/Document.icns
Binary files differ
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 6b7dcd86e6..c8c782267e 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -397,6 +397,8 @@ public:
virtual void flush_queries();
virtual void finish();
+ virtual bool is_flushing_queries() const { return false; }
+
virtual int get_process_info(ProcessInfo p_info);
CollisionObjectBullet *get_collisin_object(RID p_object) const;
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 402a276f95..441fa7c8af 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -304,7 +304,11 @@ bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) {
}
void RigidCollisionObjectBullet::shape_changed(int p_shape_index) {
- bulletdelete(shapes.write[p_shape_index].bt_shape);
+ ShapeWrapper &shp = shapes.write[p_shape_index];
+ if (shp.bt_shape == mainShape) {
+ mainShape = NULL;
+ }
+ bulletdelete(shp.bt_shape);
reload_shapes();
}
@@ -366,5 +370,8 @@ void RigidCollisionObjectBullet::body_scale_changed() {
void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) {
ShapeWrapper &shp = shapes.write[p_index];
shp.shape->remove_owner(this, p_permanentlyFromThisBody);
+ if (shp.bt_shape == mainShape) {
+ mainShape = NULL;
+ }
bulletdelete(shp.bt_shape);
}
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index a36f1123bc..a94b88d566 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -135,6 +135,15 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT:
sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis] = p_value;
break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_springDamping.m_floats[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_springStiffness.m_floats[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_equilibriumPoint.m_floats[p_axis] = p_value;
+ break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
limits_lower[1][p_axis] = p_value;
set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter
@@ -143,6 +152,9 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
limits_upper[1][p_axis] = p_value;
set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter
break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce = p_value;
+ break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP:
sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP = p_value;
break;
@@ -152,6 +164,15 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce = p_value;
break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springStiffness = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springDamping = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_equilibriumPoint = p_value;
+ break;
default:
ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
WARN_DEPRECATED
@@ -170,6 +191,12 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6
return sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis];
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT:
return sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_springDamping.m_floats[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_springStiffness.m_floats[p_axis];
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT:
+ return sixDOFConstraint->getTranslationalLimitMotor()->m_equilibriumPoint.m_floats[p_axis];
case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
return limits_lower[1][p_axis];
case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
@@ -182,6 +209,12 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6
return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity;
case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springStiffness;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springDamping;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT:
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_equilibriumPoint;
default:
ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
WARN_DEPRECATED;
@@ -215,6 +248,12 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF
case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR:
sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag];
break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING:
+ sixDOFConstraint->getTranslationalLimitMotor()->m_enableSpring[p_axis] = p_value;
+ break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING:
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableSpring = p_value;
+ break;
default:
ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated");
WARN_DEPRECATED
@@ -224,6 +263,5 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF
bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const {
ERR_FAIL_INDEX_V(p_axis, 3, false);
-
return flags[p_axis][p_flag];
}
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 37e7718969..9dd04100ed 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -268,6 +268,7 @@ RigidBodyBullet::RigidBodyBullet() :
can_integrate_forces(false),
maxCollisionsDetection(0),
collisionsCount(0),
+ prev_collision_count(0),
maxAreasWhereIam(10),
areaWhereIamCount(0),
countGravityPointSpaces(0),
@@ -293,6 +294,9 @@ RigidBodyBullet::RigidBodyBullet() :
areasWhereIam.write[i] = NULL;
}
btBody->setSleepingThresholds(0.2, 0.2);
+
+ prev_collision_traces = &collision_traces_1;
+ curr_collision_traces = &collision_traces_2;
}
RigidBodyBullet::~RigidBodyBullet() {
@@ -410,7 +414,14 @@ void RigidBodyBullet::on_collision_filters_change() {
}
void RigidBodyBullet::on_collision_checker_start() {
+
+ prev_collision_count = collisionsCount;
collisionsCount = 0;
+
+ // Swap array
+ Vector<RigidBodyBullet *> *s = prev_collision_traces;
+ prev_collision_traces = curr_collision_traces;
+ curr_collision_traces = s;
}
void RigidBodyBullet::on_collision_checker_end() {
@@ -433,10 +444,20 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const
cd.other_object_shape = p_other_shape_index;
cd.local_shape = p_local_shape_index;
+ curr_collision_traces->write[collisionsCount] = p_otherObject;
+
++collisionsCount;
return true;
}
+bool RigidBodyBullet::was_colliding(RigidBodyBullet *p_other_object) {
+ for (int i = prev_collision_count - 1; 0 <= i; --i) {
+ if ((*prev_collision_traces)[i] == p_other_object)
+ return true;
+ }
+ return false;
+}
+
void RigidBodyBullet::assert_no_constraints() {
if (btBody->getNumConstraintRefs()) {
WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body.");
@@ -797,7 +818,10 @@ void RigidBodyBullet::reload_shapes() {
const btScalar mass = invMass == 0 ? 0 : 1 / invMass;
if (mainShape) {
- btVector3 inertia;
+ // inertia initialised zero here because some of bullet's collision
+ // shapes incorrectly do not set the vector in calculateLocalIntertia.
+ // Arbitrary zero is preferable to undefined behaviour.
+ btVector3 inertia(0, 0, 0);
mainShape->calculateLocalInertia(mass, inertia);
btBody->setMassProps(mass, inertia);
}
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index 26e5018c87..0696073d21 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -205,9 +205,15 @@ private:
bool can_integrate_forces;
Vector<CollisionData> collisions;
+ Vector<RigidBodyBullet *> collision_traces_1;
+ Vector<RigidBodyBullet *> collision_traces_2;
+ Vector<RigidBodyBullet *> *prev_collision_traces;
+ Vector<RigidBodyBullet *> *curr_collision_traces;
+
// these parameters are used to avoid vector resize
int maxCollisionsDetection;
int collisionsCount;
+ int prev_collision_count;
Vector<AreaBullet *> areasWhereIam;
// these parameters are used to avoid vector resize
@@ -244,9 +250,17 @@ public:
virtual void on_collision_checker_end();
void set_max_collisions_detection(int p_maxCollisionsDetection) {
+
+ ERR_FAIL_COND(0 > p_maxCollisionsDetection);
+
maxCollisionsDetection = p_maxCollisionsDetection;
+
collisions.resize(p_maxCollisionsDetection);
+ collision_traces_1.resize(p_maxCollisionsDetection);
+ collision_traces_2.resize(p_maxCollisionsDetection);
+
collisionsCount = 0;
+ prev_collision_count = MIN(prev_collision_count, p_maxCollisionsDetection);
}
int get_max_collisions_detection() {
return maxCollisionsDetection;
@@ -254,6 +268,7 @@ public:
bool can_add_collision() { return collisionsCount < maxCollisionsDetection; }
bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index);
+ bool was_colliding(RigidBodyBullet *p_other_object);
void assert_no_constraints();
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index ab2d1781ad..fed12cd5ed 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -786,16 +786,22 @@ void SpaceBullet::check_body_collision() {
}
const int numContacts = contactManifold->getNumContacts();
+
+ /// Since I don't need report all contacts for these objects,
+ /// So report only the first
#define REPORT_ALL_CONTACTS 0
#if REPORT_ALL_CONTACTS
for (int j = 0; j < numContacts; j++) {
btManifoldPoint &pt = contactManifold->getContactPoint(j);
#else
- // Since I don't need report all contacts for these objects, I'll report only the first
if (numContacts) {
btManifoldPoint &pt = contactManifold->getContactPoint(0);
#endif
- if (pt.getDistance() <= 0.0) {
+ if (
+ pt.getDistance() <= 0.0 ||
+ bodyA->was_colliding(bodyB) ||
+ bodyB->was_colliding(bodyA)) {
+
Vector3 collisionWorldPosition;
Vector3 collisionLocalPosition;
Vector3 normalOnB;
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index d1ef08dc83..a4c34e7583 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -159,6 +159,72 @@ CSGBrush *CSGShape::_get_brush() {
return brush;
}
+int CSGShape::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ return surface.vertices.size() / 3;
+}
+
+int CSGShape::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
+ // always 3
+ return 3;
+}
+
+void CSGShape::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ Vector3 v = surface.verticesw[iFace * 3 + iVert];
+ fvPosOut[0] = v.x;
+ fvPosOut[1] = v.y;
+ fvPosOut[2] = v.z;
+}
+
+void CSGShape::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ Vector3 n = surface.normalsw[iFace * 3 + iVert];
+ fvNormOut[0] = n.x;
+ fvNormOut[1] = n.y;
+ fvNormOut[2] = n.z;
+}
+
+void CSGShape::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ Vector2 t = surface.uvsw[iFace * 3 + iVert];
+ fvTexcOut[0] = t.x;
+ fvTexcOut[1] = t.y;
+}
+
+void CSGShape::mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) {
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ int i = (iFace * 3 + iVert) * 4;
+
+ surface.tansw[i++] = fvTangent[0];
+ surface.tansw[i++] = fvTangent[1];
+ surface.tansw[i++] = fvTangent[2];
+ surface.tansw[i++] = fSign;
+}
+
+void CSGShape::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
+ const tbool bIsOrientationPreserving, const int iFace, const int iVert) {
+
+ ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
+
+ int i = iFace * 3 + iVert;
+ Vector3 normal = surface.normalsw[i];
+ Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]);
+ Vector3 bitangent = Vector3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]);
+ float d = bitangent.dot(normal.cross(tangent));
+
+ i *= 4;
+ surface.tansw[i++] = tangent.x;
+ surface.tansw[i++] = tangent.y;
+ surface.tansw[i++] = tangent.z;
+ surface.tansw[i++] = d < 0 ? -1 : 1;
+}
+
void CSGShape::_update_shape() {
if (parent)
@@ -211,6 +277,9 @@ void CSGShape::_update_shape() {
surfaces.write[i].vertices.resize(face_count[i] * 3);
surfaces.write[i].normals.resize(face_count[i] * 3);
surfaces.write[i].uvs.resize(face_count[i] * 3);
+ if (calculate_tangents) {
+ surfaces.write[i].tans.resize(face_count[i] * 3 * 4);
+ }
surfaces.write[i].last_added = 0;
if (i != surfaces.size() - 1) {
@@ -220,6 +289,9 @@ void CSGShape::_update_shape() {
surfaces.write[i].verticesw = surfaces.write[i].vertices.write();
surfaces.write[i].normalsw = surfaces.write[i].normals.write();
surfaces.write[i].uvsw = surfaces.write[i].uvs.write();
+ if (calculate_tangents) {
+ surfaces.write[i].tansw = surfaces.write[i].tans.write();
+ }
}
//fill arrays
@@ -274,9 +346,19 @@ void CSGShape::_update_shape() {
normal = -normal;
}
- surfaces[idx].verticesw[last + order[j]] = v;
- surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j];
- surfaces[idx].normalsw[last + order[j]] = normal;
+ int k = last + order[j];
+ surfaces[idx].verticesw[k] = v;
+ surfaces[idx].uvsw[k] = n->faces[i].uvs[j];
+ surfaces[idx].normalsw[k] = normal;
+
+ if (calculate_tangents) {
+ // zero out our tangents for now
+ k *= 4;
+ surfaces[idx].tansw[k++] = 0.0;
+ surfaces[idx].tansw[k++] = 0.0;
+ surfaces[idx].tansw[k++] = 0.0;
+ surfaces[idx].tansw[k++] = 0.0;
+ }
}
surfaces.write[idx].last_added += 3;
@@ -287,20 +369,43 @@ void CSGShape::_update_shape() {
//create surfaces
for (int i = 0; i < surfaces.size(); i++) {
+ // calculate tangents for this surface
+ bool have_tangents = calculate_tangents;
+ if (have_tangents) {
+ SMikkTSpaceInterface mkif;
+ mkif.m_getNormal = mikktGetNormal;
+ mkif.m_getNumFaces = mikktGetNumFaces;
+ mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace;
+ mkif.m_getPosition = mikktGetPosition;
+ mkif.m_getTexCoord = mikktGetTexCoord;
+ mkif.m_setTSpace = mikktSetTSpaceDefault;
+ mkif.m_setTSpaceBasic = NULL;
+
+ SMikkTSpaceContext msc;
+ msc.m_pInterface = &mkif;
+ msc.m_pUserData = &surfaces.write[i];
+ have_tangents = genTangSpaceDefault(&msc);
+ }
+ // unset write access
surfaces.write[i].verticesw = PoolVector<Vector3>::Write();
surfaces.write[i].normalsw = PoolVector<Vector3>::Write();
surfaces.write[i].uvsw = PoolVector<Vector2>::Write();
+ surfaces.write[i].tansw = PoolVector<float>::Write();
if (surfaces[i].last_added == 0)
continue;
+ // and convert to surface array
Array array;
array.resize(Mesh::ARRAY_MAX);
array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices;
array[Mesh::ARRAY_NORMAL] = surfaces[i].normals;
array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs;
+ if (have_tangents) {
+ array[Mesh::ARRAY_TANGENT] = surfaces[i].tans;
+ }
int idx = root_mesh->get_surface_count();
root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
@@ -400,6 +505,15 @@ CSGShape::Operation CSGShape::get_operation() const {
return operation;
}
+void CSGShape::set_calculate_tangents(bool p_calculate_tangents) {
+ calculate_tangents = p_calculate_tangents;
+ _make_dirty();
+}
+
+bool CSGShape::is_calculating_tangents() const {
+ return calculate_tangents;
+}
+
void CSGShape::_validate_property(PropertyInfo &property) const {
if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
//hide collision if not root
@@ -421,9 +535,13 @@ void CSGShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+ ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents);
+ ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
@@ -438,6 +556,7 @@ CSGShape::CSGShape() {
use_collision = false;
operation = OPERATION_UNION;
snap = 0.001;
+ calculate_tangents = true;
}
CSGShape::~CSGShape() {
@@ -521,6 +640,11 @@ CSGBrush *CSGMesh::_build_brush() {
Array arrays = mesh->surface_get_arrays(i);
+ if (arrays.size() == 0) {
+ _make_dirty();
+ ERR_FAIL_COND_V(arrays.size() == 0, NULL);
+ }
+
PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
if (avertices.size() == 0)
continue;
@@ -593,8 +717,8 @@ CSGBrush *CSGMesh::_build_brush() {
mw[j / 3] = mat;
}
} else {
- int is = vertices.size();
- int as = avertices.size();
+ int as = vertices.size();
+ int is = avertices.size();
vertices.resize(as + is);
smooth.resize((as + is) / 3);
@@ -1540,6 +1664,24 @@ CSGBrush *CSGPolygon::_build_brush() {
Path *path = NULL;
Ref<Curve3D> curve;
+ // get bounds for our polygon
+ Vector2 final_polygon_min;
+ Vector2 final_polygon_max;
+ for (int i = 0; i < final_polygon.size(); i++) {
+ Vector2 p = final_polygon[i];
+ if (i == 0) {
+ final_polygon_min = p;
+ final_polygon_max = final_polygon_min;
+ } else {
+ if (p.x < final_polygon_min.x) final_polygon_min.x = p.x;
+ if (p.y < final_polygon_min.y) final_polygon_min.y = p.y;
+
+ if (p.x > final_polygon_max.x) final_polygon_max.x = p.x;
+ if (p.y > final_polygon_max.y) final_polygon_max.y = p.y;
+ }
+ }
+ Vector2 final_polygon_size = final_polygon_max - final_polygon_min;
+
if (mode == MODE_PATH) {
if (!has_node(path_node))
return NULL;
@@ -1631,6 +1773,10 @@ CSGBrush *CSGPolygon::_build_brush() {
v.z -= depth;
}
facesw[face * 3 + k] = v;
+ uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
+ if (i == 0) {
+ uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
+ }
}
smoothw[face] = false;
@@ -1762,6 +1908,7 @@ CSGBrush *CSGPolygon::_build_brush() {
Vector2 p = final_polygon[triangles[j + src[k]]];
Vector3 v = Vector3(p.x, p.y, 0);
facesw[face * 3 + k] = v;
+ uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
}
smoothw[face] = false;
@@ -1779,6 +1926,8 @@ CSGBrush *CSGPolygon::_build_brush() {
Vector2 p = final_polygon[triangles[j + src[k]]];
Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x);
facesw[face * 3 + k] = v;
+ uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
+ uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
}
smoothw[face] = false;
@@ -1864,10 +2013,10 @@ CSGBrush *CSGPolygon::_build_brush() {
};
Vector2 u[4] = {
- Vector2(u1, 0),
Vector2(u1, 1),
- Vector2(u2, 1),
- Vector2(u2, 0)
+ Vector2(u1, 0),
+ Vector2(u2, 0),
+ Vector2(u2, 1)
};
// face 1
@@ -1910,6 +2059,7 @@ CSGBrush *CSGPolygon::_build_brush() {
Vector2 p = final_polygon[triangles[j + src[k]]];
Vector3 v = Vector3(p.x, p.y, 0);
facesw[face * 3 + k] = xf.xform(v);
+ uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
}
smoothw[face] = false;
@@ -1927,6 +2077,8 @@ CSGBrush *CSGPolygon::_build_brush() {
Vector2 p = final_polygon[triangles[j + src[k]]];
Vector3 v = Vector3(p.x, p.y, 0);
facesw[face * 3 + k] = xf.xform(v);
+ uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
+ uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
}
smoothw[face] = false;
@@ -1951,6 +2103,9 @@ CSGBrush *CSGPolygon::_build_brush() {
} else {
aabb.expand_to(facesw[i]);
}
+
+ // invert UVs on the Y-axis OpenGL = upside down
+ uvsw[i].y = 1.0 - uvsw[i].y;
}
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 6898cdaf64..0a4bb5f665 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -36,6 +36,7 @@
#include "csg.h"
#include "scene/3d/visual_instance.h"
#include "scene/resources/concave_polygon_shape.h"
+#include "thirdparty/misc/mikktspace.h"
class CSGShape : public VisualInstance {
GDCLASS(CSGShape, VisualInstance);
@@ -63,6 +64,8 @@ private:
Ref<ConcavePolygonShape> root_collision_shape;
RID root_collision_instance;
+ bool calculate_tangents;
+
Ref<ArrayMesh> root_mesh;
struct Vector3Hasher {
@@ -78,14 +81,26 @@ private:
PoolVector<Vector3> vertices;
PoolVector<Vector3> normals;
PoolVector<Vector2> uvs;
+ PoolVector<float> tans;
Ref<Material> material;
int last_added;
PoolVector<Vector3>::Write verticesw;
PoolVector<Vector3>::Write normalsw;
PoolVector<Vector2>::Write uvsw;
+ PoolVector<float>::Write tansw;
};
+ //mikktspace callbacks
+ static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
+ static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace);
+ static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert);
+ static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert);
+ static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert);
+ static void mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
+ static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
+ const tbool bIsOrientationPreserving, const int iFace, const int iVert);
+
void _update_shape();
protected:
@@ -115,6 +130,9 @@ public:
void set_snap(float p_snap);
float get_snap() const;
+ void set_calculate_tangents(bool p_calculate_tangents);
+ bool is_calculating_tangents() const;
+
bool is_root_shape() const;
CSGShape();
~CSGShape();
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
index 90621b94f4..ac3c2342fc 100644
--- a/modules/csg/doc_classes/CSGShape.xml
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -20,6 +20,9 @@
</method>
</methods>
<members>
+ <member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents">
+ Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child.
+ </member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 6d990f6f6f..e057a435ac 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -7,7 +7,7 @@
A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
<link>http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<demos>
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 7ca40c1b8b..3e2068b8db 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -59,6 +59,8 @@ if env['builtin_freetype']:
if env['platform'] == 'uwp':
# Include header for UWP to fix build issues
env_freetype.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
+ # Globally too, as freetype is used in scene (see bottom)
+ env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
sfnt = thirdparty_dir + 'src/sfnt/sfnt.c'
if env['platform'] == 'javascript':
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index ecde73ae1c..f07fdef488 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -243,12 +243,12 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable");
}
GDNative::GDNative() {
@@ -268,7 +268,7 @@ void GDNative::_bind_methods() {
ClassDB::bind_method(D_METHOD("call_native", "calling_type", "procedure_name", "arguments"), &GDNative::call_native);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
}
void GDNative::set_library(Ref<GDNativeLibrary> p_library) {
@@ -306,6 +306,13 @@ bool GDNative::initialize() {
#elif defined(UWP_ENABLED)
// On UWP we use a relative path from the app
String path = lib_path.replace("res://", "");
+#elif defined(OSX_ENABLED)
+ // On OSX the exported libraries are located under the Frameworks directory.
+ // So we need to replace the library path.
+ String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
+ if (!FileAccess::exists(path)) {
+ path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file());
+ }
#else
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
#endif
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index 1fb0ff0500..a30cc09bf6 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -318,6 +318,38 @@ void GDAPI godot_array_destroy(godot_array *p_self) {
((Array *)p_self)->~Array();
}
+godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep) {
+ const Array *self = (const Array *)p_self;
+ godot_array res;
+ Array *val = (Array *)&res;
+ memnew_placement(val, Array);
+ *val = self->duplicate(p_deep);
+ return res;
+}
+
+godot_variant GDAPI godot_array_max(const godot_array *p_self) {
+ const Array *self = (const Array *)p_self;
+ godot_variant v;
+ Variant *val = (Variant *)&v;
+ memnew_placement(val, Variant);
+ *val = self->max();
+ return v;
+}
+
+godot_variant GDAPI godot_array_min(const godot_array *p_self) {
+ const Array *self = (const Array *)p_self;
+ godot_variant v;
+ Variant *val = (Variant *)&v;
+ memnew_placement(val, Variant);
+ *val = self->min();
+ return v;
+}
+
+void GDAPI godot_array_shuffle(godot_array *p_self) {
+ Array *self = (Array *)p_self;
+ self->shuffle();
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 70d2814577..d88499ade1 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -282,6 +282,15 @@ godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self
return raw_dest;
}
+godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t) {
+ godot_basis raw_dest;
+ Basis *dest = (Basis *)&raw_dest;
+ const Basis *self = (const Basis *)p_self;
+ const Basis *b = (const Basis *)p_b;
+ *dest = self->slerp(*b, p_t);
+ return raw_dest;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp
index 4089f4458a..79f0c71b5e 100644
--- a/modules/gdnative/gdnative/color.cpp
+++ b/modules/gdnative/gdnative/color.cpp
@@ -116,6 +116,26 @@ godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) {
return self->to_rgba32();
}
+godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_abgr32();
+}
+
+godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_abgr64();
+}
+
+godot_int GDAPI godot_color_to_argb64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_argb64();
+}
+
+godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_rgba64();
+}
+
godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) {
const Color *self = (const Color *)p_self;
return self->to_argb32();
@@ -156,6 +176,27 @@ godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color
return dest;
}
+godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->darkened(p_amount);
+ return dest;
+}
+
+godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->from_hsv(p_h, p_s, p_v, p_a);
+ return dest;
+}
+
+godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->lightened(p_amount);
+ return dest;
+}
+
godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha) {
godot_string dest;
const Color *self = (const Color *)p_self;
diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp
index f24facaae8..cf82940e09 100644
--- a/modules/gdnative/gdnative/node_path.cpp
+++ b/modules/gdnative/gdnative/node_path.cpp
@@ -110,6 +110,15 @@ godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, c
return *self == *b;
}
+godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self) {
+ const NodePath *self = (const NodePath *)p_self;
+ godot_node_path res;
+ NodePath *val = (NodePath *)&res;
+ memnew_placement(val, NodePath);
+ *val = self->get_as_property_path();
+ return res;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
index ddec77edcd..2594759508 100644
--- a/modules/gdnative/gdnative/quat.cpp
+++ b/modules/gdnative/gdnative/quat.cpp
@@ -225,6 +225,12 @@ godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self) {
return raw_dest;
}
+void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle) {
+ Quat *self = (Quat *)p_self;
+ const Vector3 *axis = (const Vector3 *)p_axis;
+ self->set_axis_angle(*axis, p_angle);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index 54b98fc4e5..5cbc2712c3 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -109,6 +109,27 @@ godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p
return dest;
}
+godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom);
+ return dest;
+}
+
+godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by);
+ return dest;
+}
+
+godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->abs();
+ return dest;
+}
+
godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to) {
godot_rect2 dest;
const Rect2 *self = (const Rect2 *)p_self;
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 8ca57392a3..0996633b70 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -1285,6 +1285,64 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self) {
return self->is_valid_ip_address();
}
+godot_string GDAPI godot_string_dedent(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ String return_value = self->dedent();
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix) {
+ const String *self = (const String *)p_self;
+ String *prefix = (String *)p_prefix;
+ godot_string result;
+ String return_value = self->trim_prefix(*prefix);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix) {
+ const String *self = (const String *)p_self;
+ String *suffix = (String *)p_suffix;
+ godot_string result;
+ String return_value = self->trim_suffix(*suffix);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
+ const String *self = (const String *)p_self;
+ String *chars = (String *)p_chars;
+ godot_string result;
+ String return_value = self->rstrip(*chars);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor,
+ const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+ const String *self = (const String *)p_self;
+ String *divisor = (String *)p_divisor;
+
+ godot_pool_string_array result;
+ memnew_placement(&result, PoolStringArray);
+ PoolStringArray *proxy = (PoolStringArray *)&result;
+ PoolStringArray::Write proxy_writer = proxy->write();
+ Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit);
+ proxy->resize(tmp_result.size());
+
+ for (int i = 0; i < tmp_result.size(); i++) {
+ proxy_writer[i] = tmp_result[i];
+ }
+
+ return result;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 16a34a9a33..c5a1fa139e 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -14,6 +14,183 @@
"next": null,
"api": [
{
+ "name": "godot_color_to_abgr32",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_abgr64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_argb64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_rgba64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_darkened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_color_from_hsv",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_h"],
+ ["const godot_real", "p_s"],
+ ["const godot_real", "p_v"],
+ ["const godot_real", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_color_lightened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_array_duplicate",
+ "return_type": "godot_array",
+ "arguments": [
+ ["const godot_array *", "p_self"],
+ ["const godot_bool", "p_deep"]
+ ]
+ },
+ {
+ "name": "godot_array_max",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_array_min",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_array_shuffle",
+ "return_type": "void",
+ "arguments": [
+ ["godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_basis_slerp",
+ "return_type": "godot_basis",
+ "arguments": [
+ ["const godot_basis *", "p_self"],
+ ["const godot_basis *", "p_b"],
+ ["const godot_real", "p_t"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_as_property_path",
+ "return_type": "godot_node_path",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_set_axis_angle",
+ "return_type": "void",
+ "arguments": [
+ ["godot_quat *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["const godot_real", "p_angle"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_individual",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_real", "p_left"],
+ ["const godot_real", "p_top"],
+ ["const godot_real", "p_right"],
+ ["const godot_real", "p_bottom"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_margin",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_int", "p_margin"],
+ ["const godot_real", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2_abs",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_dedent",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_trim_prefix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_prefix"]
+ ]
+ },
+ {
+ "name": "godot_string_trim_suffix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_suffix"]
+ ]
+ },
+ {
+ "name": "godot_string_rstrip",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_chars"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit",
+ "return_type": "godot_pool_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_divisor"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
"name": "godot_basis_get_quat",
"return_type": "godot_quat",
"arguments": [
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
index f9d1ed9dc5..ff18a3ae69 100644
--- a/modules/gdnative/gdnative_builders.py
+++ b/modules/gdnative/gdnative_builders.py
@@ -87,20 +87,20 @@ def _build_gdnative_api_struct_header(api):
ret_val = []
if core['next']:
ret_val += generate_core_extension_struct(core['next'])
-
+
ret_val += [
'typedef struct godot_gdnative_core_' + ('{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + '_api_struct {',
'\tunsigned int type;',
'\tgodot_gdnative_api_version version;',
'\tconst godot_gdnative_api_struct *next;',
]
-
+
for funcdef in core['api']:
args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
-
+
ret_val += ['} godot_gdnative_core_' + '{0}_{1}'.format(core['version']['major'], core['version']['minor']) + '_api_struct;', '']
-
+
return ret_val
@@ -171,26 +171,26 @@ def _build_gdnative_api_struct_source(api):
ret_val += ['};\n']
return ret_val
-
-
+
+
def get_core_struct_definition(core):
ret_val = []
-
+
if core['next']:
ret_val += get_core_struct_definition(core['next'])
-
+
ret_val += [
'extern const godot_gdnative_core_' + ('{0}_{1}_api_struct api_{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + ' = {',
'\tGDNATIVE_' + core['type'] + ',',
'\t{' + str(core['version']['major']) + ', ' + str(core['version']['minor']) + '},',
'\t' + ('NULL' if not core['next'] else ('(const godot_gdnative_api_struct *)& api_{0}_{1}'.format(core['version']['major'], core['version']['minor']))) + ','
]
-
+
for funcdef in core['api']:
ret_val.append('\t%s,' % funcdef['name'])
-
+
ret_val += ['};\n']
-
+
return ret_val
for ext in api['extensions']:
@@ -204,7 +204,7 @@ def _build_gdnative_api_struct_source(api):
out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,']
out += ['};\n']
-
+
if api['core']['next']:
out += get_core_struct_definition(api['core']['next'])
diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h
index 1e66d133b9..876b8f8e8f 100644
--- a/modules/gdnative/include/gdnative/array.h
+++ b/modules/gdnative/include/gdnative/array.h
@@ -130,6 +130,14 @@ godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_vari
void GDAPI godot_array_destroy(godot_array *p_self);
+godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep);
+
+godot_variant GDAPI godot_array_max(const godot_array *p_self);
+
+godot_variant GDAPI godot_array_min(const godot_array *p_self);
+
+void GDAPI godot_array_shuffle(godot_array *p_self);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index ebe2b1125b..6128bf3ac3 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -127,6 +127,8 @@ godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self
godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b);
+godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h
index 1f0ac8354d..3007dbc6e3 100644
--- a/modules/gdnative/include/gdnative/color.h
+++ b/modules/gdnative/include/gdnative/color.h
@@ -81,6 +81,14 @@ godot_string GDAPI godot_color_as_string(const godot_color *p_self);
godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self);
+godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_argb64(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self);
+
godot_int GDAPI godot_color_to_argb32(const godot_color *p_self);
godot_real GDAPI godot_color_gray(const godot_color *p_self);
@@ -93,6 +101,12 @@ godot_color GDAPI godot_color_linear_interpolate(const godot_color *p_self, cons
godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over);
+godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount);
+
+godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a);
+
+godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount);
+
godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha);
godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b);
diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h
index 2b55e01d13..48fe5b4d3d 100644
--- a/modules/gdnative/include/gdnative/node_path.h
+++ b/modules/gdnative/include/gdnative/node_path.h
@@ -80,6 +80,8 @@ godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self);
godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b);
+godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h
index b1290f745c..634f486e66 100644
--- a/modules/gdnative/include/gdnative/quat.h
+++ b/modules/gdnative/include/gdnative/quat.h
@@ -109,6 +109,8 @@ godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot
godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self);
+void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h
index 4adcb73e3d..47c15c80bd 100644
--- a/modules/gdnative/include/gdnative/rect2.h
+++ b/modules/gdnative/include/gdnative/rect2.h
@@ -77,6 +77,12 @@ godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_ve
godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by);
+godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom);
+
+godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by);
+
+godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self);
+
godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to);
godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b);
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 73245160c1..95ae42a9ec 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -246,6 +246,12 @@ godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self);
+godot_string GDAPI godot_string_dedent(const godot_string *p_self);
+godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix);
+godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix);
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars);
+godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
void GDAPI godot_string_destroy(godot_string *p_self);
#ifdef __cplusplus
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 37e72bf9f8..eb133502c4 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -72,11 +72,11 @@ void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_signal_documentation", "signal_name"), &NativeScript::get_signal_documentation);
ClassDB::bind_method(D_METHOD("get_property_documentation", "path"), &NativeScript::get_property_documentation);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
ADD_GROUP("Script Class", "script_class_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path");
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &NativeScript::_new, MethodInfo(Variant::OBJECT, "new"));
}
@@ -1711,8 +1711,7 @@ void NativeReloadNode::_notification(int p_what) {
}
RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
- ResourceFormatLoaderText rsflt;
- return rsflt.load(p_path, p_original_path, r_error);
+ return ResourceFormatLoaderText::singleton->load(p_path, p_original_path, r_error);
}
void ResourceFormatLoaderNativeScript::get_recognized_extensions(List<String> *p_extensions) const {
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 632970f8c0..4cefdbd7cb 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index c199667270..e4aee842ba 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -72,6 +72,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_word = false;
bool in_function_name = false;
bool in_variable_declaration = false;
+ bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
@@ -220,17 +221,24 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
}
if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') {
+ if (in_function_name) {
+ in_function_args = true;
+ }
+
+ if (in_function_args && str[j] == ')') {
+ in_function_args = false;
+ }
+
+ if (expect_type && prev_is_char) {
expect_type = false;
}
+
if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
expect_type = true;
}
- if (in_variable_declaration || previous_text == "(" || previous_text == ",") {
+ if (in_variable_declaration || in_function_args) {
int k = j;
// Skip space
while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
@@ -244,6 +252,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
}
in_variable_declaration = false;
+ in_function_name = false;
+ in_member_variable = false;
}
if (!in_node_path && in_region == -1 && str[j] == '$') {
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 18272451cd..1f5f5035f9 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -478,8 +478,15 @@ bool GDScript::_update_exports() {
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
E->get()->set_build_failed(true);
}
+ return false;
}
} else {
+ if (!valid) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
+ return false;
+ }
}
if (base_cache.is_valid()) {
@@ -2074,12 +2081,12 @@ GDScriptLanguage::GDScriptLanguage() {
_debug_call_stack_pos = 0;
int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
+ ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
+
if (ScriptDebugger::get_singleton()) {
//debugging enabled!
_debug_max_call_stack = dmcs;
- if (_debug_max_call_stack < 1024)
- _debug_max_call_stack = 1024;
_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
} else {
@@ -2090,6 +2097,7 @@ GDScriptLanguage::GDScriptLanguage() {
#ifdef DEBUG_ENABLED
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
+ GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_"));
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 310c4e21f2..45319c59e7 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1603,13 +1603,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
return OK;
}
-Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
+Error GDScriptCompiler::_parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
Vector<int> bytecode;
CodeGen codegen;
codegen.class_node = p_class;
- codegen.script = p_script;
+ codegen.script = p_script.ptr();
codegen.function_node = p_func;
codegen.stack_max = 0;
codegen.current_line = 0;
@@ -1853,7 +1853,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
if (p_class->owner && p_class->owner->owner) {
// Owner is not root
@@ -1887,7 +1887,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->initializer = NULL;
p_script->subclasses.clear();
- p_script->_owner = p_owner;
+ p_script->_owner = p_owner.ptr();
p_script->tool = p_class->tool;
p_script->name = p_class->name;
@@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
StringName name = p_class->_signals[i].name;
- GDScript *c = p_script;
+ GDScript *c = p_script.ptr();
while (c) {
@@ -2054,7 +2054,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
return OK;
}
-Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
bool has_initializer = false;
@@ -2159,7 +2159,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
return OK;
}
-void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::_make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
@@ -2178,20 +2178,20 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
subclass.instance();
}
- subclass->_owner = const_cast<GDScript *>(p_script);
+ subclass->_owner = const_cast<GDScript *>(p_script.ptr());
class_map.insert(name, subclass);
_make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
}
}
-Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
+Error GDScriptCompiler::compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
error = "";
parser = p_parser;
- main_script = p_script;
+ main_script = p_script.ptr();
const GDScriptParser::Node *root = parser->get_parse_tree();
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 55f5e2b48e..23c6450aa7 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -148,17 +148,17 @@ class GDScriptCompiler {
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
- Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
- Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
+ Error _parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ void _make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;
String error;
public:
- Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false);
+ Error compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state = false);
String get_error() const;
int get_error_line() const;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index ddd9e6b01c..068e8d2d92 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1308,37 +1308,38 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c
return false;
}
- // Check ClassDB
- if (ClassDB::class_exists(p_identifier)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ for (int i = 0; i < 2; i++) {
+ StringName target_id;
+ switch (i) {
+ case 0:
+ // Check ClassDB
+ target_id = p_identifier;
+ break;
+ case 1:
+ // ClassDB again for underscore-prefixed classes
+ target_id = String("_") + p_identifier;
+ break;
}
- return true;
- }
- // ClassDB again for underscore-prefixed classes
- StringName under_id = String("_") + p_identifier;
- if (ClassDB::class_exists(under_id)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ if (ClassDB::class_exists(target_id)) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = target_id;
+ if (Engine::get_singleton()->has_singleton(target_id)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(target_id);
+ } else {
+ r_type.type.is_meta_type = true;
+ const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id);
+ // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript
+ if (!target_elem) {
+ return false;
+ }
+ int idx = target_elem->get();
+ r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ }
+ return true;
}
- return true;
}
// Check autoload singletons
@@ -1999,7 +2000,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context
if (!_static) {
List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods, false, true);
+ bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
+ ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name.begins_with("_")) {
continue;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index f09ff224e8..c67cf124c0 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -275,7 +275,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
uint32_t alloca_size = 0;
- GDScript *_class;
+ GDScript *script;
int ip = 0;
int line = _initial_line;
@@ -286,7 +286,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
line = p_state->line;
ip = p_state->ip;
alloca_size = p_state->stack.size();
- _class = p_state->_class;
+ script = p_state->script.ptr();
p_instance = p_state->instance;
defarg = p_state->defarg;
self = p_state->self;
@@ -368,9 +368,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else {
self = p_instance->owner;
}
- _class = p_instance->script.ptr();
+ script = p_instance->script.ptr();
} else {
- _class = _script;
+ script = this->_script.ptr();
}
}
@@ -395,7 +395,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); \
if (unlikely(!m_v)) \
OPCODE_BREAK;
@@ -404,7 +404,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text);
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text);
#endif
@@ -477,56 +477,53 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(dst, 3);
#ifdef DEBUG_ENABLED
- if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) {
-
- err_text = "Left operand of 'is' is not an instance of anything.";
- OPCODE_BREAK;
- }
if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) {
err_text = "Right operand of 'is' is not a class.";
OPCODE_BREAK;
}
#endif
- Object *obj_A = *a;
- Object *obj_B = *b;
-
- GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
bool extends_ok = false;
+ if (a->get_type() == Variant::OBJECT && a->operator Object *() != NULL) {
+ Object *obj_A = *a;
+ Object *obj_B = *b;
- if (scr_B) {
- //if B is a script, the only valid condition is that A has an instance which inherits from the script
- //in other situation, this shoul return false.
+ GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
- if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this shoul return false.
- GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
- //bool found=false;
- while (cmp) {
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
- if (cmp == scr_B) {
- //inherits from script, all ok
- extends_ok = true;
- break;
- }
+ GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while (cmp) {
+
+ if (cmp == scr_B) {
+ //inherits from script, all ok
+ extends_ok = true;
+ break;
+ }
- cmp = cmp->_base;
+ cmp = cmp->_base;
+ }
}
- }
- } else {
+ } else {
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
#ifdef DEBUG_ENABLED
- if (!nc) {
+ if (!nc) {
- err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
- OPCODE_BREAK;
- }
+ err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+ OPCODE_BREAK;
+ }
#endif
- extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ }
}
*dst = extends_ok;
@@ -1185,7 +1182,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(dst, argc + 3);
- const GDScript *gds = _script;
+ const GDScript *gds = _script.ptr();
const Map<StringName, GDScriptFunction *>::Element *E = NULL;
while (gds->base.ptr()) {
@@ -1256,11 +1253,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
gdfs->state.alloca_size = alloca_size;
- gdfs->state._class = _class;
+ gdfs->state.script = _script;
gdfs->state.ip = ip + ipofs;
gdfs->state.line = line;
gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0;
- gdfs->state.script_id = _class->get_instance_id();
//gdfs->state.result_pos=ip+ipofs-1;
gdfs->state.defarg = defarg;
gdfs->state.instance = p_instance;
@@ -1549,8 +1545,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
String err_file;
if (p_instance)
err_file = p_instance->script->path;
- else if (_class)
- err_file = _class->path;
+ else if (script)
+ err_file = script->path;
if (err_file == "")
err_file = "<built-in>";
String err_func = name;
@@ -1765,15 +1761,20 @@ GDScriptFunction::~GDScriptFunction() {
Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
#ifdef DEBUG_ENABLED
- if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
- ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ // Checking this first since it's faster
+ if (!state.script.is_valid()) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
ERR_FAIL_V(Variant());
}
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
}
+#else
+ if (!is_valid()) {
+ return Variant();
+ }
#endif
Variant arg;
@@ -1841,12 +1842,12 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
return false;
if (p_extended_check) {
+ //script gone? (checking this first since it's faster)
+ if (!state.script.is_valid())
+ return false;
//class instance gone?
if (state.instance_id && !ObjectDB::get_instance(state.instance_id))
return false;
- //script gone?
- if (state.script_id && !ObjectDB::get_instance(state.script_id))
- return false;
}
return true;
@@ -1856,13 +1857,14 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
ERR_FAIL_COND_V(!function, Variant());
#ifdef DEBUG_ENABLED
- if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
- ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ // Checking this first since it's faster
+ if (!state.script.is_valid()) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
ERR_FAIL_V(Variant());
}
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
}
#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index bfb6d673f1..a47070de4f 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -225,7 +225,7 @@ private:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
- GDScript *_script;
+ Ref<GDScript> _script;
StringName name;
Vector<Variant> constants;
@@ -272,15 +272,13 @@ private:
public:
struct CallState {
- ObjectID instance_id; //by debug only
- ObjectID script_id;
-
+ ObjectID instance_id;
GDScriptInstance *instance;
Vector<uint8_t> stack;
int stack_size;
Variant self;
uint32_t alloca_size;
- GDScript *_class;
+ Ref<GDScript> script;
int ip;
int line;
int defarg;
@@ -299,7 +297,7 @@ public:
int get_default_argument_addr(int p_idx) const;
GDScriptDataType get_return_type() const;
GDScriptDataType get_argument_type(int p_idx) const;
- GDScript *get_script() const { return _script; }
+ GDScript *get_script() const { return const_cast<GDScript *>(_script.ptr()); }
StringName get_source() const { return source; }
void debug_get_stack_member_state(int p_line, List<Pair<StringName, int> > *r_stackvars) const;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 5af9bbc05f..2f31d59c46 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -106,6 +106,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"printerr",
"printraw",
"print_debug",
+ "push_error",
+ "push_warning",
"var2str",
"str2var",
"var2bytes",
@@ -707,13 +709,40 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
ScriptLanguage *script = GDScriptLanguage::get_singleton();
if (script->debug_get_stack_level_count() > 0) {
- str += "\n\t";
- str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'";
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
}
print_line(str);
r_ret = Variant();
} break;
+ case PUSH_ERROR: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ ERR_PRINTS(message);
+ r_ret = Variant();
+ } break;
+ case PUSH_WARNING: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ WARN_PRINTS(message);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -1754,11 +1783,25 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case PUSH_ERROR: {
+
+ MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
+ case PUSH_WARNING: {
+
+ MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
+
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
-
} break;
case STR_TO_VAR: {
@@ -1768,10 +1811,10 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
+
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi;
-
} break;
case BYTES_TO_VAR: {
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index e920dd4ece..33d5f27230 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -97,6 +97,8 @@ public:
TEXT_PRINTERR,
TEXT_PRINTRAW,
TEXT_PRINT_DEBUG,
+ PUSH_ERROR,
+ PUSH_WARNING,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
@@ -117,7 +119,6 @@ public:
LEN,
IS_INSTANCE_VALID,
FUNC_MAX
-
};
static const char *get_func_name(Function p_func);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 5f2655eb92..a4bda42b16 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -4353,6 +4353,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.type = Variant::INT;
current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
+ current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
Dictionary enum_values = constant;
List<Variant> keys;
@@ -4839,6 +4840,21 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
if (tokenizer->is_token_literal(0, true)) {
enum_name = tokenizer->get_token_literal();
+
+ if (current_class->constant_expressions.has(enum_name)) {
+ _set_error("A constant named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[enum_name].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == enum_name) {
+ _set_error("A variable named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) {
@@ -4865,26 +4881,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
} else { // tokenizer->is_token_literal(0, true)
- ClassNode::Constant constant;
-
StringName const_id = tokenizer->get_token_literal();
- if (current_class->constant_expressions.has(const_id)) {
- _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " +
- itos(current_class->constant_expressions[const_id].expression->line) + ").");
- return;
- }
-
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == const_id) {
- _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
-
tokenizer->advance();
+ ConstantNode *enum_value_expr;
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
tokenizer->advance();
@@ -4901,23 +4903,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
+ enum_value_expr = static_cast<ConstantNode *>(subexpr);
- if (subexpr_const->value.get_type() != Variant::INT) {
+ if (enum_value_expr->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum");
return;
}
- last_assign = subexpr_const->value;
-
- constant.expression = subexpr_const;
+ last_assign = enum_value_expr->value;
} else {
last_assign = last_assign + 1;
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = last_assign;
- cn->datatype = _type_from_variant(cn->value);
- constant.expression = cn;
+ enum_value_expr = alloc_node<ConstantNode>();
+ enum_value_expr->value = last_assign;
+ enum_value_expr->datatype = _type_from_variant(enum_value_expr->value);
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
@@ -4925,14 +4924,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
if (enum_name != "") {
- const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression);
- enum_dict[const_id] = cn->value;
- }
+ enum_dict[const_id] = enum_value_expr->value;
+ } else {
+ if (current_class->constant_expressions.has(const_id)) {
+ _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[const_id].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == const_id) {
+ _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
- constant.type.has_type = true;
- constant.type.kind = DataType::BUILTIN;
- constant.type.builtin_type = Variant::INT;
- p_class->constant_expressions.insert(const_id, constant);
+ ClassNode::Constant constant;
+ constant.type.has_type = true;
+ constant.type.kind = DataType::BUILTIN;
+ constant.type.builtin_type = Variant::INT;
+ constant.expression = enum_value_expr;
+ p_class->constant_expressions.insert(const_id, constant);
+ }
}
}
@@ -6418,6 +6432,10 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String
StringName native;
if (p_base_type.kind == DataType::GDSCRIPT) {
base_gdscript = p_base_type.script_type;
+ if (base_gdscript.is_null() || !base_gdscript->is_valid()) {
+ // GDScript wasn't properly compíled, don't bother trying
+ return false;
+ }
} else if (p_base_type.kind == DataType::SCRIPT) {
base_script = p_base_type.script_type;
} else if (p_base_type.kind == DataType::NATIVE) {
@@ -6458,6 +6476,12 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String
base_script = base_script->get_base_script();
}
+ if (native == StringName()) {
+ // Empty native class, might happen in some Script implementations
+ // Just ignore it
+ return false;
+ }
+
#ifdef DEBUG_METHODS_ENABLED
// Only native remains
@@ -6709,9 +6733,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
}
}
+ bool rets = false;
return_type.has_type = true;
return_type.kind = DataType::BUILTIN;
- return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name);
+ return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets);
+ // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type.
+ // At least make sure we know that it returns
+ if (rets && return_type.builtin_type == Variant::NIL) {
+ return_type.has_type = false;
+ }
break;
}
@@ -6894,6 +6924,10 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN
Ref<GDScript> gds;
if (base_type.kind == DataType::GDSCRIPT) {
gds = base_type.script_type;
+ if (gds.is_null() || !gds->is_valid()) {
+ // GDScript wasn't properly compíled, don't bother trying
+ return false;
+ }
}
Ref<Script> scr;
@@ -6956,6 +6990,12 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN
scr = scr->get_base_script();
}
+ if (native == StringName()) {
+ // Empty native class, might happen in some Script implementations
+ // Just ignore it
+ return false;
+ }
+
// Check ClassDB
if (!ClassDB::class_exists(native)) {
native = "_" + native.operator String();
@@ -7074,7 +7114,6 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
DataType member_type;
for (int i = 0; i < current_class->variables.size(); i++) {
- ClassNode::Member m = current_class->variables[i];
if (current_class->variables[i].identifier == p_identifier) {
member_type = current_class->variables[i].data_type;
current_class->variables.write[i].usages += 1;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 77e1b7290e..c37142b3c1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -128,8 +128,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"'.'",
"'?'",
"':'",
- "'->'",
"'$'",
+ "'->'",
"'\\n'",
"PI",
"TAU",
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index f13479940d..9b9088dd82 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -10,7 +10,7 @@
A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index a8fdf8cf1f..274a2f0249 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -892,7 +892,7 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1));
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme");
#endif // DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library");
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index fae88042af..126b49832a 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -802,7 +802,9 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
VisualServer::get_singleton()->instance_set_visible(grid_instance[i], false);
}
- VisualServer::get_singleton()->instance_set_visible(cursor_instance, false);
+ if (cursor_instance.is_valid()) {
+ VisualServer::get_singleton()->instance_set_visible(cursor_instance, false);
+ }
return;
}
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index a49137ae73..24d40111cf 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -48,9 +48,9 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
const int image_width = decoder.get_width();
const int image_height = decoder.get_height();
- int comps = decoder.get_num_components();
- if (comps == 3)
- comps = 4; //weird
+ const int comps = decoder.get_num_components();
+ if (comps != 1 && comps != 3)
+ return ERR_FILE_CORRUPT;
if (decoder.begin_decoding() != jpgd::JPGD_SUCCESS)
return ERR_FILE_CORRUPT;
@@ -73,7 +73,19 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
}
jpgd::uint8 *pDst = pImage_data + y * dst_bpl;
- memcpy(pDst, pScan_line, dst_bpl);
+
+ if (comps == 1) {
+ memcpy(pDst, pScan_line, dst_bpl);
+ } else {
+ // For images with more than 1 channel pScan_line will always point to a buffer
+ // containing 32-bit RGBA pixels. Alpha is always 255 and we ignore it.
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x * 4 + 0];
+ pDst[1] = pScan_line[x * 4 + 1];
+ pDst[2] = pScan_line[x * 4 + 2];
+ pDst += 3;
+ }
+ }
}
//all good
@@ -82,7 +94,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
if (comps == 1)
fmt = Image::FORMAT_L8;
else
- fmt = Image::FORMAT_RGBA8;
+ fmt = Image::FORMAT_RGB8;
dw = PoolVector<uint8_t>::Write();
p_image->create(image_width, image_height, 0, fmt, data);
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index e4691ee5c8..0e5dd9b4cf 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -44,7 +44,7 @@ def make_cs_files_header(src, dst, version_dst):
if i > 0:
header.write(', ')
header.write(byte_to_str(buf[buf_idx]))
- inserted_files += '\tr_files.insert("' + filepath_src_rel + '", ' \
+ inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
'CompressedFile(_cs_' + name + '_compressed_size, ' \
'_cs_' + name + '_uncompressed_size, ' \
'_cs_' + name + '_compressed));\n'
@@ -88,9 +88,6 @@ vars.Update(env_mono)
if env_mono['mono_glue']:
env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED'])
-if ARGUMENTS.get('yolo_copy', False):
- env_mono.Append(CPPDEFINES=['YOLO_COPY'])
-
# Configure TLS checks
import tls_configure
@@ -105,6 +102,75 @@ env_mono = conf.Finish()
import os
+def find_nuget_unix():
+ import os.path
+ import sys
+
+ hint_dirs = ['/opt/novell/mono/bin']
+ if sys.platform == 'darwin':
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
+
+ for hint_dir in hint_dirs:
+ hint_path = os.path.join(hint_dir, 'nuget')
+ if os.path.isfile(hint_path):
+ return hint_path
+ elif os.path.isfile(hint_path + '.exe'):
+ return hint_path + '.exe'
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, 'nuget')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
+ return hint_path + '.exe'
+
+ return None
+
+
+def find_nuget_windows():
+ import mono_reg_utils as monoreg
+
+ mono_root = ''
+ bits = env['bits']
+
+ if bits == '32':
+ if os.getenv('MONO32_PREFIX'):
+ mono_root = os.getenv('MONO32_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+ else:
+ if os.getenv('MONO64_PREFIX'):
+ mono_root = os.getenv('MONO64_PREFIX')
+ else:
+ mono_root = monoreg.find_mono_root_dir(bits)
+
+ if mono_root:
+ mono_bin_dir = os.path.join(mono_root, 'bin')
+ nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat')
+
+ if os.path.isfile(nuget_mono):
+ return nuget_mono
+
+ # Standalone NuGet
+
+ for hint_dir in os.environ['PATH'].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, 'nuget.exe')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ if 'NUGET_PATH' in os.environ:
+ hint_path = os.environ['NUGET_PATH']
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ hint_path = os.path.join(hint_path, 'nuget.exe')
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+ return None
+
+
def find_msbuild_unix(filename):
import os.path
import sys
@@ -179,14 +245,17 @@ def mono_build_solution(source, target, env):
import mono_reg_utils as monoreg
from shutil import copyfile
- framework_path = ''
+ sln_path = os.path.abspath(str(source[0]))
+ target_path = os.path.abspath(str(target[0]))
+ framework_path = ''
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
if 'PLATFORM' in msbuild_env:
del msbuild_env['PLATFORM']
+ # Find MSBuild
if os.name == 'nt':
msbuild_info = find_msbuild_windows()
if msbuild_info is None:
@@ -216,11 +285,27 @@ def mono_build_solution(source, target, env):
print('MSBuild path: ' + msbuild_path)
+ # Find NuGet
+ nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix()
+ if nuget_path is None:
+ raise RuntimeError('Cannot find NuGet executable')
+
+ print('NuGet path: ' + nuget_path)
+
+ # Do NuGet restore
+
+ try:
+ subprocess.check_call([nuget_path, 'restore', sln_path])
+ except subprocess.CalledProcessError:
+ raise RuntimeError('GodotSharpTools: NuGet restore failed')
+
+ # Build solution
+
build_config = 'Release'
msbuild_args = [
msbuild_path,
- os.path.abspath(str(source[0])),
+ sln_path,
'/p:Configuration=' + build_config,
]
@@ -230,20 +315,24 @@ def mono_build_solution(source, target, env):
try:
subprocess.check_call(msbuild_args, env=msbuild_env)
except subprocess.CalledProcessError:
- raise RuntimeError('GodotSharpTools build failed')
+ raise RuntimeError('GodotSharpTools: Build failed')
+
+ # Copy files
- src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config))
- dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+ src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
+ dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
+ asm_file = 'GodotSharpTools.dll'
if not os.path.isdir(dst_dir):
if os.path.exists(dst_dir):
raise RuntimeError('Target directory is a file')
os.makedirs(dst_dir)
- asm_file = 'GodotSharpTools.dll'
-
copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
+ # Dependencies
+ copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
+
if env['tools']:
output_dir = Dir('#bin').abspath
editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 8427103ee7..189699cca8 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -265,11 +265,7 @@ def make_template_dir(env, mono_root):
template_dir_name = ''
- if platform == 'windows':
- template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
- elif platform == 'osx':
- template_dir_name = 'data.mono.%s.%s' % (platform, target)
- elif platform == 'x11':
+ if platform in ['windows', 'osx', 'x11']:
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
else:
assert False
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5160d42367..700e518cfc 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -32,6 +32,7 @@
#include <mono/metadata/threads.h>
+#include "core/io/json.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/os/thread.h"
@@ -42,7 +43,6 @@
#include "editor/csharp_project.h"
#include "editor/editor_node.h"
#include "editor/godotsharp_editor.h"
-#include "utils/string_utils.h"
#endif
#include "godotsharp_dirs.h"
@@ -50,6 +50,7 @@
#include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h"
#include "utils/macros.h"
+#include "utils/string_utils.h"
#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -370,6 +371,7 @@ bool CSharpLanguage::supports_builtin_mode() const {
return false;
}
+#ifdef TOOLS_ENABLED
static String variant_type_to_managed_name(const String &p_var_type_name) {
if (p_var_type_name.empty())
@@ -432,8 +434,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
return p_var_type_name;
}
-String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
-#ifdef TOOLS_ENABLED
+String CSharpLanguage::make_function(const String &, const String &p_name, const PoolStringArray &p_args) const {
// FIXME
// - Due to Godot's API limitation this just appends the function to the end of the file
// - Use fully qualified name if there is ambiguity
@@ -449,10 +450,12 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name
s += ")\n{\n // Replace with function body.\n}\n";
return s;
+}
#else
+String CSharpLanguage::make_function(const String &, const String &, const PoolStringArray &) const {
return String();
-#endif
}
+#endif
String CSharpLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
@@ -822,6 +825,49 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
}
#endif
+void CSharpLanguage::project_assembly_loaded() {
+
+ scripts_metadata.clear();
+
+ String scripts_metadata_filename = "scripts_metadata.";
+
+#ifdef TOOLS_ENABLED
+ scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player";
+#else
+#ifdef DEBUG_ENABLED
+ scripts_metadata_filename += "debug";
+#else
+ scripts_metadata_filename += "release";
+#endif
+#endif
+
+ String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename);
+
+ if (FileAccess::exists(scripts_metadata_path)) {
+ String old_json;
+
+ Error ferr = read_all_file_utf8(scripts_metadata_path, old_json);
+ ERR_FAIL_COND(ferr != OK);
+
+ Variant old_dict_var;
+ String err_str;
+ int err_line;
+ Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line);
+ if (json_err != OK) {
+ ERR_PRINTS("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ")");
+ return;
+ }
+
+ scripts_metadata = old_dict_var.operator Dictionary();
+
+ print_verbose("Successfully loaded scripts metadata");
+ } else {
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ ERR_PRINT("Missing scripts metadata file");
+ }
+ }
+}
+
void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("cs");
@@ -1407,6 +1453,8 @@ bool CSharpInstance::_unreference_owner_unsafe() {
if (!unsafe_referenced)
return false; // Already unreferenced
+ unsafe_referenced = false;
+
// Called from CSharpInstance::mono_object_disposed() or ~CSharpInstance()
// Unsafe refcount decrement. The managed instance also counts as a reference.
@@ -2208,10 +2256,27 @@ bool CSharpScript::can_instance() const {
#endif
#ifdef TOOLS_ENABLED
- return valid && (tool || ScriptServer::is_scripting_enabled());
+ bool extra_cond = tool || ScriptServer::is_scripting_enabled();
#else
- return valid;
+ bool extra_cond = true;
#endif
+
+ // FIXME Need to think this through better.
+ // For tool scripts, this will never fire if the class is not found. That's because we
+ // don't know if it's a tool script if we can't find the class to access the attributes.
+ if (extra_cond && !script_class) {
+ if (GDMono::get_singleton()->get_project_assembly() == NULL) {
+ // The project assembly is not loaded
+ ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path());
+ ERR_FAIL_V(NULL);
+ } else {
+ // The project assembly is loaded, but the class could not found
+ ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
+ ERR_FAIL_V(NULL);
+ }
+ }
+
+ return valid && extra_cond;
}
StringName CSharpScript::get_instance_base_type() const {
@@ -2317,20 +2382,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
- if (!script_class) {
- if (GDMono::get_singleton()->get_project_assembly() == NULL) {
- // The project assembly is not loaded
- ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path());
- ERR_FAIL_V(NULL);
- } else {
- // The project assembly is loaded, but the class could not found
- ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
- ERR_FAIL_V(NULL);
- }
- }
-
- ERR_FAIL_COND_V(!valid, NULL);
-
if (native) {
String native_name = native->get_name();
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -2418,7 +2469,23 @@ Error CSharpScript::reload(bool p_keep_state) {
GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly();
if (project_assembly) {
- script_class = project_assembly->get_object_derived_class(name);
+ const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path());
+ if (script_metadata_var) {
+ Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"];
+ const Variant *namespace_ = script_metadata.getptr("namespace");
+ const Variant *class_name = script_metadata.getptr("class_name");
+ ERR_FAIL_NULL_V(namespace_, ERR_BUG);
+ ERR_FAIL_NULL_V(class_name, ERR_BUG);
+ GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String());
+ if (klass) {
+ bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass);
+ ERR_FAIL_COND_V(!obj_type, ERR_BUG);
+ script_class = klass;
+ }
+ } else {
+ // Missing script metadata. Fallback to legacy method
+ script_class = project_assembly->get_object_derived_class(name);
+ }
valid = script_class != NULL;
@@ -2546,29 +2613,14 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
Error CSharpScript::load_source_code(const String &p_path) {
- PoolVector<uint8_t> sourcef;
- Error err;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
- ERR_FAIL_COND_V(err != OK, err);
-
- int len = f->get_len();
- sourcef.resize(len + 1);
- PoolVector<uint8_t>::Write w = sourcef.write();
- int r = f->get_buffer(w.ptr(), len);
- f->close();
- memdelete(f);
- ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
- w[len] = 0;
-
- String s;
- if (s.parse_utf8((const char *)w.ptr())) {
-
- ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
- ERR_FAIL_V(ERR_INVALID_DATA);
+ Error ferr = read_all_file_utf8(p_path, source);
+ if (ferr != OK) {
+ if (ferr == ERR_INVALID_DATA) {
+ ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
+ }
+ ERR_FAIL_V(ferr);
}
- source = s;
-
#ifdef TOOLS_ENABLED
source_changed_cache = true;
#endif
@@ -2718,7 +2770,8 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
"Compile",
ProjectSettings::get_singleton()->globalize_path(p_path));
} else {
- ERR_PRINTS("Cannot add " + p_path + " to the C# project because it could not be created.");
+ ERR_PRINTS("Failed to create C# project");
+ ERR_PRINTS("Cannot add " + p_path + " to the C# project");
}
}
#endif
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 8b0a095890..fc604df2a0 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -67,7 +67,7 @@ class CSharpScript : public Script {
friend class CSharpInstance;
friend class CSharpLanguage;
- friend class CSharpScriptDepSort;
+ friend struct CSharpScriptDepSort;
bool tool;
bool valid;
@@ -275,6 +275,8 @@ class CSharpLanguage : public ScriptLanguage {
int lang_idx;
+ Dictionary scripts_metadata;
+
public:
StringNameCache string_names;
@@ -295,6 +297,10 @@ public:
void reload_assemblies_if_needed(bool p_soft_reload);
#endif
+ void project_assembly_loaded();
+
+ _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; }
+
virtual String get_name() const;
/* LANGUAGE FUNCTIONS */
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
index fceb732426..f9e9f41977 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj
@@ -31,6 +31,9 @@
<Reference Include="System" />
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.Build.Framework" />
+ <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL">
+ <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="StringExtensions.cs" />
@@ -43,5 +46,8 @@
<Compile Include="Utils\OS.cs" />
<Compile Include="Editor\GodotSharpExport.cs" />
</ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project> \ No newline at end of file
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs
index f00ec5a2ad..647d9ac81d 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using DotNet.Globbing;
using Microsoft.Build.Construction;
namespace GodotSharpTools.Project
@@ -7,7 +8,10 @@ namespace GodotSharpTools.Project
{
public static bool HasItem(this ProjectRootElement root, string itemType, string include)
{
- string includeNormalized = include.NormalizePath();
+ GlobOptions globOptions = new GlobOptions();
+ globOptions.Evaluation.CaseInsensitive = false;
+
+ string normalizedInclude = include.NormalizePath();
foreach (var itemGroup in root.ItemGroups)
{
@@ -16,10 +20,14 @@ namespace GodotSharpTools.Project
foreach (var item in itemGroup.Items)
{
- if (item.ItemType == itemType)
+ if (item.ItemType != itemType)
+ continue;
+
+ var glob = Glob.Parse(item.Include.NormalizePath(), globOptions);
+
+ if (glob.IsMatch(normalizedInclude))
{
- if (item.Include.NormalizePath() == includeNormalized)
- return true;
+ return true;
}
}
}
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
index 1d863e6f61..2ce7837a27 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -6,18 +6,24 @@ namespace GodotSharpTools.Project
{
public static class ProjectGenerator
{
+ public const string CoreApiProjectName = "GodotSharp";
+ public const string EditorApiProjectName = "GodotSharpEditor";
+ const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}";
+ const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}";
+
public static string GenCoreApiProject(string dir, string[] compileItems)
{
- string path = Path.Combine(dir, CoreApiProject + ".csproj");
+ string path = Path.Combine(dir, CoreApiProjectName + ".csproj");
ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(CoreApiProject, out mainGroup);
+ var root = CreateLibraryProject(CoreApiProjectName, out mainGroup);
mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml"));
mainGroup.SetProperty("RootNamespace", "Godot");
+ mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid);
- GenAssemblyInfoFile(root, dir, CoreApiProject,
- new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" },
+ GenAssemblyInfoFile(root, dir, CoreApiProjectName,
+ new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" },
new string[] { "System.Runtime.CompilerServices" });
foreach (var item in compileItems)
@@ -27,33 +33,33 @@ namespace GodotSharpTools.Project
root.Save(path);
- return root.GetGuid().ToString().ToUpper();
+ return CoreApiProjectGuid;
}
- public static string GenEditorApiProject(string dir, string coreApiHintPath, string[] compileItems)
+ public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems)
{
- string path = Path.Combine(dir, EditorApiProject + ".csproj");
+ string path = Path.Combine(dir, EditorApiProjectName + ".csproj");
ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(EditorApiProject, out mainGroup);
+ var root = CreateLibraryProject(EditorApiProjectName, out mainGroup);
mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml"));
mainGroup.SetProperty("RootNamespace", "Godot");
+ mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid);
- GenAssemblyInfoFile(root, dir, EditorApiProject);
+ GenAssemblyInfoFile(root, dir, EditorApiProjectName);
foreach (var item in compileItems)
{
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
}
- var coreApiRef = root.AddItem("Reference", CoreApiProject);
- coreApiRef.AddMetadata("HintPath", coreApiHintPath);
+ var coreApiRef = root.AddItem("ProjectReference", coreApiProjPath.Replace("/", "\\"));
coreApiRef.AddMetadata("Private", "False");
root.Save(path);
- return root.GetGuid().ToString().ToUpper();
+ return EditorApiProjectGuid;
}
public static string GenGameProject(string dir, string name, string[] compileItems)
@@ -77,13 +83,13 @@ namespace GodotSharpTools.Project
toolsGroup.AddProperty("WarningLevel", "4");
toolsGroup.AddProperty("ConsolePause", "false");
- var coreApiRef = root.AddItem("Reference", CoreApiProject);
- coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProject + ".dll"));
+ var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
+ coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll"));
coreApiRef.AddMetadata("Private", "False");
- var editorApiRef = root.AddItem("Reference", EditorApiProject);
+ var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
- editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProject + ".dll"));
+ editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll"));
editorApiRef.AddMetadata("Private", "False");
GenAssemblyInfoFile(root, dir, name);
@@ -182,9 +188,6 @@ namespace GodotSharpTools.Project
}
}
- public const string CoreApiProject = "GodotSharp";
- public const string EditorApiProject = "GodotSharpEditor";
-
private const string assemblyInfoTemplate =
@"using System.Reflection;{0}
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs
index 6889ea715f..a13f4fd6ef 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs
@@ -1,5 +1,6 @@
-using System;
+using System.Collections.Generic;
using System.IO;
+using DotNet.Globbing;
using Microsoft.Build.Construction;
namespace GodotSharpTools.Project
@@ -10,8 +11,61 @@ namespace GodotSharpTools.Project
{
var dir = Directory.GetParent(projectPath).FullName;
var root = ProjectRootElement.Open(projectPath);
- if (root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\")))
+ var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\");
+
+ if (root.AddItemChecked(itemType, normalizedInclude))
root.Save();
}
+
+ private static string[] GetAllFilesRecursive(string rootDirectory, string mask)
+ {
+ string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
+
+ // We want relative paths
+ for (int i = 0; i < files.Length; i++) {
+ files[i] = files[i].RelativeToPath(rootDirectory);
+ }
+
+ return files;
+ }
+
+ public static string[] GetIncludeFiles(string projectPath, string itemType)
+ {
+ var result = new List<string>();
+ var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
+
+ GlobOptions globOptions = new GlobOptions();
+ globOptions.Evaluation.CaseInsensitive = false;
+
+ var root = ProjectRootElement.Open(projectPath);
+
+ foreach (var itemGroup in root.ItemGroups)
+ {
+ if (itemGroup.Condition.Length != 0)
+ continue;
+
+ foreach (var item in itemGroup.Items)
+ {
+ if (item.ItemType != itemType)
+ continue;
+
+ string normalizedInclude = item.Include.NormalizePath();
+
+ var glob = Glob.Parse(normalizedInclude, globOptions);
+
+ // TODO Check somehow if path has no blog to avoid the following loop...
+
+ foreach (var existingFile in existingFiles)
+ {
+ if (glob.IsMatch(existingFile))
+ {
+ result.Add(existingFile);
+ }
+ }
+ }
+ }
+
+ return result.ToArray();
+ }
}
}
diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotSharpTools/StringExtensions.cs
index b66c86f8ce..b0436d2f18 100644
--- a/modules/mono/editor/GodotSharpTools/StringExtensions.cs
+++ b/modules/mono/editor/GodotSharpTools/StringExtensions.cs
@@ -36,7 +36,9 @@ namespace GodotSharpTools
public static bool IsAbsolutePath(this string path)
{
- return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot);
+ return path.StartsWith("/", StringComparison.Ordinal) ||
+ path.StartsWith("\\", StringComparison.Ordinal) ||
+ path.StartsWith(driveRoot, StringComparison.Ordinal);
}
public static string CsvEscape(this string value, char delimiter = ',')
diff --git a/modules/mono/editor/GodotSharpTools/packages.config b/modules/mono/editor/GodotSharpTools/packages.config
new file mode 100644
index 0000000000..2c7cb0bd4b
--- /dev/null
+++ b/modules/mono/editor/GodotSharpTools/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" />
+</packages> \ No newline at end of file
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index bcf08026bc..166b3e1324 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -47,7 +47,6 @@
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
#include "csharp_project.h"
-#include "net_solution.h"
#define CS_INDENT " " // 4 whitespaces
@@ -97,7 +96,7 @@
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(3)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(5)
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
@@ -173,23 +172,74 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
return ret;
}
-String BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
+int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
CRASH_COND(p_ienum.constants.empty());
- const List<ConstantInterface>::Element *front = p_ienum.constants.front();
- int candidate_len = front->get().name.length();
+ const ConstantInterface &front_iconstant = p_ienum.constants.front()->get();
+ Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true);
+ int candidate_len = front_parts.size() - 1;
- for (const List<ConstantInterface>::Element *E = front->next(); E; E = E->next()) {
- int j = 0;
- for (j = 0; j < candidate_len && j < E->get().name.length(); j++) {
- if (front->get().name[j] != E->get().name[j])
- break;
+ if (candidate_len == 0)
+ return 0;
+
+ for (const List<ConstantInterface>::Element *E = p_ienum.constants.front()->next(); E; E = E->next()) {
+ const ConstantInterface &iconstant = E->get();
+
+ Vector<String> parts = iconstant.name.split("_", /* p_allow_empty: */ true);
+
+ int i;
+ for (i = 0; i < candidate_len && i < parts.size(); i++) {
+ if (front_parts[i] != parts[i]) {
+ // HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT').
+ bool hardcoded_exc = (i == candidate_len - 1 && ((front_parts[i] == "FLAGS" && parts[i] == "FLAG") || (front_parts[i] == "FLAG" && parts[i] == "FLAGS")));
+ if (!hardcoded_exc)
+ break;
+ }
}
- candidate_len = j;
+ candidate_len = i;
+
+ if (candidate_len == 0)
+ return 0;
}
- return front->get().name.substr(0, candidate_len);
+ return candidate_len;
+}
+
+void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface &p_ienum, int p_prefix_length) {
+
+ if (p_prefix_length > 0) {
+ for (List<ConstantInterface>::Element *E = p_ienum.constants.front(); E; E = E->next()) {
+ int curr_prefix_length = p_prefix_length;
+
+ ConstantInterface &curr_const = E->get();
+
+ String constant_name = curr_const.name;
+
+ Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true);
+
+ if (parts.size() <= curr_prefix_length)
+ continue;
+
+ if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
+ // The name of enum constants may begin with a numeric digit when strip from the enum prefix,
+ // so we make the prefix for this constant one word shorter in those cases.
+ for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
+ if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9')
+ break;
+ }
+ }
+
+ constant_name = "";
+ for (int i = curr_prefix_length; i < parts.size(); i++) {
+ if (i > curr_prefix_length)
+ constant_name += "_";
+ constant_name += parts[i];
+ }
+
+ curr_const.proxy_name = snake_to_pascal_case(constant_name, true);
+ }
+ }
}
void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
@@ -272,7 +322,7 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
}
p_output.push_back(MEMBER_BEGIN "public const int ");
- p_output.push_back(iconstant.name);
+ p_output.push_back(iconstant.proxy_name);
p_output.push_back(" = ");
p_output.push_back(itos(iconstant.value));
p_output.push_back(";");
@@ -334,25 +384,8 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
p_output.push_back(INDENT2 "/// </summary>\n");
}
- String constant_name = iconstant.name;
-
- if (!ienum.prefix.empty() && constant_name.begins_with(ienum.prefix)) {
- constant_name = constant_name.substr(ienum.prefix.length(), constant_name.length());
- }
-
- if (constant_name[0] >= '0' && constant_name[0] <= '9') {
- // The name of enum constants may begin with a numeric digit when strip from the enum prefix,
- // so we make the prefix one word shorter in those cases.
- int i = 0;
- for (i = ienum.prefix.length() - 1; i >= 0; i--) {
- if (ienum.prefix[i] >= 'A' && ienum.prefix[i] <= 'Z')
- break;
- }
- constant_name = ienum.prefix.substr(i, ienum.prefix.length()) + constant_name;
- }
-
p_output.push_back(INDENT2);
- p_output.push_back(constant_name);
+ p_output.push_back(iconstant.proxy_name);
p_output.push_back(" = ");
p_output.push_back(itos(iconstant.value));
p_output.push_back(E != ienum.constants.back() ? ",\n" : "\n");
@@ -367,32 +400,29 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
p_output.push_back(CLOSE_BLOCK); // end of namespace
}
-Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bool p_verbose_output) {
+Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
+ String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
+
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
- if (!DirAccess::exists(p_output_dir)) {
- Error err = da->make_dir_recursive(p_output_dir);
+ if (!DirAccess::exists(proj_dir)) {
+ Error err = da->make_dir_recursive(proj_dir);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
- da->change_dir(p_output_dir);
+ da->change_dir(proj_dir);
da->make_dir("Core");
da->make_dir("ObjectType");
- String core_dir = path_join(p_output_dir, "Core");
- String obj_type_dir = path_join(p_output_dir, "ObjectType");
+ String core_dir = path_join(proj_dir, "Core");
+ String obj_type_dir = path_join(proj_dir, "ObjectType");
Vector<String> compile_items;
- NETSolution solution(API_ASSEMBLY_NAME);
-
- if (!solution.set_path(p_output_dir))
- return ERR_FILE_NOT_FOUND;
-
// Generate source file for global scope constants and enums
{
List<String> constants_source;
@@ -496,15 +526,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
compile_items.push_back(internal_methods_file);
- String guid = CSharpProject::generate_core_api_project(p_output_dir, compile_items);
+ String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items);
- solution.add_new_project(API_ASSEMBLY_NAME, guid);
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj");
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
- Error sln_error = solution.save();
- if (sln_error != OK) {
- ERR_PRINT("Could not to save .NET solution.");
- return sln_error;
- }
+ r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
if (verbose_output)
OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
@@ -512,32 +542,29 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
return OK;
}
-Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output) {
+Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
+ String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
+
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
- if (!DirAccess::exists(p_output_dir)) {
- Error err = da->make_dir_recursive(p_output_dir);
+ if (!DirAccess::exists(proj_dir)) {
+ Error err = da->make_dir_recursive(proj_dir);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
- da->change_dir(p_output_dir);
+ da->change_dir(proj_dir);
da->make_dir("Core");
da->make_dir("ObjectType");
- String core_dir = path_join(p_output_dir, "Core");
- String obj_type_dir = path_join(p_output_dir, "ObjectType");
+ String core_dir = path_join(proj_dir, "Core");
+ String obj_type_dir = path_join(proj_dir, "ObjectType");
Vector<String> compile_items;
- NETSolution solution(EDITOR_API_ASSEMBLY_NAME);
-
- if (!solution.set_path(p_output_dir))
- return ERR_FILE_NOT_FOUND;
-
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
const TypeInterface &itype = E.get();
@@ -598,19 +625,57 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
compile_items.push_back(internal_methods_file);
- String guid = CSharpProject::generate_editor_api_project(p_output_dir, p_core_dll_path, compile_items);
+ String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items);
+
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj");
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
+
+ r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
+
+ if (verbose_output)
+ OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
+
+ return OK;
+}
+
+Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) {
- solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, guid);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
+
+ if (!DirAccess::exists(p_output_dir)) {
+ Error err = da->make_dir_recursive(p_output_dir);
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ }
+
+ DotNetSolution solution(API_SOLUTION_NAME);
+
+ if (!solution.set_path(p_output_dir))
+ return ERR_FILE_NOT_FOUND;
+
+ Error proj_err;
+
+ proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output);
+ if (proj_err != OK) {
+ ERR_PRINT("Generation of the Core API C# project failed");
+ return proj_err;
+ }
+
+ proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output);
+ if (proj_err != OK) {
+ ERR_PRINT("Generation of the Editor API C# project failed");
+ return proj_err;
+ }
Error sln_error = solution.save();
if (sln_error != OK) {
- ERR_PRINT("Could not to save .NET solution.");
+ ERR_PRINT("Failed to save API solution");
return sln_error;
}
- if (verbose_output)
- OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
-
return OK;
}
@@ -646,8 +711,11 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
List<String> output;
output.push_back("using System;\n"); // IntPtr
+ output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable
+
output.push_back("\n#pragma warning disable CS1591 // Disable warning: "
"'Missing XML comment for publicly visible type or member'\n");
+
output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
const DocData::ClassDoc *class_doc = itype.class_doc;
@@ -717,7 +785,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(MEMBER_BEGIN "public const int ");
- output.push_back(iconstant.name);
+ output.push_back(iconstant.proxy_name);
output.push_back(" = ");
output.push_back(itos(iconstant.value));
output.push_back(";");
@@ -757,25 +825,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.push_back(INDENT3 "/// </summary>\n");
}
- String constant_name = iconstant.name;
-
- if (!ienum.prefix.empty() && constant_name.begins_with(ienum.prefix)) {
- constant_name = constant_name.substr(ienum.prefix.length(), constant_name.length());
- }
-
- if (constant_name[0] >= '0' && constant_name[0] <= '9') {
- // The name of enum constants may begin with a numeric digit when strip from the enum prefix,
- // so we make the prefix one word shorter in those cases.
- int i = 0;
- for (i = ienum.prefix.length() - 1; i >= 0; i--) {
- if (ienum.prefix[i] >= 'A' && ienum.prefix[i] <= 'Z')
- break;
- }
- constant_name = ienum.prefix.substr(i, ienum.prefix.length()) + constant_name;
- }
-
output.push_back(INDENT3);
- output.push_back(constant_name);
+ output.push_back(iconstant.proxy_name);
output.push_back(" = ");
output.push_back(itos(iconstant.value));
output.push_back(E != ienum.constants.back() ? ",\n" : "\n");
@@ -1086,7 +1137,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output.push_back(MEMBER_BEGIN "private static IntPtr ");
+ p_output.push_back(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
p_output.push_back(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
p_output.push_back(p_imethod.name);
p_output.push_back("\");\n");
@@ -1843,11 +1894,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
EnumInterface ienum(enum_proxy_cname);
const List<StringName> &constants = enum_map.get(*k);
for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- int *value = class_info->constant_map.getptr(E->get());
+ const StringName &constant_cname = E->get();
+ String constant_name = constant_cname.operator String();
+ int *value = class_info->constant_map.getptr(constant_cname);
ERR_FAIL_NULL(value);
- constant_list.erase(E->get().operator String());
+ constant_list.erase(constant_name);
- ConstantInterface iconstant(snake_to_pascal_case(E->get(), true), *value);
+ ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
iconstant.const_doc = NULL;
for (int i = 0; i < itype.class_doc->constants.size(); i++) {
@@ -1862,7 +1915,9 @@ void BindingsGenerator::_populate_object_type_interfaces() {
ienum.constants.push_back(iconstant);
}
- ienum.prefix = _determine_enum_prefix(ienum);
+ int prefix_length = _determine_enum_prefix(ienum);
+
+ _apply_prefix_to_enum_constants(ienum, prefix_length);
itype.enums.push_back(ienum);
@@ -1876,10 +1931,11 @@ void BindingsGenerator::_populate_object_type_interfaces() {
}
for (const List<String>::Element *E = constant_list.front(); E; E = E->next()) {
- int *value = class_info->constant_map.getptr(E->get());
+ const String &constant_name = E->get();
+ int *value = class_info->constant_map.getptr(StringName(E->get()));
ERR_FAIL_NULL(value);
- ConstantInterface iconstant(snake_to_pascal_case(E->get(), true), *value);
+ ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
iconstant.const_doc = NULL;
for (int i = 0; i < itype.class_doc->constants.size(); i++) {
@@ -1990,18 +2046,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
TypeInterface itype;
-#define INSERT_STRUCT_TYPE(m_type, m_type_in) \
- { \
- itype = TypeInterface::create_value_type(String(#m_type)); \
- itype.c_in = "\tMARSHALLED_IN(" #m_type ", %1, %1_in);\n"; \
- itype.c_out = "\tMARSHALLED_OUT(" #m_type ", %1, ret_out)\n" \
- "\treturn mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(%2), ret_out);\n"; \
- itype.c_arg_in = "&%s_in"; \
- itype.c_type_in = m_type_in; \
- itype.cs_in = "ref %s"; \
- itype.cs_out = "return (%1)%0;"; \
- itype.im_type_out = "object"; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_STRUCT_TYPE(m_type, m_type_in) \
+ { \
+ itype = TypeInterface::create_value_type(String(#m_type)); \
+ itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \
+ itype.c_out = "\treturn MARSHALLED_OUT(" #m_type ", %1);\n"; \
+ itype.c_arg_in = "&%s_in"; \
+ itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \
+ itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
+ itype.cs_in = "ref %s"; \
+ itype.cs_out = "return (%1)%0;"; \
+ itype.im_type_out = itype.cs_type; \
+ builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2, "real_t*")
@@ -2019,26 +2075,31 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
// bool
itype = TypeInterface::create_value_type(String("bool"));
- itype.c_arg_in = "&%s_in";
- // /* MonoBoolean <---> bool
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "bool";
- // */
- itype.c_type_in = "MonoBoolean";
- itype.c_type_out = itype.c_type_in;
+
+ {
+ // MonoBoolean <---> bool
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_type = "bool";
+ itype.c_type_in = "MonoBoolean";
+ itype.c_type_out = itype.c_type_in;
+ itype.c_arg_in = "&%s_in";
+ }
itype.im_type_in = itype.name;
itype.im_type_out = itype.name;
builtin_types.insert(itype.cname, itype);
// int
+ // C interface is the same as that of enums. Remember to apply any
+ // changes done here to TypeInterface::postsetup_enum_type as well
itype = TypeInterface::create_value_type(String("int"));
itype.c_arg_in = "&%s_in";
- // /* ptrcall only supports int64_t and uint64_t
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "int64_t";
- // */
+ {
+ // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_type = "int64_t";
+ }
itype.c_type_in = "int32_t";
itype.c_type_out = itype.c_type_in;
itype.im_type_in = itype.name;
@@ -2047,21 +2108,22 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
// real_t
itype = TypeInterface();
+ itype.name = "float"; // The name is always "float" in Variant, even with REAL_T_IS_DOUBLE.
+ itype.cname = itype.name;
#ifdef REAL_T_IS_DOUBLE
- itype.name = "double";
+ itype.proxy_name = "double";
#else
- itype.name = "float";
+ itype.proxy_name = "float";
#endif
- itype.cname = itype.name;
- itype.proxy_name = itype.name;
- itype.c_arg_in = "&%s_in";
- //* ptrcall only supports double
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "double";
- //*/
- itype.c_type_in = "real_t";
- itype.c_type_out = "real_t";
+ {
+ // The expected type for parameters and return value in ptrcall is 'double'.
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_type = "double";
+ itype.c_type_in = "real_t";
+ itype.c_type_out = "real_t";
+ itype.c_arg_in = "&%s_in";
+ }
itype.cs_type = itype.proxy_name;
itype.im_type_in = itype.proxy_name;
itype.im_type_out = itype.proxy_name;
@@ -2256,7 +2318,7 @@ void BindingsGenerator::_populate_global_constants() {
int constant_value = GlobalConstants::get_global_constant_value(i);
StringName enum_name = GlobalConstants::get_global_constant_enum(i);
- ConstantInterface iconstant(snake_to_pascal_case(constant_name, true), constant_value);
+ ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
iconstant.const_doc = const_doc;
if (enum_name != StringName()) {
@@ -2284,16 +2346,18 @@ void BindingsGenerator::_populate_global_constants() {
TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
- ienum.prefix = _determine_enum_prefix(ienum);
+ int prefix_length = _determine_enum_prefix(ienum);
- // HARDCODED
+ // HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'.
if (ienum.cname == name_cache.enum_Error) {
- if (!ienum.prefix.empty()) { // Just in case it ever changes
+ if (prefix_length > 0) { // Just in case it ever changes
ERR_PRINTS("Prefix for enum 'Error' is not empty");
}
- ienum.prefix = "Err";
+ prefix_length = 1; // 'ERR_'
}
+
+ _apply_prefix_to_enum_constants(ienum, prefix_length);
}
}
@@ -2335,12 +2399,11 @@ void BindingsGenerator::initialize() {
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- const int NUM_OPTIONS = 3;
+ const int NUM_OPTIONS = 2;
int options_left = NUM_OPTIONS;
String mono_glue_option = "--generate-mono-glue";
- String cs_core_api_option = "--generate-cs-core-api";
- String cs_editor_api_option = "--generate-cs-editor-api";
+ String cs_api_option = "--generate-cs-api";
verbose_output = true;
@@ -2354,42 +2417,24 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
if (path_elem) {
if (get_singleton()->generate_glue(path_elem->get()) != OK)
- ERR_PRINT("Mono glue generation failed");
+ ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
elem = elem->next();
} else {
- ERR_PRINTS("--generate-mono-glue: No output directory specified");
+ ERR_PRINTS(mono_glue_option + ": No output directory specified");
}
--options_left;
- } else if (elem->get() == cs_core_api_option) {
+ } else if (elem->get() == cs_api_option) {
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- if (get_singleton()->generate_cs_core_project(path_elem->get()) != OK)
- ERR_PRINT("Generation of solution and C# project for the Core API failed");
+ if (get_singleton()->generate_cs_api(path_elem->get()) != OK)
+ ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
elem = elem->next();
} else {
- ERR_PRINTS(cs_core_api_option + ": No output directory specified");
- }
-
- --options_left;
-
- } else if (elem->get() == cs_editor_api_option) {
-
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- if (path_elem->next()) {
- if (get_singleton()->generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK)
- ERR_PRINT("Generation of solution and C# project for the Editor API failed");
- elem = path_elem->next();
- } else {
- ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified");
- }
- } else {
- ERR_PRINTS(cs_editor_api_option + ": No output directory specified");
+ ERR_PRINTS(cs_api_option + ": No output directory specified");
}
--options_left;
@@ -2401,7 +2446,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
verbose_output = false;
if (options_left != NUM_OPTIONS)
- exit(0);
+ ::exit(0);
}
#endif
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index ad89255ba5..91c474c4f0 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -32,6 +32,7 @@
#define BINDINGS_GENERATOR_H
#include "core/class_db.h"
+#include "dotnet_solution.h"
#include "editor/doc/doc_data.h"
#include "editor/editor_help.h"
@@ -43,20 +44,21 @@ class BindingsGenerator {
struct ConstantInterface {
String name;
+ String proxy_name;
int value;
const DocData::ConstantDoc *const_doc;
ConstantInterface() {}
- ConstantInterface(const String &p_name, int p_value) {
+ ConstantInterface(const String &p_name, const String &p_proxy_name, int p_value) {
name = p_name;
+ proxy_name = p_proxy_name;
value = p_value;
}
};
struct EnumInterface {
StringName cname;
- String prefix;
List<ConstantInterface> constants;
_FORCE_INLINE_ bool operator==(const EnumInterface &p_ienum) const {
@@ -223,7 +225,7 @@ class BindingsGenerator {
String c_in;
/**
- * Determines the name of the variable that will be passed as argument to a ptrcall.
+ * Determines the expression that will be passed as argument to ptrcall.
* By default the value equals the name of the parameter,
* this varies for types that require special manipulation via [c_in].
* Formatting elements:
@@ -333,8 +335,6 @@ class BindingsGenerator {
itype.proxy_name = itype.name;
itype.c_type = itype.name;
- itype.c_type_in = "void*";
- itype.c_type_out = "MonoObject*";
itype.cs_type = itype.proxy_name;
itype.im_type_in = "ref " + itype.proxy_name;
itype.im_type_out = itype.proxy_name;
@@ -385,10 +385,19 @@ class BindingsGenerator {
}
static void postsetup_enum_type(TypeInterface &r_enum_itype) {
- r_enum_itype.c_arg_in = "&%s";
- r_enum_itype.c_type = "int";
- r_enum_itype.c_type_in = "int";
- r_enum_itype.c_type_out = "int";
+ // C interface is the same as that of 'int'. Remember to apply any
+ // changes done here to the 'int' type interface as well
+
+ r_enum_itype.c_arg_in = "&%s_in";
+ {
+ // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
+ r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ r_enum_itype.c_out = "\treturn (%0)%1;\n";
+ r_enum_itype.c_type = "int64_t";
+ }
+ r_enum_itype.c_type_in = "int32_t";
+ r_enum_itype.c_type_out = r_enum_itype.c_type_in;
+
r_enum_itype.cs_type = r_enum_itype.proxy_name;
r_enum_itype.cs_in = "(int)%s";
r_enum_itype.cs_out = "return (%1)%0;";
@@ -513,7 +522,8 @@ class BindingsGenerator {
return p_type.name;
}
- String _determine_enum_prefix(const EnumInterface &p_ienum);
+ int _determine_enum_prefix(const EnumInterface &p_ienum);
+ void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
void _generate_method_icalls(const TypeInterface &p_itype);
@@ -547,8 +557,9 @@ class BindingsGenerator {
static BindingsGenerator *singleton;
public:
- Error generate_cs_core_project(const String &p_output_dir, bool p_verbose_output = true);
- Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
+ Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
+ Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
+ Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
static uint32_t get_version();
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index 6f223b75a3..416108603b 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -30,11 +30,17 @@
#include "csharp_project.h"
+#include "core/io/json.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"
+#include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
+#include "../utils/string_utils.h"
+#include "script_class_parser.h"
namespace CSharpProject {
@@ -58,16 +64,16 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
}
-String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files) {
+String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
Variant dir = p_dir;
- Variant core_dll_path = p_core_dll_path;
+ Variant core_proj_path = p_core_proj_path;
Variant compile_items = p_files;
- const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
+ const Variant *args[3] = { &dir, &core_proj_path, &compile_items };
MonoException *exc = NULL;
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
@@ -102,6 +108,9 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
+ if (!GLOBAL_DEF("mono/project/auto_update_project", true))
+ return;
+
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
@@ -118,4 +127,118 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
ERR_FAIL();
}
}
+
+Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) {
+
+ _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
+
+ if (FileAccess::exists(p_output_path)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Error rm_err = da->remove(p_output_path);
+
+ ERR_EXPLAIN("Failed to remove old scripts metadata file");
+ ERR_FAIL_COND_V(rm_err != OK, rm_err);
+ }
+
+ GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
+
+ void *args[2] = {
+ GDMonoMarshal::mono_string_from_godot(p_project_path),
+ GDMonoMarshal::mono_string_from_godot("Compile")
+ };
+
+ MonoException *exc = NULL;
+ MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ ERR_FAIL_V(FAILED);
+ }
+
+ PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
+ PoolStringArray::Read r = project_files.read();
+
+ Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata();
+ Dictionary new_dict;
+
+ for (int i = 0; i < project_files.size(); i++) {
+ const String &project_file = ("res://" + r[i]).simplify_path();
+
+ uint64_t modified_time = FileAccess::get_modified_time(project_file);
+
+ const Variant *old_file_var = old_dict.getptr(project_file);
+ if (old_file_var) {
+ Dictionary old_file_dict = old_file_var->operator Dictionary();
+
+ if (old_file_dict["modified_time"].operator uint64_t() == modified_time) {
+ // No changes so no need to parse again
+ new_dict[project_file] = old_file_dict;
+ continue;
+ }
+ }
+
+ ScriptClassParser scp;
+ Error err = scp.parse_file(project_file);
+ if (err != OK) {
+ ERR_PRINTS("Parse error: " + scp.get_error());
+ ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file);
+ ERR_FAIL_V(err);
+ }
+
+ Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes();
+
+ bool found = false;
+ Dictionary class_dict;
+
+ String search_name = project_file.get_file().get_basename();
+
+ for (int j = 0; j < classes.size(); j++) {
+ const ScriptClassParser::ClassDecl &class_decl = classes[j];
+
+ if (class_decl.base.size() == 0)
+ continue; // Does not inherit nor implement anything, so it can't be a script class
+
+ String class_cmp;
+
+ if (class_decl.nested) {
+ class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1);
+ } else {
+ class_cmp = class_decl.name;
+ }
+
+ if (class_cmp != search_name)
+ continue;
+
+ class_dict["namespace"] = class_decl.namespace_;
+ class_dict["class_name"] = class_decl.name;
+ class_dict["nested"] = class_decl.nested;
+
+ found = true;
+ break;
+ }
+
+ if (found) {
+ Dictionary file_dict;
+ file_dict["modified_time"] = modified_time;
+ file_dict["class"] = class_dict;
+ new_dict[project_file] = file_dict;
+ }
+ }
+
+ if (new_dict.size()) {
+ String json = JSON::print(new_dict, "", false);
+
+ Error ferr;
+ FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr);
+ ERR_EXPLAIN("Cannot open file for writing: " + p_output_path);
+ ERR_FAIL_COND_V(ferr != OK, ferr);
+ f->store_string(json);
+ f->flush();
+ f->close();
+ memdelete(f);
+ }
+
+ return OK;
+}
+
} // namespace CSharpProject
diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h
index d852139de0..aeeeff50f0 100644
--- a/modules/mono/editor/csharp_project.h
+++ b/modules/mono/editor/csharp_project.h
@@ -40,6 +40,9 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>());
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
+
+Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path);
+
} // namespace CSharpProject
#endif // CSHARP_PROJECT_H
diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/dotnet_solution.cpp
index 8bbd376c9a..ab92e2e378 100644
--- a/modules/mono/editor/net_solution.cpp
+++ b/modules/mono/editor/dotnet_solution.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* net_solution.cpp */
+/* dotnet_solution.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "net_solution.h"
+#include "dotnet_solution.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
@@ -52,33 +52,32 @@
#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject"
-#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU"
+#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU"
#define PROJECT_PLATFORMS_CONFIG \
"\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \
"\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU"
-void NETSolution::add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs) {
- if (projects.has(p_name))
- WARN_PRINT("Overriding existing project.");
-
- ProjectInfo procinfo;
- procinfo.guid = p_guid;
+void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) {
+ projects[p_name] = p_project_info;
+}
- procinfo.configs.push_back("Debug");
- procinfo.configs.push_back("Release");
+bool DotNetSolution::has_project(const String &p_name) const {
+ return projects.find(p_name) != NULL;
+}
- for (int i = 0; i < p_extra_configs.size(); i++) {
- procinfo.configs.push_back(p_extra_configs[i]);
- }
+const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const {
+ return projects[p_name];
+}
- projects[p_name] = procinfo;
+bool DotNetSolution::remove_project(const String &p_name) {
+ return projects.erase(p_name);
}
-Error NETSolution::save() {
+Error DotNetSolution::save() {
bool dir_exists = DirAccess::exists(path);
ERR_EXPLAIN("The directory does not exist.");
- ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
+ ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND);
String projs_decl;
String sln_platform_cfg;
@@ -86,34 +85,40 @@ Error NETSolution::save() {
for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) {
const String &name = E->key();
- const ProjectInfo &procinfo = E->value();
+ const ProjectInfo &proj_info = E->value();
- projs_decl += sformat(PROJECT_DECLARATION, name, name + ".csproj", procinfo.guid);
+ bool is_front = E == projects.front();
- for (int i = 0; i < procinfo.configs.size(); i++) {
- const String &config = procinfo.configs[i];
+ if (!is_front)
+ projs_decl += "\n";
- if (i != 0) {
+ projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid);
+
+ for (int i = 0; i < proj_info.configs.size(); i++) {
+ const String &config = proj_info.configs[i];
+
+ if (i != 0 || !is_front) {
sln_platform_cfg += "\n";
proj_platform_cfg += "\n";
}
sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config);
- proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, procinfo.guid, config);
+ proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config);
}
}
String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg);
- FileAccessRef file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
- ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
+ FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
+ ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE);
file->store_string(content);
file->close();
+ memdelete(file);
return OK;
}
-bool NETSolution::set_path(const String &p_existing_path) {
+bool DotNetSolution::set_path(const String &p_existing_path) {
if (p_existing_path.is_abs_path()) {
path = p_existing_path;
} else {
@@ -126,6 +131,10 @@ bool NETSolution::set_path(const String &p_existing_path) {
return true;
}
-NETSolution::NETSolution(const String &p_name) {
+String DotNetSolution::get_path() {
+ return path;
+}
+
+DotNetSolution::DotNetSolution(const String &p_name) {
name = p_name;
}
diff --git a/modules/mono/editor/net_solution.h b/modules/mono/editor/dotnet_solution.h
index bdff24af0b..629605ad18 100644
--- a/modules/mono/editor/net_solution.h
+++ b/modules/mono/editor/dotnet_solution.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* net_solution.h */
+/* dotnet_solution.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,23 +34,28 @@
#include "core/map.h"
#include "core/ustring.h"
-struct NETSolution {
+struct DotNetSolution {
String name;
- void add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs = Vector<String>());
+ struct ProjectInfo {
+ String guid;
+ String relpath; // Must be relative to the solution directory
+ Vector<String> configs;
+ };
+
+ void add_new_project(const String &p_name, const ProjectInfo &p_project_info);
+ bool has_project(const String &p_name) const;
+ const ProjectInfo &get_project_info(const String &p_name) const;
+ bool remove_project(const String &p_name);
Error save();
bool set_path(const String &p_existing_path);
+ String get_path();
- NETSolution(const String &p_name);
+ DotNetSolution(const String &p_name);
private:
- struct ProjectInfo {
- String guid;
- Vector<String> configs;
- };
-
String path;
Map<String, ProjectInfo> projects;
};
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 8fe6e46b60..0f12fc5243 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -39,6 +39,7 @@
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "bindings_generator.h"
+#include "csharp_project.h"
#include "godotsharp_editor.h"
#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
@@ -226,20 +227,24 @@ void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
MonoBottomPanel::get_singleton()->show_build_tab();
}
-bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) {
+bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) {
- String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln");
- String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config);
- String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll");
+ String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
- if (!FileAccess::exists(api_assembly_file)) {
+ String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
+ String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+
+ String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
+ String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) {
MonoBuildInfo api_build_info(api_sln_file, p_config);
// TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
// once we start to actively document manually maintained C# classes
api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
- show_build_error_dialog("Failed to build " + p_name + " solution.");
+ show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution.");
return false;
}
}
@@ -268,7 +273,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
if (!FileAccess::exists(assembly_dst) ||
FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
@@ -280,8 +285,6 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
Error err = da->copy(assembly_src, assembly_dst);
- memdelete(da);
-
if (err != OK) {
show_build_error_dialog("Failed to copy " + assembly_file);
return false;
@@ -303,61 +306,50 @@ String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
"_" + String::num_uint64(CS_GLUE_VERSION);
}
-bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
+bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
- String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
- String api_build_config = "Release";
+ String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
- EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3);
+ String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
+ String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
- pr.step("Generating " + api_name + " solution", 0);
+ if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(api_name + ".dll"))) {
+ EditorProgress pr("mono_copy_prebuilt_api_assembly", "Copying prebuilt " + api_name + " assembly...", 1);
+ pr.step("Copying " + api_name + " assembly", 0);
+ return GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir, api_name, p_api_type);
+ }
- String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
- .plus_file(_api_folder_name(APIAssembly::API_CORE))
- .plus_file(API_ASSEMBLY_NAME);
- String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
- .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
- .plus_file(EDITOR_API_ASSEMBLY_NAME);
+ String api_build_config = "Release";
- String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
- String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
+ EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3);
- if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
- String core_api_assembly;
+ pr.step("Generating " API_SOLUTION_NAME " solution", 0);
- if (p_api_type == APIAssembly::API_EDITOR) {
- core_api_assembly = core_api_sln_dir.plus_file("bin")
- .plus_file(api_build_config)
- .plus_file(API_ASSEMBLY_NAME ".dll");
- }
+ String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_CORE));
-#ifndef DEBUG_METHODS_ENABLED
-#error "How am I supposed to generate the bindings?"
-#endif
+ String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
+ if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
- Error err = p_api_type == APIAssembly::API_CORE ?
- gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
- gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
-
+ Error err = gen->generate_cs_api(api_sln_dir, gen_verbose);
if (err != OK) {
- show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err));
+ show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
return false;
}
}
- pr.step("Building " + api_name + " solution", 1);
+ pr.step("Building " API_SOLUTION_NAME " solution", 1);
- if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
+ if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config))
return false;
pr.step("Copying " + api_name + " assembly", 2);
// Copy the built assembly to the assemblies directory
- String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
- String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
+ String api_assembly_dir = api_sln_dir.plus_file(api_name).plus_file("bin").plus_file(api_build_config);
if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
return false;
@@ -369,36 +361,11 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
- String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
- String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
-
- if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(API_ASSEMBLY_NAME ".dll"))) {
- EditorProgress pr("mono_copy_prebuilt_api_assemblies",
- "Copying prebuilt " API_ASSEMBLY_NAME " assemblies...", 1);
- pr.step("Copying " API_ASSEMBLY_NAME " assembly", 0);
-
- if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
- API_ASSEMBLY_NAME, APIAssembly::API_CORE)) {
- return false;
- }
- } else {
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
- return false;
- }
-
- if (DirAccess::exists(editor_prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"))) {
- EditorProgress pr("mono_copy_prebuilt_api_assemblies",
- "Copying prebuilt " EDITOR_API_ASSEMBLY_NAME " assemblies...", 1);
- pr.step("Copying " EDITOR_API_ASSEMBLY_NAME " assembly", 0);
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
+ return false;
- if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
- EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
- return false;
- }
- } else {
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
- return false;
- }
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
+ return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 1);
pr.step("Building project solution", 0);
@@ -414,6 +381,20 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
bool GodotSharpBuilds::editor_build_callback() {
+ String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
+ String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
+
+ Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
+ ERR_FAIL_COND_V(metadata_err != OK, false);
+
+ if (FileAccess::exists(scripts_metadata_path_editor)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
+
+ ERR_EXPLAIN("Failed to copy scripts metadata file");
+ ERR_FAIL_COND_V(copy_err != OK, false);
+ }
+
return build_project_blocking("Tools");
}
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index c6dc6b6236..7f38b0aa49 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -84,10 +84,10 @@ public:
bool build(const MonoBuildInfo &p_build_info);
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
- static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
+ static bool build_api_sln(const String &p_api_sln_dir, const String &p_config);
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
- static bool make_api_sln(APIAssembly::Type p_api_type);
+ static bool make_api_assembly(APIAssembly::Type p_api_type);
static bool build_project_blocking(const String &p_config);
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index fca88a7164..f27511ad5e 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -42,8 +42,8 @@
#include "../utils/path_utils.h"
#include "bindings_generator.h"
#include "csharp_project.h"
+#include "dotnet_solution.h"
#include "godotsharp_export.h"
-#include "net_solution.h"
#ifdef OSX_ENABLED
#include "../utils/osx_utils.h"
@@ -71,17 +71,21 @@ bool GodotSharpEditor::_create_project_solution() {
if (guid.length()) {
- NETSolution solution(name);
+ DotNetSolution solution(name);
if (!solution.set_path(path)) {
show_error_dialog(TTR("Failed to create solution."));
return false;
}
- Vector<String> extra_configs;
- extra_configs.push_back("Tools");
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = name + ".csproj";
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
+ proj_info.configs.push_back("Tools");
- solution.add_new_project(name, guid, extra_configs);
+ solution.add_new_project(name, proj_info);
Error sln_error = solution.save();
@@ -90,10 +94,10 @@ bool GodotSharpEditor::_create_project_solution() {
return false;
}
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
return false;
pr.step(TTR("Done"));
@@ -108,6 +112,33 @@ bool GodotSharpEditor::_create_project_solution() {
return true;
}
+void GodotSharpEditor::_make_api_solutions_if_needed() {
+ // I'm sick entirely of ProgressDialog
+ static bool recursion_guard = false;
+ if (!recursion_guard) {
+ recursion_guard = true;
+ _make_api_solutions_if_needed_impl();
+ recursion_guard = false;
+ }
+}
+
+void GodotSharpEditor::_make_api_solutions_if_needed_impl() {
+ // If the project has a solution and C# project make sure the API assemblies are present and up to date
+ String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
+
+ if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) ||
+ GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
+ return;
+ }
+
+ if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) ||
+ GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
+ return; // Redundant? I don't think so
+ }
+}
+
void GodotSharpEditor::_remove_create_sln_menu_option() {
menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN));
@@ -169,6 +200,7 @@ void GodotSharpEditor::_notification(int p_notification) {
void GodotSharpEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution);
+ ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed);
ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option);
ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start);
ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
@@ -390,7 +422,10 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
String sln_path = GodotSharpDirs::get_project_sln_path();
String csproj_path = GodotSharpDirs::get_project_csproj_path();
- if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
+ if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) {
+ // We can't use EditorProgress here. It calls Main::iterarion() and the main loop is not initialized yet.
+ call_deferred("_make_api_solutions_if_needed");
+ } else {
bottom_panel_btn->hide();
menu_popup->add_item(TTR("Create C# solution"), MENU_CREATE_SLN);
}
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 46b6bd5ebf..9fb0e40132 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -56,6 +56,8 @@ class GodotSharpEditor : public Node {
#endif
bool _create_project_solution();
+ void _make_api_solutions_if_needed();
+ void _make_api_solutions_if_needed_impl();
void _remove_create_sln_menu_option();
void _show_about_dialog();
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 933ede93dc..34c710320a 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -37,6 +37,7 @@
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
+#include "csharp_project.h"
#include "godotsharp_builds.h"
static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() {
@@ -86,6 +87,12 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
String build_config = p_debug ? "Debug" : "Release";
+ String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release"));
+ Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path);
+ ERR_FAIL_COND(metadata_err != OK);
+
+ ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path));
+
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
// Add dependency assemblies
@@ -110,7 +117,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
GDMonoAssembly *scripts_assembly = NULL;
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
- project_dll_src_dir, &scripts_assembly, /* refonly: */ true);
+ project_dll_src_path, &scripts_assembly, /* refonly: */ true);
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
ERR_FAIL_COND(!load_success);
@@ -124,7 +131,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
String depend_src_path = E->value();
String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
- ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
+ ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path));
}
// Mono specific export template extras (data dir)
@@ -155,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
}
}
-bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
+bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) {
FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
ERR_FAIL_COND_V(!f, false);
@@ -164,7 +171,7 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d
data.resize(f->get_len());
f->get_buffer(data.ptrw(), data.size());
- add_file(p_dst_path, data, false);
+ add_file(p_dst_path, data, p_remap);
return true;
}
@@ -191,21 +198,21 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
if (has_extension) {
path = search_dir.plus_file(ref_name);
if (FileAccess::exists(path)) {
- GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), search_dir, ref_aname, &ref_assembly, true);
+ GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true);
if (ref_assembly != NULL)
break;
}
} else {
path = search_dir.plus_file(ref_name + ".dll");
if (FileAccess::exists(path)) {
- GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
+ GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
if (ref_assembly != NULL)
break;
}
path = search_dir.plus_file(ref_name + ".exe");
if (FileAccess::exists(path)) {
- GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
+ GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true);
if (ref_assembly != NULL)
break;
}
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
index f007016578..10d4375567 100644
--- a/modules/mono/editor/godotsharp_export.h
+++ b/modules/mono/editor/godotsharp_export.h
@@ -41,7 +41,7 @@ class GodotSharpExport : public EditorExportPlugin {
MonoAssemblyName *aname_prealloc;
- bool _add_assembly(const String &p_src_path, const String &p_dst_path);
+ bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false);
Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies);
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 0ac59a1be8..d7bfa54aba 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -31,6 +31,8 @@
#include "mono_bottom_panel.h"
#include "../csharp_script.h"
+#include "../godotsharp_dirs.h"
+#include "csharp_project.h"
#include "godotsharp_editor.h"
MonoBottomPanel *MonoBottomPanel::singleton = NULL;
@@ -148,6 +150,10 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
void MonoBottomPanel::_build_project_pressed() {
+ String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
+ Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path);
+ ERR_FAIL_COND(metadata_err != OK);
+
GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
MonoReloadNode::get_singleton()->restart_reload_timer();
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
new file mode 100644
index 0000000000..bc8eed81cf
--- /dev/null
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -0,0 +1,542 @@
+#include "script_class_parser.h"
+
+#include "core/map.h"
+#include "core/os/os.h"
+
+#include "../utils/string_utils.h"
+
+const char *ScriptClassParser::token_names[ScriptClassParser::TK_MAX] = {
+ "[",
+ "]",
+ "{",
+ "}",
+ ".",
+ ":",
+ ",",
+ "Symbol",
+ "Identifier",
+ "String",
+ "Number",
+ "<",
+ ">",
+ "EOF",
+ "Error"
+};
+
+String ScriptClassParser::get_token_name(ScriptClassParser::Token p_token) {
+
+ ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>");
+ return token_names[p_token];
+}
+
+ScriptClassParser::Token ScriptClassParser::get_token() {
+
+ while (true) {
+ switch (code[idx]) {
+ case '\n': {
+ line++;
+ idx++;
+ break;
+ };
+ case 0: {
+ return TK_EOF;
+ } break;
+ case '{': {
+ idx++;
+ return TK_CURLY_BRACKET_OPEN;
+ };
+ case '}': {
+ idx++;
+ return TK_CURLY_BRACKET_CLOSE;
+ };
+ case '[': {
+ idx++;
+ return TK_BRACKET_OPEN;
+ };
+ case ']': {
+ idx++;
+ return TK_BRACKET_CLOSE;
+ };
+ case '<': {
+ idx++;
+ return TK_OP_LESS;
+ };
+ case '>': {
+ idx++;
+ return TK_OP_GREATER;
+ };
+ case ':': {
+ idx++;
+ return TK_COLON;
+ };
+ case ',': {
+ idx++;
+ return TK_COMMA;
+ };
+ case '.': {
+ idx++;
+ return TK_PERIOD;
+ };
+ case '#': {
+ //compiler directive
+ while (code[idx] != '\n' && code[idx] != 0) {
+ idx++;
+ }
+ continue;
+ } break;
+ case '/': {
+ switch (code[idx + 1]) {
+ case '*': { // block comment
+ idx += 2;
+ while (true) {
+ if (code[idx] == 0) {
+ error_str = "Unterminated comment";
+ error = true;
+ return TK_ERROR;
+ } else if (code[idx] == '*' && code[idx + 1] == '/') {
+ idx += 2;
+ break;
+ } else if (code[idx] == '\n') {
+ line++;
+ }
+
+ idx++;
+ }
+
+ } break;
+ case '/': { // line comment skip
+ while (code[idx] != '\n' && code[idx] != 0) {
+ idx++;
+ }
+
+ } break;
+ default: {
+ value = "/";
+ idx++;
+ return TK_SYMBOL;
+ }
+ }
+
+ continue; // a comment
+ } break;
+ case '\'':
+ case '"': {
+ bool verbatim = idx != 0 && code[idx - 1] == '@';
+
+ CharType begin_str = code[idx];
+ idx++;
+ String tk_string = String();
+ while (true) {
+ if (code[idx] == 0) {
+ error_str = "Unterminated String";
+ error = true;
+ return TK_ERROR;
+ } else if (code[idx] == begin_str) {
+ if (verbatim && code[idx + 1] == '"') { // `""` is verbatim string's `\"`
+ idx += 2; // skip next `"` as well
+ continue;
+ }
+
+ idx += 1;
+ break;
+ } else if (code[idx] == '\\' && !verbatim) {
+ //escaped characters...
+ idx++;
+ CharType next = code[idx];
+ if (next == 0) {
+ error_str = "Unterminated String";
+ error = true;
+ return TK_ERROR;
+ }
+ CharType res = 0;
+
+ switch (next) {
+ case 'b': res = 8; break;
+ case 't': res = 9; break;
+ case 'n': res = 10; break;
+ case 'f': res = 12; break;
+ case 'r':
+ res = 13;
+ break;
+ case '\"': res = '\"'; break;
+ case '\\':
+ res = '\\';
+ break;
+ default: {
+ res = next;
+ } break;
+ }
+
+ tk_string += res;
+
+ } else {
+ if (code[idx] == '\n')
+ line++;
+ tk_string += code[idx];
+ }
+ idx++;
+ }
+
+ value = tk_string;
+
+ return TK_STRING;
+ } break;
+ default: {
+ if (code[idx] <= 32) {
+ idx++;
+ break;
+ }
+
+ if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) {
+ value = String::chr(code[idx]);
+ idx++;
+ return TK_SYMBOL;
+ }
+
+ if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
+ //a number
+ const CharType *rptr;
+ double number = String::to_double(&code[idx], &rptr);
+ idx += (rptr - &code[idx]);
+ value = number;
+ return TK_NUMBER;
+
+ } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) {
+ String id;
+
+ id += code[idx];
+ idx++;
+
+ while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) {
+ id += code[idx];
+ idx++;
+ }
+
+ value = id;
+ return TK_IDENTIFIER;
+ } else if (code[idx] == '@' && code[idx + 1] == '"') {
+ // begin of verbatim string
+ idx++;
+ } else {
+ error_str = "Unexpected character.";
+ error = true;
+ return TK_ERROR;
+ }
+ }
+ }
+ }
+}
+
+Error ScriptClassParser::_skip_generic_type_params() {
+
+ Token tk;
+
+ while (true) {
+ tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ tk = get_token();
+
+ if (tk == TK_PERIOD) {
+ while (true) {
+ tk = get_token();
+
+ if (tk != TK_IDENTIFIER) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = get_token();
+
+ if (tk != TK_PERIOD)
+ break;
+ }
+ }
+
+ if (tk == TK_OP_LESS) {
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ continue;
+ } else if (tk != TK_COMMA) {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ } else if (tk == TK_OP_LESS) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found " + get_token_name(TK_OP_LESS);
+ error = true;
+ return ERR_PARSE_ERROR;
+ } else if (tk == TK_OP_GREATER) {
+ return OK;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+}
+
+Error ScriptClassParser::_parse_type_full_name(String &r_full_name) {
+
+ Token tk = get_token();
+
+ if (tk != TK_IDENTIFIER) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ r_full_name += String(value);
+
+ if (code[idx] != '.') // We only want to take the next token if it's a period
+ return OK;
+
+ tk = get_token();
+
+ CRASH_COND(tk != TK_PERIOD); // Assertion
+
+ r_full_name += ".";
+
+ return _parse_type_full_name(r_full_name);
+}
+
+Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
+
+ String name;
+
+ Error err = _parse_type_full_name(name);
+ if (err)
+ return err;
+
+ Token tk = get_token();
+
+ if (tk == TK_OP_LESS) {
+ // We don't add it to the base list if it's generic
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ } else if (tk == TK_COMMA) {
+ Error err = _parse_class_base(r_base);
+ if (err)
+ return err;
+ r_base.push_back(name);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ r_base.push_back(name);
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
+Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) {
+
+ Token tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ r_name += String(value);
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = get_token();
+
+ if (tk == TK_PERIOD) {
+ r_name += ".";
+ return _parse_namespace_name(r_name, r_curly_stack);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ r_curly_stack++;
+ return OK;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+}
+
+Error ScriptClassParser::parse(const String &p_code) {
+
+ code = p_code;
+ idx = 0;
+ line = 0;
+ error_str = String();
+ error = false;
+ value = Variant();
+ classes.clear();
+
+ Token tk = get_token();
+
+ Map<int, NameDecl> name_stack;
+ int curly_stack = 0;
+ int type_curly_stack = 0;
+
+ while (!error && tk != TK_EOF) {
+ if (tk == TK_IDENTIFIER && String(value) == "class") {
+ tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ String name = value;
+ int at_level = type_curly_stack;
+
+ ClassDecl class_decl;
+
+ for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) {
+ const NameDecl &name_decl = E->value();
+
+ if (name_decl.type == NameDecl::NAMESPACE_DECL) {
+ if (E != name_stack.front())
+ class_decl.namespace_ += ".";
+ class_decl.namespace_ += name_decl.name;
+ } else {
+ class_decl.name += name_decl.name + ".";
+ }
+ }
+
+ class_decl.name += name;
+ class_decl.nested = type_curly_stack > 0;
+
+ bool generic = false;
+
+ while (true) {
+ tk = get_token();
+
+ if (tk == TK_COLON) {
+ Error err = _parse_class_base(class_decl.base);
+ if (err)
+ return err;
+
+ curly_stack++;
+ type_curly_stack++;
+
+ break;
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ curly_stack++;
+ type_curly_stack++;
+ break;
+ } else if (tk == TK_OP_LESS && !generic) {
+ generic = true;
+
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::CLASS_DECL;
+ name_stack[at_level] = name_decl;
+
+ if (!generic) { // no generics, thanks
+ classes.push_back(class_decl);
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ String full_name = class_decl.namespace_;
+ if (full_name.length())
+ full_name += ".";
+ full_name += class_decl.name;
+ OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8());
+ }
+ }
+ } else if (tk == TK_IDENTIFIER && String(value) == "struct") {
+ String name;
+ int at_level = type_curly_stack;
+ while (true) {
+ tk = get_token();
+ if (tk == TK_IDENTIFIER && name.empty()) {
+ name = String(value);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ if (name.empty()) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + " after keyword `struct`, found " + get_token_name(TK_CURLY_BRACKET_OPEN);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ curly_stack++;
+ type_curly_stack++;
+ break;
+ } else if (tk == TK_EOF) {
+ error_str = "Expected " + get_token_name(TK_CURLY_BRACKET_OPEN) + " after struct decl, found " + get_token_name(TK_EOF);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::STRUCT_DECL;
+ name_stack[at_level] = name_decl;
+ } else if (tk == TK_IDENTIFIER && String(value) == "namespace") {
+ if (type_curly_stack > 0) {
+ error_str = "Found namespace nested inside type.";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ String name;
+ int at_level = curly_stack;
+
+ Error err = _parse_namespace_name(name, curly_stack);
+ if (err)
+ return err;
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::NAMESPACE_DECL;
+ name_stack[at_level] = name_decl;
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ curly_stack++;
+ } else if (tk == TK_CURLY_BRACKET_CLOSE) {
+ curly_stack--;
+ if (name_stack.has(curly_stack)) {
+ if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL)
+ type_curly_stack--;
+ name_stack.erase(curly_stack);
+ }
+ }
+
+ tk = get_token();
+ }
+
+ if (!error && tk == TK_EOF && curly_stack > 0) {
+ error_str = "Reached EOF with missing close curly brackets.";
+ error = true;
+ }
+
+ if (error)
+ return ERR_PARSE_ERROR;
+
+ return OK;
+}
+
+Error ScriptClassParser::parse_file(const String &p_filepath) {
+
+ String source;
+
+ Error ferr = read_all_file_utf8(p_filepath, source);
+ if (ferr != OK) {
+ if (ferr == ERR_INVALID_DATA) {
+ ERR_EXPLAIN("File '" + p_filepath + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
+ }
+ ERR_FAIL_V(ferr);
+ }
+
+ return parse(source);
+}
+
+String ScriptClassParser::get_error() {
+ return error_str;
+}
+
+Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() {
+ return classes;
+}
diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h
new file mode 100644
index 0000000000..1e174c28a9
--- /dev/null
+++ b/modules/mono/editor/script_class_parser.h
@@ -0,0 +1,79 @@
+#ifndef SCRIPT_CLASS_PARSER_H
+#define SCRIPT_CLASS_PARSER_H
+
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vector.h"
+
+class ScriptClassParser {
+
+public:
+ struct NameDecl {
+ enum Type {
+ NAMESPACE_DECL,
+ CLASS_DECL,
+ STRUCT_DECL
+ };
+
+ String name;
+ Type type;
+ };
+
+ struct ClassDecl {
+ String name;
+ String namespace_;
+ Vector<String> base;
+ bool nested;
+ bool has_script_attr;
+ };
+
+private:
+ String code;
+ int idx;
+ int line;
+ String error_str;
+ bool error;
+ Variant value;
+
+ Vector<ClassDecl> classes;
+
+ enum Token {
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_PERIOD,
+ TK_COLON,
+ TK_COMMA,
+ TK_SYMBOL,
+ TK_IDENTIFIER,
+ TK_STRING,
+ TK_NUMBER,
+ TK_OP_LESS,
+ TK_OP_GREATER,
+ TK_EOF,
+ TK_ERROR,
+ TK_MAX
+ };
+
+ static const char *token_names[TK_MAX];
+ static String get_token_name(Token p_token);
+
+ Token get_token();
+
+ Error _skip_generic_type_params();
+
+ Error _parse_type_full_name(String &r_full_name);
+ Error _parse_class_base(Vector<String> &r_base);
+ Error _parse_namespace_name(String &r_name, int &r_curly_stack);
+
+public:
+ Error parse(const String &p_code);
+ Error parse_file(const String &p_filepath);
+
+ String get_error();
+
+ Vector<ClassDecl> get_classes();
+};
+
+#endif // SCRIPT_CLASS_PARSER_H
diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs
index 66490b5e25..33b2b46712 100644
--- a/modules/mono/glue/Managed/Files/AABB.cs
+++ b/modules/mono/glue/Managed/Files/AABB.cs
@@ -407,8 +407,8 @@ namespace Godot
return new AABB(min, max - min);
}
-
- // Constructors
+
+ // Constructors
public AABB(Vector3 position, Vector3 size)
{
_position = position;
diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs
index a5618cb28d..b318d96bb9 100644
--- a/modules/mono/glue/Managed/Files/Basis.cs
+++ b/modules/mono/glue/Managed/Files/Basis.cs
@@ -13,9 +13,9 @@ namespace Godot
{
private static readonly Basis identity = new Basis
(
- new Vector3(1f, 0f, 0f),
- new Vector3(0f, 1f, 0f),
- new Vector3(0f, 0f, 1f)
+ 1f, 0f, 0f,
+ 0f, 1f, 0f,
+ 0f, 0f, 1f
);
private static readonly Basis[] orthoBases = {
@@ -159,9 +159,9 @@ namespace Godot
{
return new Basis
(
- new Vector3(xAxis.x, yAxis.x, zAxis.x),
- new Vector3(xAxis.y, yAxis.y, zAxis.y),
- new Vector3(xAxis.z, yAxis.z, zAxis.z)
+ xAxis.x, yAxis.x, zAxis.x,
+ xAxis.y, yAxis.y, zAxis.y,
+ xAxis.z, yAxis.z, zAxis.z
);
}
@@ -410,10 +410,12 @@ namespace Godot
);
}
- public Quat Quat() {
+ public Quat Quat()
+ {
real_t trace = _x[0] + _y[1] + _z[2];
- if (trace > 0.0f) {
+ if (trace > 0.0f)
+ {
real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
@@ -424,7 +426,8 @@ namespace Godot
);
}
- if (_x[0] > _y[1] && _x[0] > _z[2]) {
+ if (_x[0] > _y[1] && _x[0] > _z[2])
+ {
real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
@@ -435,7 +438,8 @@ namespace Godot
);
}
- if (_y[1] > _z[2]) {
+ if (_y[1] > _z[2])
+ {
real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
@@ -444,7 +448,9 @@ namespace Godot
(_y[2] + _z[1]) * inv_s,
(_x[2] - _z[0]) * inv_s
);
- } else {
+ }
+ else
+ {
real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
return new Quat(
@@ -502,8 +508,8 @@ namespace Godot
{
var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- real_t cosine = Mathf.Cos( phi);
- real_t sine = Mathf.Sin( phi);
+ real_t cosine = Mathf.Cos(phi);
+ real_t sine = Mathf.Sin(phi);
_x = new Vector3
(
@@ -529,12 +535,17 @@ namespace Godot
public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
- _x = xAxis;
- _y = yAxis;
- _z = zAxis;
+ _x = new Vector3(xAxis.x, yAxis.x, zAxis.x);
+ _y = new Vector3(xAxis.y, yAxis.y, zAxis.y);
+ _z = new Vector3(xAxis.z, yAxis.z, zAxis.z);
+ // Same as:
+ // SetAxis(0, xAxis);
+ // SetAxis(1, yAxis);
+ // SetAxis(2, zAxis);
+ // We need to assign the struct fields so we can't do that...
}
- public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
+ internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
{
_x = new Vector3(xx, xy, xz);
_y = new Vector3(yx, yy, yz);
diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs
index 88cb8524b8..fc5bb010a9 100644
--- a/modules/mono/glue/Managed/Files/Color.cs
+++ b/modules/mono/glue/Managed/Files/Color.cs
@@ -379,8 +379,8 @@ namespace Godot
return txt;
}
-
- // Constructors
+
+ // Constructors
public Color(float r, float g, float b, float a = 1.0f)
{
this.r = r;
diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs
index e4818e186c..75a35a9eea 100644
--- a/modules/mono/glue/Managed/Files/GD.cs
+++ b/modules/mono/glue/Managed/Files/GD.cs
@@ -70,6 +70,16 @@ namespace Godot
return ResourceLoader.Load<T>(path);
}
+ public static void PushError(string message)
+ {
+ godot_icall_GD_pusherror(message);
+ }
+
+ public static void PushWarning(string message)
+ {
+ godot_icall_GD_pushwarning(message);
+ }
+
public static void Print(params object[] what)
{
godot_icall_GD_print(what);
@@ -238,5 +248,11 @@ namespace Godot
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_var2str(object var);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_pusherror(string type);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_pushwarning(string type);
}
}
diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs
index a89dfe5f27..dcab3c1ffc 100644
--- a/modules/mono/glue/Managed/Files/Mathf.cs
+++ b/modules/mono/glue/Managed/Files/Mathf.cs
@@ -206,7 +206,7 @@ namespace Godot
public static real_t PosMod(real_t a, real_t b)
{
real_t c = a % b;
- if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
{
c += b;
}
@@ -219,7 +219,7 @@ namespace Godot
public static int PosMod(int a, int b)
{
int c = a % b;
- if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
{
c += b;
}
diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs
index 739b7fb568..2ef02cc288 100644
--- a/modules/mono/glue/Managed/Files/MathfEx.cs
+++ b/modules/mono/glue/Managed/Files/MathfEx.cs
@@ -29,7 +29,7 @@ namespace Godot
public static int FloorToInt(real_t s)
{
return (int)Math.Floor(s);
- }
+ }
public static int RoundToInt(real_t s)
{
diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs
index 9611dce11e..f11cd490a9 100644
--- a/modules/mono/glue/Managed/Files/Plane.cs
+++ b/modules/mono/glue/Managed/Files/Plane.cs
@@ -144,7 +144,7 @@ namespace Godot
{
return point - _normal * DistanceTo(point);
}
-
+
// Constants
private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0);
private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
@@ -153,8 +153,8 @@ namespace Godot
public static Plane PlaneYZ { get { return _planeYZ; } }
public static Plane PlaneXZ { get { return _planeXZ; } }
public static Plane PlaneXY { get { return _planeXY; } }
-
- // Constructors
+
+ // Constructors
public Plane(real_t a, real_t b, real_t c, real_t d)
{
_normal = new Vector3(a, b, c);
diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs
index eaa027eb69..fd1ac01083 100644
--- a/modules/mono/glue/Managed/Files/Quat.cs
+++ b/modules/mono/glue/Managed/Files/Quat.cs
@@ -202,7 +202,7 @@ namespace Godot
// Static Readonly Properties
public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f);
- // Constructors
+ // Constructors
public Quat(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs
index cb25c267bc..888f300347 100644
--- a/modules/mono/glue/Managed/Files/Rect2.cs
+++ b/modules/mono/glue/Managed/Files/Rect2.cs
@@ -182,8 +182,8 @@ namespace Godot
return newRect;
}
-
- // Constructors
+
+ // Constructors
public Rect2(Vector2 position, Vector2 size)
{
_position = position;
diff --git a/modules/mono/glue/Managed/Files/StringExtensions.cs b/modules/mono/glue/Managed/Files/StringExtensions.cs
index 21c9be98c1..c194facd0b 100644
--- a/modules/mono/glue/Managed/Files/StringExtensions.cs
+++ b/modules/mono/glue/Managed/Files/StringExtensions.cs
@@ -185,7 +185,7 @@ namespace Godot
int instanceIndex = 0;
int toIndex = 0;
-
+
if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication.
{
while (true)
@@ -480,9 +480,9 @@ namespace Godot
return false; // Don't start with number plz
}
- bool validChar = instance[i] >= '0' &&
- instance[i] <= '9' || instance[i] >= 'a' &&
- instance[i] <= 'z' || instance[i] >= 'A' &&
+ bool validChar = instance[i] >= '0' &&
+ instance[i] <= '9' || instance[i] >= 'a' &&
+ instance[i] <= 'z' || instance[i] >= 'A' &&
instance[i] <= 'Z' || instance[i] == '_';
if (!validChar)
diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs
index 068007d7f0..fa85855edd 100644
--- a/modules/mono/glue/Managed/Files/Transform.cs
+++ b/modules/mono/glue/Managed/Files/Transform.cs
@@ -124,16 +124,16 @@ namespace Godot
// Constants
private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero);
- private static readonly Transform _flipX = new Transform(new Basis(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)), Vector3.Zero);
- private static readonly Transform _flipY = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)), Vector3.Zero);
- private static readonly Transform _flipZ = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)), Vector3.Zero);
+ private static readonly Transform _flipX = new Transform(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero);
+ private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
+ private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero);
public static Transform Identity { get { return _identity; } }
public static Transform FlipX { get { return _flipX; } }
public static Transform FlipY { get { return _flipY; } }
public static Transform FlipZ { get { return _flipZ; } }
- // Constructors
+ // Constructors
public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
{
basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs
index 8d30833066..c9e5b560b2 100644
--- a/modules/mono/glue/Managed/Files/Transform2D.cs
+++ b/modules/mono/glue/Managed/Files/Transform2D.cs
@@ -261,15 +261,15 @@ namespace Godot
public static Transform2D Identity { get { return _identity; } }
public static Transform2D FlipX { get { return _flipX; } }
public static Transform2D FlipY { get { return _flipY; } }
-
- // Constructors
+
+ // Constructors
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
{
x = xAxis;
y = yAxis;
o = origin;
}
-
+
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
x = new Vector2(xx, xy);
diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs
index 080b8802ba..ce41886bfc 100644
--- a/modules/mono/glue/Managed/Files/Vector2.cs
+++ b/modules/mono/glue/Managed/Files/Vector2.cs
@@ -215,7 +215,7 @@ namespace Godot
x = v.x;
y = v.y;
}
-
+
public Vector2 Slerp(Vector2 b, real_t t)
{
real_t theta = AngleTo(b);
@@ -242,7 +242,7 @@ namespace Godot
private static readonly Vector2 _one = new Vector2(1, 1);
private static readonly Vector2 _negOne = new Vector2(-1, -1);
private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf);
-
+
private static readonly Vector2 _up = new Vector2(0, -1);
private static readonly Vector2 _down = new Vector2(0, 1);
private static readonly Vector2 _right = new Vector2(1, 0);
diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs
index 6fffe5e4d6..f6ff27989d 100644
--- a/modules/mono/glue/Managed/Files/Vector3.cs
+++ b/modules/mono/glue/Managed/Files/Vector3.cs
@@ -204,9 +204,9 @@ namespace Godot
public Basis Outer(Vector3 b)
{
return new Basis(
- new Vector3(x * b.x, x * b.y, x * b.z),
- new Vector3(y * b.x, y * b.y, y * b.z),
- new Vector3(z * b.x, z * b.y, z * b.z)
+ x * b.x, x * b.y, x * b.z,
+ y * b.x, y * b.y, y * b.z,
+ z * b.x, z * b.y, z * b.z
);
}
@@ -276,13 +276,13 @@ namespace Godot
0f, 0f, z
);
}
-
+
// Constants
private static readonly Vector3 _zero = new Vector3(0, 0, 0);
private static readonly Vector3 _one = new Vector3(1, 1, 1);
private static readonly Vector3 _negOne = new Vector3(-1, -1, -1);
private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf);
-
+
private static readonly Vector3 _up = new Vector3(0, 1, 0);
private static readonly Vector3 _down = new Vector3(0, -1, 0);
private static readonly Vector3 _right = new Vector3(1, 0, 0);
@@ -294,7 +294,7 @@ namespace Godot
public static Vector3 One { get { return _one; } }
public static Vector3 NegOne { get { return _negOne; } }
public static Vector3 Inf { get { return _inf; } }
-
+
public static Vector3 Up { get { return _up; } }
public static Vector3 Down { get { return _down; } }
public static Vector3 Right { get { return _right; } }
diff --git a/modules/mono/glue/Managed/Properties/AssemblyInfo.cs b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs
index 7ed68acad7..77b3774e81 100644
--- a/modules/mono/glue/Managed/Properties/AssemblyInfo.cs
+++ b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs
@@ -1,7 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
-// Information about this assembly is defined by the following attributes.
+// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("Managed")]
@@ -19,7 +19,7 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyVersion("1.0.*")]
-// The following attributes are used to specify the signing key for the assembly,
+// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 051f42b966..9f5bcecdd9 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -157,6 +157,14 @@ bool godot_icall_GD_type_exists(MonoString *p_type) {
return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
}
+void godot_icall_GD_pusherror(MonoString *p_str) {
+ ERR_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str));
+}
+
+void godot_icall_GD_pushwarning(MonoString *p_str) {
+ WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str));
+}
+
MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) {
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
@@ -186,6 +194,8 @@ void godot_register_gd_icalls() {
mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert);
mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash);
mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning);
mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print);
mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr);
mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw);
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 39d608de9f..5a6a2d1742 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -36,7 +36,8 @@
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "ptr"
#define BINDINGS_NATIVE_NAME_FIELD "nativeName"
-#define API_ASSEMBLY_NAME "GodotSharp"
+#define API_SOLUTION_NAME "GodotSharp"
+#define CORE_API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0c4433112d..a80155bd89 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -191,7 +191,6 @@ void GDMono::initialize() {
String hint_config_dir = path_join(locations[i], "etc");
if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
- need_set_mono_dirs = false;
assembly_rootdir = hint_assembly_rootdir;
config_dir = hint_config_dir;
break;
@@ -378,34 +377,24 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
- return load_assembly_from(p_name, String(), r_assembly, p_refonly);
-}
-
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
-
- return load_assembly_from(p_name, String(), p_aname, r_assembly, p_refonly);
-}
-
-bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly) {
-
CRASH_COND(!r_assembly);
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- bool result = load_assembly_from(p_name, p_basedir, aname, r_assembly, p_refonly);
+ bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
mono_assembly_name_free(aname);
mono_free(aname);
return result;
}
-bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
+bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
CRASH_COND(!r_assembly);
print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
MonoImageOpenStatus status = MONO_IMAGE_OK;
- MonoAssembly *assembly = mono_assembly_load_full(p_aname, p_basedir.length() ? p_basedir.utf8().get_data() : NULL, &status, p_refonly);
+ MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
if (!assembly)
return false;
@@ -426,6 +415,32 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, M
return true;
}
+bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) {
+
+ CRASH_COND(!r_assembly);
+
+ print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
+
+ GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
+
+ if (!assembly)
+ return false;
+
+#ifdef DEBUG_ENABLED
+ uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+ GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
+
+ ERR_FAIL_COND_V(stored_assembly == NULL, false);
+ ERR_FAIL_COND_V(*stored_assembly != assembly, false);
+#endif
+
+ *r_assembly = assembly;
+
+ print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
+
+ return true;
+}
+
APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
APIAssembly::Version api_assembly_version;
@@ -481,7 +496,14 @@ bool GDMono::_load_core_api_assembly() {
}
#endif
- bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly);
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(assembly_path))
+ return false;
+
+ bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME,
+ assembly_path,
+ &core_api_assembly);
if (success) {
#ifdef MONO_GLUE_ENABLED
@@ -511,7 +533,14 @@ bool GDMono::_load_editor_api_assembly() {
return false;
}
- bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
+ String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(assembly_path))
+ return false;
+
+ bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME,
+ assembly_path,
+ &editor_api_assembly);
if (success) {
#ifdef MONO_GLUE_ENABLED
@@ -552,6 +581,8 @@ bool GDMono::_load_project_assembly() {
if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
+
+ CSharpLanguage::get_singleton()->project_assembly_loaded();
} else {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load project assembly");
@@ -604,7 +635,7 @@ void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type,
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
- API_ASSEMBLY_NAME ".dll" :
+ CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
ERR_FAIL_COND(!FileAccess::exists(assembly_path));
@@ -635,7 +666,7 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type)
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
- API_ASSEMBLY_NAME ".dll" :
+ CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(assembly_path))
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 745dcc34eb..fd9551b6de 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -199,8 +199,7 @@ public:
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
+ bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
Error finalize_and_unload_domain(MonoDomain *p_domain);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 1067c11e0e..72b0204672 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -457,6 +457,20 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
return match;
}
+GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
+
+ GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
+ if (loaded_asm)
+ return *loaded_asm;
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!FileAccess::exists(p_path));
+#endif
+ no_search = true;
+ GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
+ no_search = false;
+ return res;
+}
+
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
loaded = false;
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 4c9b1cb10d..2af40ec901 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -128,6 +128,8 @@ public:
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String());
+ static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
+
GDMonoAssembly(const String &p_name, const String &p_path = String());
~GDMonoAssembly();
};
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index d3a673dc1b..5d9b9213e7 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -40,65 +40,71 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
}
void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) {
-#define SET_FROM_STRUCT_AND_BREAK(m_type) \
- { \
- const m_type &val = p_value.operator ::m_type(); \
- MARSHALLED_OUT(m_type, val, raw); \
- mono_field_set_value(p_object, mono_field, raw); \
- break; \
+#define SET_FROM_STRUCT(m_type) \
+ { \
+ GDMonoMarshal::M_##m_type from = MARSHALLED_OUT(m_type, p_value.operator ::m_type()); \
+ mono_field_set_value(p_object, mono_field, &from); \
}
-#define SET_FROM_PRIMITIVE(m_type) \
- { \
- m_type val = p_value.operator m_type(); \
- mono_field_set_value(p_object, mono_field, &val); \
- break; \
- }
-
-#define SET_FROM_ARRAY_AND_BREAK(m_type) \
- { \
- MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator m_type()); \
- mono_field_set_value(p_object, mono_field, &managed); \
- break; \
+#define SET_FROM_ARRAY(m_type) \
+ { \
+ MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \
+ mono_field_set_value(p_object, mono_field, &managed); \
}
switch (type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
- SET_FROM_PRIMITIVE(bool);
+ MonoBoolean val = p_value.operator bool();
+ mono_field_set_value(p_object, mono_field, &val);
+ } break;
+
+ case MONO_TYPE_CHAR: {
+ int16_t val = p_value.operator unsigned short();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_I1: {
- SET_FROM_PRIMITIVE(signed char);
+ int8_t val = p_value.operator signed char();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_I2: {
- SET_FROM_PRIMITIVE(signed short);
+ int16_t val = p_value.operator signed short();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_I4: {
- SET_FROM_PRIMITIVE(signed int);
+ int32_t val = p_value.operator signed int();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_I8: {
- SET_FROM_PRIMITIVE(int64_t);
+ int64_t val = p_value.operator int64_t();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_U1: {
- SET_FROM_PRIMITIVE(unsigned char);
+ uint8_t val = p_value.operator unsigned char();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_U2: {
- SET_FROM_PRIMITIVE(unsigned short);
+ uint16_t val = p_value.operator unsigned short();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_U4: {
- SET_FROM_PRIMITIVE(unsigned int);
+ uint32_t val = p_value.operator unsigned int();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_U8: {
- SET_FROM_PRIMITIVE(uint64_t);
+ uint64_t val = p_value.operator uint64_t();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_R4: {
- SET_FROM_PRIMITIVE(float);
+ float val = p_value.operator float();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_R8: {
- SET_FROM_PRIMITIVE(double);
+ double val = p_value.operator double();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case MONO_TYPE_STRING: {
@@ -109,38 +115,117 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = type.type_class;
- if (tclass == CACHED_CLASS(Vector2))
- SET_FROM_STRUCT_AND_BREAK(Vector2);
+ if (tclass == CACHED_CLASS(Vector2)) {
+ SET_FROM_STRUCT(Vector2);
+ break;
+ }
+
+ if (tclass == CACHED_CLASS(Rect2)) {
+ SET_FROM_STRUCT(Rect2);
+ break;
+ }
- if (tclass == CACHED_CLASS(Rect2))
- SET_FROM_STRUCT_AND_BREAK(Rect2);
+ if (tclass == CACHED_CLASS(Transform2D)) {
+ SET_FROM_STRUCT(Transform2D);
+ break;
+ }
- if (tclass == CACHED_CLASS(Transform2D))
- SET_FROM_STRUCT_AND_BREAK(Transform2D);
+ if (tclass == CACHED_CLASS(Vector3)) {
+ SET_FROM_STRUCT(Vector3);
+ break;
+ }
- if (tclass == CACHED_CLASS(Vector3))
- SET_FROM_STRUCT_AND_BREAK(Vector3);
+ if (tclass == CACHED_CLASS(Basis)) {
+ SET_FROM_STRUCT(Basis);
+ break;
+ }
- if (tclass == CACHED_CLASS(Basis))
- SET_FROM_STRUCT_AND_BREAK(Basis);
+ if (tclass == CACHED_CLASS(Quat)) {
+ SET_FROM_STRUCT(Quat);
+ break;
+ }
- if (tclass == CACHED_CLASS(Quat))
- SET_FROM_STRUCT_AND_BREAK(Quat);
+ if (tclass == CACHED_CLASS(Transform)) {
+ SET_FROM_STRUCT(Transform);
+ break;
+ }
- if (tclass == CACHED_CLASS(Transform))
- SET_FROM_STRUCT_AND_BREAK(Transform);
+ if (tclass == CACHED_CLASS(AABB)) {
+ SET_FROM_STRUCT(AABB);
+ break;
+ }
- if (tclass == CACHED_CLASS(AABB))
- SET_FROM_STRUCT_AND_BREAK(AABB);
+ if (tclass == CACHED_CLASS(Color)) {
+ SET_FROM_STRUCT(Color);
+ break;
+ }
- if (tclass == CACHED_CLASS(Color))
- SET_FROM_STRUCT_AND_BREAK(Color);
+ if (tclass == CACHED_CLASS(Plane)) {
+ SET_FROM_STRUCT(Plane);
+ break;
+ }
- if (tclass == CACHED_CLASS(Plane))
- SET_FROM_STRUCT_AND_BREAK(Plane);
+ if (mono_class_is_enum(tclass->get_mono_ptr())) {
+ MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN: {
+ MonoBoolean val = p_value.operator bool();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_CHAR: {
+ uint16_t val = p_value.operator unsigned short();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_I1: {
+ int8_t val = p_value.operator signed char();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_I2: {
+ int16_t val = p_value.operator signed short();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_I4: {
+ int32_t val = p_value.operator signed int();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_I8: {
+ int64_t val = p_value.operator int64_t();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_U1: {
+ uint8_t val = p_value.operator unsigned char();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_U2: {
+ uint16_t val = p_value.operator unsigned short();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_U4: {
+ uint32_t val = p_value.operator unsigned int();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ case MONO_TYPE_U8: {
+ uint64_t val = p_value.operator uint64_t();
+ mono_field_set_value(p_object, mono_field, &val);
+ break;
+ }
+ default: {
+ ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
+ ERR_FAIL();
+ }
+ }
- if (mono_class_is_enum(tclass->get_mono_ptr()))
- SET_FROM_PRIMITIVE(signed int);
+ break;
+ }
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name());
ERR_FAIL();
@@ -150,29 +235,45 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
- SET_FROM_ARRAY_AND_BREAK(Array);
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
+ SET_FROM_ARRAY(Array);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
- SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
+ SET_FROM_ARRAY(PoolByteArray);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
- SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
+ SET_FROM_ARRAY(PoolIntArray);
+ break;
+ }
- if (array_type->eklass == REAL_T_MONOCLASS)
- SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
+ if (array_type->eklass == REAL_T_MONOCLASS) {
+ SET_FROM_ARRAY(PoolRealArray);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(String))
- SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
+ SET_FROM_ARRAY(PoolStringArray);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
- SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
+ SET_FROM_ARRAY(PoolVector2Array);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
- SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
+ SET_FROM_ARRAY(PoolVector3Array);
+ break;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Color))
- SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
+ SET_FROM_ARRAY(PoolColorArray);
+ break;
+ }
ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
ERR_FAIL();
@@ -220,32 +321,56 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
// Variant
switch (p_value.get_type()) {
case Variant::BOOL: {
- SET_FROM_PRIMITIVE(bool);
+ MonoBoolean val = p_value.operator bool();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case Variant::INT: {
- SET_FROM_PRIMITIVE(int);
+ int32_t val = p_value.operator signed int();
+ mono_field_set_value(p_object, mono_field, &val);
} break;
case Variant::REAL: {
#ifdef REAL_T_IS_DOUBLE
- SET_FROM_PRIMITIVE(double);
+ double val = p_value.operator double();
+ mono_field_set_value(p_object, mono_field, &val);
#else
- SET_FROM_PRIMITIVE(float);
+ float val = p_value.operator float();
+ mono_field_set_value(p_object, mono_field, &val);
#endif
} break;
case Variant::STRING: {
MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
mono_field_set_value(p_object, mono_field, mono_string);
} break;
- case Variant::VECTOR2: SET_FROM_STRUCT_AND_BREAK(Vector2);
- case Variant::RECT2: SET_FROM_STRUCT_AND_BREAK(Rect2);
- case Variant::VECTOR3: SET_FROM_STRUCT_AND_BREAK(Vector3);
- case Variant::TRANSFORM2D: SET_FROM_STRUCT_AND_BREAK(Transform2D);
- case Variant::PLANE: SET_FROM_STRUCT_AND_BREAK(Plane);
- case Variant::QUAT: SET_FROM_STRUCT_AND_BREAK(Quat);
- case Variant::AABB: SET_FROM_STRUCT_AND_BREAK(AABB);
- case Variant::BASIS: SET_FROM_STRUCT_AND_BREAK(Basis);
- case Variant::TRANSFORM: SET_FROM_STRUCT_AND_BREAK(Transform);
- case Variant::COLOR: SET_FROM_STRUCT_AND_BREAK(Color);
+ case Variant::VECTOR2: {
+ SET_FROM_STRUCT(Vector2);
+ } break;
+ case Variant::RECT2: {
+ SET_FROM_STRUCT(Rect2);
+ } break;
+ case Variant::VECTOR3: {
+ SET_FROM_STRUCT(Vector3);
+ } break;
+ case Variant::TRANSFORM2D: {
+ SET_FROM_STRUCT(Transform2D);
+ } break;
+ case Variant::PLANE: {
+ SET_FROM_STRUCT(Plane);
+ } break;
+ case Variant::QUAT: {
+ SET_FROM_STRUCT(Quat);
+ } break;
+ case Variant::AABB: {
+ SET_FROM_STRUCT(AABB);
+ } break;
+ case Variant::BASIS: {
+ SET_FROM_STRUCT(Basis);
+ } break;
+ case Variant::TRANSFORM: {
+ SET_FROM_STRUCT(Transform);
+ } break;
+ case Variant::COLOR: {
+ SET_FROM_STRUCT(Color);
+ } break;
case Variant::NODE_PATH: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, managed);
@@ -267,14 +392,27 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
- case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
- case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
- case Variant::POOL_STRING_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
- case Variant::POOL_VECTOR2_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
- case Variant::POOL_VECTOR3_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
- case Variant::POOL_COLOR_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
-#undef SET_FROM_ARRAY_AND_BREAK
+ case Variant::POOL_BYTE_ARRAY: {
+ SET_FROM_ARRAY(PoolByteArray);
+ } break;
+ case Variant::POOL_INT_ARRAY: {
+ SET_FROM_ARRAY(PoolIntArray);
+ } break;
+ case Variant::POOL_REAL_ARRAY: {
+ SET_FROM_ARRAY(PoolRealArray);
+ } break;
+ case Variant::POOL_STRING_ARRAY: {
+ SET_FROM_ARRAY(PoolStringArray);
+ } break;
+ case Variant::POOL_VECTOR2_ARRAY: {
+ SET_FROM_ARRAY(PoolVector2Array);
+ } break;
+ case Variant::POOL_VECTOR3_ARRAY: {
+ SET_FROM_ARRAY(PoolVector3Array);
+ } break;
+ case Variant::POOL_COLOR_ARRAY: {
+ SET_FROM_ARRAY(PoolColorArray);
+ } break;
default: break;
}
} break;
@@ -312,8 +450,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
} break;
}
+#undef SET_FROM_ARRAY_AND_BREAK
#undef SET_FROM_STRUCT_AND_BREAK
-#undef SET_FROM_PRIMITIVE
}
MonoObject *GDMonoField::get_value(MonoObject *p_object) {
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 4f2efc7b92..51d0c9b356 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -55,14 +55,4 @@ struct ManagedType {
}
};
-typedef union {
- uint32_t _uint32;
- float _float;
-} mono_float;
-
-typedef union {
- uint64_t _uint64;
- float _double;
-} mono_double;
-
#endif // GD_MONO_HEADER_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index de91e71bab..2543f5dc47 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -35,20 +35,6 @@
namespace GDMonoMarshal {
-#define RETURN_BOXED_STRUCT(m_t, m_var_in) \
- { \
- const m_t &m_in = m_var_in->operator ::m_t(); \
- MARSHALLED_OUT(m_t, m_in, raw); \
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \
- }
-
-#define RETURN_UNBOXED_STRUCT(m_t, m_var_in) \
- { \
- float *raw = (float *)mono_object_unbox(m_var_in); \
- MARSHALLED_IN(m_t, raw, ret); \
- return ret; \
- }
-
Variant::Type managed_to_variant_type(const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
@@ -252,16 +238,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_BOOLEAN(val);
}
+ case MONO_TYPE_CHAR: {
+ uint16_t val = p_var->operator unsigned short();
+ return BOX_UINT16(val);
+ }
+
case MONO_TYPE_I1: {
- char val = p_var->operator signed char();
+ int8_t val = p_var->operator signed char();
return BOX_INT8(val);
}
case MONO_TYPE_I2: {
- short val = p_var->operator signed short();
+ int16_t val = p_var->operator signed short();
return BOX_INT16(val);
}
case MONO_TYPE_I4: {
- int val = p_var->operator signed int();
+ int32_t val = p_var->operator signed int();
return BOX_INT32(val);
}
case MONO_TYPE_I8: {
@@ -270,15 +261,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
case MONO_TYPE_U1: {
- char val = p_var->operator unsigned char();
+ uint8_t val = p_var->operator unsigned char();
return BOX_UINT8(val);
}
case MONO_TYPE_U2: {
- short val = p_var->operator unsigned short();
+ uint16_t val = p_var->operator unsigned short();
return BOX_UINT16(val);
}
case MONO_TYPE_U4: {
- int val = p_var->operator unsigned int();
+ uint32_t val = p_var->operator unsigned int();
return BOX_UINT32(val);
}
case MONO_TYPE_U8: {
@@ -302,39 +293,105 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = p_type.type_class;
- if (tclass == CACHED_CLASS(Vector2))
- RETURN_BOXED_STRUCT(Vector2, p_var);
+ if (tclass == CACHED_CLASS(Vector2)) {
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
+ }
- if (tclass == CACHED_CLASS(Rect2))
- RETURN_BOXED_STRUCT(Rect2, p_var);
+ if (tclass == CACHED_CLASS(Rect2)) {
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
+ }
- if (tclass == CACHED_CLASS(Transform2D))
- RETURN_BOXED_STRUCT(Transform2D, p_var);
+ if (tclass == CACHED_CLASS(Transform2D)) {
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
+ }
- if (tclass == CACHED_CLASS(Vector3))
- RETURN_BOXED_STRUCT(Vector3, p_var);
+ if (tclass == CACHED_CLASS(Vector3)) {
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
+ }
- if (tclass == CACHED_CLASS(Basis))
- RETURN_BOXED_STRUCT(Basis, p_var);
+ if (tclass == CACHED_CLASS(Basis)) {
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
+ }
- if (tclass == CACHED_CLASS(Quat))
- RETURN_BOXED_STRUCT(Quat, p_var);
+ if (tclass == CACHED_CLASS(Quat)) {
+ GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
+ }
- if (tclass == CACHED_CLASS(Transform))
- RETURN_BOXED_STRUCT(Transform, p_var);
+ if (tclass == CACHED_CLASS(Transform)) {
+ GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
+ }
- if (tclass == CACHED_CLASS(AABB))
- RETURN_BOXED_STRUCT(AABB, p_var);
+ if (tclass == CACHED_CLASS(AABB)) {
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
+ }
- if (tclass == CACHED_CLASS(Color))
- RETURN_BOXED_STRUCT(Color, p_var);
+ if (tclass == CACHED_CLASS(Color)) {
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
+ }
- if (tclass == CACHED_CLASS(Plane))
- RETURN_BOXED_STRUCT(Plane, p_var);
+ if (tclass == CACHED_CLASS(Plane)) {
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
+ }
if (mono_class_is_enum(tclass->get_mono_ptr())) {
- int val = p_var->operator signed int();
- return BOX_ENUM(tclass->get_mono_ptr(), val);
+ MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
+ MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN: {
+ MonoBoolean val = p_var->operator bool();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_CHAR: {
+ uint16_t val = p_var->operator unsigned short();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_I1: {
+ int8_t val = p_var->operator signed char();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_I2: {
+ int16_t val = p_var->operator signed short();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_I4: {
+ int32_t val = p_var->operator signed int();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_I8: {
+ int64_t val = p_var->operator int64_t();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_U1: {
+ uint8_t val = p_var->operator unsigned char();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_U2: {
+ uint16_t val = p_var->operator unsigned short();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_U4: {
+ uint32_t val = p_var->operator unsigned int();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ case MONO_TYPE_U8: {
+ uint64_t val = p_var->operator uint64_t();
+ return BOX_ENUM(enum_baseclass, val);
+ }
+ default: {
+ ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
+ ERR_FAIL_V(NULL);
+ }
+ }
}
} break;
@@ -402,7 +459,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_BOOLEAN(val);
}
case Variant::INT: {
- int val = p_var->operator signed int();
+ int32_t val = p_var->operator signed int();
return BOX_INT32(val);
}
case Variant::REAL: {
@@ -416,33 +473,52 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
case Variant::STRING:
return (MonoObject *)mono_string_from_godot(p_var->operator String());
- case Variant::VECTOR2:
- RETURN_BOXED_STRUCT(Vector2, p_var);
- case Variant::RECT2:
- RETURN_BOXED_STRUCT(Rect2, p_var);
- case Variant::VECTOR3:
- RETURN_BOXED_STRUCT(Vector3, p_var);
- case Variant::TRANSFORM2D:
- RETURN_BOXED_STRUCT(Transform2D, p_var);
- case Variant::PLANE:
- RETURN_BOXED_STRUCT(Plane, p_var);
- case Variant::QUAT:
- RETURN_BOXED_STRUCT(Quat, p_var);
- case Variant::AABB:
- RETURN_BOXED_STRUCT(AABB, p_var);
- case Variant::BASIS:
- RETURN_BOXED_STRUCT(Basis, p_var);
- case Variant::TRANSFORM:
- RETURN_BOXED_STRUCT(Transform, p_var);
- case Variant::COLOR:
- RETURN_BOXED_STRUCT(Color, p_var);
+ case Variant::VECTOR2: {
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
+ }
+ case Variant::RECT2: {
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
+ }
+ case Variant::VECTOR3: {
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
+ }
+ case Variant::TRANSFORM2D: {
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
+ }
+ case Variant::PLANE: {
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
+ }
+ case Variant::QUAT: {
+ GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
+ }
+ case Variant::AABB: {
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
+ }
+ case Variant::BASIS: {
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
+ }
+ case Variant::TRANSFORM: {
+ GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
+ }
+ case Variant::COLOR: {
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
+ }
case Variant::NODE_PATH:
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
case Variant::_RID:
return GDMonoUtils::create_managed_from(p_var->operator RID());
- case Variant::OBJECT: {
+ case Variant::OBJECT:
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
- }
case Variant::DICTIONARY:
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
case Variant::ARRAY:
@@ -512,6 +588,9 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_BOOLEAN:
return (bool)unbox<MonoBoolean>(p_obj);
+ case MONO_TYPE_CHAR:
+ return unbox<uint16_t>(p_obj);
+
case MONO_TYPE_I1:
return unbox<int8_t>(p_obj);
case MONO_TYPE_I2:
@@ -545,34 +624,34 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
GDMonoClass *tclass = type.type_class;
if (tclass == CACHED_CLASS(Vector2))
- RETURN_UNBOXED_STRUCT(Vector2, p_obj);
+ return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Rect2))
- RETURN_UNBOXED_STRUCT(Rect2, p_obj);
+ return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Transform2D))
- RETURN_UNBOXED_STRUCT(Transform2D, p_obj);
+ return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Vector3))
- RETURN_UNBOXED_STRUCT(Vector3, p_obj);
+ return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Basis))
- RETURN_UNBOXED_STRUCT(Basis, p_obj);
+ return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Quat))
- RETURN_UNBOXED_STRUCT(Quat, p_obj);
+ return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Transform))
- RETURN_UNBOXED_STRUCT(Transform, p_obj);
+ return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(AABB))
- RETURN_UNBOXED_STRUCT(AABB, p_obj);
+ return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Color))
- RETURN_UNBOXED_STRUCT(Color, p_obj);
+ return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj));
if (tclass == CACHED_CLASS(Plane))
- RETURN_UNBOXED_STRUCT(Plane, p_obj);
+ return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj));
if (mono_class_is_enum(tclass->get_mono_ptr()))
return unbox<int32_t>(p_obj);
@@ -708,13 +787,13 @@ Array mono_array_to_Array(MonoArray *p_array) {
return ret;
}
-// TODO Optimize reading/writing from/to PoolArrays
-
MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
+ PoolIntArray::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, int32_t, i, p_array[i]);
+ mono_array_set(ret, int32_t, i, r[i]);
}
return ret;
@@ -726,19 +805,22 @@ PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolIntArray::Write w = ret.write();
+
for (int i = 0; i < length; i++) {
- int32_t elem = mono_array_get(p_array, int32_t, i);
- ret.set(i, elem);
+ w[i] = mono_array_get(p_array, int32_t, i);
}
return ret;
}
MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) {
+ PoolByteArray::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, uint8_t, i, p_array[i]);
+ mono_array_set(ret, uint8_t, i, r[i]);
}
return ret;
@@ -750,20 +832,22 @@ PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolByteArray::Write w = ret.write();
for (int i = 0; i < length; i++) {
- uint8_t elem = mono_array_get(p_array, uint8_t, i);
- ret.set(i, elem);
+ w[i] = mono_array_get(p_array, uint8_t, i);
}
return ret;
}
MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) {
+ PoolRealArray::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- mono_array_set(ret, real_t, i, p_array[i]);
+ mono_array_set(ret, real_t, i, r[i]);
}
return ret;
@@ -775,20 +859,22 @@ PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolRealArray::Write w = ret.write();
for (int i = 0; i < length; i++) {
- real_t elem = mono_array_get(p_array, real_t, i);
- ret.set(i, elem);
+ w[i] = mono_array_get(p_array, real_t, i);
}
return ret;
}
MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
+ PoolStringArray::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- MonoString *boxed = mono_string_from_godot(p_array[i]);
+ MonoString *boxed = mono_string_from_godot(r[i]);
mono_array_set(ret, MonoString *, i, boxed);
}
@@ -801,29 +887,24 @@ PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolStringArray::Write w = ret.write();
for (int i = 0; i < length; i++) {
MonoString *elem = mono_array_get(p_array, MonoString *, i);
- ret.set(i, mono_string_to_godot(elem));
+ w[i] = mono_string_to_godot(elem);
}
return ret;
}
MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) {
+ PoolColorArray::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
-#ifdef YOLOCOPY
- mono_array_set(ret, Color, i, p_array[i]);
-#else
- real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 4, i);
- const Color &elem = p_array[i];
- raw[0] = elem.r;
- raw[1] = elem.g;
- raw[2] = elem.b;
- raw[3] = elem.a;
-#endif
+ M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
+ *raw = MARSHALLED_OUT(Color, r[i]);
}
return ret;
@@ -835,28 +916,23 @@ PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolColorArray::Write w = ret.write();
for (int i = 0; i < length; i++) {
- real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i);
- MARSHALLED_IN(Color, raw_elem, elem);
- ret.set(i, elem);
+ w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
}
return ret;
}
MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) {
+ PoolVector2Array::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
-#ifdef YOLOCOPY
- mono_array_set(ret, Vector2, i, p_array[i]);
-#else
- real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 2, i);
- const Vector2 &elem = p_array[i];
- raw[0] = elem.x;
- raw[1] = elem.y;
-#endif
+ M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
+ *raw = MARSHALLED_OUT(Vector2, r[i]);
}
return ret;
@@ -868,29 +944,23 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolVector2Array::Write w = ret.write();
for (int i = 0; i < length; i++) {
- real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i);
- MARSHALLED_IN(Vector2, raw_elem, elem);
- ret.set(i, elem);
+ w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
}
return ret;
}
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) {
+ PoolVector3Array::Read r = p_array.read();
+
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
-#ifdef YOLOCOPY
- mono_array_set(ret, Vector3, i, p_array[i]);
-#else
- real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 3, i);
- const Vector3 &elem = p_array[i];
- raw[0] = elem.x;
- raw[1] = elem.y;
- raw[2] = elem.z;
-#endif
+ M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
+ *raw = MARSHALLED_OUT(Vector3, r[i]);
}
return ret;
@@ -902,11 +972,10 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
int length = mono_array_length(p_array);
ret.resize(length);
+ PoolVector3Array::Write w = ret.write();
for (int i = 0; i < length; i++) {
- real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i);
- MARSHALLED_IN(Vector3, raw_elem, elem);
- ret.set(i, elem);
+ w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
}
return ret;
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index cc0ab5fa05..4002f2a225 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -147,78 +147,271 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
-#ifdef YOLO_COPY
-#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
-#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
+// Structures
+
+namespace InteropLayout {
+
+enum {
+ MATCHES_float = (sizeof(float) == sizeof(uint32_t)),
+
+ MATCHES_double = (sizeof(double) == sizeof(uint64_t)),
+
+#ifdef REAL_T_IS_DOUBLE
+ MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)),
#else
+ MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)),
+#endif
-// Expects m_in to be of type float*
+ MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) &&
+ offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
+ offsetof(Vector2, y) == (sizeof(real_t) * 1)),
-#define MARSHALLED_OUT(m_t, m_in, m_out) MARSHALLED_OUT_##m_t(m_in, m_out)
-#define MARSHALLED_IN(m_t, m_in, m_out) MARSHALLED_IN_##m_t(m_in, m_out)
+ MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) &&
+ offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
+ offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
-// Vector2
+ MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array
-#define MARSHALLED_OUT_Vector2(m_in, m_out) real_t m_out[2] = { m_in.x, m_in.y };
-#define MARSHALLED_IN_Vector2(m_in, m_out) Vector2 m_out(m_in[0], m_in[1]);
+ MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) &&
+ offsetof(Vector3, x) == (sizeof(real_t) * 0) &&
+ offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
+ offsetof(Vector3, z) == (sizeof(real_t) * 2)),
-// Rect2
+ MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array
-#define MARSHALLED_OUT_Rect2(m_in, m_out) real_t m_out[4] = { m_in.position.x, m_in.position.y, m_in.size.width, m_in.size.height };
-#define MARSHALLED_IN_Rect2(m_in, m_out) Rect2 m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+ MATCHES_Quat = (MATCHES_real_t && (sizeof(Quat) == (sizeof(real_t) * 4)) &&
+ offsetof(Quat, x) == (sizeof(real_t) * 0) &&
+ offsetof(Quat, y) == (sizeof(real_t) * 1) &&
+ offsetof(Quat, z) == (sizeof(real_t) * 2) &&
+ offsetof(Quat, w) == (sizeof(real_t) * 3)),
-// Transform2D
+ MATCHES_Transform = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform) == (sizeof(Basis) + sizeof(Vector3))) &&
+ offsetof(Transform, basis) == 0 &&
+ offsetof(Transform, origin) == sizeof(Basis)),
-#define MARSHALLED_OUT_Transform2D(m_in, m_out) real_t m_out[6] = { m_in[0].x, m_in[0].y, m_in[1].x, m_in[1].y, m_in[2].x, m_in[2].y };
-#define MARSHALLED_IN_Transform2D(m_in, m_out) Transform2D m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5]);
+ MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) &&
+ offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
+ offsetof(AABB, size) == (sizeof(Vector3) * 1)),
-// Vector3
+ MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) &&
+ offsetof(Color, r) == (sizeof(float) * 0) &&
+ offsetof(Color, g) == (sizeof(float) * 1) &&
+ offsetof(Color, b) == (sizeof(float) * 2) &&
+ offsetof(Color, a) == (sizeof(float) * 3)),
+
+ MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) &&
+ offsetof(Plane, normal) == 0 &&
+ offsetof(Plane, d) == sizeof(Vector3))
+};
+
+// In the future we may force this if we want to ref return these structs
+#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
+// Sometimes clang-format can be an ass
+GD_STATIC_ASSERT(MATCHES_Vector2 &&MATCHES_Rect2 &&MATCHES_Transform2D &&MATCHES_Vector3 &&
+ MATCHES_Basis &&MATCHES_Quat &&MATCHES_Transform &&MATCHES_AABB &&MATCHES_Color &&MATCHES_Plane);
+#endif
-#define MARSHALLED_OUT_Vector3(m_in, m_out) real_t m_out[3] = { m_in.x, m_in.y, m_in.z };
-#define MARSHALLED_IN_Vector3(m_in, m_out) Vector3 m_out(m_in[0], m_in[1], m_in[2]);
+} // namespace InteropLayout
-// Basis
+#pragma pack(push, 1)
-#define MARSHALLED_OUT_Basis(m_in, m_out) real_t m_out[9] = { \
- m_in[0].x, m_in[0].y, m_in[0].z, \
- m_in[1].x, m_in[1].y, m_in[1].z, \
- m_in[2].x, m_in[2].y, m_in[2].z \
+struct M_Vector2 {
+ real_t x, y;
+
+ static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) {
+ return Vector2(p_from.x, p_from.y);
+ }
+
+ static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) {
+ M_Vector2 ret = { p_from.x, p_from.y };
+ return ret;
+ }
};
-#define MARSHALLED_IN_Basis(m_in, m_out) Basis m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]);
-// Quat
+struct M_Rect2 {
+ M_Vector2 position;
+ M_Vector2 size;
-#define MARSHALLED_OUT_Quat(m_in, m_out) real_t m_out[4] = { m_in.x, m_in.y, m_in.z, m_in.w };
-#define MARSHALLED_IN_Quat(m_in, m_out) Quat m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+ static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) {
+ return Rect2(M_Vector2::convert_to(p_from.position),
+ M_Vector2::convert_to(p_from.size));
+ }
-// Transform
+ static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) {
+ M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) };
+ return ret;
+ }
+};
-#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \
- m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \
- m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \
- m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \
- m_in.origin.x, m_in.origin.y, m_in.origin.z \
+struct M_Transform2D {
+ M_Vector2 elements[3];
+
+ static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) {
+ return Transform2D(p_from.elements[0].x, p_from.elements[0].y,
+ p_from.elements[1].x, p_from.elements[1].y,
+ p_from.elements[2].x, p_from.elements[2].y);
+ }
+
+ static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) {
+ M_Transform2D ret = {
+ M_Vector2::convert_from(p_from.elements[0]),
+ M_Vector2::convert_from(p_from.elements[1]),
+ M_Vector2::convert_from(p_from.elements[2])
+ };
+ return ret;
+ }
};
-#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \
- Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \
- Vector3(m_in[9], m_in[10], m_in[11]));
-// AABB
+struct M_Vector3 {
+ real_t x, y, z;
+
+ static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) {
+ return Vector3(p_from.x, p_from.y, p_from.z);
+ }
-#define MARSHALLED_OUT_AABB(m_in, m_out) real_t m_out[6] = { m_in.position.x, m_in.position.y, m_in.position.z, m_in.size.x, m_in.size.y, m_in.size.z };
-#define MARSHALLED_IN_AABB(m_in, m_out) AABB m_out(Vector3(m_in[0], m_in[1], m_in[2]), Vector3(m_in[3], m_in[4], m_in[5]));
+ static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) {
+ M_Vector3 ret = { p_from.x, p_from.y, p_from.z };
+ return ret;
+ }
+};
-// Color
+struct M_Basis {
+ M_Vector3 elements[3];
+
+ static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) {
+ return Basis(M_Vector3::convert_to(p_from.elements[0]),
+ M_Vector3::convert_to(p_from.elements[1]),
+ M_Vector3::convert_to(p_from.elements[2]));
+ }
+
+ static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) {
+ M_Basis ret = {
+ M_Vector3::convert_from(p_from.elements[0]),
+ M_Vector3::convert_from(p_from.elements[1]),
+ M_Vector3::convert_from(p_from.elements[2])
+ };
+ return ret;
+ }
+};
-#define MARSHALLED_OUT_Color(m_in, m_out) real_t m_out[4] = { m_in.r, m_in.g, m_in.b, m_in.a };
-#define MARSHALLED_IN_Color(m_in, m_out) Color m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+struct M_Quat {
+ real_t x, y, z, w;
-// Plane
+ static _FORCE_INLINE_ Quat convert_to(const M_Quat &p_from) {
+ return Quat(p_from.x, p_from.y, p_from.z, p_from.w);
+ }
-#define MARSHALLED_OUT_Plane(m_in, m_out) real_t m_out[4] = { m_in.normal.x, m_in.normal.y, m_in.normal.z, m_in.d };
-#define MARSHALLED_IN_Plane(m_in, m_out) Plane m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
+ static _FORCE_INLINE_ M_Quat convert_from(const Quat &p_from) {
+ M_Quat ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+ return ret;
+ }
+};
-#endif
+struct M_Transform {
+ M_Basis basis;
+ M_Vector3 origin;
+
+ static _FORCE_INLINE_ Transform convert_to(const M_Transform &p_from) {
+ return Transform(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin));
+ }
+
+ static _FORCE_INLINE_ M_Transform convert_from(const Transform &p_from) {
+ M_Transform ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) };
+ return ret;
+ }
+};
+
+struct M_AABB {
+ M_Vector3 position;
+ M_Vector3 size;
+
+ static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) {
+ return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size));
+ }
+
+ static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) {
+ M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) };
+ return ret;
+ }
+};
+
+struct M_Color {
+ float r, g, b, a;
+
+ static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) {
+ return Color(p_from.r, p_from.g, p_from.b, p_from.a);
+ }
+
+ static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) {
+ M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a };
+ return ret;
+ }
+};
+
+struct M_Plane {
+ M_Vector3 normal;
+ real_t d;
+
+ static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) {
+ return Plane(M_Vector3::convert_to(p_from.normal), p_from.d);
+ }
+
+ static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) {
+ M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d };
+ return ret;
+ }
+};
+
+#pragma pack(pop)
+
+#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \
+ template <int> \
+ _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl(const M_##m_type *p_from); \
+ \
+ template <> \
+ _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<0>(const M_##m_type *p_from) { \
+ return M_##m_type::convert_to(*p_from); \
+ } \
+ \
+ template <> \
+ _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<1>(const M_##m_type *p_from) { \
+ return *reinterpret_cast<const m_type *>(p_from); \
+ } \
+ \
+ _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \
+ return marshalled_in_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
+ } \
+ \
+ template <int> \
+ _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl(const m_type &p_from); \
+ \
+ template <> \
+ _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<0>(const m_type &p_from) { \
+ return M_##m_type::convert_from(p_from); \
+ } \
+ \
+ template <> \
+ _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<1>(const m_type &p_from) { \
+ return *reinterpret_cast<const M_##m_type *>(&p_from); \
+ } \
+ \
+ _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \
+ return marshalled_out_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
+ }
+
+DECL_TYPE_MARSHAL_TEMPLATES(Vector2)
+DECL_TYPE_MARSHAL_TEMPLATES(Rect2)
+DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
+DECL_TYPE_MARSHAL_TEMPLATES(Basis)
+DECL_TYPE_MARSHAL_TEMPLATES(Quat)
+DECL_TYPE_MARSHAL_TEMPLATES(Transform)
+DECL_TYPE_MARSHAL_TEMPLATES(AABB)
+DECL_TYPE_MARSHAL_TEMPLATES(Color)
+DECL_TYPE_MARSHAL_TEMPLATES(Plane)
+
+#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr))
+#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from))
} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index b97a24b09c..fe2c09799c 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -631,36 +631,36 @@ void set_pending_exception(MonoException *p_exc) {
_THREAD_LOCAL_(int)
current_invoke_count = 0;
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) {
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) {
+MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) {
+MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc);
+ MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
}
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index ec3a57eb46..f00680ff03 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -233,13 +233,13 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
return current_invoke_count;
}
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc);
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc);
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
+MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc);
-MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc);
+MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc);
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc);
+void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
+MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error);
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index b4c78df538..fa1fbebb16 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -141,7 +141,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
if (exc) {
GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL_V();
+ ERR_FAIL();
}
}
}
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index 337a86870e..40b47e8648 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -33,6 +33,15 @@
// noreturn
+#if __cpp_static_assert
+#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed")
+#else
+#define _GD_STATIC_ASSERT_VARNAME_CONCAT_B(m_ignore, m_name) m_name
+#define _GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_B(hello there, m_a##m_b)
+#define _GD_STATIC_ASSERT_VARNAME_CONCAT(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b)
+#define GD_STATIC_ASSERT(m_cond) typedef int GD_STATIC_ASSERT_VARNAME_CONCAT(godot_static_assert_, __COUNTER__)[((m_cond) ? 1 : -1)]
+#endif
+
#undef _NO_RETURN_
#ifdef __GNUC__
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index ea942a9a8e..e663ee3c4a 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -88,7 +88,7 @@ void fix_path(const String &p_path, String &r_out) {
bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
#ifdef WINDOWS_ENABLED
CharType ret[_MAX_PATH];
- if (_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
+ if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
String abspath = String(ret).replace("\\", "/");
int pos = abspath.find(":/");
if (pos != -1) {
@@ -99,10 +99,12 @@ bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
return true;
}
#else
- char ret[PATH_MAX];
- if (realpath(p_existing_path.utf8().get_data(), ret)) {
+ char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL);
+ if (resolved_path) {
String retstr;
- if (!retstr.parse_utf8(ret)) {
+ bool success = !retstr.parse_utf8(resolved_path);
+ ::free(resolved_path);
+ if (success) {
r_abs_path = retstr;
return true;
}
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 8691932f9a..6900866725 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -30,6 +30,8 @@
#include "string_utils.h"
+#include "core/os/file_access.h"
+
namespace {
int sfind(const String &p_text, int p_from) {
@@ -128,6 +130,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
return new_string;
}
+#ifdef TOOLS_ENABLED
bool is_csharp_keyword(const String &p_name) {
// Reserved keywords
@@ -156,3 +159,28 @@ bool is_csharp_keyword(const String &p_name) {
String escape_csharp_keyword(const String &p_name) {
return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
}
+#endif
+
+Error read_all_file_utf8(const String &p_path, String &r_content) {
+ PoolVector<uint8_t> sourcef;
+ Error err;
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ int len = f->get_len();
+ sourcef.resize(len + 1);
+ PoolVector<uint8_t>::Write w = sourcef.write();
+ int r = f->get_buffer(w.ptr(), len);
+ f->close();
+ memdelete(f);
+ ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
+ w[len] = 0;
+
+ String source;
+ if (source.parse_utf8((const char *)w.ptr())) {
+ ERR_FAIL_V(ERR_INVALID_DATA);
+ }
+
+ r_content = source;
+ return OK;
+}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index f2df2340ae..ee803bd720 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -42,4 +42,6 @@ bool is_csharp_keyword(const String &p_name);
String escape_csharp_keyword(const String &p_name);
#endif
+Error read_all_file_utf8(const String &p_path, String &r_content);
+
#endif // STRING_FORMAT_H
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index d88abb3180..ba54160a90 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -17,6 +17,9 @@
<member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap">
If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map.
</member>
+ <member name="height" type="int" setter="set_height" getter="get_height">
+ Height of the generated texture.
+ </member>
<member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
The [OpenSimplexNoise] instance used to generate the noise.
</member>
@@ -26,9 +29,6 @@
<member name="width" type="int" setter="set_width" getter="get_width">
Width of the generated texture.
</member>
- <member name="height" type="int" setter="set_height" getter="get_height">
- Height of the generated texture.
- </member>
</members>
<constants>
</constants>
diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp
index 98351fbaee..d121a82d9e 100644
--- a/modules/recast/navigation_mesh_editor_plugin.cpp
+++ b/modules/recast/navigation_mesh_editor_plugin.cpp
@@ -103,24 +103,25 @@ void NavigationMeshEditor::_bind_methods() {
NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox = memnew(HBoxContainer);
+
button_bake = memnew(ToolButton);
- button_bake->set_text(TTR("Bake!"));
+ bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
- button_reset = memnew(Button);
- button_bake->set_tooltip(TTR("Bake the navigation mesh.") + "\n");
+ button_bake->set_text(TTR("Bake NavMesh"));
+ button_bake->connect("pressed", this, "_bake_pressed");
- bake_info = memnew(Label);
- bake_hbox->add_child(button_bake);
+ button_reset = memnew(ToolButton);
bake_hbox->add_child(button_reset);
+ // No button text, we only use a revert icon which is set when entering the tree.
+ button_reset->set_tooltip(TTR("Clear the navigation mesh."));
+ button_reset->connect("pressed", this, "_clear_pressed");
+
+ bake_info = memnew(Label);
bake_hbox->add_child(bake_info);
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
node = NULL;
-
- button_bake->connect("pressed", this, "_bake_pressed");
- button_reset->connect("pressed", this, "_clear_pressed");
- button_reset->set_tooltip(TTR("Clear the navigation mesh."));
}
NavigationMeshEditor::~NavigationMeshEditor() {
diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h
index 4f3e222002..914d9ab8b9 100644
--- a/modules/recast/navigation_mesh_editor_plugin.h
+++ b/modules/recast/navigation_mesh_editor_plugin.h
@@ -43,8 +43,8 @@ class NavigationMeshEditor : public Control {
AcceptDialog *err_dialog;
HBoxContainer *bake_hbox;
- Button *button_bake;
- Button *button_reset;
+ ToolButton *button_bake;
+ ToolButton *button_reset;
Label *bake_info;
NavigationMeshInstance *node;
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index bdd3e31eb8..03ef024587 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -178,13 +178,17 @@ void RegEx::clear() {
if (sizeof(CharType) == 2) {
- if (code)
+ if (code) {
pcre2_code_free_16((pcre2_code_16 *)code);
+ code = NULL;
+ }
} else {
- if (code)
+ if (code) {
pcre2_code_free_32((pcre2_code_32 *)code);
+ code = NULL;
+ }
}
}
diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp
index 26cb76011c..a08ac7bd28 100644
--- a/modules/squish/image_compress_squish.cpp
+++ b/modules/squish/image_compress_squish.cpp
@@ -64,12 +64,13 @@ void image_decompress_squish(Image *p_image) {
return;
}
- int dst_ofs = 0;
-
for (int i = 0; i <= mm_count; i++) {
int src_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0;
p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
- squish::DecompressImage(&wb[dst_ofs], mipmap_w, mipmap_h, &rb[src_ofs], squish_flags);
+ int dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
+ squish::DecompressImage(&wb[dst_ofs], w, h, &rb[src_ofs], squish_flags);
+ w >>= 1;
+ h >>= 1;
}
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index 28764aca40..70849c5a80 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
index b8e77a1b0f..ff3ed66e81 100644
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
@@ -125,12 +125,10 @@
</argument>
<description>
Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error.
-
The [code]inputs[/code] array contains the values of the input ports.
[code]outputs[/code] is an array whose indices should be set to the respective outputs.
The [code]start_mode[/code] is usually [code]START_MODE_BEGIN_SEQUENCE[/code], unless you have used the STEP_* constants.
[code]working_mem[/code] is an array which can be used to persist information between runs of the custom node.
-
When returning, you can mask the returned value with one of the STEP_* constants.
</description>
</method>
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index ff97c21fd9..186e9e63b1 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -48,20 +48,22 @@ bool VisualScriptNode::is_breakpoint() const {
void VisualScriptNode::_notification(int p_what) {
if (p_what == NOTIFICATION_POSTINITIALIZE) {
-
- int dvc = get_input_value_port_count();
- for (int i = 0; i < dvc; i++) {
- Variant::Type expected = get_input_value_port_info(i).type;
- Variant::CallError ce;
- default_input_values.push_back(Variant::construct(expected, NULL, 0, ce, false));
- }
+ _update_input_ports();
}
}
-void VisualScriptNode::ports_changed_notify() {
-
+void VisualScriptNode::_update_input_ports() {
default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize
+ int port_count = get_input_value_port_count();
+ for (int i = 0; i < port_count; i++) {
+ Variant::Type expected = get_input_value_port_info(i).type;
+ Variant::CallError ce;
+ set_default_input_value(i, Variant::construct(expected, NULL, 0, ce, false));
+ }
+}
+void VisualScriptNode::ports_changed_notify() {
+ _update_input_ports();
emit_signal("ports_changed");
}
@@ -2697,11 +2699,11 @@ VisualScriptLanguage::VisualScriptLanguage() {
_debug_parse_err_file = "";
_debug_call_stack_pos = 0;
int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024);
+ ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
+
if (ScriptDebugger::get_singleton()) {
//debugging enabled!
_debug_max_call_stack = dmcs;
- if (_debug_max_call_stack < 1024)
- _debug_max_call_stack = 1024;
_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
} else {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index ea99ce4970..bd666447a3 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -52,6 +52,7 @@ class VisualScriptNode : public Resource {
Array _get_default_input_values() const;
void validate_input_default_values();
+ void _update_input_ports();
protected:
void _notification(int p_what);
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 7fdd7fe446..afaa6a9b95 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -765,6 +765,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
}
void VisualScriptEditor::_update_members() {
+ ERR_FAIL_COND(!script.is_valid());
updating_members = true;
@@ -1922,7 +1923,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
}
-void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) {
+void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) {
Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id);
if (!vsfc.is_valid())
@@ -3018,11 +3019,15 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) {
void VisualScriptEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_READY) {
+ if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) {
+
node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons"));
node_filter->set_clear_button_enabled(true);
- variable_editor->connect("changed", this, "_update_members");
- signal_editor->connect("changed", this, "_update_members");
+
+ if (p_what == NOTIFICATION_READY) {
+ variable_editor->connect("changed", this, "_update_members");
+ signal_editor->connect("changed", this, "_update_members");
+ }
Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
@@ -3056,8 +3061,12 @@ void VisualScriptEditor::_notification(int p_what) {
node_styles[E->get().first] = frame_style;
}
}
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+
+ if (is_visible_in_tree() && script.is_valid()) {
+ _update_members();
+ _update_graph();
+ }
+ } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
left_vsplit->set_visible(is_visible_in_tree());
}
}
@@ -3644,7 +3653,7 @@ VisualScriptEditor::VisualScriptEditor() {
new_virtual_method_select = memnew(VisualScriptPropertySelector);
add_child(new_virtual_method_select);
new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method");
- new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method");
+ new_virtual_method_select->get_cancel();
member_popup = memnew(PopupMenu);
add_child(member_popup);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 97c75dae94..5f707c9e4c 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -176,7 +176,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _cancel_connect_node();
void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point);
- void _selected_new_virtual_method(const String &p_text = String(""), const String &p_category = String(""), const bool p_connecting = true);
+ void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
int error_line;
@@ -234,7 +234,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _comment_node_resized(const Vector2 &p_new_size, int p_node);
int selecting_method_id;
- void _selected_method(const String &p_method, const String &p_type);
+ void _selected_method(const String &p_method, const String &p_type, const bool p_connecting);
void _draw_color_over_button(Object *obj, Color p_color);
void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud);
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 865f93a148..99748af8a1 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -3708,18 +3708,18 @@ void register_visual_script_nodes() {
for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
if (E->get().arguments.size() > 0) {
-
- String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + " ( ";
+ String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + "(";
for (int j = 0; j < E->get().arguments.size(); j++) {
- if (j > 0)
+ if (j > 0) {
name += ", ";
- if (E->get().arguments.size() == 1)
+ }
+ if (E->get().arguments.size() == 1) {
name += Variant::get_type_name(E->get().arguments[j].type);
- else
+ } else {
name += E->get().arguments[j].name;
+ }
}
- name += ") ";
-
+ name += ")";
VisualScriptLanguage::singleton->add_register_func(name, create_constructor_node);
Pair<Variant::Type, MethodInfo> pair;
pair.first = Variant::Type(i);
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index d3637939c9..e5d12cb495 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -304,31 +304,36 @@ void VisualScriptPropertySelector::_update_search() {
continue;
MethodInfo mi = E->get();
- String desc = mi.name.capitalize() + " (";
+ String desc_arguments;
+ if (mi.arguments.size() > 0) {
+ desc_arguments = "(";
+ for (int i = 0; i < mi.arguments.size(); i++) {
+
+ if (i > 0) {
+ desc_arguments += ", ";
+ }
+ if (mi.arguments[i].type == Variant::NIL) {
+ desc_arguments += "var";
+ } else if (mi.arguments[i].name.find(":") != -1) {
+ desc_arguments += mi.arguments[i].name.get_slice(":", 1);
+ mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
+ } else {
+ desc_arguments += Variant::get_type_name(mi.arguments[i].type);
+ }
+ }
+ desc_arguments += ")";
+ }
+ String desc_raw = mi.name + desc_arguments;
+ String desc = desc_raw.capitalize().replace("( ", "(");
if (search_box->get_text() != String() &&
name.findn(search_box->get_text()) == -1 &&
- desc.findn(search_box->get_text()) == -1)
+ desc.findn(search_box->get_text()) == -1 &&
+ desc_raw.findn(search_box->get_text()) == -1) {
continue;
-
- TreeItem *item = search_options->create_item(category ? category : root);
-
- for (int i = 0; i < mi.arguments.size(); i++) {
-
- if (i > 0)
- desc += ", ";
-
- if (mi.arguments[i].type == Variant::NIL)
- desc += "var";
- else if (mi.arguments[i].name.find(":") != -1) {
- desc += mi.arguments[i].name.get_slice(":", 1);
- mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
- } else
- desc += Variant::get_type_name(mi.arguments[i].type);
}
- desc += ")";
-
+ TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, desc);
item->set_icon(0, get_icon("MemberMethod", "EditorIcons"));
item->set_metadata(0, name);
@@ -414,11 +419,16 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type());
type_name = basic_type.capitalize() + " ";
}
- VisualScriptBuiltinFunc *vnode_builtin_function_call = Object::cast_to<VisualScriptBuiltinFunc>(*VisualScriptLanguage::singleton->create_node_from_name(E->get()));
- if (vnode_builtin_function_call != NULL) {
- type_name = "Builtin ";
+
+ Vector<String> desc = path[path.size() - 1].replace("(", "( ").replace(")", " )").replace(",", ", ").split(" ");
+ for (size_t i = 0; i < desc.size(); i++) {
+ desc.write[i] = desc[i].capitalize();
+ if (desc[i].ends_with(",")) {
+ desc.write[i] = desc[i].replace(",", ", ");
+ }
}
- item->set_text(0, type_name + path[path.size() - 1].capitalize());
+
+ item->set_text(0, type_name + String("").join(desc));
item->set_icon(0, get_icon("VisualScript", "EditorIcons"));
item->set_selectable(0, true);
item->set_metadata(0, E->get());
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 8255ed7116..82a577790e 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -31,6 +31,7 @@
#include "emws_client.h"
#include "core/io/ip.h"
+#include "core/project_settings.h"
#include "emscripten.h"
extern "C" {
@@ -43,8 +44,9 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
- static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
- client->_on_peer_packet();
+ Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
+ if (err == OK)
+ client->_on_peer_packet();
}
EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
@@ -159,7 +161,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
/* clang-format on */
- static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock);
+ static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size);
return OK;
};
@@ -198,7 +200,13 @@ uint16_t EMWSClient::get_connected_port() const {
return 1025;
};
+int EMWSClient::get_max_packet_size() const {
+ return (1 << _in_buf_size) - PROTO_SIZE;
+}
+
EMWSClient::EMWSClient() {
+ _in_buf_size = GLOBAL_GET(WSC_IN_BUF);
+ _in_pkt_size = GLOBAL_GET(WSC_IN_PKT);
_is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
/* clang-format off */
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index b20633baff..a21090a1a3 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -41,6 +41,8 @@ class EMWSClient : public WebSocketClient {
GDCIIMPL(EMWSClient, WebSocketClient);
private:
+ int _in_buf_size;
+ int _in_pkt_size;
int _js_id;
public:
@@ -52,6 +54,7 @@ public:
IP_Address get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
+ int get_max_packet_size() const;
virtual void poll();
EMWSClient();
~EMWSClient();
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 68f41165eb..bb97934824 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -32,11 +32,11 @@
#include "emws_peer.h"
#include "core/io/ip.h"
-void EMWSPeer::set_sock(int p_sock) {
+void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) {
peer_sock = p_sock;
- in_buffer.clear();
- queue_count = 0;
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _packet_buffer.resize((1 << p_in_buf_size));
}
void EMWSPeer::set_write_mode(WriteMode p_mode) {
@@ -47,18 +47,10 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
return write_mode;
}
-void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
-
- if (in_buffer.space_left() < p_size + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL();
- }
+Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
uint8_t is_string = p_is_string ? 1 : 0;
- in_buffer.write((uint8_t *)&p_size, 4);
- in_buffer.write((uint8_t *)&is_string, 1);
- in_buffer.write(p_data, p_size);
- queue_count++;
+ return _in_buffer.write_packet(p_data, p_size, &is_string);
}
Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
@@ -89,40 +81,28 @@ Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- if (queue_count == 0)
+ if (_in_buffer.packets_left() == 0)
return ERR_UNAVAILABLE;
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- in_buffer.read((uint8_t *)&to_read, 4);
- --queue_count;
- left = in_buffer.data_left();
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ int read = 0;
+ Error err = _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
+ ERR_FAIL_COND_V(err != OK, err);
- if (left < to_read + 1) {
- in_buffer.advance_read(left);
- return FAILED;
- }
-
- in_buffer.read(&is_string, 1);
- _was_string = is_string == 1;
- in_buffer.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
return OK;
};
int EMWSPeer::get_available_packet_count() const {
- return queue_count;
+ return _in_buffer.packets_left();
};
bool EMWSPeer::was_string_packet() const {
- return _was_string;
+ return _is_string;
};
bool EMWSPeer::is_connected_to_host() const {
@@ -143,9 +123,9 @@ void EMWSPeer::close(int p_code, String p_reason) {
}, peer_sock, p_code, p_reason.utf8().get_data());
/* clang-format on */
}
+ _is_string = 0;
+ _in_buffer.clear();
peer_sock = -1;
- queue_count = 0;
- in_buffer.clear();
};
IP_Address EMWSPeer::get_connected_host() const {
@@ -162,15 +142,12 @@ uint16_t EMWSPeer::get_connected_port() const {
EMWSPeer::EMWSPeer() {
peer_sock = -1;
- queue_count = 0;
- _was_string = false;
- in_buffer.resize(16);
write_mode = WRITE_MODE_BINARY;
+ close();
};
EMWSPeer::~EMWSPeer() {
- in_buffer.resize(0);
close();
};
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index a4b2c8f50b..4beb86d45b 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -36,6 +36,7 @@
#include "core/io/packet_peer.h"
#include "core/ring_buffer.h"
#include "emscripten.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
class EMWSPeer : public WebSocketPeer {
@@ -43,25 +44,20 @@ class EMWSPeer : public WebSocketPeer {
GDCIIMPL(EMWSPeer, WebSocketPeer);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
- };
-
int peer_sock;
WriteMode write_mode;
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- RingBuffer<uint8_t> in_buffer;
- int queue_count;
- bool _was_string;
+ PoolVector<uint8_t> _packet_buffer;
+ PacketBuffer<uint8_t> _in_buffer;
+ uint8_t _is_string;
public:
- void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
- void set_sock(int sock);
+ Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size);
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
@@ -72,10 +68,6 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
- Error read_wsi(void *in, size_t len);
- Error write_wsi();
-
EMWSPeer();
~EMWSPeer();
};
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index ad4a758c0f..09f9c1ceec 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -71,6 +71,13 @@ int EMWSServer::get_peer_port(int p_peer_id) const {
void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
}
+void EMWSServer::poll() {
+}
+
+int EMWSServer::get_max_packet_size() const {
+ return 0;
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index 74b689a29b..2dc455c389 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -49,6 +49,7 @@ public:
IP_Address get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
+ int get_max_packet_size() const;
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index b3e5f6ffab..fa0bb4cfb2 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -32,6 +32,7 @@
#include "lws_client.h"
#include "core/io/ip.h"
#include "core/io/stream_peer_ssl.h"
+#include "core/project_settings.h"
#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@@ -76,26 +77,11 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
ERR_FAIL_V(FAILED);
}
- char abuf[1024];
- char hbuf[1024];
- char pbuf[2048];
- String addr_str = (String)addr;
- strncpy(abuf, addr_str.ascii().get_data(), 1023);
- abuf[1023] = '\0';
- strncpy(hbuf, p_host.utf8().get_data(), 1023);
- hbuf[1023] = '\0';
- strncpy(pbuf, p_path.utf8().get_data(), 2047);
- pbuf[2047] = '\0';
-
i.context = context;
if (p_protocols.size() > 0)
i.protocol = _lws_ref->lws_names;
else
i.protocol = NULL;
- i.address = abuf;
- i.host = hbuf;
- i.path = pbuf;
- i.port = p_port;
if (p_ssl) {
i.ssl_connection = LCCSCF_USE_SSL;
@@ -105,10 +91,24 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
i.ssl_connection = 0;
}
+ // These CharStrings needs to survive till we call lws_client_connect_via_info
+ CharString addr_ch = ((String)addr).ascii();
+ CharString host_ch = p_host.utf8();
+ CharString path_ch = p_path.utf8();
+ i.address = addr_ch.get_data();
+ i.host = host_ch.get_data();
+ i.path = path_ch.get_data();
+ i.port = p_port;
+
lws_client_connect_via_info(&i);
+
return OK;
};
+int LWSClient::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
void LWSClient::poll() {
_lws_poll();
@@ -129,7 +129,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
} break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
- peer->set_wsi(wsi);
+ peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
peer_data->peer_id = 0;
peer_data->force_close = false;
peer_data->clean_close = false;
@@ -212,6 +212,11 @@ uint16_t LWSClient::get_connected_port() const {
};
LWSClient::LWSClient() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1);
+
context = NULL;
_lws_ref = NULL;
_peer = Ref<LWSPeer>(memnew(LWSPeer));
diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h
index 1bbc19f352..fdecb99925 100644
--- a/modules/websocket/lws_client.h
+++ b/modules/websocket/lws_client.h
@@ -43,8 +43,15 @@ class LWSClient : public WebSocketClient {
LWS_HELPER(LWSClient);
+private:
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+
public:
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
+ int get_max_packet_size() const;
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
IP_Address get_connected_host() const;
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index b5c130b308..04e6e7c951 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -41,11 +41,12 @@
#include "drivers/unix/net_socket_posix.h"
-void LWSPeer::set_wsi(struct lws *p_wsi) {
+void LWSPeer::set_wsi(struct lws *p_wsi, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) {
ERR_FAIL_COND(wsi != NULL);
- rbw.resize(16);
- rbr.resize(16);
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _out_buffer.resize(p_out_pkt_size, p_out_buf_size);
+ _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE);
wsi = p_wsi;
};
@@ -61,24 +62,29 @@ Error LWSPeer::read_wsi(void *in, size_t len) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- uint32_t size = in_size;
- uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
+ if (lws_is_first_fragment(wsi))
+ _in_size = 0;
+ else if (_in_size == -1) // Trash this frame
+ return ERR_FILE_CORRUPT;
- if (rbr.space_left() < len + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL_V(FAILED);
+ Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL);
+
+ if (err != OK) {
+ _in_buffer.discard_payload(_in_size);
+ _in_size = -1;
+ ERR_FAIL_V(err);
}
- copymem(&(input_buffer[size]), in, len);
- size += len;
+ _in_size += len;
- in_size = size;
if (lws_is_final_fragment(wsi)) {
- rbr.write((uint8_t *)&size, 4);
- rbr.write((uint8_t *)&is_string, 1);
- rbr.write(input_buffer, size);
- in_count++;
- in_size = 0;
+ uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
+ err = _in_buffer.write_packet(NULL, _in_size, &is_string);
+ if (err != OK) {
+ _in_buffer.discard_payload(_in_size);
+ _in_size = -1;
+ ERR_FAIL_V(err);
+ }
}
return OK;
@@ -89,26 +95,20 @@ Error LWSPeer::write_wsi() {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
PoolVector<uint8_t> tmp;
- int left = rbw.data_left();
- uint32_t to_write = 0;
+ int count = _out_buffer.packets_left();
- if (left == 0 || out_count == 0)
+ if (count == 0)
return OK;
- rbw.read((uint8_t *)&to_write, 4);
- out_count--;
-
- if (left < to_write) {
- rbw.advance_read(left);
- return FAILED;
- }
+ int read = 0;
+ uint8_t is_string;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read);
- tmp.resize(LWS_PRE + to_write);
- rbw.read(&(tmp.write()[LWS_PRE]), to_write);
- lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode);
- tmp.resize(0);
+ enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY;
+ lws_write(wsi, &(rw[LWS_PRE]), read, mode);
- if (out_count > 0)
+ if (count > 1)
lws_callback_on_writable(wsi); // we want to write more!
return OK;
@@ -118,40 +118,27 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- rbw.write((uint8_t *)&p_buffer_size, 4);
- rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left()));
- out_count++;
-
+ uint8_t is_string = write_mode == WRITE_MODE_TEXT;
+ _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string);
lws_callback_on_writable(wsi); // notify that we want to write
return OK;
};
Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ r_buffer_size = 0;
+
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- if (in_count == 0)
+ if (_in_buffer.packets_left() == 0)
return ERR_UNAVAILABLE;
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- rbr.read((uint8_t *)&to_read, 4);
- in_count--;
- left = rbr.data_left();
-
- if (left < to_read + 1) {
- rbr.advance_read(left);
- return FAILED;
- }
+ int read = 0;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
- rbr.read(&is_string, 1);
- rbr.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
- _was_string = is_string;
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
return OK;
};
@@ -161,12 +148,12 @@ int LWSPeer::get_available_packet_count() const {
if (!is_connected_to_host())
return 0;
- return in_count;
+ return _in_buffer.packets_left();
};
bool LWSPeer::was_string_packet() const {
- return _was_string;
+ return _is_string;
};
bool LWSPeer::is_connected_to_host() const {
@@ -219,12 +206,11 @@ void LWSPeer::close(int p_code, String p_reason) {
close_reason = "";
}
wsi = NULL;
- rbw.resize(0);
- rbr.resize(0);
- in_count = 0;
- in_size = 0;
- out_count = 0;
- _was_string = false;
+ _in_buffer.clear();
+ _out_buffer.clear();
+ _in_size = 0;
+ _is_string = 0;
+ _packet_buffer.resize(0);
};
IP_Address LWSPeer::get_connected_host() const {
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h
index 571445db01..3ded3810d1 100644
--- a/modules/websocket/lws_peer.h
+++ b/modules/websocket/lws_peer.h
@@ -37,6 +37,7 @@
#include "core/ring_buffer.h"
#include "libwebsockets.h"
#include "lws_config.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
class LWSPeer : public WebSocketPeer {
@@ -44,14 +45,16 @@ class LWSPeer : public WebSocketPeer {
GDCIIMPL(LWSPeer, WebSocketPeer);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type
- };
+ int _in_size;
+ uint8_t _is_string;
+ // Our packet info is just a boolean (is_string), using uint8_t for it.
+ PacketBuffer<uint8_t> _in_buffer;
+ PacketBuffer<uint8_t> _out_buffer;
+
+ PoolVector<uint8_t> _packet_buffer;
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
struct lws *wsi;
WriteMode write_mode;
- bool _was_string;
int close_code;
String close_reason;
@@ -63,17 +66,10 @@ public:
bool clean_close;
};
- RingBuffer<uint8_t> rbw;
- RingBuffer<uint8_t> rbr;
- uint8_t input_buffer[PACKET_BUFFER_SIZE];
- uint32_t in_size;
- int in_count;
- int out_count;
-
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
@@ -84,7 +80,7 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
+ void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size);
Error read_wsi(void *in, size_t len);
Error write_wsi();
void send_close_status(struct lws *wsi);
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 58fa043346..0e551eb318 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -31,6 +31,7 @@
#include "lws_server.h"
#include "core/os/os.h"
+#include "core/project_settings.h"
Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
@@ -67,6 +68,10 @@ bool LWSServer::is_listening() const {
return context != NULL;
}
+int LWSServer::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
@@ -85,7 +90,7 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
int32_t id = _gen_unique_id();
Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer));
- peer->set_wsi(wsi);
+ peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
_peer_map[id] = peer;
peer_data->peer_id = id;
@@ -192,6 +197,10 @@ void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
}
LWSServer::LWSServer() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1);
context = NULL;
_lws_ref = NULL;
}
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index 346773ebc4..c43044f194 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -45,11 +45,16 @@ class LWSServer : public WebSocketServer {
private:
Map<int, Ref<LWSPeer> > peer_map;
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
public:
Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
void stop();
bool is_listening() const;
+ int get_max_packet_size() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
IP_Address get_peer_address(int p_peer_id) const;
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
new file mode 100644
index 0000000000..a3af7f728a
--- /dev/null
+++ b/modules/websocket/packet_buffer.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* packet_buffer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 PACKET_BUFFER_H
+#define PACKET_BUFFER_H
+
+#include "core/os/copymem.h"
+#include "core/ring_buffer.h"
+
+template <class T>
+class PacketBuffer {
+
+private:
+ typedef struct {
+ uint32_t size;
+ T info;
+ } _Packet;
+
+ RingBuffer<_Packet> _packets;
+ RingBuffer<uint8_t> _payload;
+
+public:
+ Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) {
+#ifdef TOOLS_ENABLED
+ // Verbose buffer warnings
+ if (p_payload && _payload.space_left() < p_size) {
+ ERR_PRINT("Buffer payload full! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+ if (p_info && _packets.space_left() < 1) {
+ ERR_PRINT("Too many packets in queue! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+#else
+ ERR_FAIL_COND_V(p_payload && _payload.space_left() < p_size, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY);
+#endif
+
+ // If p_info is NULL, only the payload is written
+ if (p_info) {
+ _Packet p;
+ p.size = p_size;
+ copymem(&p.info, p_info, sizeof(T));
+ _packets.write(p);
+ }
+
+ // If p_payload is NULL, only the packet information is written.
+ if (p_payload) {
+ _payload.write((const uint8_t *)p_payload, p_size);
+ }
+
+ return OK;
+ }
+
+ Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) {
+ ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE);
+ _Packet p;
+ _packets.read(&p, 1);
+ ERR_FAIL_COND_V(_payload.data_left() < p.size, ERR_BUG);
+ ERR_FAIL_COND_V(p_bytes < p.size, ERR_OUT_OF_MEMORY);
+
+ r_read = p.size;
+ copymem(r_info, &p.info, sizeof(T));
+ _payload.read(r_payload, p.size);
+ return OK;
+ }
+
+ void discard_payload(int p_size) {
+ _packets.decrease_write(p_size);
+ }
+
+ void resize(int p_pkt_shift, int p_buf_shift) {
+ _packets.resize(p_pkt_shift);
+ _payload.resize(p_buf_shift);
+ }
+
+ int packets_left() const {
+ return _packets.data_left();
+ }
+
+ void clear() {
+ _payload.resize(0);
+ _packets.resize(0);
+ }
+
+ PacketBuffer() {
+ clear();
+ }
+
+ ~PacketBuffer() {
+ clear();
+ }
+};
+
+#endif // PACKET_BUFFER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 538cd40454..8946faffa9 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "register_types.h"
#include "core/error_macros.h"
+#include "core/project_settings.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
@@ -41,6 +42,22 @@
#endif
void register_websocket_types() {
+#define _SET_HINT(NAME, _VAL_, _MAX_) \
+ GLOBAL_DEF(NAME, _VAL_); \
+ ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
+
+ // Client buffers project settings
+ _SET_HINT(WSC_IN_BUF, 64, 4096);
+ _SET_HINT(WSC_IN_PKT, 1024, 16384);
+ _SET_HINT(WSC_OUT_BUF, 64, 4096);
+ _SET_HINT(WSC_OUT_PKT, 1024, 16384);
+
+ // Server buffers project settings
+ _SET_HINT(WSS_IN_BUF, 64, 4096);
+ _SET_HINT(WSS_IN_PKT, 1024, 16384);
+ _SET_HINT(WSS_OUT_BUF, 64, 4096);
+ _SET_HINT(WSS_OUT_PKT, 1024, 16384);
+
#ifdef JAVASCRIPT_ENABLED
EM_ASM({
var IDHandler = {};
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index f9b94dc519..6c5018bb79 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -136,7 +136,7 @@ void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index d27fb4d778..45dd30d0ce 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -30,6 +30,16 @@
#ifndef WEBSOCKETMACTOS_H
#define WEBSOCKETMACTOS_H
+#define WSC_IN_BUF "network/limits/websocket_client/max_in_buffer_kb"
+#define WSC_IN_PKT "network/limits/websocket_client/max_in_packets"
+#define WSC_OUT_BUF "network/limits/websocket_client/max_out_buffer_kb"
+#define WSC_OUT_PKT "network/limits/websocket_client/max_out_packets"
+
+#define WSS_IN_BUF "network/limits/websocket_server/max_in_buffer_kb"
+#define WSS_IN_PKT "network/limits/websocket_server/max_in_packets"
+#define WSS_OUT_BUF "network/limits/websocket_server/max_out_buffer_kb"
+#define WSS_OUT_PKT "network/limits/websocket_server/max_out_packets"
+
/* clang-format off */
#define GDCICLASS(CNAME) \
public:\
diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp
index b948c439df..9a95c17e47 100644
--- a/modules/websocket/websocket_multiplayer.cpp
+++ b/modules/websocket/websocket_multiplayer.cpp
@@ -100,13 +100,6 @@ int WebSocketMultiplayerPeer::get_available_packet_count() const {
return _incoming_packets.size();
}
-int WebSocketMultiplayerPeer::get_max_packet_size() const {
-
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
-
- return MAX_PACKET_SIZE;
-}
-
Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
r_buffer_size = 0;
@@ -313,7 +306,7 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
} else if (to < 0) {
// All but one, for us if not excluded
- if (_peer_id != -p_peer_id)
+ if (_peer_id != -(int32_t)p_peer_id)
_store_pkt(from, to, in_buffer, data_size);
} else {
diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h
index 8edfc5296e..3cba0011fc 100644
--- a/modules/websocket/websocket_multiplayer.h
+++ b/modules/websocket/websocket_multiplayer.h
@@ -51,9 +51,7 @@ protected:
SYS_DEL = 2,
SYS_ID = 3,
- PROTO_SIZE = 9,
- SYS_PACKET_SIZE = 13,
- MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer
+ PROTO_SIZE = 9
};
struct Packet {
@@ -93,7 +91,7 @@ public:
/* PacketPeer */
virtual int get_available_packet_count() const;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const = 0;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 5918fda3c2..4966cdfc72 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -32,7 +32,6 @@
#include "core/error_list.h"
#include "core/io/packet_peer.h"
-#include "core/ring_buffer.h"
#include "websocket_macros.h"
class WebSocketPeer : public PacketPeer {
diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template
index 1603ea70d9..18ffc74fc3 100644
--- a/platform/android/build.gradle.template
+++ b/platform/android/build.gradle.template
@@ -14,9 +14,9 @@ apply plugin: 'com.android.application'
allprojects {
repositories {
- jcenter()
mavenCentral()
google()
+ jcenter()
$$GRADLE_REPOSITORY_URLS$$
}
}
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 953a2fa6d2..7a728e4ef1 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -186,7 +186,7 @@ def configure(env):
env.PrependENVPath('PATH', tools_path)
ccache_path = os.environ.get("CCACHE")
- if ccache_path == None:
+ if ccache_path is None:
env['CC'] = compiler_path + '/clang'
env['CXX'] = compiler_path + '/clang++'
else:
@@ -223,7 +223,7 @@ def configure(env):
else:
print("Using NDK deprecated headers")
env.Append(CPPFLAGS=["-isystem", lib_sysroot + "/usr/include"])
-
+
env.Append(CPPFLAGS='-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing'.split())
env.Append(CPPFLAGS='-DNO_STATVFS -DGLES_ENABLED'.split())
@@ -267,12 +267,12 @@ def configure(env):
env.Append(LINKFLAGS=['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel'])
if mt_link:
env.Append(LINKFLAGS=['-Wl,--threads'])
-
+
if env["android_arch"] == "armv7":
env.Append(LINKFLAGS='-Wl,--fix-cortex-a8'.split())
env.Append(LINKFLAGS='-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now'.split())
env.Append(LINKFLAGS='-Wl,-soname,libgodot_android.so -Wl,--gc-sections'.split())
-
+
env.Append(LINKFLAGS=target_opts)
env.Append(LINKFLAGS=common_opts)
@@ -293,7 +293,7 @@ def configure(env):
# Return NDK version string in source.properties (adapted from the Chromium project).
def get_ndk_version(path):
- if path == None:
+ if path is None:
return None
prop_file_path = os.path.join(path, "source.properties")
try:
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 3b503e2657..3766f732e4 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -395,7 +395,7 @@ class EditorExportAndroid : public EditorExportPlatform {
return aname;
}
- String get_package_name(const String &p_package) {
+ String get_package_name(const String &p_package) const {
String pname = p_package;
String basename = ProjectSettings::get_singleton()->get("application/config/name");
@@ -420,6 +420,70 @@ class EditorExportAndroid : public EditorExportPlatform {
return pname;
}
+ bool is_package_name_valid(const String &p_package, String *r_error = NULL) const {
+
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = "Package name is missing.";
+ }
+ return false;
+ }
+
+ int segments = 0;
+ bool first = true;
+ for (int i = 0; i < pname.length(); i++) {
+ CharType c = pname[i];
+ if (first && c == '.') {
+ if (r_error) {
+ *r_error = "Package segments must be of non-zero length.";
+ }
+ return false;
+ }
+ if (c == '.') {
+ segments++;
+ first = true;
+ continue;
+ }
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+ if (r_error) {
+ *r_error = "The character '" + String::chr(c) + "' is not allowed in Android application package names.";
+ }
+ return false;
+ }
+ if (first && (c >= '0' && c <= '9')) {
+ if (r_error) {
+ *r_error = "A digit cannot be the first character in a package segment.";
+ }
+ return false;
+ }
+ if (first && c == '_') {
+ if (r_error) {
+ *r_error = "The character '" + String::chr(c) + "' cannot be the first character in a package segment.";
+ }
+ return false;
+ }
+ first = false;
+ }
+
+ if (segments == 0) {
+ if (r_error) {
+ *r_error = "The package must have at least one '.' separator.";
+ }
+ return false;
+ }
+
+ if (first) {
+ if (r_error) {
+ *r_error = "Package segments must be of non-zero length.";
+ }
+ return false;
+ }
+
+ return true;
+ }
+
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
/*
@@ -1382,12 +1446,23 @@ public:
}
}
+ String pn = p_preset->get("package/unique_name");
+ String pn_err;
+
+ if (!is_package_name_valid(get_package_name(pn), &pn_err)) {
+
+ valid = false;
+ err += "Invalid package name - " + pn_err + "\n";
+ }
+
r_error = err;
return valid;
}
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const {
- return "apk";
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ list.push_back("apk");
+ return list;
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
diff --git a/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml b/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
index 23bac02294..104993da7e 100644
--- a/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
+++ b/platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
@@ -32,9 +32,9 @@
android:id="@+id/appIcon"
android:layout_width="fill_parent"
android:layout_height="25dp"
- android:scaleType="centerInside"
+ android:scaleType="centerInside"
android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
+ android:layout_alignParentTop="true"
android:src="@android:drawable/stat_sys_download" />
<TextView
diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java
index ab37f7a02c..88194f00d1 100644
--- a/platform/android/java/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/src/org/godotengine/godot/Godot.java
@@ -279,7 +279,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
layout.addView(edittext);
- mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl,this);
+ mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl, this);
layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
edittext.setView(mView);
io.setEdit(edittext);
diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java
index 181ffc3b4b..4cb4db33de 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotView.java
@@ -86,7 +86,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
private Godot activity;
private InputManagerCompat mInputManager;
- public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl,Godot p_activity) {
+ public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl, Godot p_activity) {
super(context);
ctx = context;
io = p_io;
@@ -204,48 +204,65 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
return i;
}
}
- onInputDeviceAdded(device_id);
- return joy_devices.size() - 1;
+
+ return -1;
}
@Override
public void onInputDeviceAdded(int deviceId) {
- joystick joy = new joystick();
- joy.device_id = deviceId;
- final int id = joy_devices.size();
- InputDevice device = mInputManager.getInputDevice(deviceId);
- final String name = device.getName();
- joy.name = device.getName();
- joy.axes = new ArrayList<InputDevice.MotionRange>();
- joy.hats = new ArrayList<InputDevice.MotionRange>();
- List<InputDevice.MotionRange> ranges = device.getMotionRanges();
- Collections.sort(ranges, new RangeComparator());
- for (InputDevice.MotionRange range : ranges) {
- if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
- joy.hats.add(range);
- } else {
- joy.axes.add(range);
+ int id = find_joy_device(deviceId);
+
+ // Check if the device has not been already added
+ if (id < 0) {
+ InputDevice device = mInputManager.getInputDevice(deviceId);
+
+ id = joy_devices.size();
+
+ joystick joy = new joystick();
+ joy.device_id = deviceId;
+ joy.name = device.getName();
+ joy.axes = new ArrayList<InputDevice.MotionRange>();
+ joy.hats = new ArrayList<InputDevice.MotionRange>();
+
+ List<InputDevice.MotionRange> ranges = device.getMotionRanges();
+ Collections.sort(ranges, new RangeComparator());
+
+ for (InputDevice.MotionRange range : ranges) {
+ if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+ joy.hats.add(range);
+ } else {
+ joy.axes.add(range);
+ }
}
+
+ joy_devices.add(joy);
+
+ final int device_id = id;
+ final String name = joy.name;
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyconnectionchanged(device_id, true, name);
+ }
+ });
}
- joy_devices.add(joy);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(id, true, name);
- }
- });
}
@Override
public void onInputDeviceRemoved(int deviceId) {
- final int id = find_joy_device(deviceId);
- joy_devices.remove(id);
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(id, false, "");
- }
- });
+ final int device_id = find_joy_device(deviceId);
+
+ // Check if the evice has not been already removed
+ if (device_id > -1) {
+ joy_devices.remove(device_id);
+
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyconnectionchanged(device_id, false, "");
+ }
+ });
+ }
}
@Override
@@ -266,15 +283,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
final int button = get_godot_button(keyCode);
- final int device = find_joy_device(event.getDeviceId());
+ final int device_id = find_joy_device(event.getDeviceId());
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joybutton(device, button, false);
- }
- });
- return true;
+ // Check if the device exists
+ if (device_id > -1) {
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joybutton(device_id, button, false);
+ }
+ });
+ return true;
+ }
} else {
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@@ -284,6 +304,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
}
});
};
+
return super.onKeyUp(keyCode, event);
};
@@ -308,18 +329,20 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
- final int button = get_godot_button(keyCode);
- final int device = find_joy_device(event.getDeviceId());
- //Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device));
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joybutton(device, button, true);
- }
- });
- return true;
+ final int button = get_godot_button(keyCode);
+ final int device_id = find_joy_device(event.getDeviceId());
+ // Check if the device exists
+ if (device_id > -1) {
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joybutton(device_id, button, true);
+ }
+ });
+ return true;
+ }
} else {
final int chr = event.getUnicodeChar(0);
queueEvent(new Runnable() {
@@ -329,6 +352,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
}
});
};
+
return super.onKeyDown(keyCode, event);
}
@@ -338,33 +362,35 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
final int device_id = find_joy_device(event.getDeviceId());
- joystick joy = joy_devices.get(device_id);
- for (int i = 0; i < joy.axes.size(); i++) {
- InputDevice.MotionRange range = joy.axes.get(i);
- final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
- //Log.e(TAG, String.format("axis event: %d, value %f", i, value));
- final int idx = i;
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyaxis(device_id, idx, value);
- }
- });
- }
+ // Check if the device exists
+ if (device_id > -1) {
+ joystick joy = joy_devices.get(device_id);
+
+ for (int i = 0; i < joy.axes.size(); i++) {
+ InputDevice.MotionRange range = joy.axes.get(i);
+ final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
+ final int idx = i;
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyaxis(device_id, idx, value);
+ }
+ });
+ }
- for (int i = 0; i < joy.hats.size(); i += 2) {
- final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
- final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis()));
- //Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY));
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyhat(device_id, hatX, hatY);
- }
- });
+ for (int i = 0; i < joy.hats.size(); i += 2) {
+ final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
+ final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis()));
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyhat(device_id, hatX, hatY);
+ }
+ });
+ }
+ return true;
}
- return true;
};
return super.onGenericMotionEvent(event);
@@ -408,9 +434,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
setRenderer(new Renderer());
}
- private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC;
- private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR= 0x00000001;
-
+ private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC;
+ private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001;
+
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
@@ -422,9 +448,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
checkEglError("Before eglCreateContext", egl);
EGLContext context;
if (use_debug_opengl) {
- int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2,_EGL_CONTEXT_FLAGS_KHR,_EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
- int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3,_EGL_CONTEXT_FLAGS_KHR,_EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
- context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2);
+ int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
+ int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
+ context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2);
} else {
int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE };
diff --git a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java
index 7216d8b5a4..03a7a71bb1 100644
--- a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java
+++ b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java
@@ -44,7 +44,7 @@ import javax.net.ssl.TrustManagerFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
/**
- *
+ *
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class CustomSSLSocketFactory extends SSLSocketFactory {
diff --git a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java
index b84f5cce2e..cfe9c4fef0 100644
--- a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java
+++ b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java
@@ -69,7 +69,7 @@ import android.content.SharedPreferences;
import android.util.Log;
/**
- *
+ *
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class HttpRequester {
diff --git a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java
index 2368766afa..a1d5b26b3c 100644
--- a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java
+++ b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java
@@ -39,7 +39,7 @@ import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
/**
- *
+ *
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class RequestParams {
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index ad8f21785d..43bd841baf 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -268,11 +268,11 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
return ret;
};
- if (name == "java.lang.Integer") {
+ if (name == "java.lang.Integer" || name == "java.lang.Long") {
jclass nclass = env->FindClass("java/lang/Number");
- jmethodID intValue = env->GetMethodID(nclass, "intValue", "()I");
- int ret = env->CallIntMethod(obj, intValue);
+ jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
+ jlong ret = env->CallLongMethod(obj, longValue);
return ret;
};
@@ -883,6 +883,8 @@ static void _initialize_java_modules() {
ERR_EXPLAIN("Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: " + m);
ERR_CONTINUE(!initialize);
}
+ jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, _godot_instance);
+ env->NewGlobalRef(obj);
}
}
}
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 30bc413459..96ff226402 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -39,7 +39,7 @@
#include "file_access_android.h"
#include "main/main.h"
#include "servers/visual/visual_server_raster.h"
-//#include "servers/visual/visual_server_wrap_mt.h"
+#include "servers/visual/visual_server_wrap_mt.h"
#ifdef ANDROID_NATIVE_ACTIVITY
#include "dir_access_android.h"
@@ -183,13 +183,11 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int
video_driver_index = p_video_driver;
visual_server = memnew(VisualServerRaster);
- /* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
-
+ if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
visual_server = memnew(VisualServerWrapMT(visual_server, false));
- };*/
+ }
visual_server->init();
- // visual_server->cursor_set_visible(false, 0);
AudioDriverManager::initialize(p_audio_driver);
@@ -586,7 +584,7 @@ Error OS_Android::shell_open(String p_uri) {
String OS_Android::get_resource_dir() const {
- return "/"; //android has it's own filesystem for resources inside the APK
+ return "/"; //android has its own filesystem for resources inside the APK
}
String OS_Android::get_locale() const {
diff --git a/platform/android/power_android.cpp b/platform/android/power_android.cpp
index 51283183df..0a6bf9dfcb 100644
--- a/platform/android/power_android.cpp
+++ b/platform/android/power_android.cpp
@@ -98,7 +98,7 @@ ANativeWindow *Android_JNI_GetNativeWindow(void) {
return anw;
}
-/*
+/*
* CODE CHUNK IMPORTED FROM SDL 2.0
* returns 0 on success or -1 on error (others undefined then)
* returns truthy or falsy value in plugged, charged and battery
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index c80365f1f3..f9f12af817 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -117,16 +117,13 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p
#endif
- visual_server = memnew(VisualServerRaster());
-
- ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
-
- // TODO: enable multithreaded VS
- /*
+ visual_server = memnew(VisualServerRaster);
+ // FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
+ visual_server = memnew(VisualServerWrapMT(visual_server, false));
}
- */
+
+ ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
video_driver_index = p_video_driver;
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index b13a1e9643..c9f37931b0 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -1,7 +1,7 @@
import os
import string
import sys
-
+from methods import detect_darwin_sdk_path
def is_active():
return True
@@ -22,9 +22,8 @@ def can_build():
def get_opts():
from SCons.Variables import BoolVariable
return [
- ('IPHONEPLATFORM', 'Name of the iPhone platform', 'iPhoneOS'),
('IPHONEPATH', 'Path to iPhone toolchain', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'),
- ('IPHONESDK', 'Path to the iPhone SDK', '/Applications/Xcode.app/Contents/Developer/Platforms/${IPHONEPLATFORM}.platform/Developer/SDKs/${IPHONEPLATFORM}.sdk/'),
+ ('IPHONESDK', 'Path to the iPhone SDK', ''),
BoolVariable('game_center', 'Support for game center', True),
BoolVariable('store_kit', 'Support for in-app store', True),
BoolVariable('icloud', 'Support for iCloud', True),
@@ -87,7 +86,7 @@ def configure(env):
s_compiler_path = '$IPHONEPATH/Developer/usr/bin/'
ccache_path = os.environ.get("CCACHE")
- if ccache_path == None:
+ if ccache_path is None:
env['CC'] = compiler_path + 'clang'
env['CXX'] = compiler_path + 'clang++'
env['S_compiler'] = s_compiler_path + 'gcc'
@@ -103,13 +102,15 @@ def configure(env):
## Compile flags
if (env["arch"] == "x86" or env["arch"] == "x86_64"):
- env['IPHONEPLATFORM'] = 'iPhoneSimulator'
+ detect_darwin_sdk_path('iphonesimulator', env)
env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
arch_flag = "i386" if env["arch"] == "x86" else env["arch"]
env.Append(CCFLAGS=('-arch ' + arch_flag + ' -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=9.0 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\"').split())
elif (env["arch"] == "arm"):
+ detect_darwin_sdk_path('iphone', env)
env.Append(CCFLAGS='-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=9.0 -MMD -MT dependencies'.split())
elif (env["arch"] == "arm64"):
+ detect_darwin_sdk_path('iphone', env)
env.Append(CCFLAGS='-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=9.0 -isysroot $IPHONESDK'.split())
env.Append(CPPFLAGS=['-DNEED_LONG_INT'])
env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON'])
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 63bc4a519b..1fc497456c 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -108,7 +108,11 @@ public:
virtual String get_os_name() const { return "iOS"; }
virtual Ref<Texture> get_logo() const { return logo; }
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "ipa"; }
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ list.push_back("ipa");
+ return list;
+ }
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index addef61ec7..e996a5905b 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -34,7 +34,7 @@
#include "drivers/gles3/rasterizer_gles3.h"
#include "servers/visual/visual_server_raster.h"
-//#include "servers/visual/visual_server_wrap_mt.h"
+#include "servers/visual/visual_server_wrap_mt.h"
#include "main/main.h"
@@ -107,13 +107,11 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
- visual_server = memnew(VisualServerRaster());
- /*
- FIXME: Reimplement threaded rendering? Or remove?
+ visual_server = memnew(VisualServerRaster);
+ // FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
visual_server = memnew(VisualServerWrapMT(visual_server, false));
- };
- */
+ }
visual_server->init();
//visual_server->cursor_set_visible(false, 0);
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 17b31f8d73..cf85c3df7f 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -128,6 +128,10 @@ def configure(env):
# us since we don't know requirements at compile-time.
env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
+ # Since we use both memory growth and MEMFS preloading,
+ # this avoids unecessary copying on start-up.
+ env.Append(LINKFLAGS=['--no-heap-copy'])
+
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js
index c3ef5bbbb5..91458eb4c3 100644
--- a/platform/javascript/engine.js
+++ b/platform/javascript/engine.js
@@ -1,3 +1,6 @@
+ // The following is concatenated with generated code, and acts as the end
+ // of a wrapper for said code. See pre.js for the other part of the
+ // wrapper.
exposedLibs['PATH'] = PATH;
exposedLibs['FS'] = FS;
return Module;
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 1e47d8db95..7a325e81dd 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -58,7 +58,7 @@ public:
virtual Ref<Texture> get_logo() const;
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual bool poll_devices();
@@ -174,9 +174,11 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p
return valid;
}
-String EditorExportPlatformJavaScript::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const {
+List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
- return "html";
+ List<String> list;
+ list.push_back("html");
+ return list;
}
Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 7c7aeac980..9250ca4903 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -862,6 +862,24 @@ void OS_JavaScript::finalize() {
// Miscellaneous
+Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) {
+
+ ERR_EXPLAIN("OS::execute() is not available on the HTML5 platform");
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+}
+
+Error OS_JavaScript::kill(const ProcessID &p_pid) {
+
+ ERR_EXPLAIN("OS::kill() is not available on the HTML5 platform");
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+}
+
+int OS_JavaScript::get_process_id() const {
+
+ ERR_EXPLAIN("OS::get_process_id() is not available on the HTML5 platform");
+ ERR_FAIL_V(0);
+}
+
extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) {
if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 84075898ac..79dac5940f 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -133,6 +133,10 @@ public:
void run_async();
bool main_loop_iterate();
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false);
+ virtual Error kill(const ProcessID &p_pid);
+ virtual int get_process_id() const;
+
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
virtual void set_window_title(const String &p_title);
virtual void set_icon(const Ref<Image> &p_icon);
diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js
index 02194bc75e..a870e676ea 100644
--- a/platform/javascript/pre.js
+++ b/platform/javascript/pre.js
@@ -1,2 +1,5 @@
var Engine = {
RuntimeEnvironment: function(Module, exposedLibs) {
+ // The above is concatenated with generated code, and acts as the start of
+ // a wrapper for said code. See engine.js for the other part of the
+ // wrapper.
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 8a0883eca3..051836b66d 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -1,5 +1,6 @@
import os
import sys
+from methods import detect_darwin_sdk_path
def is_active():
@@ -23,6 +24,7 @@ def get_opts():
return [
('osxcross_sdk', 'OSXCross SDK version', 'darwin14'),
+ ('MACOS_SDK_PATH', 'Path to the macOS SDK', ''),
EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')),
BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False),
]
@@ -84,12 +86,16 @@ def configure(env):
env['AS'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as"
env.Append(CCFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
+ detect_darwin_sdk_path('osx', env)
+ env.Append(CPPFLAGS=['-isysroot', '$MACOS_SDK_PATH'])
+ env.Append(LINKFLAGS=['-isysroot', '$MACOS_SDK_PATH'])
+
else: # osxcross build
root = os.environ.get("OSXCROSS_ROOT", 0)
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
ccache_path = os.environ.get("CCACHE")
- if ccache_path == None:
+ if ccache_path is None:
env['CC'] = basecmd + "cc"
env['CXX'] = basecmd + "c++"
else:
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 27b4fdc228..f27c042637 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -74,7 +74,14 @@ public:
virtual String get_os_name() const { return "OSX"; }
virtual Ref<Texture> get_logo() const { return logo; }
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return use_dmg() ? "dmg" : "zip"; }
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ if (use_dmg()) {
+ list.push_back("dmg");
+ }
+ list.push_back("zip");
+ return list;
+ }
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
@@ -334,7 +341,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
io2.opaque = &dst_f;
zipFile dst_pkg_zip = NULL;
- if (use_dmg()) {
+ String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
+ if (export_format == "dmg") {
// We're on OSX so we can export to DMG, but first we create our application bundle
tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app");
print_line("Exporting to " + tmp_app_path_name);
@@ -429,7 +437,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
print_line("ADDING: " + file + " size: " + itos(data.size()));
total_size += data.size();
- if (use_dmg()) {
+ if (export_format == "dmg") {
// write it into our application bundle
file = tmp_app_path_name + "/" + file;
@@ -491,7 +499,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (err == OK) {
ep.step("Making PKG", 1);
- if (use_dmg()) {
+ if (export_format == "dmg") {
String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
Vector<SharedObject> shared_objects;
err = save_pack(p_preset, pack_path, &shared_objects);
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 6fd52f09d1..546c88e74a 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -74,8 +74,12 @@ public:
IP_Unix *ip_unix;
+#ifdef COREAUDIO_ENABLED
AudioDriverCoreAudio audio_driver;
+#endif
+#ifdef COREMIDI_ENABLED
MIDIDriverCoreMidi midi_driver;
+#endif
InputDefault *input;
JoypadOSX *joypad_osx;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 6ab433203f..e7b3e35381 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -76,6 +76,13 @@
#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
#endif
+#ifndef NSAppKitVersionNumber10_12
+#define NSAppKitVersionNumber10_12 1504
+#endif
+#ifndef NSAppKitVersionNumber10_14
+#define NSAppKitVersionNumber10_14 1671
+#endif
+
static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) {
state->set_shift((p_osx_state & NSEventModifierFlagShift));
@@ -100,12 +107,13 @@ static int prev_mouse_y = 0;
static int button_mask = 0;
static bool mouse_down_control = false;
-static Vector2 get_mouse_pos(NSEvent *event) {
+static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFactor) {
const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSPoint p = [event locationInWindow];
- mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
- mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]);
+ const NSPoint p = locationInWindow;
+ const float s = OS_OSX::singleton->_mouse_scale(backingScaleFactor);
+ mouse_x = p.x * s;
+ mouse_y = (contentRect.size.height - p.y) * s;
return Vector2(mouse_x, mouse_y);
}
@@ -325,8 +333,15 @@ static Vector2 get_mouse_pos(NSEvent *event) {
- (void)windowDidBecomeKey:(NSNotification *)notification {
//_GodotInputWindowFocus(window, GL_TRUE);
//_GodotPlatformSetCursorMode(window, window->cursorMode);
- if (OS_OSX::singleton->get_main_loop())
+
+ if (OS_OSX::singleton->get_main_loop()) {
+ get_mouse_pos(
+ [OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream],
+ [OS_OSX::singleton->window_view backingScaleFactor]);
+ OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
+
OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
+ }
}
- (void)windowDidResignKey:(NSNotification *)notification {
@@ -356,6 +371,8 @@ static Vector2 get_mouse_pos(NSEvent *event) {
bool imeMode;
}
- (void)cancelComposition;
+- (BOOL)wantsUpdateLayer;
+- (void)updateLayer;
@end
@implementation GodotContentView
@@ -366,6 +383,14 @@ static Vector2 get_mouse_pos(NSEvent *event) {
}
}
+- (BOOL)wantsUpdateLayer {
+ return YES;
+}
+
+- (void)updateLayer {
+ [OS_OSX::singleton->context update];
+}
+
- (id)init {
self = [super init];
trackingArea = nil;
@@ -593,12 +618,13 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
mm->set_button_mask(button_mask);
prev_mouse_x = mouse_x;
prev_mouse_y = mouse_y;
- const Vector2 pos = get_mouse_pos(event);
+ const CGFloat backingScaleFactor = [[event window] backingScaleFactor];
+ const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
mm->set_position(pos);
mm->set_global_position(pos);
Vector2 relativeMotion = Vector2();
- relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale([[event window] backingScaleFactor]);
- relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale([[event window] backingScaleFactor]);
+ relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
+ relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
mm->set_relative(relativeMotion);
get_key_modifier_state([event modifierFlags], mm);
@@ -681,7 +707,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
Ref<InputEventMagnifyGesture> ev;
ev.instance();
get_key_modifier_state([event modifierFlags], ev);
- ev->set_position(get_mouse_pos(event));
+ ev->set_position(get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]));
ev->set_factor([event magnification] + 1.0);
OS_OSX::singleton->push_input(ev);
}
@@ -924,7 +950,7 @@ static int remapKey(unsigned int key) {
CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
if (!layoutData)
- return 0;
+ return translateKey(key);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
@@ -1073,6 +1099,8 @@ inline void sendPanEvent(double dx, double dy, int modifierFlags) {
- (void)scrollWheel:(NSEvent *)event {
double deltaX, deltaY;
+ get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]);
+
deltaX = [event scrollingDeltaX];
deltaY = [event scrollingDeltaY];
@@ -1216,6 +1244,9 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
ERR_FAIL_COND_V(window_object == nil, ERR_UNAVAILABLE);
window_view = [[GodotContentView alloc] init];
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
+ [window_view setWantsLayer:TRUE];
+ }
float displayScale = 1.0;
if (is_hidpi_allowed()) {
@@ -1373,7 +1404,6 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
visual_server = memnew(VisualServerRaster);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
-
visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
@@ -1397,7 +1427,9 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
void OS_OSX::finalize() {
+#ifdef COREMIDI_ENABLED
midi_driver.close();
+#endif
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
@@ -1447,7 +1479,7 @@ public:
switch (p_type) {
case ERR_WARNING:
- if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) {
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_info(OS_LOG_DEFAULT,
"WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.",
p_function, err_details, p_file, p_line);
@@ -1457,7 +1489,7 @@ public:
logf_error("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line);
break;
case ERR_SCRIPT:
- if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) {
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
"SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
p_function, err_details, p_file, p_line);
@@ -1467,7 +1499,7 @@ public:
logf_error("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line);
break;
case ERR_SHADER:
- if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) {
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
"SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
p_function, err_details, p_file, p_line);
@@ -1478,7 +1510,7 @@ public:
break;
case ERR_ERROR:
default:
- if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) {
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
"ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
p_function, err_details, p_file, p_line);
@@ -2149,11 +2181,7 @@ void OS_OSX::set_window_size(const Size2 p_size) {
if (menuBarHeight != 0.f) {
size.y += menuBarHeight;
} else {
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_12) {
-#else
- {
-#endif
size.y += [[NSStatusBar systemStatusBar] thickness];
}
}
@@ -2714,7 +2742,9 @@ OS_OSX::OS_OSX() {
[NSApp sendEvent:event];
}
+#ifdef COREAUDIO_ENABLED
AudioDriverManager::add_driver(&audio_driver);
+#endif
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
diff --git a/platform/server/SCsub b/platform/server/SCsub
index c9082f9b3a..51fd05a87e 100644
--- a/platform/server/SCsub
+++ b/platform/server/SCsub
@@ -1,10 +1,21 @@
#!/usr/bin/env python
+import os
+import platform
+import sys
+
Import('env')
common_server = [\
"os_server.cpp",\
- "#platform/x11/crash_handler_x11.cpp",
- "#platform/x11/power_x11.cpp",
]
+
+if sys.platform == "darwin":
+ common_server.append("#platform/osx/crash_handler_osx.mm")
+ common_server.append("#platform/osx/power_osx.cpp")
+ common_server.append("#platform/osx/sem_osx.cpp")
+else:
+ common_server.append("#platform/x11/crash_handler_x11.cpp")
+ common_server.append("#platform/x11/power_x11.cpp")
+
prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server)
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 597a2ff6a0..0b23e9c649 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -11,9 +11,15 @@ def get_name():
return "Server"
+def get_program_suffix():
+ if (sys.platform == "darwin"):
+ return "osx"
+ return "x11"
+
+
def can_build():
- if (os.name != "posix" or sys.platform == "darwin"):
+ if (os.name != "posix"):
return False
return True
@@ -147,6 +153,10 @@ def configure(env):
env.Append(CPPPATH=['#platform/server'])
env.Append(CPPFLAGS=['-DSERVER_ENABLED', '-DUNIX_ENABLED'])
+
+ if (platform.system() == "Darwin"):
+ env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-lz', '-framework', 'IOKit'])
+
env.Append(LIBS=['pthread'])
if (platform.system() == "Linux"):
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 6a7038e946..60f20d6009 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -68,6 +68,10 @@ void OS_Server::initialize_core() {
crash_handler.initialize();
OS_Unix::initialize_core();
+
+#ifdef __APPLE__
+ SemaphoreOSX::make_default();
+#endif
}
Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
@@ -87,7 +91,11 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
input = memnew(InputDefault);
+#ifdef __APPLE__
+ power_manager = memnew(power_osx);
+#else
power_manager = memnew(PowerX11);
+#endif
_ensure_user_data_dir();
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 07d70e5236..0367ec3db9 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -34,8 +34,14 @@
#include "drivers/rtaudio/audio_driver_rtaudio.h"
#include "drivers/unix/os_unix.h"
#include "main/input_default.h"
+#ifdef __APPLE__
+#include "platform/osx/crash_handler_osx.h"
+#include "platform/osx/power_osx.h"
+#include "platform/osx/sem_osx.h"
+#else
#include "platform/x11/crash_handler_x11.h"
#include "platform/x11/power_x11.h"
+#endif
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
@@ -61,7 +67,11 @@ class OS_Server : public OS_Unix {
InputDefault *input;
+#ifdef __APPLE__
+ power_osx *power_manager;
+#else
PowerX11 *power_manager;
+#endif
CrashHandler crash_handler;
diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h
index 2fa8eda337..26ba8f26c6 100644
--- a/platform/server/platform_config.h
+++ b/platform/server/platform_config.h
@@ -28,10 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef __linux__
+#if defined(__linux__) || defined(__APPLE__)
#include <alloca.h>
#endif
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <stdlib.h>
#define PTHREAD_BSD_SET_NAME
#endif
+#ifdef __APPLE__
+#define PTHREAD_RENAME_SELF
+#endif
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 559f23ca5b..f25b9ba9cd 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -17,7 +17,7 @@ def can_build():
# building natively on windows!
if (os.getenv("VSINSTALLDIR")):
- if (os.getenv("ANGLE_SRC_PATH") == None):
+ if (os.getenv("ANGLE_SRC_PATH") is None):
return False
return True
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 6a7284f770..41e59a5352 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -1021,8 +1021,10 @@ public:
return "UWP";
}
- virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const {
- return "appx";
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> list;
+ list.push_back("appx");
+ return list;
}
virtual Ref<Texture> get_logo() const {
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 6410378593..1f81d476ea 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -47,6 +47,7 @@
#include "platform/windows/windows_terminal_logger.h"
#include "servers/audio_server.h"
#include "servers/visual/visual_server_raster.h"
+#include "servers/visual/visual_server_wrap_mt.h"
#include "thread_uwp.h"
#include <ppltasks.h>
@@ -295,13 +296,10 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
set_video_mode(vm);
visual_server = memnew(VisualServerRaster);
- // FIXME: Reimplement threaded rendering? Or remove?
- /*
+ // FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
-
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
+ visual_server = memnew(VisualServerWrapMT(visual_server, false));
}
- */
visual_server->init();
input = memnew(InputDefault);
diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp
index 794f6df31f..2d70b00dda 100644
--- a/platform/windows/context_gl_win.cpp
+++ b/platform/windows/context_gl_win.cpp
@@ -91,18 +91,18 @@ Error ContextGL_Win::initialize() {
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER,
- PFD_TYPE_RGBA,
- OS::get_singleton()->is_layered_allowed() ? 32 : 24,
- 0, 0, 0, 0, 0, 0, // Color Bits Ignored
- OS::get_singleton()->is_layered_allowed() ? 8 : 0, // Alpha Buffer
- 0, // Shift Bit Ignored
- 0, // No Accumulation Buffer
- 0, 0, 0, 0, // Accumulation Bits Ignored
- 24, // 24Bit Z-Buffer (Depth Buffer)
- 0, // No Stencil Buffer
- 0, // No Auxiliary Buffer
- PFD_MAIN_PLANE, // Main Drawing Layer
- 0, // Reserved
+ (BYTE)PFD_TYPE_RGBA,
+ (BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24),
+ (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored
+ (BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer
+ (BYTE)0, // Shift Bit Ignored
+ (BYTE)0, // No Accumulation Buffer
+ (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored
+ (BYTE)24, // 24Bit Z-Buffer (Depth Buffer)
+ (BYTE)0, // No Stencil Buffer
+ (BYTE)0, // No Auxiliary Buffer
+ (BYTE)PFD_MAIN_PLANE, // Main Drawing Layer
+ (BYTE)0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 5d5af17086..dd0042668f 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -262,7 +262,7 @@ def configure_mingw(env):
env.Append(CCFLAGS=['-O2'])
else: #optimize for size
env.Prepend(CCFLAGS=['-Os'])
-
+
env.Append(LINKFLAGS=['-Wl,--subsystem,windows'])
@@ -281,7 +281,7 @@ def configure_mingw(env):
env.Append(CCFLAGS=['-O2'])
else: #optimize for size
env.Prepend(CCFLAGS=['-Os'])
-
+
elif (env["target"] == "debug"):
env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED'])
diff --git a/platform/windows/joypad.cpp b/platform/windows/joypad.cpp
index b56fb6509e..7201714fb8 100644
--- a/platform/windows/joypad.cpp
+++ b/platform/windows/joypad.cpp
@@ -163,7 +163,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
const GUID &guid = instance->guidProduct;
char uid[128];
- sprintf(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+ sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
__builtin_bswap32(guid.Data1), guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
@@ -172,7 +172,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
joy->di_joy->SetDataFormat(&c_dfDIJoystick2);
joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND);
- joy->di_joy->EnumObjects(objectsCallback, this, NULL);
+ joy->di_joy->EnumObjects(objectsCallback, this, 0);
joy->joy_axis.sort();
joy->guid = instance->guidInstance;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 739fcbacda..bdf459fd83 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -58,11 +58,8 @@
static const WORD MAX_CONSOLE_LINES = 1500;
extern "C" {
-#ifdef _MSC_VER
-_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
-#else
-__attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001;
-#endif
+__declspec(dllexport) DWORD NvOptimusEnablement = 1;
+__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
// Workaround mingw-w64 < 4.0 bug
@@ -303,19 +300,17 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
case WM_SETFOCUS: {
window_has_focus = true;
- // Re-capture cursor if we're in one of the capture modes
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
- SetCapture(hWnd);
- }
+
+ // Restore mouse mode
+ _set_mouse_mode_impl(mouse_mode);
+
break;
}
case WM_KILLFOCUS: {
window_has_focus = false;
- // Release capture if we're in one of the capture modes
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
- ReleaseCapture();
- }
+ // Release capture unconditionally because it can be set due to dragging, in addition to captured mode
+ ReleaseCapture();
// Release every touch to avoid sticky points
for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
@@ -337,15 +332,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
alt_mem = false;
control_mem = false;
shift_mem = false;
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
- RECT clipRect;
- GetClientRect(hWnd, &clipRect);
- ClientToScreen(hWnd, (POINT *)&clipRect.left);
- ClientToScreen(hWnd, (POINT *)&clipRect.right);
- ClipCursor(&clipRect);
- SetCapture(hWnd);
- }
- } else {
+ } else { // WM_INACTIVE
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
alt_mem = false;
};
@@ -705,12 +692,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
if (mb->is_pressed()) {
- if (++pressrc > 0)
+ if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
SetCapture(hWnd);
} else {
if (--pressrc <= 0) {
- ReleaseCapture();
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ ReleaseCapture();
+ }
pressrc = 0;
}
}
@@ -763,7 +752,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
RECT r;
GetWindowRect(hWnd, &r);
- dib_size = Size2(r.right - r.left, r.bottom - r.top);
+ dib_size = Size2i(r.right - r.left, r.bottom - r.top);
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
@@ -773,7 +762,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4;
+ bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
SelectObject(hDC_dib, hBitmap);
@@ -1050,7 +1039,6 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau
UINT x = 0, y = 0;
HRESULT hr = E_FAIL;
- bool bSet = false;
if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
@@ -1204,7 +1192,14 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
- char *windowid = getenv("GODOT_WINDOWID");
+ char *windowid;
+#ifdef MINGW_ENABLED
+ windowid = getenv("GODOT_WINDOWID");
+#else
+ size_t len;
+ _dupenv_s(&windowid, &len, "GODOT_WINDOWID");
+#endif
+
if (windowid) {
// strtoull on mingw
@@ -1213,6 +1208,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
#else
hWnd = (HWND)_strtoui64(windowid, NULL, 0);
#endif
+ free(windowid);
SetLastError(0);
user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc);
@@ -1221,7 +1217,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
printf("Error setting WNDPROC: %li\n", le);
};
- LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ GetWindowLongPtr(hWnd, GWLP_WNDPROC);
RECT rect;
if (!GetClientRect(hWnd, &rect)) {
@@ -1327,24 +1323,9 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
visual_server = memnew(VisualServerRaster);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
-
visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
- /*
- DEVMODE dmScreenSettings; // Device Mode
- memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
- dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
- dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
- dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
- dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
- dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
- if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
-
-
-
-
- */
visual_server->init();
input = memnew(InputDefault);
@@ -1527,18 +1508,27 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
if (mouse_mode == p_mode)
return;
+
+ _set_mouse_mode_impl(p_mode);
+
mouse_mode = p_mode;
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
+}
+
+void OS_Windows::_set_mouse_mode_impl(MouseMode p_mode) {
+
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
RECT clipRect;
GetClientRect(hWnd, &clipRect);
ClientToScreen(hWnd, (POINT *)&clipRect.left);
ClientToScreen(hWnd, (POINT *)&clipRect.right);
ClipCursor(&clipRect);
- center = Point2i(video_mode.width / 2, video_mode.height / 2);
- POINT pos = { (int)center.x, (int)center.y };
- ClientToScreen(hWnd, &pos);
- if (mouse_mode == MOUSE_MODE_CAPTURED)
+ if (p_mode == MOUSE_MODE_CAPTURED) {
+ center = Point2i(video_mode.width / 2, video_mode.height / 2);
+ POINT pos = { (int)center.x, (int)center.y };
+ ClientToScreen(hWnd, &pos);
SetCursorPos(pos.x, pos.y);
+ SetCapture(hWnd);
+ }
} else {
ReleaseCapture();
ClipCursor(NULL);
@@ -1552,7 +1542,6 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
set_cursor_shape(c);
}
}
-
OS_Windows::MouseMode OS_Windows::get_mouse_mode() const {
return mouse_mode;
@@ -1720,14 +1709,18 @@ void OS_Windows::set_window_position(const Point2 &p_position) {
Size2 OS_Windows::get_window_size() const {
RECT r;
- GetClientRect(hWnd, &r);
- return Vector2(r.right - r.left, r.bottom - r.top);
+ if (GetClientRect(hWnd, &r)) { // Only area inside of window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
}
Size2 OS_Windows::get_real_window_size() const {
RECT r;
- GetWindowRect(hWnd, &r);
- return Vector2(r.right - r.left, r.bottom - r.top);
+ if (GetWindowRect(hWnd, &r)) { // Includes area of the window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
}
void OS_Windows::set_window_size(const Size2 p_size) {
@@ -2266,7 +2259,6 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
ERR_FAIL_COND(!image.is_valid());
UINT image_size = texture_size.width * texture_size.height;
- UINT size = sizeof(UINT) * image_size;
// Create the BITMAP with alpha channel
COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
@@ -2546,7 +2538,16 @@ void OS_Windows::set_icon(const Ref<Image> &p_icon) {
bool OS_Windows::has_environment(const String &p_var) const {
+#ifdef MINGW_ENABLED
return _wgetenv(p_var.c_str()) != NULL;
+#else
+ wchar_t *env;
+ size_t len;
+ _wdupenv_s(&env, &len, p_var.c_str());
+ const bool has_env = env != NULL;
+ free(env);
+ return has_env;
+#endif
};
String OS_Windows::get_environment(const String &p_var) const {
@@ -2729,11 +2730,6 @@ void OS_Windows::run() {
main_loop->init();
- uint64_t last_ticks = get_ticks_usec();
-
- int frames = 0;
- uint64_t frame = 0;
-
while (!force_quit) {
process_events(); // get rid of pending events
@@ -2926,7 +2922,7 @@ bool OS_Windows::is_disable_crash_handler() const {
Error OS_Windows::move_to_trash(const String &p_path) {
SHFILEOPSTRUCTW sf;
WCHAR *from = new WCHAR[p_path.length() + 2];
- wcscpy(from, p_path.c_str());
+ wcscpy_s(from, p_path.length() + 1, p_path.c_str());
from[p_path.length() + 1] = 0;
sf.hwnd = hWnd;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 01e1c51ca5..6aadd6994c 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -157,6 +157,8 @@ class OS_Windows : public OS {
void _update_window_style(bool repaint = true);
+ void _set_mouse_mode_impl(MouseMode p_mode);
+
// functions used by main to initialize/deinitialize the OS
protected:
virtual int get_current_video_driver() const;
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index ee59e9b5a1..524c8448bc 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -1,6 +1,7 @@
import os
import platform
import sys
+from compat import decode_utf8
def is_active():
@@ -149,6 +150,19 @@ def configure(env):
env.Append(CCFLAGS=['-pipe'])
env.Append(LINKFLAGS=['-pipe'])
+ # Check for gcc version > 5 before adding -no-pie
+ import re
+ import subprocess
+ proc = subprocess.Popen([env['CXX'], '--version'], stdout=subprocess.PIPE)
+ (stdout, _) = proc.communicate()
+ stdout = decode_utf8(stdout)
+ match = re.search('[0-9][0-9.]*', stdout)
+ if match is not None:
+ version = match.group().split('.')
+ if (version[0] > '5'):
+ env.Append(CCFLAGS=['-fpie'])
+ env.Append(LINKFLAGS=['-no-pie'])
+
## Dependencies
env.ParseConfig('pkg-config x11 --cflags --libs')
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 7c4c8f0eff..04854e93b6 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -267,6 +267,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
// maybe contextgl wants to be in charge of creating the window
#if defined(OPENGL_ENABLED)
+ // Set DRI_PRIME if not set. This means that Godot should default to a higher-power GPU if it exists.
+ // Note: Due to the final '0' parameter to setenv any existing DRI_PRIME environment variables will not
+ // be overwritten.
+ setenv("DRI_PRIME", "1", 0);
ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE;
@@ -342,12 +346,12 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
context_gl->set_use_vsync(current_videomode.use_vsync);
#endif
- visual_server = memnew(VisualServerRaster);
+ visual_server = memnew(VisualServerRaster);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
-
visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
+
if (current_videomode.maximized) {
current_videomode.maximized = false;
set_window_maximized(true);
@@ -365,7 +369,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
}
// disable resizable window
- if (!current_videomode.resizable) {
+ if (!current_videomode.resizable && !current_videomode.fullscreen) {
XSizeHints *xsh;
xsh = XAllocSizeHints();
xsh->flags = PMinSize | PMaxSize;
@@ -2095,7 +2099,7 @@ void OS_X11::process_xevents() {
last_timestamp = event.xkey.time;
// key event is a little complex, so
- // it will be handled in it's own function.
+ // it will be handled in its own function.
handle_key_event((XKeyEvent *)&event);
} break;
case SelectionRequest: {
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 8a5910c10e..b31bb39c0d 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -312,12 +312,12 @@ void SpriteFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames);
ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames);
- ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", 0), "_set_frames", "_get_frames"); //compatibility
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", 0), "_set_frames", "_get_frames"); //compatibility
ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations);
ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations);
- ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
}
SpriteFrames::SpriteFrames() {
@@ -693,15 +693,15 @@ void AnimatedSprite::_bind_methods() {
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("animation_finished"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+ 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");
}
AnimatedSprite::AnimatedSprite() {
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index c375374dce..ae5891fa50 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -399,7 +399,7 @@ void Area2D::set_monitoring(bool p_enable) {
if (p_enable == monitoring)
return;
if (locked) {
- ERR_EXPLAIN("Function blocked during in/out signal. Use call_deferred(\"set_monitoring\",true/false)");
+ ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitoring\",true/false)");
}
ERR_FAIL_COND(locked);
@@ -424,10 +424,10 @@ bool Area2D::is_monitoring() const {
void Area2D::set_monitorable(bool p_enable) {
- if (locked) {
- ERR_EXPLAIN("This function can't be used during the in/out signal.");
+ if (locked || Physics2DServer::get_singleton()->is_flushing_queries()) {
+ ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitorable\",true/false)");
}
- ERR_FAIL_COND(locked);
+ ERR_FAIL_COND(locked || Physics2DServer::get_singleton()->is_flushing_queries());
if (p_enable == monitorable)
return;
@@ -664,19 +664,19 @@ void Area2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D")));
ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D")));
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_angular_damp", "get_angular_damp");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
ADD_GROUP("Collision", "collision_");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Audio Bus", "audio_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus");
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index a1ae05d971..c2af725919 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -92,6 +92,9 @@ void AudioStreamPlayer2D::_mix_audio() {
int cc = AudioServer::get_singleton()->get_channel_count();
if (cc == 1) {
+ if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, 0))
+ continue; //may have been removed
+
AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 0);
for (int j = 0; j < buffer_size; j++) {
@@ -102,11 +105,20 @@ void AudioStreamPlayer2D::_mix_audio() {
} else {
AudioFrame *targets[4];
+ bool valid = true;
for (int k = 0; k < cc; k++) {
+ if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) {
+ valid = false; //may have been removed
+ break;
+ }
+
targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
}
+ if (!valid)
+ continue;
+
for (int j = 0; j < buffer_size; j++) {
AudioFrame frame = buffer[j] * vol;
@@ -179,7 +191,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {
Physics2DDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS];
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask);
+ int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
for (int i = 0; i < areas; i++) {
@@ -311,6 +323,7 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
if (stream_playback.is_valid()) {
+ active = true;
setplay = p_from_pos;
output_ready = false;
set_physics_process_internal(true);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 788a39d05d..cc297d742d 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -710,7 +710,7 @@ void Camera2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled);
ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled);
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "_set_current", "is_current");
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index fab0b7d433..0ea2e85dfa 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -44,12 +44,19 @@
Mutex *CanvasItemMaterial::material_mutex = NULL;
SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;
Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = NULL;
void CanvasItemMaterial::init_shaders() {
#ifndef NO_THREADS
material_mutex = Mutex::create();
#endif
+
+ shader_names = memnew(ShaderNames);
+
+ shader_names->particles_anim_h_frames = "particles_anim_h_frames";
+ shader_names->particles_anim_v_frames = "particles_anim_v_frames";
+ shader_names->particles_anim_loop = "particles_anim_loop";
}
void CanvasItemMaterial::finish_shaders() {
@@ -102,7 +109,37 @@ void CanvasItemMaterial::_update_shader() {
case LIGHT_MODE_UNSHADED: code += ",unshaded"; break;
case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break;
}
- code += ";\n"; //that's it.
+
+ code += ";\n";
+
+ if (particles_animation) {
+
+ code += "uniform int particles_anim_h_frames;\n";
+ code += "uniform int particles_anim_v_frames;\n";
+ code += "uniform bool particles_anim_loop;\n";
+
+ code += "void vertex() {\n";
+
+ code += "\tfloat h_frames = float(particles_anim_h_frames);\n";
+ code += "\tfloat v_frames = float(particles_anim_v_frames);\n";
+
+ code += "\tVERTEX.xy /= vec2(h_frames, v_frames);\n";
+
+ code += "\tint total_frames = particles_anim_h_frames * particles_anim_v_frames;\n";
+ code += "\tint frame = int(float(total_frames) * INSTANCE_CUSTOM.z);\n";
+ code += "\tif (particles_anim_loop) {\n";
+ code += "\t\tframe = abs(frame) % total_frames;\n";
+ code += "\t} else {\n";
+ code += "\t\tframe = clamp(frame, 0, total_frames - 1);\n";
+ code += "\t}\n";
+
+ code += "\tfloat frame_w = 1.0 / h_frames;\n";
+ code += "\tfloat frame_h = 1.0 / v_frames;\n";
+ code += "\tUV.x = UV.x * frame_w + frame_w * float(frame % particles_anim_h_frames);\n";
+ code += "\tUV.y = UV.y * frame_h + frame_h * float(frame / particles_anim_h_frames);\n";
+
+ code += "}\n";
+ }
ShaderData shader_data;
shader_data.shader = VS::get_singleton()->shader_create();
@@ -177,7 +214,52 @@ CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
return light_mode;
}
+void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) {
+ particles_animation = p_particles_anim;
+ _queue_shader_change();
+ _change_notify();
+}
+
+bool CanvasItemMaterial::get_particles_animation() const {
+ return particles_animation;
+}
+
+void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) {
+
+ particles_anim_h_frames = p_frames;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_h_frames() const {
+
+ return particles_anim_h_frames;
+}
+void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) {
+
+ particles_anim_v_frames = p_frames;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_v_frames() const {
+
+ return particles_anim_v_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) {
+
+ particles_anim_loop = p_loop;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
+}
+
+bool CanvasItemMaterial::get_particles_anim_loop() const {
+
+ return particles_anim_loop;
+}
+
void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("particles_anim_") && !particles_animation) {
+ property.usage = 0;
+ }
}
RID CanvasItemMaterial::get_shader_rid() const {
@@ -199,8 +281,25 @@ void CanvasItemMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
+ ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation);
+ ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop");
BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
@@ -218,6 +317,11 @@ CanvasItemMaterial::CanvasItemMaterial() :
blend_mode = BLEND_MODE_MIX;
light_mode = LIGHT_MODE_NORMAL;
+ particles_animation = false;
+
+ set_particles_anim_h_frames(1);
+ set_particles_anim_v_frames(1);
+ set_particles_anim_loop(false);
current_key.key = 0;
current_key.invalid_key = 1;
@@ -272,6 +376,9 @@ bool CanvasItem::is_visible_in_tree() const {
void CanvasItem::_propagate_visibility_changed(bool p_visible) {
+ if (p_visible && first_draw) { //avoid propagating it twice
+ first_draw = false;
+ }
notification(NOTIFICATION_VISIBILITY_CHANGED);
if (p_visible)
@@ -861,6 +968,15 @@ RID CanvasItem::get_canvas() const {
return get_viewport()->find_world_2d()->get_canvas();
}
+ObjectID CanvasItem::get_canvas_layer_instance_id() const {
+
+ if (canvas_layer) {
+ return canvas_layer->get_instance_id();
+ } else {
+ return 0;
+ }
+}
+
CanvasItem *CanvasItem::get_toplevel() const {
CanvasItem *ci = const_cast<CanvasItem *>(this);
@@ -1080,16 +1196,16 @@ void CanvasItem::_bind_methods() {
BIND_VMETHOD(MethodInfo("_draw"));
ADD_GROUP("Visibility", "");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
- ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
- ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Material", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
//exporting these things doesn't really make much sense i think
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_as_toplevel", "is_set_as_toplevel");
// ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled");
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 36a0e4039a..9fe7cb1e00 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -70,6 +70,7 @@ private:
struct {
uint32_t blend_mode : 4;
uint32_t light_mode : 4;
+ uint32_t particles_animation : 1;
uint32_t invalid_key : 1;
};
@@ -80,6 +81,14 @@ private:
}
};
+ struct ShaderNames {
+ StringName particles_anim_h_frames;
+ StringName particles_anim_v_frames;
+ StringName particles_anim_loop;
+ };
+
+ static ShaderNames *shader_names;
+
struct ShaderData {
RID shader;
int users;
@@ -95,6 +104,7 @@ private:
mk.key = 0;
mk.blend_mode = blend_mode;
mk.light_mode = light_mode;
+ mk.particles_animation = particles_animation;
return mk;
}
@@ -108,6 +118,11 @@ private:
BlendMode blend_mode;
LightMode light_mode;
+ bool particles_animation;
+
+ int particles_anim_h_frames;
+ int particles_anim_v_frames;
+ bool particles_anim_loop;
protected:
static void _bind_methods();
@@ -120,6 +135,17 @@ public:
void set_light_mode(LightMode p_light_mode);
LightMode get_light_mode() const;
+ void set_particles_animation(bool p_particles_anim);
+ bool get_particles_animation() const;
+
+ void set_particles_anim_h_frames(int p_frames);
+ int get_particles_anim_h_frames() const;
+ void set_particles_anim_v_frames(int p_frames);
+ int get_particles_anim_v_frames() const;
+
+ void set_particles_anim_loop(bool p_frames);
+ bool get_particles_anim_loop() const;
+
static void init_shaders();
static void finish_shaders();
static void flush_changes();
@@ -326,6 +352,7 @@ public:
Rect2 get_viewport_rect() const;
RID get_viewport_rid() const;
RID get_canvas() const;
+ ObjectID get_canvas_layer_instance_id() const;
Ref<World2D> get_world_2d() const;
virtual void set_material(const Ref<Material> &p_material);
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 7ade74e8a6..738f7ddf59 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -58,6 +58,14 @@ void CollisionObject2D::_notification(int p_what) {
//get space
}
+ case NOTIFICATION_ENTER_CANVAS: {
+
+ if (area)
+ Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, get_canvas_layer_instance_id());
+ else
+ Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, get_canvas_layer_instance_id());
+ }
+
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_pickable();
@@ -86,6 +94,14 @@ void CollisionObject2D::_notification(int p_what) {
Physics2DServer::get_singleton()->body_set_space(rid, RID());
} break;
+
+ case NOTIFICATION_EXIT_CANVAS: {
+
+ if (area)
+ Physics2DServer::get_singleton()->area_attach_canvas_instance_id(rid, 0);
+ else
+ Physics2DServer::get_singleton()->body_attach_canvas_instance_id(rid, 0);
+ }
}
}
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 410b27c691..07bbbd9321 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -325,8 +325,8 @@ void CollisionPolygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
BIND_ENUM_CONSTANT(BUILD_SOLIDS);
BIND_ENUM_CONSTANT(BUILD_SEGMENTS);
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index cb9c8ecf95..d3e25d541a 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -229,9 +229,9 @@ void CollisionShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled);
ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape2D::_shape_changed);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
}
CollisionShape2D::CollisionShape2D() {
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index e6dcd643be..4fc2282ae7 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -29,8 +29,9 @@
/*************************************************************************/
#include "cpu_particles_2d.h"
-
-//#include "scene/resources/particles_material.h"
+#include "particles_2d.h"
+#include "scene/2d/canvas_item.h"
+#include "scene/resources/particles_material.h"
#include "servers/visual_server.h"
void CPUParticles2D::set_emitting(bool p_emitting) {
@@ -237,6 +238,17 @@ String CPUParticles2D::get_configuration_warning() const {
String warnings;
+ CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
+
+ if (get_material().is_null() || (mat && !mat->get_particles_animation())) {
+ if (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 ||
+ get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid()) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.");
+ }
+ }
+
return warnings;
}
@@ -396,6 +408,7 @@ bool CPUParticles2D::get_particle_flag(Flags p_flag) const {
void CPUParticles2D::set_emission_shape(EmissionShape p_shape) {
emission_shape = p_shape;
+ _change_notify();
}
void CPUParticles2D::set_emission_sphere_radius(float p_radius) {
@@ -479,6 +492,15 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
+
+ if (property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
+ property.usage = 0;
+ }
+
+ if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
+ property.usage = 0;
+ }
+
/*
if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
property.usage = 0;
@@ -531,7 +553,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (!local_coords) {
emission_xform = get_global_transform();
velocity_xform = emission_xform;
- emission_xform[2] = Vector2();
+ velocity_xform[2] = Vector2();
}
for (int i = 0; i < pcount; i++) {
@@ -618,9 +640,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
- p.custom[0] = Math::deg2rad(base_angle); //angle
- p.custom[1] = 0.0; //phase
- p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1)
+ p.rotation = Math::deg2rad(base_angle);
+
+ p.custom[0] = 0.0; // unused
+ p.custom[1] = 0.0; // phase [0..1]
+ p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation phase [0..1]
+ p.custom[3] = 0.0;
p.transform = Transform2D();
p.time = 0;
p.base_color = Color(1, 1, 1, 1);
@@ -767,14 +792,9 @@ void CPUParticles2D::_particles_process(float p_delta) {
}
float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]);
base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
- p.custom[0] = Math::deg2rad(base_angle); //angle
- p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
- if (flags[FLAG_ANIM_LOOP]) {
- p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
-
- } else {
- p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
- }
+ p.rotation = Math::deg2rad(base_angle); //angle
+ float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]);
+ p.custom[2] = animation_phase;
}
//apply color
//apply hue rotation
@@ -825,8 +845,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
}
} else {
- p.transform.elements[0] = Vector2(Math::cos(p.custom[0]), -Math::sin(p.custom[0]));
- p.transform.elements[1] = Vector2(Math::sin(p.custom[0]), Math::cos(p.custom[0]));
+ p.transform.elements[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation));
+ p.transform.elements[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation));
}
//scale by scale
@@ -1058,8 +1078,7 @@ void CPUParticles2D::_notification(int p_what) {
}
void CPUParticles2D::convert_from_particles(Node *p_particles) {
-#if 0
- Particles *particles = Object::cast_to<Particles>(p_particles);
+ Particles2D *particles = Object::cast_to<Particles2D>(p_particles);
ERR_FAIL_COND(!particles);
set_emitting(particles->is_emitting());
@@ -1074,7 +1093,12 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
set_fractional_delta(particles->get_fractional_delta());
set_speed_scale(particles->get_speed_scale());
set_draw_order(DrawOrder(particles->get_draw_order()));
- set_mesh(particles->get_draw_pass_mesh(0));
+ set_texture(particles->get_texture());
+
+ Ref<Material> mat = particles->get_material();
+ if (mat.is_valid()) {
+ set_material(mat);
+ }
Ref<ParticlesMaterial> material = particles->get_process_material();
if (material.is_null())
@@ -1091,15 +1115,14 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
}
set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
- set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
- set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
- set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
set_emission_shape(EmissionShape(material->get_emission_shape()));
set_emission_sphere_radius(material->get_emission_sphere_radius());
- set_emission_rect_extents(material->get_emission_rect_extents());
+ Vector2 rect_extents = Vector2(material->get_emission_box_extents().x, material->get_emission_box_extents().y);
+ set_emission_rect_extents(rect_extents);
- set_gravity(material->get_gravity());
+ Vector2 gravity = Vector2(material->get_gravity().x, material->get_gravity().y);
+ set_gravity(gravity);
#define CONVERT_PARAM(m_param) \
set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \
@@ -1123,7 +1146,6 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
CONVERT_PARAM(PARAM_ANIM_OFFSET);
#undef CONVERT_PARAM
-#endif
}
void CPUParticles2D::_bind_methods() {
@@ -1253,7 +1275,7 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
/*
@@ -1291,7 +1313,7 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
@@ -1301,7 +1323,6 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", 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);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 4f51eb1062..d967c3be26 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -68,7 +68,6 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
- FLAG_ANIM_LOOP,
FLAG_MAX
};
@@ -87,6 +86,7 @@ private:
Transform2D transform;
Color color;
float custom[4];
+ float rotation;
Vector2 velocity;
bool active;
float angle_rand;
@@ -168,7 +168,6 @@ private:
PoolVector<Color> emission_colors;
int emission_point_count;
- bool anim_loop;
Vector2 gravity;
void _particles_process(float p_delta);
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index e164f0ca75..6faf8c2855 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -347,13 +347,13 @@ void Line2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "width"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color");
ADD_GROUP("Fill", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "set_texture_mode", "get_texture_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "set_texture_mode", "get_texture_mode");
ADD_GROUP("Capping", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_end_cap_mode", "get_end_cap_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_end_cap_mode", "get_end_cap_mode");
ADD_GROUP("Border", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "sharp_limit"), "set_sharp_limit", "get_sharp_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision"), "set_round_precision", "get_round_precision");
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index a3f1b25e05..d5e8a33805 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -294,7 +294,6 @@ void LineBuilder::build() {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
- uvx0 = current_distance0 / total_distance;
uvx1 = current_distance1 / total_distance;
}
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index 9f21fe1a1f..46777665d8 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -51,8 +51,8 @@ void MeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
}
void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) {
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 29065a89b3..2d9bbfe657 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -204,12 +204,6 @@ Size2 Node2D::get_scale() const {
return _scale;
}
-void Node2D::_notification(int p_what) {
-
- switch (p_what) {
- }
-}
-
Transform2D Node2D::get_transform() const {
return _mat;
@@ -441,10 +435,10 @@ void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
ADD_GROUP("Transform", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
- ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", 0), "set_transform", "get_transform");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
@@ -454,8 +448,8 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
ADD_GROUP("Z Index", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
}
Node2D::Node2D() {
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 725686cdf8..924a84fb88 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -52,8 +52,6 @@ class Node2D : public CanvasItem {
void _update_xform_values();
protected:
- void _notification(int p_what);
-
static void _bind_methods();
public:
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 7e824cdf75..35b7e7da3e 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -219,6 +219,20 @@ String Particles2D::get_configuration_warning() const {
if (warnings != String())
warnings += "\n";
warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted.");
+ } else {
+
+ CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
+
+ if (get_material().is_null() || (mat && !mat->get_particles_animation())) {
+ const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr());
+ if (process &&
+ (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
+ process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled.");
+ }
+ }
}
return warnings;
@@ -257,30 +271,6 @@ Ref<Texture> Particles2D::get_normal_map() const {
void Particles2D::_validate_property(PropertyInfo &property) const {
}
-void Particles2D::set_v_frames(int p_count) {
-
- ERR_FAIL_COND(p_count < 1);
- v_frames = p_count;
- update();
-}
-
-int Particles2D::get_v_frames() const {
-
- return v_frames;
-}
-
-void Particles2D::set_h_frames(int p_count) {
-
- ERR_FAIL_COND(p_count < 1);
- h_frames = p_count;
- update();
-}
-
-int Particles2D::get_h_frames() const {
-
- return h_frames;
-}
-
void Particles2D::restart() {
VS::get_singleton()->particles_restart(particles);
}
@@ -296,7 +286,7 @@ void Particles2D::_notification(int p_what) {
if (normal_map.is_valid())
normal_rid = normal_map->get_rid();
- VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames);
+ VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid);
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
@@ -361,12 +351,6 @@ void Particles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect);
- ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames);
- ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames);
-
- ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames);
- ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
-
ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
@@ -389,8 +373,6 @@ void Particles2D::_bind_methods() {
ADD_GROUP("Textures", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames");
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
@@ -413,8 +395,6 @@ Particles2D::Particles2D() {
set_use_local_coordinates(true);
set_draw_order(DRAW_ORDER_INDEX);
set_speed_scale(1);
- h_frames = 1;
- v_frames = 1;
}
Particles2D::~Particles2D() {
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
index af673841b1..6d52f8b28e 100644
--- a/scene/2d/particles_2d.h
+++ b/scene/2d/particles_2d.h
@@ -59,8 +59,6 @@ private:
bool local_coords;
int fixed_fps;
bool fractional_delta;
- int v_frames;
- int h_frames;
Ref<Material> process_material;
@@ -118,12 +116,6 @@ public:
virtual String get_configuration_warning() const;
- void set_v_frames(int p_count);
- int get_v_frames() const;
-
- void set_h_frames(int p_count);
- int get_h_frames() const;
-
void restart();
Rect2 capture_rect() const;
Particles2D();
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index cdb208e6cd..4276918f53 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -63,6 +63,10 @@ bool Path2D::_edit_use_rect() const {
bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+ if (curve.is_null()) {
+ return false;
+ }
+
for (int i = 0; i < curve->get_point_count(); i++) {
Vector2 s[2];
s[0] = curve->get_point_position(i);
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index d0bebd3354..3dde228bfa 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -32,8 +32,11 @@
#include "core/core_string_names.h"
#include "core/engine.h"
+#include "core/list.h"
#include "core/math/math_funcs.h"
#include "core/method_bind_ext.gen.inc"
+#include "core/object.h"
+#include "core/rid.h"
#include "scene/scene_string_names.h"
void PhysicsBody2D::_notification(int p_what) {
@@ -65,6 +68,8 @@ void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers);
ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers);
+
+ ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody2D::remove_collision_exception_with);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat
@@ -134,6 +139,20 @@ PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) :
set_pickable(false);
}
+Array PhysicsBody2D::get_collision_exceptions() {
+ List<RID> exceptions;
+ Physics2DServer::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);
+ Array ret;
+ for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) {
+ RID body = E->get();
+ ObjectID instance_id = Physics2DServer::get_singleton()->body_get_object_instance_id(body);
+ Object *obj = ObjectDB::get_instance(instance_id);
+ PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(obj);
+ ret.append(physics_body);
+ }
+ return ret;
+}
+
void PhysicsBody2D::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
@@ -280,10 +299,10 @@ void StaticBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
#endif // DISABLE_DEPRECATED
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
}
StaticBody2D::StaticBody2D() :
@@ -1042,10 +1061,10 @@ void RigidBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
#endif // DISABLE_DEPRECATED
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode");
@@ -1060,8 +1079,8 @@ void RigidBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_angular_damp", "get_angular_damp");
ADD_GROUP("Applied Forces", "applied_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "applied_torque"), "set_applied_torque", "get_applied_torque");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "applied_torque"), "set_applied_torque", "get_applied_torque");
ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape")));
@@ -1207,7 +1226,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
-Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) {
+Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
Vector2 floor_motion = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
@@ -1218,7 +1237,8 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
}
}
- Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time();
+ // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
+ Vector2 motion = (floor_motion + p_linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
Vector2 lv = p_linear_velocity;
on_floor = false;
@@ -1313,11 +1333,11 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return lv;
}
-Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) {
+Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
bool was_on_floor = on_floor;
- Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_stop_on_slope, p_max_slides, p_floor_max_angle);
+ Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia);
if (!was_on_floor || p_snap == Vector2()) {
return ret;
}
@@ -1405,6 +1425,10 @@ void KinematicBody2D::set_sync_to_physics(bool p_enable) {
return;
}
sync_to_physics = p_enable;
+
+ if (Engine::get_singleton()->is_editor_hint())
+ return;
+
if (p_enable) {
Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
set_only_update_transform_changes(true);
@@ -1451,10 +1475,10 @@ void KinematicBody2D::_notification(int p_what) {
void KinematicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
- ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move);
+ ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling);
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 29befb0375..c7b42add84 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -67,6 +67,7 @@ public:
void set_collision_layer_bit(int p_bit, bool p_value);
bool get_collision_layer_bit(int p_bit) const;
+ Array get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
@@ -331,15 +332,15 @@ protected:
public:
bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
- bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia);
+ bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true);
bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision);
void set_safe_margin(float p_margin);
float get_safe_margin() const;
- Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
- Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
+ Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 2c362f1b31..1c504d00fc 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -298,6 +298,7 @@ Skeleton2D::Skeleton2D() {
transform_dirty = true;
skeleton = VS::get_singleton()->skeleton_create();
+ set_notify_transform(true);
}
Skeleton2D::~Skeleton2D() {
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 04e199a21b..f4e6e2bdbb 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -435,22 +435,22 @@ void Sprite::_bind_methods() {
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("texture_changed"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
ADD_GROUP("Offset", "");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+ 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");
ADD_GROUP("Animation", "");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
ADD_GROUP("Region", "region_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
- ADD_PROPERTYNZ(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "region_filter_clip"), "set_region_filter_clip", "is_region_filter_clip_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_filter_clip"), "set_region_filter_clip", "is_region_filter_clip_enabled");
}
Sprite::Sprite() {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index a60ce47f5c..8fe65f53a9 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -593,7 +593,7 @@ void TileMap::update_dirty_quadrants() {
if (quadrant_order_dirty) {
- int index = -0x80000000; //always must be drawn below children
+ int index = -(int64_t)0x80000000; //always must be drawn below children
for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
Quadrant &q = E->get();
@@ -1114,6 +1114,8 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
*/
set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y));
}
+
+ format = FORMAT_2;
}
PoolVector<int> TileMap::_get_tile_data() const {
@@ -1403,7 +1405,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "format") {
- r_ret = FORMAT_2;
+ r_ret = format;
return true;
} else if (p_name == "tile_data") {
r_ret = _get_tile_data();
@@ -1445,6 +1447,11 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const {
default: {}
}
+ // Account for precision errors on the border (GH-23250).
+ // 0.00005 is 5*CMP_EPSILON, results would start being unpredictible if
+ // cell size is > 15,000, but we can hardly have more precision anyway with
+ // floating point.
+ ret += Vector2(0.00005, 0.00005);
return ret.floor();
}
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 7d7c47619a..d656ba0f64 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -190,7 +190,7 @@ void VisibilityEnabler2D::_find_nodes(Node *p_node) {
if (enabler[ENABLER_FREEZE_BODIES]) {
RigidBody2D *rb2d = Object::cast_to<RigidBody2D>(p_node);
- if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_CHARACTER || (rb2d->get_mode() == RigidBody2D::MODE_RIGID && !rb2d->is_able_to_sleep())))) {
+ if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_CHARACTER || rb2d->get_mode() == RigidBody2D::MODE_RIGID))) {
add = true;
meta = rb2d->get_mode();
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 40a1029201..5e78368804 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -290,7 +290,7 @@ void Area::_notification(int p_what) {
void Area::set_monitoring(bool p_enable) {
if (locked) {
- ERR_EXPLAIN("This function can't be used during the in/out signal.");
+ ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitoring\",true/false)");
}
ERR_FAIL_COND(locked);
@@ -437,10 +437,10 @@ Array Area::get_overlapping_bodies() const {
void Area::set_monitorable(bool p_enable) {
- if (locked) {
- ERR_EXPLAIN("This function can't be used during the in/out signal.");
+ if (locked || PhysicsServer::get_singleton()->is_flushing_queries()) {
+ ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitorable\",true/false)");
}
- ERR_FAIL_COND(locked);
+ ERR_FAIL_COND(locked || PhysicsServer::get_singleton()->is_flushing_queries());
if (p_enable == monitorable)
return;
diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp
index 4bff26a200..2dc500f7ab 100644
--- a/scene/3d/arvr_nodes.cpp
+++ b/scene/3d/arvr_nodes.cpp
@@ -38,14 +38,14 @@
void ARVRCamera::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- // need to find our ARVROrigin parent and let it know we're it's camera!
+ // need to find our ARVROrigin parent and let it know we're its camera!
ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent());
if (origin != NULL) {
origin->set_tracked_camera(this);
}
}; break;
case NOTIFICATION_EXIT_TREE: {
- // need to find our ARVROrigin parent and let it know we're no longer it's camera!
+ // need to find our ARVROrigin parent and let it know we're no longer its camera!
ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent());
if (origin != NULL) {
origin->clear_tracked_camera_if(this);
diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h
index 67fb658562..d6690676cc 100644
--- a/scene/3d/arvr_nodes.h
+++ b/scene/3d/arvr_nodes.h
@@ -62,7 +62,7 @@ public:
};
/*
- ARVRController is a helper node that automatically updates it's position based on tracker data.
+ ARVRController is a helper node that automatically updates its position based on tracker data.
It must be a child node of our ARVROrigin node
*/
@@ -102,7 +102,7 @@ public:
};
/*
- ARVRAnchor is a helper node that automatically updates it's position based on anchor data, it represents a real world location.
+ ARVRAnchor is a helper node that automatically updates its position based on anchor data, it represents a real world location.
It must be a child node of our ARVROrigin node
*/
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index abf022ecb3..afd87deca6 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -112,8 +112,10 @@ void AudioStreamPlayer3D::_mix_audio() {
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k];
- AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
+ if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k))
+ continue; //may have been deleted, will be updated on process
+ AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k);
current.filter.set_mode(AudioFilterSW::HIGHSHELF);
current.filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate());
current.filter.set_cutoff(attenuation_filter_cutoff_hz);
@@ -159,6 +161,9 @@ void AudioStreamPlayer3D::_mix_audio() {
if (current.reverb_bus_index >= 0) {
+ if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.reverb_bus_index, k))
+ continue; //may have been deleted, will be updated on process
+
AudioFrame *rtarget = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.reverb_bus_index, k);
if (current.reverb_bus_index == prev_outputs[i].reverb_bus_index) {
@@ -290,7 +295,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
PhysicsDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS];
- int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask);
+ int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true);
Area *area = NULL;
for (int i = 0; i < areas; i++) {
@@ -448,7 +453,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
//float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life
- float center_val[3] = { 0.5, 0.25, 0.16666 };
+ float center_val[3] = { 0.5f, 0.25f, 0.16666f };
AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]);
if (attenuation < 1.0) {
@@ -636,6 +641,7 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
void AudioStreamPlayer3D::play(float p_from_pos) {
if (stream_playback.is_valid()) {
+ active = true;
setplay = p_from_pos;
output_ready = false;
set_physics_process_internal(true);
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index 35a2049bda..69839a0087 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -198,6 +198,35 @@ String CPUParticles::get_configuration_warning() const {
String warnings;
+ bool mesh_found = false;
+ bool anim_material_found = false;
+
+ if (get_mesh().is_valid()) {
+ mesh_found = true;
+ for (int j = 0; j < get_mesh()->get_surface_count(); j++) {
+ anim_material_found = Object::cast_to<ShaderMaterial>(get_mesh()->surface_get_material(j).ptr()) != NULL;
+ SpatialMaterial *spat = Object::cast_to<SpatialMaterial>(get_mesh()->surface_get_material(j).ptr());
+ anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == SpatialMaterial::BILLBOARD_PARTICLES);
+ }
+ }
+
+ anim_material_found = anim_material_found || Object::cast_to<ShaderMaterial>(get_material_override().ptr()) != NULL;
+ SpatialMaterial *spat = Object::cast_to<SpatialMaterial>(get_material_override().ptr());
+ anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == SpatialMaterial::BILLBOARD_PARTICLES);
+
+ if (!mesh_found) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("Nothing is visible because no mesh has not been assigned.");
+ }
+
+ if (!anim_material_found && (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 ||
+ get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled.");
+ }
+
return warnings;
}
@@ -781,12 +810,6 @@ void CPUParticles::_particles_process(float p_delta) {
base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]);
p.custom[0] = Math::deg2rad(base_angle); //angle
p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle
- if (flags[FLAG_ANIM_LOOP]) {
- p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop
-
- } else {
- p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only
- }
}
//apply color
//apply hue rotation
@@ -1151,7 +1174,6 @@ void CPUParticles::convert_from_particles(Node *p_particles) {
set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
- set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP));
set_emission_shape(EmissionShape(material->get_emission_shape()));
set_emission_sphere_radius(material->get_emission_sphere_radius());
@@ -1309,7 +1331,7 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
/*
@@ -1347,7 +1369,7 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
@@ -1357,7 +1379,6 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", 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);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP);
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index 77a144b70b..2e83924dfc 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -70,7 +70,6 @@ public:
FLAG_ALIGN_Y_TO_VELOCITY,
FLAG_ROTATE_Y,
FLAG_DISABLE_Z,
- FLAG_ANIM_LOOP,
FLAG_MAX
};
@@ -170,7 +169,6 @@ private:
PoolVector<Color> emission_colors;
int emission_point_count;
- bool anim_loop;
Vector3 gravity;
void _particles_process(float p_delta);
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 7e1d60ab8e..11d61315ba 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -163,11 +163,6 @@ void Light::_update_visibility() {
if (!is_inside_tree())
return;
- // FIXME: Since the call to VS::instance_light_set_enabled was disabled below,
- // the whole logic became pointless so editor_ok triggers unused variable warnings.
- // Commenting out for now but this should be fixed/reimplemented so that editor_only
- // works as expected (GH-17989).
- /*
bool editor_ok = true;
#ifdef TOOLS_ENABLED
@@ -184,8 +179,8 @@ void Light::_update_visibility() {
}
#endif
- //VS::get_singleton()->instance_light_set_enabled(get_instance(),is_visible_in_tree() && editor_ok);
- */
+ VS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree() && editor_ok);
+
_change_notify("geometry/visible");
}
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 4cbf6f2de3..cf0317cd58 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -253,6 +253,11 @@ void MeshInstance::_notification(int p_what) {
}
}
+int MeshInstance::get_surface_material_count() const {
+
+ return materials.size();
+}
+
void MeshInstance::set_surface_material(int p_surface, const Ref<Material> &p_material) {
ERR_FAIL_INDEX(p_surface, materials.size());
@@ -359,6 +364,7 @@ void MeshInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path);
ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path);
+ ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count);
ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material);
ClassDB::bind_method(D_METHOD("get_surface_material", "surface"), &MeshInstance::get_surface_material);
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 0dfec538f9..0b5b4b9e7b 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -76,6 +76,7 @@ public:
void set_skeleton_path(const NodePath &p_skeleton);
NodePath get_skeleton_path();
+ int get_surface_material_count() const;
void set_surface_material(int p_surface, const Ref<Material> &p_material);
Ref<Material> get_surface_material(int p_surface) const;
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 10b26778ef..3fff42aa78 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "particles.h"
+#include "scene/resources/particles_material.h"
#include "servers/visual_server.h"
@@ -226,15 +227,27 @@ String Particles::get_configuration_warning() const {
String warnings;
bool meshes_found = false;
+ bool anim_material_found = false;
for (int i = 0; i < draw_passes.size(); i++) {
if (draw_passes[i].is_valid()) {
meshes_found = true;
- break;
+ for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) {
+ anim_material_found = Object::cast_to<ShaderMaterial>(draw_passes[i]->surface_get_material(j).ptr()) != NULL;
+ SpatialMaterial *spat = Object::cast_to<SpatialMaterial>(draw_passes[i]->surface_get_material(j).ptr());
+ anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == SpatialMaterial::BILLBOARD_PARTICLES);
+ }
+ if (meshes_found && anim_material_found) break;
}
}
+ anim_material_found = anim_material_found || Object::cast_to<ShaderMaterial>(get_material_override().ptr()) != NULL;
+ SpatialMaterial *spat = Object::cast_to<SpatialMaterial>(get_material_override().ptr());
+ anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == SpatialMaterial::BILLBOARD_PARTICLES);
+
if (!meshes_found) {
+ if (warnings != String())
+ warnings += "\n";
warnings += "- " + TTR("Nothing is visible because meshes have not been assigned to draw passes.");
}
@@ -242,6 +255,15 @@ String Particles::get_configuration_warning() const {
if (warnings != String())
warnings += "\n";
warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted.");
+ } else {
+ const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr());
+ if (!anim_material_found && process &&
+ (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
+ process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("Particles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled.");
+ }
}
return warnings;
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index e37efa0e8a..339a434a6e 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -43,6 +43,16 @@ void Path::_curve_changed() {
if (is_inside_tree()) {
emit_signal("curve_changed");
}
+
+ // update the configuration warnings of all children of type OrientedPathFollows
+ if (is_inside_tree()) {
+ for (int i = 0; i < get_child_count(); i++) {
+ OrientedPathFollow *child = Object::cast_to<OrientedPathFollow>(get_child(i));
+ if (child) {
+ child->update_configuration_warning();
+ }
+ }
+ }
}
void Path::set_curve(const Ref<Curve3D> &p_curve) {
@@ -207,6 +217,18 @@ void PathFollow::_validate_property(PropertyInfo &property) const {
}
}
+String PathFollow::get_configuration_warning() const {
+
+ if (!is_visible_in_tree() || !is_inside_tree())
+ return String();
+
+ if (!Object::cast_to<Path>(get_parent())) {
+ return TTR("PathFollow only works when set as a child of a Path node.");
+ }
+
+ return String();
+}
+
void PathFollow::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow::set_offset);
@@ -444,6 +466,23 @@ void OrientedPathFollow::_validate_property(PropertyInfo &property) const {
}
}
+String OrientedPathFollow::get_configuration_warning() const {
+
+ if (!is_visible_in_tree() || !is_inside_tree())
+ return String();
+
+ if (!Object::cast_to<Path>(get_parent())) {
+ return TTR("OrientedPathFollow only works when set as a child of a Path node.");
+ } else {
+ Path *path = Object::cast_to<Path>(get_parent());
+ if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled()) {
+ return TTR("OrientedPathFollow requires up vectors enabled in its parent Path.");
+ }
+ }
+
+ return String();
+}
+
void OrientedPathFollow::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &OrientedPathFollow::set_offset);
diff --git a/scene/3d/path.h b/scene/3d/path.h
index f73bf17dfe..beb37d9714 100644
--- a/scene/3d/path.h
+++ b/scene/3d/path.h
@@ -106,6 +106,8 @@ public:
void set_cubic_interpolation(bool p_enable);
bool get_cubic_interpolation() const;
+ String get_configuration_warning() const;
+
PathFollow();
};
@@ -151,6 +153,8 @@ public:
void set_cubic_interpolation(bool p_enable);
bool get_cubic_interpolation() const;
+ String get_configuration_warning() const;
+
OrientedPathFollow();
};
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 0fb0869979..bcfcf33e57 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -32,7 +32,10 @@
#include "core/core_string_names.h"
#include "core/engine.h"
+#include "core/list.h"
#include "core/method_bind_ext.gen.inc"
+#include "core/object.h"
+#include "core/rid.h"
#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
@@ -108,6 +111,20 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const {
return get_collision_layer() & (1 << p_bit);
}
+Array PhysicsBody::get_collision_exceptions() {
+ List<RID> exceptions;
+ PhysicsServer::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);
+ Array ret;
+ for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) {
+ RID body = E->get();
+ ObjectID instance_id = PhysicsServer::get_singleton()->body_get_object_instance_id(body);
+ Object *obj = ObjectDB::get_instance(instance_id);
+ PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(obj);
+ ret.append(physics_body);
+ }
+ return ret;
+}
+
void PhysicsBody::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
@@ -289,14 +306,15 @@ void StaticBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics);
+ ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with);
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
#endif // DISABLE_DEPRECATED
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
}
@@ -1006,10 +1024,10 @@ void RigidBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight");
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_bounce", "get_bounce");
#endif // DISABLE_DEPRECATED
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection");
@@ -1149,7 +1167,8 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
}
}
- Vector3 motion = (floor_velocity + lv) * get_physics_process_delta_time();
+ // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
+ Vector3 motion = (floor_velocity + lv) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
on_floor = false;
on_ceiling = false;
@@ -1248,7 +1267,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
return lv;
}
-Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) {
+Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
bool was_on_floor = on_floor;
@@ -1384,9 +1403,9 @@ void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
+ ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move);
+ ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody::is_on_ceiling);
@@ -1888,6 +1907,26 @@ bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant
if (j.is_valid())
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, axis_data[axis].linear_limit_softness);
+ } else if ("linear_spring_enabled" == var_name) {
+ axis_data[axis].linear_spring_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING, axis_data[axis].linear_spring_enabled);
+
+ } else if ("linear_spring_stiffness" == var_name) {
+ axis_data[axis].linear_spring_stiffness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS, axis_data[axis].linear_spring_stiffness);
+
+ } else if ("linear_spring_damping" == var_name) {
+ axis_data[axis].linear_spring_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING, axis_data[axis].linear_spring_damping);
+
+ } else if ("linear_equilibrium_point" == var_name) {
+ axis_data[axis].linear_equilibrium_point = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT, axis_data[axis].linear_equilibrium_point);
+
} else if ("linear_restitution" == var_name) {
axis_data[axis].linear_restitution = p_value;
if (j.is_valid())
@@ -1933,6 +1972,26 @@ bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant
if (j.is_valid())
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, axis_data[axis].erp);
+ } else if ("angular_spring_enabled" == var_name) {
+ axis_data[axis].angular_spring_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING, axis_data[axis].angular_spring_enabled);
+
+ } else if ("angular_spring_stiffness" == var_name) {
+ axis_data[axis].angular_spring_stiffness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS, axis_data[axis].angular_spring_stiffness);
+
+ } else if ("angular_spring_damping" == var_name) {
+ axis_data[axis].angular_spring_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING, axis_data[axis].angular_spring_damping);
+
+ } else if ("angular_equilibrium_point" == var_name) {
+ axis_data[axis].angular_equilibrium_point = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, axis_data[axis].angular_equilibrium_point);
+
} else {
return false;
}
@@ -1971,6 +2030,14 @@ bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_re
r_ret = axis_data[axis].linear_limit_lower;
} else if ("linear_limit_softness" == var_name) {
r_ret = axis_data[axis].linear_limit_softness;
+ } else if ("linear_spring_enabled" == var_name) {
+ r_ret = axis_data[axis].linear_spring_enabled;
+ } else if ("linear_spring_stiffness" == var_name) {
+ r_ret = axis_data[axis].linear_spring_stiffness;
+ } else if ("linear_spring_damping" == var_name) {
+ r_ret = axis_data[axis].linear_spring_damping;
+ } else if ("linear_equilibrium_point" == var_name) {
+ r_ret = axis_data[axis].linear_equilibrium_point;
} else if ("linear_restitution" == var_name) {
r_ret = axis_data[axis].linear_restitution;
} else if ("linear_damping" == var_name) {
@@ -1989,6 +2056,14 @@ bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_re
r_ret = axis_data[axis].angular_damping;
} else if ("erp" == var_name) {
r_ret = axis_data[axis].erp;
+ } else if ("angular_spring_enabled" == var_name) {
+ r_ret = axis_data[axis].angular_spring_enabled;
+ } else if ("angular_spring_stiffness" == var_name) {
+ r_ret = axis_data[axis].angular_spring_stiffness;
+ } else if ("angular_spring_damping" == var_name) {
+ r_ret = axis_data[axis].angular_spring_damping;
+ } else if ("angular_equilibrium_point" == var_name) {
+ r_ret = axis_data[axis].angular_equilibrium_point;
} else {
return false;
}
@@ -2003,6 +2078,10 @@ void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_lis
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_upper"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_lower"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_spring_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_spring_stiffness"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_spring_damping"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_equilibrium_point"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled"));
@@ -2012,6 +2091,10 @@ void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_lis
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/erp"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_spring_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_spring_stiffness"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_spring_damping"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_equilibrium_point"));
}
}
@@ -2275,6 +2358,10 @@ void PhysicalBone::_reload_joint() {
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, g6dofjd->axis_data[axis].linear_limit_upper);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, g6dofjd->axis_data[axis].linear_limit_lower);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].linear_limit_softness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING, g6dofjd->axis_data[axis].linear_spring_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS, g6dofjd->axis_data[axis].linear_spring_stiffness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING, g6dofjd->axis_data[axis].linear_spring_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT, g6dofjd->axis_data[axis].linear_equilibrium_point);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, g6dofjd->axis_data[axis].linear_restitution);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, g6dofjd->axis_data[axis].linear_damping);
PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, g6dofjd->axis_data[axis].angular_limit_enabled);
@@ -2284,6 +2371,10 @@ void PhysicalBone::_reload_joint() {
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, g6dofjd->axis_data[axis].angular_restitution);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, g6dofjd->axis_data[axis].angular_damping);
PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, g6dofjd->axis_data[axis].erp);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING, g6dofjd->axis_data[axis].angular_spring_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS, g6dofjd->axis_data[axis].angular_spring_stiffness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING, g6dofjd->axis_data[axis].angular_spring_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, g6dofjd->axis_data[axis].angular_equilibrium_point);
}
} break;
@@ -2326,7 +2417,8 @@ void PhysicalBone::set_joint_type(JointType p_joint_type) {
if (p_joint_type == get_joint_type())
return;
- memdelete(joint_data);
+ if (joint_data)
+ memdelete(joint_data);
joint_data = NULL;
switch (p_joint_type) {
case JOINT_TYPE_PIN:
@@ -2526,7 +2618,8 @@ PhysicalBone::PhysicalBone() :
}
PhysicalBone::~PhysicalBone() {
- memdelete(joint_data);
+ if (joint_data)
+ memdelete(joint_data);
}
void PhysicalBone::update_bone_id() {
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index ed9f41197b..5474290c07 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -69,6 +69,7 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
+ Array get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
@@ -328,7 +329,7 @@ public:
float get_safe_margin() const;
Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
- Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45));
+ Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
@@ -493,6 +494,10 @@ public:
real_t linear_limit_softness;
real_t linear_restitution;
real_t linear_damping;
+ bool linear_spring_enabled;
+ real_t linear_spring_stiffness;
+ real_t linear_spring_damping;
+ real_t linear_equilibrium_point;
bool angular_limit_enabled;
real_t angular_limit_upper;
real_t angular_limit_lower;
@@ -500,6 +505,10 @@ public:
real_t angular_restitution;
real_t angular_damping;
real_t erp;
+ bool angular_spring_enabled;
+ real_t angular_spring_stiffness;
+ real_t angular_spring_damping;
+ real_t angular_equilibrium_point;
SixDOFAxisData() :
linear_limit_enabled(true),
@@ -508,13 +517,21 @@ public:
linear_limit_softness(0.7),
linear_restitution(0.5),
linear_damping(1.),
+ linear_spring_enabled(false),
+ linear_spring_stiffness(0),
+ linear_spring_damping(0),
+ linear_equilibrium_point(0),
angular_limit_enabled(true),
angular_limit_upper(0),
angular_limit_lower(0),
angular_limit_softness(0.5),
angular_restitution(0),
angular_damping(1.),
- erp(0.5) {}
+ erp(0.5),
+ angular_spring_enabled(false),
+ angular_spring_stiffness(0),
+ angular_spring_damping(0.),
+ angular_equilibrium_point(0) {}
};
virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; }
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
index a30fc0ac3e..02c6b1d969 100644
--- a/scene/3d/physics_joint.cpp
+++ b/scene/3d/physics_joint.cpp
@@ -716,6 +716,11 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x");
@@ -727,6 +732,10 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/upper_distance"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT);
@@ -737,6 +746,10 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y");
@@ -748,6 +761,10 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/upper_distance"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT);
@@ -758,6 +775,10 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z");
@@ -769,6 +790,10 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
BIND_ENUM_CONSTANT(PARAM_LINEAR_LOWER_LIMIT);
BIND_ENUM_CONSTANT(PARAM_LINEAR_UPPER_LIMIT);
@@ -790,6 +815,8 @@ void Generic6DOFJoint::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_LIMIT);
BIND_ENUM_CONSTANT(FLAG_ENABLE_ANGULAR_LIMIT);
+ BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_SPRING);
+ BIND_ENUM_CONSTANT(FLAG_ENABLE_ANGULAR_SPRING);
BIND_ENUM_CONSTANT(FLAG_ENABLE_MOTOR);
BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_MOTOR);
BIND_ENUM_CONSTANT(FLAG_MAX);
@@ -923,6 +950,9 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_x(PARAM_LINEAR_DAMPING, 1.0);
set_param_x(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
set_param_x(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
+ set_param_x(PARAM_LINEAR_SPRING_STIFFNESS, 0.01);
+ set_param_x(PARAM_LINEAR_SPRING_DAMPING, 0.01);
+ set_param_x(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0);
set_param_x(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_x(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_x(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -932,9 +962,14 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_x(PARAM_ANGULAR_ERP, 0.5);
set_param_x(PARAM_ANGULAR_MOTOR_TARGET_VELOCITY, 0);
set_param_x(PARAM_ANGULAR_MOTOR_FORCE_LIMIT, 300);
+ set_param_x(PARAM_ANGULAR_SPRING_STIFFNESS, 0);
+ set_param_x(PARAM_ANGULAR_SPRING_DAMPING, 0);
+ set_param_x(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0);
set_flag_x(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_x(FLAG_ENABLE_LINEAR_LIMIT, true);
+ set_flag_x(FLAG_ENABLE_ANGULAR_SPRING, false);
+ set_flag_x(FLAG_ENABLE_LINEAR_SPRING, false);
set_flag_x(FLAG_ENABLE_MOTOR, false);
set_flag_x(FLAG_ENABLE_LINEAR_MOTOR, false);
@@ -945,6 +980,9 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_y(PARAM_LINEAR_DAMPING, 1.0);
set_param_y(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
set_param_y(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
+ set_param_y(PARAM_LINEAR_SPRING_STIFFNESS, 0.01);
+ set_param_y(PARAM_LINEAR_SPRING_DAMPING, 0.01);
+ set_param_y(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0);
set_param_y(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_y(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_y(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -954,9 +992,14 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_y(PARAM_ANGULAR_ERP, 0.5);
set_param_y(PARAM_ANGULAR_MOTOR_TARGET_VELOCITY, 0);
set_param_y(PARAM_ANGULAR_MOTOR_FORCE_LIMIT, 300);
+ set_param_y(PARAM_ANGULAR_SPRING_STIFFNESS, 0);
+ set_param_y(PARAM_ANGULAR_SPRING_DAMPING, 0);
+ set_param_y(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0);
set_flag_y(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_y(FLAG_ENABLE_LINEAR_LIMIT, true);
+ set_flag_y(FLAG_ENABLE_ANGULAR_SPRING, false);
+ set_flag_y(FLAG_ENABLE_LINEAR_SPRING, false);
set_flag_y(FLAG_ENABLE_MOTOR, false);
set_flag_y(FLAG_ENABLE_LINEAR_MOTOR, false);
@@ -967,6 +1010,9 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_z(PARAM_LINEAR_DAMPING, 1.0);
set_param_z(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
set_param_z(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
+ set_param_z(PARAM_LINEAR_SPRING_STIFFNESS, 0.01);
+ set_param_z(PARAM_LINEAR_SPRING_DAMPING, 0.01);
+ set_param_z(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0);
set_param_z(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_z(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_z(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -976,9 +1022,14 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_z(PARAM_ANGULAR_ERP, 0.5);
set_param_z(PARAM_ANGULAR_MOTOR_TARGET_VELOCITY, 0);
set_param_z(PARAM_ANGULAR_MOTOR_FORCE_LIMIT, 300);
+ set_param_z(PARAM_ANGULAR_SPRING_STIFFNESS, 0);
+ set_param_z(PARAM_ANGULAR_SPRING_DAMPING, 0);
+ set_param_z(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0);
set_flag_z(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_z(FLAG_ENABLE_LINEAR_LIMIT, true);
+ set_flag_z(FLAG_ENABLE_ANGULAR_SPRING, false);
+ set_flag_z(FLAG_ENABLE_LINEAR_SPRING, false);
set_flag_z(FLAG_ENABLE_MOTOR, false);
set_flag_z(FLAG_ENABLE_LINEAR_MOTOR, false);
}
diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h
index 37870d6f30..ee4ca28658 100644
--- a/scene/3d/physics_joint.h
+++ b/scene/3d/physics_joint.h
@@ -251,6 +251,9 @@ public:
PARAM_LINEAR_DAMPING = PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING,
PARAM_LINEAR_MOTOR_TARGET_VELOCITY = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY,
PARAM_LINEAR_MOTOR_FORCE_LIMIT = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT,
+ PARAM_LINEAR_SPRING_STIFFNESS = PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS,
+ PARAM_LINEAR_SPRING_DAMPING = PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING,
+ PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT = PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT,
PARAM_ANGULAR_LOWER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT,
PARAM_ANGULAR_UPPER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT,
PARAM_ANGULAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS,
@@ -260,12 +263,17 @@ public:
PARAM_ANGULAR_ERP = PhysicsServer::G6DOF_JOINT_ANGULAR_ERP,
PARAM_ANGULAR_MOTOR_TARGET_VELOCITY = PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY,
PARAM_ANGULAR_MOTOR_FORCE_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT,
+ PARAM_ANGULAR_SPRING_STIFFNESS = PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS,
+ PARAM_ANGULAR_SPRING_DAMPING = PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING,
+ PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT = PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT,
PARAM_MAX = PhysicsServer::G6DOF_JOINT_MAX,
};
enum Flag {
FLAG_ENABLE_LINEAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT,
FLAG_ENABLE_ANGULAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT,
+ FLAG_ENABLE_LINEAR_SPRING = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING,
+ FLAG_ENABLE_ANGULAR_SPRING = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING,
FLAG_ENABLE_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR,
FLAG_ENABLE_LINEAR_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR,
FLAG_MAX = PhysicsServer::G6DOF_JOINT_FLAG_MAX
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
index 4ebc941ebc..1e730d0b3d 100644
--- a/scene/3d/soft_body.cpp
+++ b/scene/3d/soft_body.cpp
@@ -29,8 +29,12 @@
/*************************************************************************/
#include "soft_body.h"
+#include "core/list.h"
+#include "core/object.h"
#include "core/os/os.h"
+#include "core/rid.h"
#include "scene/3d/collision_object.h"
+#include "scene/3d/physics_body.h"
#include "scene/3d/skeleton.h"
#include "servers/physics_server.h"
@@ -335,6 +339,7 @@ void SoftBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore);
ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore);
+ ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with);
@@ -547,6 +552,20 @@ PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() {
return pinned_points;
}
+Array SoftBody::get_collision_exceptions() {
+ List<RID> exceptions;
+ PhysicsServer::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions);
+ Array ret;
+ for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) {
+ RID body = E->get();
+ ObjectID instance_id = PhysicsServer::get_singleton()->body_get_object_instance_id(body);
+ Object *obj = ObjectDB::get_instance(instance_id);
+ PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(obj);
+ ret.append(physics_body);
+ }
+ return ret;
+}
+
void SoftBody::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h
index ee3d8d87cf..b1e699e839 100644
--- a/scene/3d/soft_body.h
+++ b/scene/3d/soft_body.h
@@ -166,6 +166,7 @@ public:
void set_drag_coefficient(real_t p_drag_coefficient);
real_t get_drag_coefficient();
+ Array get_collision_exceptions();
void add_collision_exception_with(Node *p_node);
void remove_collision_exception_with(Node *p_node);
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index fcc908cdc6..dc09392713 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -800,15 +800,15 @@ void Spatial::_bind_methods() {
//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ;
ADD_GROUP("Transform", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_translation", "get_translation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
ADD_GROUP("Matrix", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo");
ADD_SIGNAL(MethodInfo("visibility_changed"));
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 774ee49af2..5bde224ce3 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -388,7 +388,7 @@ void Sprite3D::_draw() {
return;
Size2i s;
- Rect2i src_rect;
+ Rect2 src_rect;
if (region) {
@@ -396,18 +396,18 @@ void Sprite3D::_draw() {
src_rect = region_rect;
} else {
s = texture->get_size();
- s = s / Size2i(hframes, vframes);
+ s = s / Size2(hframes, vframes);
src_rect.size = s;
src_rect.position.x += (frame % hframes) * s.x;
src_rect.position.y += (frame / hframes) * s.y;
}
- Point2i ofs = get_offset();
+ Point2 ofs = get_offset();
if (is_centered())
ofs -= s / 2;
- Rect2i dst_rect(ofs, s);
+ Rect2 dst_rect(ofs, s);
Rect2 final_rect;
Rect2 final_src_rect;
@@ -461,6 +461,13 @@ void Sprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
+ Plane tangent;
+ if (axis == Vector3::AXIS_X) {
+ tangent = Plane(0, 0, -1, -1);
+ } else {
+ tangent = Plane(1, 0, 0, -1);
+ }
+
RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
@@ -487,6 +494,7 @@ void Sprite3D::_draw() {
for (int i = 0; i < 4; i++) {
VS::get_singleton()->immediate_normal(immediate, normal);
+ VS::get_singleton()->immediate_tangent(immediate, tangent);
VS::get_singleton()->immediate_color(immediate, color);
VS::get_singleton()->immediate_uv(immediate, uvs[i]);
@@ -612,7 +620,7 @@ Rect2 Sprite3D::get_item_rect() const {
s = s / Point2(hframes, vframes);
}
- Point2i ofs = get_offset();
+ Point2 ofs = get_offset();
if (is_centered())
ofs -= s / 2;
@@ -699,15 +707,15 @@ void AnimatedSprite3D::_draw() {
return;
Size2i s = tsize;
- Rect2i src_rect;
+ Rect2 src_rect;
src_rect.size = s;
- Point2i ofs = get_offset();
+ Point2 ofs = get_offset();
if (is_centered())
ofs -= s / 2;
- Rect2i dst_rect(ofs, s);
+ Rect2 dst_rect(ofs, s);
Rect2 final_rect;
Rect2 final_src_rect;
@@ -761,6 +769,13 @@ void AnimatedSprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
+ Plane tangent;
+ if (axis == Vector3::AXIS_X) {
+ tangent = Plane(0, 0, -1, -1);
+ } else {
+ tangent = Plane(1, 0, 0, -1);
+ }
+
RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
@@ -788,6 +803,7 @@ void AnimatedSprite3D::_draw() {
for (int i = 0; i < 4; i++) {
VS::get_singleton()->immediate_normal(immediate, normal);
+ VS::get_singleton()->immediate_tangent(immediate, tangent);
VS::get_singleton()->immediate_color(immediate, color);
VS::get_singleton()->immediate_uv(immediate, uvs[i]);
@@ -1078,10 +1094,10 @@ void AnimatedSprite3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("frame_changed"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
}
AnimatedSprite3D::AnimatedSprite3D() {
diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp
index c69387d082..9a0832b27a 100644
--- a/scene/3d/visibility_notifier.cpp
+++ b/scene/3d/visibility_notifier.cpp
@@ -153,7 +153,7 @@ void VisibilityEnabler::_find_nodes(Node *p_node) {
if (enabler[ENABLER_FREEZE_BODIES]) {
RigidBody *rb = Object::cast_to<RigidBody>(p_node);
- if (rb && ((rb->get_mode() == RigidBody::MODE_CHARACTER || (rb->get_mode() == RigidBody::MODE_RIGID && !rb->is_able_to_sleep())))) {
+ if (rb && ((rb->get_mode() == RigidBody::MODE_CHARACTER || rb->get_mode() == RigidBody::MODE_RIGID))) {
add = true;
meta = rb->get_mode();
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index f9bd905181..0eccbbc8f9 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -261,7 +261,7 @@ static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3
void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector3 *p_normal, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) {
if (p_level == cell_subdiv - 1) {
- //plot the face by guessing it's albedo and emission value
+ //plot the face by guessing its albedo and emission value
//find best axis to map to, for scanning values
int closest_axis = 0;
@@ -1619,7 +1619,7 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const
Vector3(-0.700629, -0.509037, 0.5),
Vector3(0.267617, -0.823639, 0.5)
};
- static const float weights[6] = { 0.25, 0.15, 0.15, 0.15, 0.15, 0.15 };
+ static const float weights[6] = { 0.25f, 0.15f, 0.15f, 0.15f, 0.15f, 0.15f };
//
cone_dirs = dirs;
cone_dir_count = 6;
@@ -1641,7 +1641,7 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const
Vector3(0.19124006749743122, 0.39355745585016605, 0.8991883926788214),
Vector3(0.19124006749743122, -0.39355745585016605, 0.8991883926788214),
};
- static const float weights[10] = { 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.133333, 0.133333, 0.13333 };
+ static const float weights[10] = { 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.133333f, 0.133333f, 0.13333f };
cone_dirs = dirs;
cone_dir_count = 10;
cone_aperture = 0.404; // tan(angle) 45 degrees
@@ -1875,7 +1875,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh
if (bake_mode == BAKE_MODE_RAY_TRACE) {
//blur
//gauss kernel, 7 step sigma 2
- static const float gauss_kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
+ static const float gauss_kernel[4] = { 0.214607f, 0.189879f, 0.131514f, 0.071303f };
//horizontal pass
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 9321133d5f..866b85c4c7 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -33,9 +33,17 @@
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position));
+ r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", 0));
+ r_list->push_back(PropertyInfo(Variant::REAL, length_internal, PROPERTY_HINT_NONE, "", 0));
}
Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const {
- return Vector2();
+ if (p_parameter == closest) {
+ return -1;
+ } else if (p_parameter == length_internal) {
+ return 0;
+ } else {
+ return Vector2();
+ }
}
void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) {
@@ -412,84 +420,124 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) {
_update_triangles();
Vector2 blend_pos = get_parameter(blend_position);
+ int closest = get_parameter(this->closest);
+ float length_internal = get_parameter(this->length_internal);
+ float mind = 0; //time of min distance point
- if (triangles.size() == 0)
- return 0;
+ if (blend_mode == BLEND_MODE_INTERPOLATED) {
- Vector2 best_point;
- bool first = true;
- int blend_triangle = -1;
- float blend_weights[3] = { 0, 0, 0 };
+ if (triangles.size() == 0)
+ return 0;
- for (int i = 0; i < triangles.size(); i++) {
- Vector2 points[3];
- for (int j = 0; j < 3; j++) {
- points[j] = get_blend_point_position(get_triangle_point(i, j));
- }
+ Vector2 best_point;
+ bool first = true;
+ int blend_triangle = -1;
+ float blend_weights[3] = { 0, 0, 0 };
- if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) {
+ for (int i = 0; i < triangles.size(); i++) {
+ Vector2 points[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = get_blend_point_position(get_triangle_point(i, j));
+ }
- blend_triangle = i;
- _blend_triangle(blend_pos, points, blend_weights);
- break;
- }
+ if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) {
- for (int j = 0; j < 3; j++) {
- Vector2 s[2] = {
- points[j],
- points[(j + 1) % 3]
- };
- Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s);
- if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
- best_point = closest;
blend_triangle = i;
- first = false;
- float d = s[0].distance_to(s[1]);
- if (d == 0.0) {
- blend_weights[j] = 1.0;
- blend_weights[(j + 1) % 3] = 0.0;
- blend_weights[(j + 2) % 3] = 0.0;
- } else {
- float c = s[0].distance_to(closest) / d;
-
- blend_weights[j] = 1.0 - c;
- blend_weights[(j + 1) % 3] = c;
- blend_weights[(j + 2) % 3] = 0.0;
+ _blend_triangle(blend_pos, points, blend_weights);
+ break;
+ }
+
+ for (int j = 0; j < 3; j++) {
+ Vector2 s[2] = {
+ points[j],
+ points[(j + 1) % 3]
+ };
+ Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s);
+ if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) {
+ best_point = closest;
+ blend_triangle = i;
+ first = false;
+ float d = s[0].distance_to(s[1]);
+ if (d == 0.0) {
+ blend_weights[j] = 1.0;
+ blend_weights[(j + 1) % 3] = 0.0;
+ blend_weights[(j + 2) % 3] = 0.0;
+ } else {
+ float c = s[0].distance_to(closest) / d;
+
+ blend_weights[j] = 1.0 - c;
+ blend_weights[(j + 1) % 3] = c;
+ blend_weights[(j + 2) % 3] = 0.0;
+ }
}
}
}
- }
- ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here
+ ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here
- int triangle_points[3];
- for (int j = 0; j < 3; j++) {
- triangle_points[j] = get_triangle_point(blend_triangle, j);
- }
+ int triangle_points[3];
+ for (int j = 0; j < 3; j++) {
+ triangle_points[j] = get_triangle_point(blend_triangle, j);
+ }
- first = true;
- float mind = 0;
- for (int i = 0; i < blend_points_used; i++) {
+ first = true;
- bool found = false;
- for (int j = 0; j < 3; j++) {
- if (i == triangle_points[j]) {
- //blend with the given weight
- float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
- if (first || t < mind) {
- mind = t;
- first = false;
+ for (int i = 0; i < blend_points_used; i++) {
+
+ bool found = false;
+ for (int j = 0; j < 3; j++) {
+ if (i == triangle_points[j]) {
+ //blend with the given weight
+ float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ if (first || t < mind) {
+ mind = t;
+ first = false;
+ }
+ found = true;
+ break;
}
- found = true;
- break;
+ }
+
+ if (!found) {
+ //ignore
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+ }
+ }
+ } else {
+
+ int new_closest = -1;
+ float new_closest_dist = 1e20;
+
+ for (int i = 0; i < blend_points_used; i++) {
+
+ float d = blend_points[i].position.distance_squared_to(blend_pos);
+ if (d < new_closest_dist) {
+
+ new_closest = i;
+ new_closest_dist = d;
}
}
- if (!found) {
- //ignore
- blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+ if (new_closest != closest) {
+
+ float from = 0;
+ if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
+ //see how much animation remains
+ from = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, true, 0.0, FILTER_IGNORE, false) - length_internal;
+ }
+
+ mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false) + from;
+ length_internal = from + mind;
+
+ closest = new_closest;
+
+ } else {
+ mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
}
}
+
+ set_parameter(this->closest, closest);
+ set_parameter(this->length_internal, length_internal);
return mind;
}
@@ -527,6 +575,14 @@ void AnimationNodeBlendSpace2D::_tree_changed() {
emit_signal("tree_changed");
}
+void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
+ blend_mode = p_blend_mode;
+}
+
+AnimationNodeBlendSpace2D::BlendMode AnimationNodeBlendSpace2D::get_blend_mode() const {
+ return blend_mode;
+}
+
void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
@@ -565,6 +621,9 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles);
ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles);
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace2D::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace2D::get_blend_mode);
+
ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace2D::_tree_changed);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles");
@@ -581,6 +640,11 @@ void AnimationNodeBlendSpace2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NOEDITOR), "set_blend_mode", "get_blend_mode");
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
}
AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
@@ -597,6 +661,9 @@ AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() {
y_label = "y";
trianges_dirty = false;
blend_position = "blend_position";
+ closest = "closest";
+ length_internal = "length_internal";
+ blend_mode = BLEND_MODE_INTERPOLATED;
}
AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() {
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 2c684687de..60671f1816 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -35,7 +35,14 @@
class AnimationNodeBlendSpace2D : public AnimationRootNode {
GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode)
+public:
+ enum BlendMode {
+ BLEND_MODE_INTERPOLATED,
+ BLEND_MODE_DISCRETE,
+ BLEND_MODE_DISCRETE_CARRY,
+ };
+protected:
enum {
MAX_BLEND_POINTS = 64
};
@@ -56,11 +63,14 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode {
Vector<BlendTriangle> triangles;
StringName blend_position;
+ StringName closest;
+ StringName length_internal;
Vector2 max_space;
Vector2 min_space;
Vector2 snap;
String x_label;
String y_label;
+ BlendMode blend_mode;
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
void _set_triangles(const Vector<int> &p_triangles);
@@ -122,10 +132,15 @@ public:
void set_auto_triangles(bool p_enable);
bool get_auto_triangles() const;
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name);
AnimationNodeBlendSpace2D();
~AnimationNodeBlendSpace2D();
};
+VARIANT_ENUM_CAST(AnimationNodeBlendSpace2D::BlendMode)
+
#endif // ANIMATION_BLEND_SPACE_2D_H
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index b85d4e541e..5b413737a9 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -40,12 +40,11 @@ StringName AnimationNodeAnimation::get_animation() const {
return animation;
}
-float AnimationNodeAnimation::get_playback_time() const {
- return time;
-}
-
Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = NULL;
+void AnimationNodeAnimation::get_parameter_list(List<PropertyInfo> *r_list) const {
+ r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0));
+}
void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
if (property.name == "animation" && get_editable_animation_list) {
@@ -70,6 +69,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) {
AnimationPlayer *ap = state->player;
ERR_FAIL_COND_V(!ap, 0);
+ float time = get_parameter(this->time);
+
if (!ap->has_animation(animation)) {
AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(parent);
@@ -86,6 +87,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) {
Ref<Animation> anim = ap->get_animation(animation);
+ float step;
+
if (p_seek) {
time = p_time;
step = 0;
@@ -109,6 +112,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) {
blend_animation(animation, time, step, p_seek, 1.0);
+ set_parameter(this->time, time);
+
return anim_size - time;
}
@@ -120,16 +125,13 @@ void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
- ClassDB::bind_method(D_METHOD("get_playback_time"), &AnimationNodeAnimation::get_playback_time);
-
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
}
AnimationNodeAnimation::AnimationNodeAnimation() {
last_version = 0;
skip = false;
- time = 0;
- step = 0;
+ time = "time";
}
////////////////////////////////////////////////////////
@@ -266,7 +268,6 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) {
if (do_start) {
remaining = os_rem;
- do_start = false;
}
if (!p_seek) {
@@ -699,7 +700,7 @@ String AnimationNodeTransition::get_input_caption(int p_input) const {
if (tree.is_valid() && current >= 0) {
prev = current;
- prev_xfading = xfade;
+ prev_xfading = xfade;
time = 0;
current = p_current;
switched = true;
@@ -770,8 +771,6 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) {
rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
}
- switched = false;
-
if (p_seek) { // don't seek prev animation
blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
time = p_time;
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 4ca11e464b..5adb7fd71a 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -38,10 +38,9 @@ class AnimationNodeAnimation : public AnimationRootNode {
GDCLASS(AnimationNodeAnimation, AnimationRootNode);
StringName animation;
+ StringName time;
uint64_t last_version;
- float time;
- float step;
bool skip;
protected:
@@ -50,6 +49,8 @@ protected:
static void _bind_methods();
public:
+ void get_parameter_list(List<PropertyInfo> *r_list) const;
+
static Vector<String> (*get_editable_animation_list)();
virtual String get_caption() const;
@@ -58,8 +59,6 @@ public:
void set_animation(const StringName &p_name);
StringName get_animation() const;
- float get_playback_time() const;
-
AnimationNodeAnimation();
};
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 5923ad7657..a7af3f687b 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -345,7 +345,6 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm,
len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
loops_current = 0;
- play_start = false;
}
if (!sm->states.has(current)) {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 102f05a146..64202ba0e3 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1348,6 +1348,9 @@ void AnimationPlayer::_animation_changed() {
clear_caches();
emit_signal("caches_cleared");
+ if (is_playing()) {
+ playback.seeked = true; //need to restart stuff, like audio
+ }
}
void AnimationPlayer::_stop_playing_caches() {
diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp
index 83f45afac8..3119e29586 100644
--- a/scene/animation/skeleton_ik.cpp
+++ b/scene/animation/skeleton_ik.cpp
@@ -280,7 +280,7 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_
}
}
-void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) {
+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) {
return; // Skip solving
@@ -314,7 +314,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_u
}
} else {
// Set target orientation to tip
- new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
+ if (override_tip_basis)
+ new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
+ else
+ new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
}
p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose);
@@ -366,6 +369,9 @@ void SkeletonIK::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node);
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node);
+ ClassDB::bind_method(D_METHOD("set_override_tip_basis", "override"), &SkeletonIK::set_override_tip_basis);
+ ClassDB::bind_method(D_METHOD("is_override_tip_basis"), &SkeletonIK::is_override_tip_basis);
+
ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet);
ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet);
@@ -388,6 +394,7 @@ void SkeletonIK::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node");
@@ -418,6 +425,7 @@ void SkeletonIK::_notification(int p_what) {
SkeletonIK::SkeletonIK() :
Node(),
interpolation(1),
+ override_tip_basis(true),
use_magnet(false),
min_distance(0.01),
max_iterations(10),
@@ -478,6 +486,14 @@ NodePath SkeletonIK::get_target_node() {
return target_node_path_override;
}
+void SkeletonIK::set_override_tip_basis(bool p_override) {
+ override_tip_basis = p_override;
+}
+
+bool SkeletonIK::is_override_tip_basis() const {
+ return override_tip_basis;
+}
+
void SkeletonIK::set_use_magnet(bool p_use) {
use_magnet = p_use;
}
@@ -555,7 +571,7 @@ void SkeletonIK::reload_goal() {
void SkeletonIK::_solve_chain() {
if (!task)
return;
- FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position);
+ FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position);
}
#endif // _3D_DISABLED
diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h
index 202d6959bb..b9628c479c 100644
--- a/scene/animation/skeleton_ik.h
+++ b/scene/animation/skeleton_ik.h
@@ -138,7 +138,7 @@ public:
// The goal of chain should be always in local space
static void set_goal(Task *p_task, const Transform &p_goal);
static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta);
- static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position);
+ static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position);
};
class SkeletonIK : public Node {
@@ -149,6 +149,7 @@ class SkeletonIK : public Node {
real_t interpolation;
Transform target;
NodePath target_node_path_override;
+ bool override_tip_basis;
bool use_magnet;
Vector3 magnet_position;
@@ -185,6 +186,9 @@ public:
void set_target_node(const NodePath &p_node);
NodePath get_target_node();
+ void set_override_tip_basis(bool p_override);
+ bool is_override_tip_basis() const;
+
void set_use_magnet(bool p_use);
bool is_using_magnet() const;
diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp
index 64af7efb16..1d9d048fe1 100644
--- a/scene/audio/audio_player.cpp
+++ b/scene/audio/audio_player.cpp
@@ -219,6 +219,7 @@ void AudioStreamPlayer::seek(float p_seconds) {
void AudioStreamPlayer::stop() {
if (stream_playback.is_valid()) {
+ stream_playback->stop();
active = false;
set_process_internal(false);
}
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 6dfaacc776..1ac19774f7 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -406,6 +406,16 @@ bool BaseButton::is_toggle_mode() const {
return toggle_mode;
}
+void BaseButton::set_shortcut_in_tooltip(bool p_on) {
+
+ shortcut_in_tooltip = p_on;
+}
+
+bool BaseButton::is_shortcut_in_tooltip_enabled() const {
+
+ return shortcut_in_tooltip;
+}
+
void BaseButton::set_action_mode(ActionMode p_mode) {
action_mode = p_mode;
@@ -471,7 +481,7 @@ void BaseButton::_unhandled_input(Ref<InputEvent> p_event) {
String BaseButton::get_tooltip(const Point2 &p_pos) const {
String tooltip = Control::get_tooltip(p_pos);
- if (shortcut.is_valid() && shortcut->is_valid()) {
+ if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->is_valid()) {
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
if (shortcut->get_name().nocasecmp_to(tooltip) != 0) {
text += "\n" + tooltip;
@@ -510,6 +520,8 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_hovered"), &BaseButton::is_hovered);
ClassDB::bind_method(D_METHOD("set_toggle_mode", "enabled"), &BaseButton::set_toggle_mode);
ClassDB::bind_method(D_METHOD("is_toggle_mode"), &BaseButton::is_toggle_mode);
+ ClassDB::bind_method(D_METHOD("set_shortcut_in_tooltip", "enabled"), &BaseButton::set_shortcut_in_tooltip);
+ ClassDB::bind_method(D_METHOD("is_shortcut_in_tooltip_enabled"), &BaseButton::is_shortcut_in_tooltip_enabled);
ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &BaseButton::set_disabled);
ClassDB::bind_method(D_METHOD("is_disabled"), &BaseButton::is_disabled);
ClassDB::bind_method(D_METHOD("set_action_mode", "mode"), &BaseButton::set_action_mode);
@@ -533,11 +545,12 @@ void BaseButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
@@ -555,6 +568,7 @@ void BaseButton::_bind_methods() {
BaseButton::BaseButton() {
toggle_mode = false;
+ shortcut_in_tooltip = true;
status.pressed = false;
status.press_attempt = false;
status.hovering = false;
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 176d9fc213..a131e719ad 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -51,6 +51,7 @@ public:
private:
int button_mask;
bool toggle_mode;
+ bool shortcut_in_tooltip;
FocusMode enabled_focus_mode;
Ref<ShortCut> shortcut;
@@ -100,6 +101,9 @@ public:
void set_toggle_mode(bool p_on);
bool is_toggle_mode() const;
+ void set_shortcut_in_tooltip(bool p_on);
+ bool is_shortcut_in_tooltip_enabled() const;
+
void set_disabled(bool p_disabled);
bool is_disabled() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 2d17fb1391..621153b6de 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -274,10 +274,10 @@ void Button::_bind_methods() {
BIND_ENUM_CONSTANT(ALIGN_CENTER);
BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_button_icon", "get_button_icon");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index e155d0ef86..79e1d35b94 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -328,13 +328,15 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const {
}
void Control::_get_property_list(List<PropertyInfo> *p_list) const {
- Ref<Theme> theme;
+ Ref<Theme> theme = Theme::get_default();
+ /* Using the default theme since the properties below are meant for editor only
if (data.theme.is_valid()) {
theme = data.theme;
} else {
theme = Theme::get_default();
- }
+
+ }*/
{
List<StringName> names;
@@ -2844,36 +2846,36 @@ void Control::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
ADD_GROUP("Margin", "margin_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM);
ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees");
- ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
- ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents");
ADD_GROUP("Hint", "hint_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
ADD_GROUP("Focus", "focus_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_GROUP("Mouse", "mouse_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
@@ -2882,9 +2884,9 @@ void Control::_bind_methods() {
ADD_GROUP("Size Flags", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_stretch_ratio", "get_stretch_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Theme", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
ADD_GROUP("", "");
BIND_ENUM_CONSTANT(FOCUS_NONE);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index e3a21eb10d..50511f6bbd 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -535,7 +535,7 @@ void AcceptDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("custom_action", PropertyInfo(Variant::STRING, "action")));
ADD_GROUP("Dialog", "dialog");
- ADD_PROPERTYNZ(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, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
}
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 934b84ec0c..c13964d196 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -236,8 +236,8 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
//Snap to nearest point if holding shift
if (mm->get_shift()) {
- float snap_treshhold = 0.03;
- float smallest_ofs = snap_treshhold;
+ float snap_threshold = 0.03;
+ float smallest_ofs = snap_threshold;
bool found = false;
int nearest_point = 0;
for (int i = 0; i < points.size(); ++i) {
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index b4fd7484e9..b3bebc88ec 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -406,7 +406,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_color = Object::cast_to<GraphNode>(to)->get_connection_input_color(E->get().to_port);
connecting_target = false;
connecting_to = pos;
- just_disconected = true;
+ just_disconnected = true;
emit_signal("disconnection_request", E->get().from, E->get().from_port, E->get().to, E->get().to_port);
to = get_node(String(connecting_from)); //maybe it was erased
@@ -427,7 +427,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_color = gn->get_connection_output_color(j);
connecting_target = false;
connecting_to = pos;
- just_disconected = false;
+ just_disconnected = false;
return;
}
}
@@ -453,7 +453,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_color = Object::cast_to<GraphNode>(fr)->get_connection_output_color(E->get().from_port);
connecting_target = false;
connecting_to = pos;
- just_disconected = true;
+ just_disconnected = true;
emit_signal("disconnection_request", E->get().from, E->get().from_port, E->get().to, E->get().to_port);
fr = get_node(String(connecting_from)); //maybe it was erased
@@ -474,7 +474,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_color = gn->get_connection_input_color(j);
connecting_target = false;
connecting_to = pos;
- just_disconected = true;
+ just_disconnected = true;
return;
}
@@ -544,7 +544,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
emit_signal("connection_request", from, from_slot, to, to_slot);
- } else if (!just_disconected) {
+ } else if (!just_disconnected) {
String from = connecting_from;
int from_slot = connecting_index;
Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y);
@@ -1368,6 +1368,6 @@ GraphEdit::GraphEdit() {
zoom_hb->add_child(snap_amount);
setting_scroll_ofs = false;
- just_disconected = false;
+ just_disconnected = false;
set_clip_contents(true);
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 31a449eb59..71165e3dc9 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -94,7 +94,7 @@ private:
Vector2 connecting_to;
String connecting_target_to;
int connecting_target_index;
- bool just_disconected;
+ bool just_disconnected;
bool dragging;
bool just_selected;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 0d5fbee9ee..1669d62082 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -36,6 +36,7 @@ void ItemList::add_item(const String &p_item, const Ref<Texture> &p_texture, boo
Item item;
item.icon = p_texture;
+ item.icon_transposed = false;
item.icon_region = Rect2i();
item.icon_modulate = Color(1, 1, 1, 1);
item.text = p_item;
@@ -54,6 +55,7 @@ void ItemList::add_icon_item(const Ref<Texture> &p_item, bool p_selectable) {
Item item;
item.icon = p_item;
+ item.icon_transposed = false;
item.icon_region = Rect2i();
item.icon_modulate = Color(1, 1, 1, 1);
//item.text=p_item;
@@ -124,6 +126,22 @@ Ref<Texture> ItemList::get_item_icon(int p_idx) const {
return items[p_idx].icon;
}
+void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ items.write[p_idx].icon_transposed = p_transposed;
+ update();
+ shape_changed = true;
+}
+
+bool ItemList::is_item_icon_transposed(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx, items.size(), false);
+
+ return items[p_idx].icon_transposed;
+}
+
void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -416,6 +434,7 @@ void ItemList::set_icon_mode(IconMode p_mode) {
update();
shape_changed = true;
}
+
ItemList::IconMode ItemList::get_icon_mode() const {
return icon_mode;
@@ -435,10 +454,18 @@ Size2 ItemList::Item::get_icon_size() const {
if (icon.is_null())
return Size2();
- if (icon_region.has_no_area())
- return icon->get_size();
- return icon_region.size;
+ Size2 size_result = Size2(icon_region.size).abs();
+ if (icon_region.size.x == 0 || icon_region.size.y == 0)
+ size_result = icon->get_size();
+
+ if (icon_transposed) {
+ Size2 size_tmp = size_result;
+ size_result.x = size_tmp.y;
+ size_result.y = size_tmp.x;
+ }
+
+ return size_result;
}
void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
@@ -1067,10 +1094,15 @@ void ItemList::_notification(int p_what) {
if (items[i].disabled)
modulate.a *= 0.5;
- if (items[i].icon_region.has_no_area())
- draw_texture_rect(items[i].icon, draw_rect, false, modulate);
- else
- draw_texture_rect_region(items[i].icon, draw_rect, items[i].icon_region, modulate);
+ // If the icon is transposed, we have to swith the size so that it is drawn correctly
+ if (items[i].icon_transposed) {
+ Size2 size_tmp = draw_rect.size;
+ draw_rect.size.x = size_tmp.y;
+ draw_rect.size.y = size_tmp.x;
+ }
+
+ Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region);
+ draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed);
}
if (items[i].tag_icon.is_valid()) {
@@ -1405,6 +1437,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &ItemList::set_item_icon);
ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &ItemList::get_item_icon);
+ ClassDB::bind_method(D_METHOD("set_item_icon_transposed", "idx", "rect"), &ItemList::set_item_icon_transposed);
+ ClassDB::bind_method(D_METHOD("is_item_icon_transposed", "idx"), &ItemList::is_item_icon_transposed);
+
ClassDB::bind_method(D_METHOD("set_item_icon_region", "idx", "rect"), &ItemList::set_item_icon_region);
ClassDB::bind_method(D_METHOD("get_item_icon_region", "idx"), &ItemList::get_item_icon_region);
@@ -1498,17 +1533,17 @@ void ItemList::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "max_text_lines"), "set_max_text_lines", "get_max_text_lines");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines"), "set_max_text_lines", "get_max_text_lines");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
ADD_GROUP("Columns", "");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "max_columns"), "set_max_columns", "get_max_columns");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fixed_column_width"), "set_fixed_column_width", "get_fixed_column_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns"), "set_max_columns", "get_max_columns");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_column_width"), "set_fixed_column_width", "get_fixed_column_width");
ADD_GROUP("Icon", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_mode", PROPERTY_HINT_ENUM, "Top,Left"), "set_icon_mode", "get_icon_mode");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "icon_scale"), "set_icon_scale", "get_icon_scale");
- ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "icon_scale"), "set_icon_scale", "get_icon_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size");
BIND_ENUM_CONSTANT(ICON_MODE_TOP);
BIND_ENUM_CONSTANT(ICON_MODE_LEFT);
@@ -1524,6 +1559,7 @@ void ItemList::_bind_methods() {
ADD_SIGNAL(MethodInfo("nothing_selected"));
GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers
}
ItemList::ItemList() {
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 58771c1777..acd17986eb 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -53,6 +53,7 @@ private:
struct Item {
Ref<Texture> icon;
+ bool icon_transposed;
Rect2i icon_region;
Color icon_modulate;
Ref<Texture> tag_icon;
@@ -133,6 +134,9 @@ public:
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
Ref<Texture> get_item_icon(int p_idx) const;
+ void set_item_icon_transposed(int p_idx, const bool transposed);
+ bool is_item_icon_transposed(int p_idx) const;
+
void set_item_icon_region(int p_idx, const Rect2 &p_region);
Rect2 get_item_icon_region(int p_idx) const;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 6ff2d232f0..a7f88514e0 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -393,7 +393,7 @@ void Label::regenerate_word_cache() {
WordCache *last = NULL;
- for (int i = 0; i < xl_text.size() + 1; i++) {
+ for (int i = 0; i <= xl_text.length(); i++) {
CharType current = i < xl_text.length() ? xl_text[i] : ' '; //always a space at the end, so the algo works
@@ -429,12 +429,11 @@ void Label::regenerate_word_cache() {
if (current == '\n') {
insert_newline = true;
- } else {
+ } else if (current != ' ') {
total_char_cache++;
}
if (i < xl_text.length() && xl_text[i] == ' ') {
- total_char_cache--; // do not count spaces
if (line_width > 0 || last == NULL || last->char_pos != WordCache::CHAR_WRAPLINE) {
space_count++;
line_width += space_width;
@@ -665,12 +664,12 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(VALIGN_BOTTOM);
BIND_ENUM_CONSTANT(VALIGN_FILL);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "has_autowrap");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "has_autowrap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1", PROPERTY_USAGE_EDITOR), "set_visible_characters", "get_visible_characters");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 6f344f1028..299c304c5f 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -1573,22 +1573,22 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_REDO);
BIND_ENUM_CONSTANT(MENU_MAX);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
ADD_GROUP("Placeholder", "placeholder_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position");
}
@@ -1608,6 +1608,8 @@ LineEdit::LineEdit() {
text_changed_dirty = false;
placeholder_alpha = 0.6;
clear_button_enabled = false;
+ clear_button_status.press_attempt = false;
+ clear_button_status.pressing_inside = false;
deselect();
set_focus_mode(FOCUS_ALL);
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index d38a067fef..a603487cf1 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -75,6 +75,7 @@ void LinkButton::_notification(int p_what) {
color = get_color("font_color");
do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
} break;
+ case DRAW_HOVER_PRESSED:
case DRAW_PRESSED: {
if (has_color("font_color_pressed"))
@@ -91,7 +92,6 @@ void LinkButton::_notification(int p_what) {
do_underline = underline_mode != UNDERLINE_MODE_NEVER;
} break;
- case DRAW_HOVER_PRESSED: break; // Not used in this class
case DRAW_DISABLED: {
color = get_color("font_color_disabled");
@@ -134,8 +134,8 @@ void LinkButton::_bind_methods() {
BIND_ENUM_CONSTANT(UNDERLINE_MODE_ON_HOVER);
BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
}
LinkButton::LinkButton() {
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index c6a34921c7..802dc0db4f 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -70,18 +70,18 @@ void NinePatchRect::_bind_methods() {
ADD_SIGNAL(MethodInfo("texture_changed"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
ADD_GROUP("Patch Margin", "patch_margin_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
ADD_GROUP("Axis Stretch", "axis_stretch_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE);
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 3239641c2f..f621522102 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -557,6 +557,21 @@ void PopupMenu::_notification(int p_what) {
mouse_over = -1;
update();
}
+
+ for (int i = 0; i < items.size(); i++) {
+ if (items[i].submenu == "")
+ continue;
+
+ Node *n = get_node(items[i].submenu);
+ if (!n)
+ continue;
+
+ PopupMenu *pm = Object::cast_to<PopupMenu>(n);
+ if (!pm || !pm->is_visible())
+ continue;
+
+ pm->hide();
+ }
} break;
}
}
@@ -1012,8 +1027,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
code |= KEY_MASK_SHIFT;
}
- int il = items.size();
- for (int i = 0; i < il; i++) {
+ for (int i = 0; i < items.size(); i++) {
if (is_item_disabled(i) || items[i].shortcut_is_disabled)
continue;
@@ -1381,9 +1395,9 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
@@ -1411,6 +1425,7 @@ PopupMenu::PopupMenu() {
set_hide_on_item_selection(true);
set_hide_on_checkable_item_selection(true);
set_hide_on_multistate_item_selection(false);
+ set_hide_on_window_lose_focus(true);
submenu_timer = memnew(Timer);
submenu_timer->set_wait_time(0.3);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index bb36852cf9..9f1687262f 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -220,13 +220,14 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
case ALIGN_LEFT: l.offset_caches.push_back(0); break; \
case ALIGN_CENTER: l.offset_caches.push_back(((p_width - margin) - used) / 2); break; \
case ALIGN_RIGHT: l.offset_caches.push_back(((p_width - margin) - used)); break; \
- case ALIGN_FILL: l.offset_caches.push_back((p_width - margin) - used /*+spaces_size*/); break; \
+ case ALIGN_FILL: l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); break; \
} \
l.height_caches.push_back(line_height); \
l.ascent_caches.push_back(line_ascent); \
l.descent_caches.push_back(line_descent); \
l.space_caches.push_back(spaces); \
} \
+ line_wrapped = false; \
y += line_height + get_constant(SceneStringNames::get_singleton()->line_separation); \
line_height = 0; \
line_ascent = 0; \
@@ -254,6 +255,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
l.minimum_width = MAX(l.minimum_width, m_width); \
} \
if (wofs + m_width > p_width) { \
+ line_wrapped = true; \
if (p_mode == PROCESS_CACHE) { \
if (spaces > 0) \
spaces -= 1; \
@@ -298,6 +300,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int rchar = 0;
int lh = 0;
bool line_is_blank = true;
+ bool line_wrapped = false;
int fh = 0;
while (it) {
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 26da16569a..9c22a049b8 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -246,7 +246,7 @@ void ScrollContainer::_notification(int p_what) {
size.y -= h_scroll->get_minimum_size().y;
if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) //scrolls may have been moved out for reasons
- size.x -= h_scroll->get_minimum_size().x;
+ size.x -= v_scroll->get_minimum_size().x;
for (int i = 0; i < get_child_count(); i++) {
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index f766c0722d..ace22dddff 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -276,7 +276,7 @@ void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("_line_edit_input"), &SpinBox::_line_edit_input);
ClassDB::bind_method(D_METHOD("_range_click_timeout"), &SpinBox::_range_click_timeout);
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix");
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index c38c411333..3554f04cc0 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -62,39 +62,28 @@ void SplitContainer::_resort() {
// If we have only one element
if (!first || !second) {
if (first) {
- fit_child_in_rect(_getch(0), Rect2(Point2(), get_size()));
+ fit_child_in_rect(first, Rect2(Point2(), get_size()));
} else if (second) {
- fit_child_in_rect(_getch(1), Rect2(Point2(), get_size()));
+ fit_child_in_rect(second, Rect2(Point2(), get_size()));
}
return;
}
// Determine expanded children
- bool first_expanded = false;
- bool second_expanded = false;
- if (vertical) {
- first_expanded = first->get_v_size_flags() & SIZE_EXPAND;
- second_expanded = second->get_v_size_flags() & SIZE_EXPAND;
- } else {
- first_expanded = first->get_h_size_flags() & SIZE_EXPAND;
- second_expanded = second->get_h_size_flags() & SIZE_EXPAND;
- }
+ bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
+ bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
// Determine the separation between items
Ref<Texture> g = get_icon("grabber");
int sep = get_constant("separation");
- if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) {
- sep = 0;
- } else {
- sep = MAX(sep, vertical ? g->get_height() : g->get_width());
- }
+ sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0;
// Compute the minimum size
Size2 ms_first = first->get_combined_minimum_size();
Size2 ms_second = second->get_combined_minimum_size();
+ // Compute the separator position without the split offset
float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio());
-
int no_offset_middle_sep = 0;
if (first_expanded && second_expanded) {
no_offset_middle_sep = get_size()[axis] * ratio - sep / 2;
@@ -104,12 +93,13 @@ void SplitContainer::_resort() {
no_offset_middle_sep = ms_first[axis];
}
- middle_sep = no_offset_middle_sep;
- middle_sep += (collapsed) ? 0 : split_offset;
- middle_sep = MIN(middle_sep, get_size()[axis] - ms_second[axis] - sep);
- middle_sep = MAX(middle_sep, ms_first[axis]);
- if (!collapsed) {
- split_offset = middle_sep - no_offset_middle_sep;
+ // Compute the final middle separation
+ int clamped_split_offset = CLAMP(split_offset, ms_first[axis] - no_offset_middle_sep, (get_size()[axis] - ms_second[axis] - sep) - no_offset_middle_sep);
+ middle_sep = no_offset_middle_sep + clamped_split_offset;
+ if (!collapsed && should_clamp_split_offset) {
+ split_offset = clamped_split_offset;
+ _change_notify("split_offset");
+ should_clamp_split_offset = false;
}
if (vertical) {
@@ -123,7 +113,6 @@ void SplitContainer::_resort() {
}
update();
- _change_notify("split_offset");
}
Size2 SplitContainer::get_minimum_size() const {
@@ -131,8 +120,8 @@ Size2 SplitContainer::get_minimum_size() const {
/* Calculate MINIMUM SIZE */
Size2i minimum;
- int sep = get_constant("separation");
Ref<Texture> g = get_icon("grabber");
+ int sep = get_constant("separation");
sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0;
for (int i = 0; i < 2; i++) {
@@ -248,6 +237,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid() && dragging) {
split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from);
+ should_clamp_split_offset = true;
queue_sort();
emit_signal("dragged", get_split_offset());
}
@@ -282,6 +272,7 @@ void SplitContainer::set_split_offset(int p_offset) {
return;
split_offset = p_offset;
+
queue_sort();
}
@@ -290,6 +281,12 @@ int SplitContainer::get_split_offset() const {
return split_offset;
}
+void SplitContainer::clamp_split_offset() {
+ should_clamp_split_offset = true;
+
+ queue_sort();
+}
+
void SplitContainer::set_collapsed(bool p_collapsed) {
if (collapsed == p_collapsed)
@@ -319,8 +316,10 @@ bool SplitContainer::is_collapsed() const {
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &SplitContainer::_gui_input);
+
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
+ ClassDB::bind_method(D_METHOD("clamp_split_offset"), &SplitContainer::clamp_split_offset);
ClassDB::bind_method(D_METHOD("set_collapsed", "collapsed"), &SplitContainer::set_collapsed);
ClassDB::bind_method(D_METHOD("is_collapsed"), &SplitContainer::is_collapsed);
@@ -343,6 +342,7 @@ SplitContainer::SplitContainer(bool p_vertical) {
mouse_inside = false;
split_offset = 0;
+ should_clamp_split_offset = false;
middle_sep = 0;
vertical = p_vertical;
dragging = false;
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 321f7fd3b7..f8b3343aa8 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -45,9 +45,10 @@ public:
};
private:
- bool vertical;
+ bool should_clamp_split_offset;
int split_offset;
int middle_sep;
+ bool vertical;
bool dragging;
int drag_from;
int drag_ofs;
@@ -67,6 +68,7 @@ protected:
public:
void set_split_offset(int p_offset);
int get_split_offset() const;
+ void clamp_split_offset();
void set_collapsed(bool p_collapsed);
bool is_collapsed() const;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index c56b7d0f26..cf3113ca8c 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -106,41 +106,8 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- // test hovering to display right or close button
- int hover_now = -1;
- int hover_buttons = -1;
- for (int i = 0; i < tabs.size(); i++) {
-
- if (i < offset)
- continue;
-
- Rect2 rect = get_tab_rect(i);
- if (rect.has_point(pos)) {
- hover_now = i;
- }
- if (tabs[i].rb_rect.has_point(pos)) {
- rb_hover = i;
- cb_hover = -1;
- hover_buttons = i;
- break;
- } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
- cb_hover = i;
- rb_hover = -1;
- hover_buttons = i;
- break;
- }
- }
- if (hover != hover_now) {
- hover = hover_now;
- emit_signal("tab_hover", hover);
- }
-
- if (hover_buttons == -1) { // no hover
- rb_hover = hover_buttons;
- cb_hover = hover_buttons;
- }
+ _update_hover();
update();
-
return;
}
@@ -522,6 +489,48 @@ Ref<Texture> Tabs::get_tab_right_button(int p_tab) const {
return tabs[p_tab].right_button;
}
+void Tabs::_update_hover() {
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ const Point2 &pos = get_local_mouse_position();
+ // test hovering to display right or close button
+ int hover_now = -1;
+ int hover_buttons = -1;
+ for (int i = 0; i < tabs.size(); i++) {
+
+ if (i < offset)
+ continue;
+
+ Rect2 rect = get_tab_rect(i);
+ if (rect.has_point(pos)) {
+ hover_now = i;
+ }
+ if (tabs[i].rb_rect.has_point(pos)) {
+ rb_hover = i;
+ cb_hover = -1;
+ hover_buttons = i;
+ break;
+ } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
+ cb_hover = i;
+ rb_hover = -1;
+ hover_buttons = i;
+ break;
+ }
+ }
+ if (hover != hover_now) {
+ hover = hover_now;
+ emit_signal("tab_hover", hover);
+ }
+
+ if (hover_buttons == -1) { // no hover
+ rb_hover = hover_buttons;
+ cb_hover = hover_buttons;
+ }
+}
+
void Tabs::_update_cache() {
Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
@@ -597,6 +606,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) {
tabs.push_back(t);
_update_cache();
+ call_deferred("_update_hover");
update();
minimum_size_changed();
}
@@ -604,6 +614,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) {
void Tabs::clear_tabs() {
tabs.clear();
current = 0;
+ call_deferred("_update_hover");
update();
}
@@ -614,6 +625,7 @@ void Tabs::remove_tab(int p_idx) {
if (current >= p_idx)
current--;
_update_cache();
+ call_deferred("_update_hover");
update();
minimum_size_changed();
@@ -931,6 +943,7 @@ bool Tabs::get_select_with_rmb() const {
void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
+ ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover);
ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
@@ -970,7 +983,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index e204f4364b..a98744b804 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -97,6 +97,8 @@ private:
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
+
+ void _update_hover();
void _update_cache();
protected:
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 32580a5ae6..7bfcd0843c 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1429,9 +1429,6 @@ void TextEdit::_notification(int p_what) {
if (OS::get_singleton()->has_virtual_keyboard())
OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect());
- if (raised_from_completion) {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
- }
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -1443,9 +1440,6 @@ void TextEdit::_notification(int p_what) {
if (OS::get_singleton()->has_virtual_keyboard())
OS::get_singleton()->hide_virtual_keyboard();
- if (raised_from_completion) {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
- }
} break;
}
}
@@ -2331,9 +2325,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// no need to indent if we are going upwards.
if (auto_indent && !(k->get_command() && k->get_shift())) {
- // indent once again if previous line will end with ':' or '{'
+ // indent once again if previous line will end with ':' or '{' and the line is not a comment
// (i.e. colon/brace precedes current cursor position)
- if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{')) {
+ if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) {
if (indent_using_spaces) {
ins += space_indent;
} else {
@@ -5666,16 +5660,12 @@ void TextEdit::_confirm_completion() {
void TextEdit::_cancel_code_hint() {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
- raised_from_completion = false;
completion_hint = "";
update();
}
void TextEdit::_cancel_completion() {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0);
- raised_from_completion = false;
if (!completion_active)
return;
@@ -5833,8 +5823,6 @@ void TextEdit::query_code_comple() {
void TextEdit::set_code_hint(const String &p_hint) {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
- raised_from_completion = true;
completion_hint = p_hint;
completion_hint_offset = -0xFFFF;
update();
@@ -5842,8 +5830,6 @@ void TextEdit::set_code_hint(const String &p_hint) {
void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) {
- VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
- raised_from_completion = true;
completion_strings = p_strings;
completion_active = true;
completion_forced = p_forced;
@@ -6214,7 +6200,7 @@ void TextEdit::_bind_methods() {
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret");
ADD_SIGNAL(MethodInfo("cursor_changed"));
@@ -6232,6 +6218,7 @@ void TextEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers
}
TextEdit::TextEdit() {
@@ -6345,8 +6332,6 @@ TextEdit::TextEdit() {
target_v_scroll = 0;
v_scroll_speed = 80;
- raised_from_completion = false;
-
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index f0c18ad047..8a508a8738 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -306,8 +306,6 @@ private:
float target_v_scroll;
float v_scroll_speed;
- bool raised_from_completion;
-
String highlighted_word;
uint64_t last_dblclk;
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 413f9dbbe6..4a8d87902b 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -128,6 +128,7 @@ void TextureButton::_notification(int p_what) {
if (normal.is_valid())
texdraw = normal;
} break;
+ case DRAW_HOVER_PRESSED:
case DRAW_PRESSED: {
if (pressed.is_null()) {
@@ -150,7 +151,6 @@ void TextureButton::_notification(int p_what) {
} else
texdraw = hover;
} break;
- case DRAW_HOVER_PRESSED: break; // Not used in this class
case DRAW_DISABLED: {
if (disabled.is_null()) {
@@ -247,14 +247,14 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode);
ADD_GROUP("Textures", "texture_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_texture", "get_normal_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_pressed_texture", "get_pressed_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_hover", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_hover_texture", "get_hover_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_disabled_texture", "get_disabled_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_focused_texture", "get_focused_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
- ADD_PROPERTYNZ(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::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_texture", "get_normal_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_pressed_texture", "get_pressed_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_hover", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_hover_texture", "get_hover_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_disabled_texture", "get_disabled_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_focused_texture", "get_focused_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
+ 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");
BIND_ENUM_CONSTANT(STRETCH_SCALE);
BIND_ENUM_CONSTANT(STRETCH_TILE);
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 75f88dc4e6..a37659e521 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -486,21 +486,21 @@ void TextureProgress::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_under_texture", "get_under_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
ADD_GROUP("Tint", "tint_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress");
ADD_GROUP("Radial Fill", "radial_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset"), "set_radial_center_offset", "get_radial_center_offset");
ADD_GROUP("Stretch", "stretch_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_BOTTOM);
BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT);
BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT);
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index f4285525f6..4371baf08c 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -109,9 +109,9 @@ void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stretch_mode", "stretch_mode"), &TextureRect::set_stretch_mode);
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
BIND_ENUM_CONSTANT(STRETCH_SCALE_ON_EXPAND);
BIND_ENUM_CONSTANT(STRETCH_SCALE);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index effa44968d..f441364c44 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -901,6 +901,7 @@ void Tree::update_cache() {
cache.item_margin = get_constant("item_margin");
cache.button_margin = get_constant("button_margin");
cache.guide_width = get_constant("guide_width");
+ cache.draw_guides = get_constant("draw_guides");
cache.draw_relationship_lines = get_constant("draw_relationship_lines");
cache.relationship_line_color = get_color("relationship_line_color");
cache.scroll_border = get_constant("scroll_border");
@@ -1132,7 +1133,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
cell_rect.size.x += cache.hseparation;
}
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(cell_rect.position.x, cell_rect.position.y + cell_rect.size.height), cell_rect.position + cell_rect.size, cache.guide_color, 1);
+ if (cache.draw_guides) {
+ VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(cell_rect.position.x, cell_rect.position.y + cell_rect.size.height), cell_rect.position + cell_rect.size, cache.guide_color, 1);
+ }
if (i == 0) {
@@ -1416,7 +1419,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
while (c) {
- if (cache.draw_relationship_lines == 1 && (c->get_parent() != root || !hide_root)) {
+ if (cache.draw_relationship_lines == 1 && (!hide_root || c->parent != root)) {
int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs;
@@ -2519,7 +2522,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
pressing_for_editor = false;
}
- if (cache.click_type == Cache::CLICK_BUTTON) {
+ if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item != NULL) {
// make sure in case of wrong reference after reconstructing whole TreeItems
cache.click_item = get_item_at_position(cache.click_pos);
emit_signal("button_pressed", cache.click_item, cache.click_column, cache.click_id);
@@ -2934,7 +2937,7 @@ void Tree::_notification(int p_what) {
if (show_column_titles) {
- //title butons
+ //title buttons
int ofs = cache.bg->get_margin(MARGIN_LEFT);
for (int i = 0; i < columns.size(); i++) {
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 34138acb85..886ce66e2c 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -434,6 +434,7 @@ private:
int button_margin;
Point2 offset;
int draw_relationship_lines;
+ int draw_guides;
int scroll_border;
int scroll_speed;
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 39e7c73390..812ca0042b 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -90,53 +90,32 @@ void VideoPlayer::_mix_audio() {
AudioFrame vol = AudioFrame(volume, volume);
- // Copy to server's audio buffer
- switch (AudioServer::get_singleton()->get_speaker_mode()) {
+ int cc = AudioServer::get_singleton()->get_channel_count();
- case AudioServer::SPEAKER_MODE_STEREO: {
- AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
+ if (cc == 1) {
+ AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0);
+ ERR_FAIL_COND(!target);
- for (int j = 0; j < buffer_size; j++) {
+ for (int j = 0; j < buffer_size; j++) {
- target[j] += buffer[j] * vol;
- }
-
- } break;
- case AudioServer::SPEAKER_SURROUND_31: {
-
- // FIXME: Implement
- } break;
- case AudioServer::SPEAKER_SURROUND_51: {
-
- AudioFrame *targets[2] = {
- AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1),
- AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2),
- };
-
- for (int j = 0; j < buffer_size; j++) {
+ target[j] += buffer[j] * vol;
+ }
- AudioFrame frame = buffer[j] * vol;
- targets[0][j] = frame;
- targets[1][j] = frame;
- }
- } break;
- case AudioServer::SPEAKER_SURROUND_71: {
+ } else {
+ AudioFrame *targets[4];
- AudioFrame *targets[3] = {
- AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1),
- AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2),
- AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 3)
- };
+ for (int k = 0; k < cc; k++) {
+ targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, k);
+ ERR_FAIL_COND(!targets[k]);
+ }
- for (int j = 0; j < buffer_size; j++) {
+ for (int j = 0; j < buffer_size; j++) {
- AudioFrame frame = buffer[j] * vol;
- targets[0][j] += frame;
- targets[1][j] += frame;
- targets[2][j] += frame;
+ AudioFrame frame = buffer[j] * vol;
+ for (int k = 0; k < cc; k++) {
+ targets[k][j] += frame;
}
-
- } break;
+ }
}
}
diff --git a/scene/gui/viewport_container.cpp b/scene/gui/viewport_container.cpp
index ac5e6020eb..8e0d1cdd38 100644
--- a/scene/gui/viewport_container.cpp
+++ b/scene/gui/viewport_container.cpp
@@ -121,6 +121,8 @@ void ViewportContainer::_notification(int p_what) {
c->set_update_mode(Viewport::UPDATE_ALWAYS);
else
c->set_update_mode(Viewport::UPDATE_DISABLED);
+
+ c->set_handle_input_locally(false); //do not handle input locally here
}
}
@@ -165,8 +167,34 @@ void ViewportContainer::_input(const Ref<InputEvent> &p_event) {
}
}
+void ViewportContainer::_unhandled_input(const Ref<InputEvent> &p_event) {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ return;
+
+ Transform2D xform = get_global_transform();
+
+ if (stretch) {
+ Transform2D scale_xf;
+ scale_xf.scale(Vector2(shrink, shrink));
+ xform *= scale_xf;
+ }
+
+ Ref<InputEvent> ev = p_event->xformed_by(xform.affine_inverse());
+
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Viewport *c = Object::cast_to<Viewport>(get_child(i));
+ if (!c || c->is_input_disabled())
+ continue;
+
+ c->unhandled_input(ev);
+ }
+}
+
void ViewportContainer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_unhandled_input", "event"), &ViewportContainer::_unhandled_input);
ClassDB::bind_method(D_METHOD("_input", "event"), &ViewportContainer::_input);
ClassDB::bind_method(D_METHOD("set_stretch", "enable"), &ViewportContainer::set_stretch);
ClassDB::bind_method(D_METHOD("is_stretch_enabled"), &ViewportContainer::is_stretch_enabled);
diff --git a/scene/gui/viewport_container.h b/scene/gui/viewport_container.h
index 45c4cd03a1..60aec25959 100644
--- a/scene/gui/viewport_container.h
+++ b/scene/gui/viewport_container.h
@@ -49,6 +49,7 @@ public:
bool is_stretch_enabled() const;
void _input(const Ref<InputEvent> &p_event);
+ void _unhandled_input(const Ref<InputEvent> &p_event);
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index a2e890e7a7..89bc8c1226 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -35,7 +35,7 @@ void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
}
int CanvasLayer::get_layer() const {
@@ -146,19 +146,28 @@ void CanvasLayer::_notification(int p_what) {
vp = Node::get_viewport();
}
ERR_FAIL_COND(!vp);
+
+ vp->_canvas_layer_add(this);
viewport = vp->get_viewport_rid();
VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
} break;
case NOTIFICATION_EXIT_TREE: {
+ vp->_canvas_layer_remove(this);
VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
} break;
+ case NOTIFICATION_MOVED_IN_PARENT: {
+
+ if (is_inside_tree())
+ VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
+
+ } break;
}
}
@@ -179,6 +188,7 @@ RID CanvasLayer::get_viewport() const {
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
ERR_FAIL_NULL(p_viewport);
if (is_inside_tree()) {
+ vp->_canvas_layer_remove(this);
VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
}
@@ -198,10 +208,11 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) {
else
vp = Node::get_viewport();
+ vp->_canvas_layer_add(this);
viewport = vp->get_viewport_rid();
VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8fd7dc1d7b..ea50e7289d 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -157,7 +157,7 @@ void Node::_notification(int p_notification) {
// kill children as cleanly as possible
while (data.children.size()) {
- Node *child = data.children[0];
+ Node *child = data.children[data.children.size() - 1]; //begin from the end because its faster and more consistent with creation
remove_child(child);
memdelete(child);
}
@@ -1008,6 +1008,32 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
}
}
+// Return s + 1 as if it were an integer
+String increase_numeric_string(const String &s) {
+
+ String res = s;
+ bool carry = res.length() > 0;
+
+ for (int i = res.length() - 1; i >= 0; i--) {
+ if (!carry) {
+ break;
+ }
+ CharType n = s[i];
+ if (n == '9') { // keep carry as true: 9 + 1
+ res[i] = '0';
+ } else {
+ res[i] = s[i] + 1;
+ carry = false;
+ }
+ }
+
+ if (carry) {
+ res = "1" + res;
+ }
+
+ return res;
+}
+
String Node::_generate_serial_child_name(Node *p_child) {
String name = p_child->data.name;
@@ -1040,42 +1066,38 @@ String Node::_generate_serial_child_name(Node *p_child) {
}
String nnsep = _get_name_num_separator();
- int num = 0;
- bool explicit_zero = false;
- if (nums.length() > 0 && name.substr(name.length() - nnsep.length() - nums.length(), nnsep.length()) == nnsep) {
- // Base name + Separator + Number
- num = nums.to_int();
- name = name.substr(0, name.length() - nnsep.length() - nums.length()); // Keep base name
- if (num == 0) {
- explicit_zero = true;
+ int name_last_index = name.length() - nnsep.length() - nums.length();
+
+ // Assign the base name + separator to name if we have numbers preceded by a separator
+ if (nums.length() > 0 && name.substr(name_last_index, nnsep.length()) == nnsep) {
+ name = name.substr(0, name_last_index + nnsep.length()).strip_edges();
+ } else {
+ nums = "";
+ }
+
+ Vector<String> children_names;
+
+ for (int i = 0; i < data.children.size(); i++) {
+ String child_name = data.children[i]->data.name;
+ if (data.children[i] == p_child)
+ continue;
+ if (child_name.begins_with(name)) {
+ children_names.push_back(child_name);
}
}
- int num_places = nums.length();
for (;;) {
- String attempt = (name + (num > 0 || explicit_zero ? nnsep + itos(num).pad_zeros(num_places) : "")).strip_edges();
- bool found = false;
- for (int i = 0; i < data.children.size(); i++) {
- if (data.children[i] == p_child)
- continue;
- if (data.children[i]->data.name == attempt) {
- found = true;
- break;
- }
- }
- if (!found) {
+ String attempt = name + nums;
+
+ if (children_names.find(attempt) == -1) {
return attempt;
} else {
- if (num == 0) {
- if (explicit_zero) {
- // Name ended in separator + 0; user expects to get to separator + 1
- num = 1;
- } else {
- // Name was undecorated so skip to 2 for a more natural result
- num = 2;
- }
+ if (nums.length() == 0) {
+ // Name was undecorated so skip to 2 for a more natural result
+ nums = "2";
+ name += nnsep; // Add separator because nums.length() > 0 was false
} else {
- num++;
+ nums = increase_numeric_string(nums);
}
}
}
@@ -1182,13 +1204,24 @@ void Node::remove_child(Node *p_child) {
ERR_FAIL_COND(data.blocked > 0);
}
+ int child_count = data.children.size();
+ Node **children = data.children.ptrw();
int idx = -1;
- for (int i = 0; i < data.children.size(); i++) {
- if (data.children[i] == p_child) {
+ if (p_child->data.pos >= 0 && p_child->data.pos < child_count) {
+ if (children[p_child->data.pos] == p_child) {
+ idx = p_child->data.pos;
+ }
+ }
- idx = i;
- break;
+ if (idx == -1) { //maybe removed while unparenting or something and index was not updated, so just in case the above fails, try this.
+ for (int i = 0; i < child_count; i++) {
+
+ if (children[i] == p_child) {
+
+ idx = i;
+ break;
+ }
}
}
@@ -1205,9 +1238,14 @@ void Node::remove_child(Node *p_child) {
data.children.remove(idx);
- for (int i = idx; i < data.children.size(); i++) {
+ //update pointer and size
+ child_count = data.children.size();
+ children = data.children.ptrw();
- data.children[i]->data.pos = i;
+ for (int i = idx; i < child_count; i++) {
+
+ children[i]->data.pos = i;
+ children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
}
p_child->data.parent = NULL;
@@ -1835,7 +1873,7 @@ void Node::set_editable_instance(Node *p_node, bool p_editable) {
}
}
-bool Node::is_editable_instance(Node *p_node) const {
+bool Node::is_editable_instance(const Node *p_node) const {
if (!p_node)
return false; //easier, null is never editable :)
@@ -2192,7 +2230,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
if (p_copy->has_node(ptarget))
copytarget = p_copy->get_node(ptarget);
- if (copy && copytarget) {
+ if (copy && copytarget && !copy->is_connected(E->get().signal, copytarget, E->get().method)) {
copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, E->get().flags);
}
}
@@ -2724,7 +2762,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
{
MethodInfo mi;
@@ -2782,18 +2820,18 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
- //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ;
- //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/physics_process" ), "set_physics_process","is_physics_processing") ;
- //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/input" ), "set_process_input","is_processing_input" ) ;
- //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ;
+ //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ;
+ //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/physics_process" ), "set_physics_process","is_physics_processing") ;
+ //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/input" ), "set_process_input","is_processing_input" ) ;
+ //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ;
ADD_GROUP("Pause", "pause_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));
diff --git a/scene/main/node.h b/scene/main/node.h
index a7baebc9c2..78db12dda9 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -303,7 +303,7 @@ public:
String get_filename() const;
void set_editable_instance(Node *p_node, bool p_editable);
- bool is_editable_instance(Node *p_node) const;
+ bool is_editable_instance(const Node *p_node) const;
void set_editable_instances(const HashMap<NodePath, int> &p_editable_instances);
HashMap<NodePath, int> get_editable_instances() const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index fdbe3b57f0..3f664bab10 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -535,10 +535,15 @@ bool SceneTree::idle(float p_time) {
//go through timers
+ List<Ref<SceneTreeTimer> >::Element *L = timers.back(); //last element
+
for (List<Ref<SceneTreeTimer> >::Element *E = timers.front(); E;) {
List<Ref<SceneTreeTimer> >::Element *N = E->next();
if (pause && !E->get()->is_pause_mode_process()) {
+ if (E == L) {
+ break; //break on last, so if new timers were added during list traversal, ignore them.
+ }
E = N;
continue;
}
@@ -550,6 +555,9 @@ bool SceneTree::idle(float p_time) {
E->get()->emit_signal("timeout");
timers.erase(E);
}
+ if (E == L) {
+ break; //break on last, so if new timers were added during list traversal, ignore them.
+ }
E = N;
}
@@ -1944,6 +1952,7 @@ SceneTree::SceneTree() {
debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4));
debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4));
collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000);
+ ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative
tree_version = 1;
physics_process_time = 1;
@@ -1964,6 +1973,7 @@ SceneTree::SceneTree() {
root = memnew(Viewport);
root->set_name("root");
+ root->set_handle_input_locally(false);
if (!root->get_world().is_valid())
root->set_world(Ref<World>(memnew(World)));
@@ -1977,7 +1987,9 @@ SceneTree::SceneTree() {
current_scene = NULL;
int ref_atlas_size = GLOBAL_DEF("rendering/quality/reflections/atlas_size", 2048);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_size", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_size", PROPERTY_HINT_RANGE, "0,8192,or_greater")); //next_power_of_2 will return a 0 as min value
int ref_atlas_subdiv = GLOBAL_DEF("rendering/quality/reflections/atlas_subdiv", 8);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_subdiv", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_subdiv", PROPERTY_HINT_RANGE, "0,32,or_greater")); //next_power_of_2 will return a 0 as min value
int msaa_mode = GLOBAL_DEF("rendering/quality/filters/msaa", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/filters/msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"));
root->set_msaa(Viewport::MSAA(msaa_mode));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index bb379ff4af..8545efb966 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -45,6 +45,7 @@
#include "scene/gui/panel.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/popup_menu.h"
+#include "scene/main/canvas_layer.h"
#include "scene/main/timer.h"
#include "scene/resources/mesh.h"
#include "scene/scene_string_names.h"
@@ -231,6 +232,25 @@ void Viewport::update_worlds() {
find_world()->_update(get_tree()->get_frame());
}
+void Viewport::_collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape, bool p_discard_empty_motion) {
+
+ Transform object_transform = p_object->get_global_transform();
+ Transform camera_transform = p_camera->get_global_transform();
+ ObjectID id = p_object->get_instance_id();
+
+ if (p_discard_empty_motion) {
+ //avoid sending the event unnecesarily if nothing really changed in the context
+ Ref<InputEventMouseMotion> mm = p_input_event;
+ if (mm.is_valid() && object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
+ return; //discarded
+ }
+ }
+ p_object->_input_event(camera, p_input_event, p_pos, p_normal, p_shape);
+ physics_last_object_transform = object_transform;
+ physics_last_camera_transform = camera_transform;
+ physics_last_id = id;
+}
+
void Viewport::_test_new_mouseover(ObjectID new_collider) {
#ifndef _3D_DISABLED
if (new_collider != physics_object_over) {
@@ -402,6 +422,34 @@ void Viewport::_notification(int p_what) {
PhysicsDirectSpaceState::RayResult result;
Physics2DDirectSpaceState *ss2d = Physics2DServer::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
+ bool discard_empty_motion = false;
+
+ { // if no motion event exists, create a new one. This is necessary because objects or camera may have moved.
+ // while this extra event is sent, it is checked if both camera and last object and last ID did not move. If nothing changed, the event is discarded to avoid flooding with unnecesary motion events every frame
+ bool has_mouse_motion = false;
+ for (List<Ref<InputEvent> >::Element *E = physics_picking_events.front(); E; E = E->next()) {
+ Ref<InputEventMouseMotion> mm = E->get();
+ if (mm.is_valid()) {
+ has_mouse_motion = true;
+ break;
+ }
+ }
+
+ if (!has_mouse_motion) {
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+ mm->set_global_position(physics_last_mousepos);
+ mm->set_position(physics_last_mousepos);
+ mm->set_alt(physics_last_mouse_state.alt);
+ mm->set_shift(physics_last_mouse_state.shift);
+ mm->set_control(physics_last_mouse_state.control);
+ mm->set_metakey(physics_last_mouse_state.meta);
+ mm->set_button_mask(physics_last_mouse_state.mouse_mask);
+ physics_picking_events.push_back(mm);
+ discard_empty_motion = true;
+ }
+ }
+
bool motion_tested = false;
while (physics_picking_events.size()) {
@@ -418,12 +466,37 @@ void Viewport::_notification(int p_what) {
pos = mm->get_position();
motion_tested = true;
physics_last_mousepos = pos;
+ physics_last_mouse_state.alt = mm->get_alt();
+ physics_last_mouse_state.shift = mm->get_shift();
+ physics_last_mouse_state.control = mm->get_control();
+ physics_last_mouse_state.meta = mm->get_metakey();
+ physics_last_mouse_state.mouse_mask = mm->get_button_mask();
}
Ref<InputEventMouseButton> mb = ev;
if (mb.is_valid()) {
pos = mb->get_position();
+ physics_last_mouse_state.alt = mb->get_alt();
+ physics_last_mouse_state.shift = mb->get_shift();
+ physics_last_mouse_state.control = mb->get_control();
+ physics_last_mouse_state.meta = mb->get_metakey();
+
+ if (mb->is_pressed()) {
+ physics_last_mouse_state.mouse_mask |= (1 << (mb->get_button_index() - 1));
+ } else {
+ physics_last_mouse_state.mouse_mask &= ~(1 << (mb->get_button_index() - 1));
+ }
+ }
+
+ Ref<InputEventKey> k = ev;
+ if (k.is_valid()) {
+ //only for mask
+ physics_last_mouse_state.alt = k->get_alt();
+ physics_last_mouse_state.shift = k->get_shift();
+ physics_last_mouse_state.control = k->get_control();
+ physics_last_mouse_state.meta = k->get_metakey();
+ continue;
}
Ref<InputEventScreenDrag> sd = ev;
@@ -443,24 +516,39 @@ void Viewport::_notification(int p_what) {
uint64_t frame = get_tree()->get_frame();
- Vector2 point = get_canvas_transform().affine_inverse().xform(pos);
Physics2DDirectSpaceState::ShapeResult res[64];
- int rc = ss2d->intersect_point(point, res, 64, Set<RID>(), 0xFFFFFFFF, true, true, true);
- for (int i = 0; i < rc; i++) {
-
- if (res[i].collider_id && res[i].collider) {
- CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
- if (co) {
-
- Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.find(res[i].collider_id);
- if (!E) {
- E = physics_2d_mouseover.insert(res[i].collider_id, frame);
- co->_mouse_enter();
- } else {
- E->get() = frame;
- }
+ for (Set<CanvasLayer *>::Element *E = canvas_layers.front(); E; E = E->next()) {
+ Transform2D canvas_transform;
+ ObjectID canvas_layer_id;
+ if (E->get()) {
+ // A descendant CanvasLayer
+ canvas_transform = E->get()->get_transform();
+ canvas_layer_id = E->get()->get_instance_id();
+ } else {
+ // This Viewport's builtin canvas
+ canvas_transform = get_canvas_transform();
+ canvas_layer_id = 0;
+ }
+
+ Vector2 point = canvas_transform.affine_inverse().xform(pos);
+
+ int rc = ss2d->intersect_point_on_canvas(point, canvas_layer_id, res, 64, Set<RID>(), 0xFFFFFFFF, true, true, true);
+ for (int i = 0; i < rc; i++) {
+
+ if (res[i].collider_id && res[i].collider) {
+ CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
+ if (co) {
+
+ Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.find(res[i].collider_id);
+ if (!E) {
+ E = physics_2d_mouseover.insert(res[i].collider_id, frame);
+ co->_mouse_enter();
+ } else {
+ E->get() = frame;
+ }
- co->_input_event(this, ev, res[i].shape);
+ co->_input_event(this, ev, res[i].shape);
+ }
}
}
}
@@ -494,7 +582,7 @@ void Viewport::_notification(int p_what) {
CollisionObject *co = Object::cast_to<CollisionObject>(ObjectDB::get_instance(physics_object_capture));
if (co) {
- co->_input_event(camera, ev, Vector3(), Vector3(), 0);
+ _collision_object_input_event(co, camera, ev, Vector3(), Vector3(), 0, discard_empty_motion);
captured = true;
if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
physics_object_capture = 0;
@@ -512,7 +600,7 @@ void Viewport::_notification(int p_what) {
if (last_id) {
if (ObjectDB::get_instance(last_id) && last_object) {
//good, exists
- last_object->_input_event(camera, ev, result.position, result.normal, result.shape);
+ _collision_object_input_event(last_object, camera, ev, result.position, result.normal, result.shape, discard_empty_motion);
if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
physics_object_capture = last_id;
}
@@ -535,7 +623,7 @@ void Viewport::_notification(int p_what) {
CollisionObject *co = Object::cast_to<CollisionObject>(result.collider);
if (co) {
- co->_input_event(camera, ev, result.position, result.normal, result.shape);
+ _collision_object_input_event(co, camera, ev, result.position, result.normal, result.shape, discard_empty_motion);
last_object = co;
last_id = result.collider_id;
new_collider = last_id;
@@ -629,10 +717,8 @@ Rect2 Viewport::get_visible_rect() const {
Rect2 r;
if (size == Size2()) {
-
- r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height));
+ r = Rect2(Point2(), OS::get_singleton()->get_window_size());
} else {
-
r = Rect2(Point2(), size);
}
@@ -856,6 +942,16 @@ void Viewport::_camera_make_next_current(Camera *p_exclude) {
}
#endif
+void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) {
+
+ canvas_layers.insert(p_canvas_layer);
+}
+
+void Viewport::_canvas_layer_remove(CanvasLayer *p_canvas_layer) {
+
+ canvas_layers.erase(p_canvas_layer);
+}
+
void Viewport::set_transparent_background(bool p_enable) {
transparent_bg = p_enable;
@@ -1414,12 +1510,17 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
Control *control = Object::cast_to<Control>(ci);
if (control) {
- control->emit_signal(SceneStringNames::get_singleton()->gui_input, ev); //signal should be first, so it's possible to override an event (and then accept it)
+ if (control->data.mouse_filter != Control::MOUSE_FILTER_IGNORE) {
+ control->emit_signal(SceneStringNames::get_singleton()->gui_input, ev); //signal should be first, so it's possible to override an event (and then accept it)
+ }
if (gui.key_event_accepted)
break;
if (!control->is_inside_tree())
break;
- control->call_multilevel(SceneStringNames::get_singleton()->_gui_input, ev);
+
+ if (control->data.mouse_filter != Control::MOUSE_FILTER_IGNORE) {
+ control->call_multilevel(SceneStringNames::get_singleton()->_gui_input, ev);
+ }
if (!control->is_inside_tree() || control->is_set_as_toplevel())
break;
@@ -1603,7 +1704,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (top->data.modal_exclusive || top->data.modal_frame == Engine::get_singleton()->get_frames_drawn()) {
//cancel event, sorry, modal exclusive EATS UP ALL
//alternative, you can't pop out a window the same frame it was made modal (fixes many issues)
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return; // no one gets the event if exclusive NO ONE
}
@@ -1621,7 +1722,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (is_handled) {
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
@@ -1691,7 +1792,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(gui.mouse_focus, mb);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) {
@@ -1765,7 +1866,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_data=Variant(); //always clear
}*/
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
}
}
@@ -1799,7 +1900,13 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = NULL;
+ break;
} else {
+ if (gui.drag_preview != NULL) {
+ ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
+ memdelete(gui.drag_preview);
+ gui.drag_preview = NULL;
+ }
gui.dragging = false;
}
@@ -1841,8 +1948,16 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
MenuButton *popup_menu_parent = NULL;
MenuButton *menu_button = Object::cast_to<MenuButton>(over);
- if (popup_menu)
+ if (popup_menu) {
popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent());
+ if (!popup_menu_parent) {
+ // Go through the parents to see if there's a MenuButton at the end.
+ while (Object::cast_to<PopupMenu>(popup_menu->get_parent())) {
+ popup_menu = Object::cast_to<PopupMenu>(popup_menu->get_parent());
+ }
+ popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent());
+ }
+ }
// If the mouse is over a menu button, this menu will open automatically
// if there is already a pop-up menu open at the same hierarchical level.
@@ -1960,7 +2075,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(over, mm);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
if (gui.drag_data.get_type() != Variant::NIL && mm->get_button_mask() & BUTTON_MASK_LEFT) {
@@ -2003,7 +2118,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
touch_event->set_position(pos);
_gui_call_input(over, touch_event);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
} else if (gui.mouse_focus) {
@@ -2015,7 +2130,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(gui.mouse_focus, touch_event);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
}
@@ -2043,7 +2158,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gesture_event->set_position(pos);
_gui_call_input(over, gesture_event);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
}
@@ -2081,7 +2196,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(over, drag_event);
}
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
}
@@ -2103,7 +2218,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.key_event_accepted) {
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
}
@@ -2118,7 +2233,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
top->_modal_stack_remove();
top->hide();
// Close modal, set input as handled
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
return;
}
}
@@ -2167,7 +2282,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (next) {
next->grab_focus();
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
}
}
}
@@ -2371,7 +2486,7 @@ void Viewport::_gui_accept_event() {
gui.key_event_accepted = true;
if (is_inside_tree())
- get_tree()->set_input_as_handled();
+ set_input_as_handled();
}
List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
@@ -2448,11 +2563,13 @@ void Viewport::input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(!is_inside_tree());
- if (!get_tree()->is_input_handled()) {
+ local_input_handled = false;
+
+ if (!is_input_handled()) {
get_tree()->_call_input_pause(input_group, "_input", p_event); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input
}
- if (!get_tree()->is_input_handled()) {
+ if (!is_input_handled()) {
_gui_input_event(p_event);
}
//get_tree()->call_group(SceneTree::GROUP_CALL_REVERSE|SceneTree::GROUP_CALL_REALTIME|SceneTree::GROUP_CALL_MULIILEVEL,gui_input_group,"_gui_input",p_event); //special one for GUI, as controls use their own process check
@@ -2475,7 +2592,10 @@ void Viewport::unhandled_input(const Ref<InputEvent> &p_event) {
(Object::cast_to<InputEventMouseButton>(*p_event) ||
Object::cast_to<InputEventMouseMotion>(*p_event) ||
Object::cast_to<InputEventScreenDrag>(*p_event) ||
- Object::cast_to<InputEventScreenTouch>(*p_event))) {
+ Object::cast_to<InputEventScreenTouch>(*p_event) ||
+ Object::cast_to<InputEventKey>(*p_event) //to remember state
+
+ )) {
physics_picking_events.push_back(p_event);
}
}
@@ -2683,6 +2803,33 @@ bool Viewport::is_snap_controls_to_pixels_enabled() const {
bool Viewport::gui_is_dragging() const {
return gui.dragging;
}
+
+void Viewport::set_input_as_handled() {
+ if (handle_input_locally) {
+ local_input_handled = true;
+ } else {
+ ERR_FAIL_COND(!is_inside_tree());
+ get_tree()->set_input_as_handled();
+ }
+}
+
+bool Viewport::is_input_handled() const {
+ if (handle_input_locally) {
+ return local_input_handled;
+ } else {
+ ERR_FAIL_COND_V(!is_inside_tree(), false);
+ return get_tree()->is_input_handled();
+ }
+}
+
+void Viewport::set_handle_input_locally(bool p_enable) {
+ handle_input_locally = p_enable;
+}
+
+bool Viewport::is_handling_input_locally() const {
+ return handle_input_locally;
+}
+
void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &Viewport::set_use_arvr);
@@ -2795,6 +2942,12 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv);
+ ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled);
+ ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled);
+
+ ClassDB::bind_method(D_METHOD("set_handle_input_locally", "enable"), &Viewport::set_handle_input_locally);
+ ClassDB::bind_method(D_METHOD("is_handling_input_locally"), &Viewport::is_handling_input_locally);
+
ClassDB::bind_method(D_METHOD("_subwindow_visibility_changed"), &Viewport::_subwindow_visibility_changed);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "use_arvr");
@@ -2804,6 +2957,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world", PROPERTY_HINT_RESOURCE_TYPE, "World"), "set_world", "get_world");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr");
@@ -2905,6 +3059,7 @@ Viewport::Viewport() {
parent = NULL;
listener = NULL;
camera = NULL;
+ canvas_layers.insert(NULL); // This eases picking code (interpreted as the canvas of the Viewport)
arvr = false;
size_override = false;
size_override_stretch = false;
@@ -2945,6 +3100,7 @@ Viewport::Viewport() {
//gui.tooltip_timer->force_parent_owned();
gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers
gui.tooltip = NULL;
gui.tooltip_label = NULL;
@@ -2961,6 +3117,14 @@ Viewport::Viewport() {
clear_mode = CLEAR_MODE_ALWAYS;
snap_controls_to_pixels = true;
+ physics_last_mouse_state.alt = false;
+ physics_last_mouse_state.control = false;
+ physics_last_mouse_state.shift = false;
+ physics_last_mouse_state.meta = false;
+ physics_last_mouse_state.mouse_mask = 0;
+ local_input_handled = false;
+ handle_input_locally = true;
+ physics_last_id = 0; //ensures first time there will be a check
}
Viewport::~Viewport() {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index c1a4c0e3eb..44fb322ae2 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -45,10 +45,12 @@ class Camera2D;
class Listener;
class Control;
class CanvasItem;
+class CanvasLayer;
class Panel;
class Label;
class Timer;
class Viewport;
+class CollisionObject;
class ViewportTexture : public Texture {
@@ -163,6 +165,7 @@ private:
Camera *camera;
Set<Camera *> cameras;
+ Set<CanvasLayer *> canvas_layers;
RID viewport;
RID current_canvas;
@@ -203,7 +206,25 @@ private:
List<Ref<InputEvent> > physics_picking_events;
ObjectID physics_object_capture;
ObjectID physics_object_over;
+ Transform physics_last_object_transform;
+ Transform physics_last_camera_transform;
+ ObjectID physics_last_id;
Vector2 physics_last_mousepos;
+ struct {
+
+ bool alt;
+ bool control;
+ bool shift;
+ bool meta;
+ int mouse_mask;
+
+ } physics_last_mouse_state;
+
+ void _collision_object_input_event(CollisionObject *p_object, Camera *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape, bool p_discard_empty_motion);
+
+ bool handle_input_locally;
+ bool local_input_handled;
+
void _test_new_mouseover(ObjectID new_collider);
Map<ObjectID, uint64_t> physics_2d_mouseover;
@@ -354,6 +375,10 @@ private:
void _camera_remove(Camera *p_camera);
void _camera_make_next_current(Camera *p_exclude);
+ friend class CanvasLayer;
+ void _canvas_layer_add(CanvasLayer *p_canvas_layer);
+ void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -475,6 +500,12 @@ public:
void _subwindow_visibility_changed();
+ void set_input_as_handled();
+ bool is_input_handled() const;
+
+ void set_handle_input_locally(bool p_enable);
+ bool is_handling_input_locally() const;
+
bool gui_is_dragging() const;
Viewport();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 5c01cadcd5..803c85ef34 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1469,7 +1469,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
middle = (low + high) / 2;
- if (p_time == keys[middle].time) { //match
+ if (Math::abs(p_time - keys[middle].time) < CMP_EPSILON) { //match
return middle;
} else if (p_time < keys[middle].time)
high = middle - 1; //search low end of array
diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp
index 9d47bca5ed..39488760cd 100644
--- a/scene/resources/convex_polygon_shape.cpp
+++ b/scene/resources/convex_polygon_shape.cpp
@@ -38,10 +38,9 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() {
if (points.size() > 3) {
- QuickHull qh;
Vector<Vector3> varr = Variant(points);
Geometry::MeshData md;
- Error err = qh.build(varr, md);
+ Error err = QuickHull::build(varr, md);
if (err == OK) {
Vector<Vector3> lines;
lines.resize(md.edges.size() * 2);
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index f325af7ea4..fc3d3f3334 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -41,7 +41,11 @@ bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, dou
void ConvexPolygonShape2D::_update_shape() {
- Physics2DServer::get_singleton()->shape_set_data(get_rid(), points);
+ Vector<Vector2> final_points = points;
+ if (Geometry::is_polygon_clockwise(final_points)) { //needs to be counter clockwise
+ final_points.invert();
+ }
+ Physics2DServer::get_singleton()->shape_set_data(get_rid(), final_points);
emit_changed();
}
@@ -55,6 +59,7 @@ void ConvexPolygonShape2D::set_point_cloud(const Vector<Vector2> &p_points) {
void ConvexPolygonShape2D::set_points(const Vector<Vector2> &p_points) {
points = p_points;
+
_update_shape();
}
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 4de47b2cb0..fff136cdc3 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -653,6 +653,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("item_margin", "Tree", 12 * scale);
theme->set_constant("button_margin", "Tree", 4 * scale);
theme->set_constant("draw_relationship_lines", "Tree", 0);
+ theme->set_constant("draw_guides", "Tree", 1);
theme->set_constant("scroll_border", "Tree", 4);
theme->set_constant("scroll_speed", "Tree", 12);
diff --git a/scene/resources/default_theme/error_icon.png b/scene/resources/default_theme/error_icon.png
index 7741d00749..00680db5df 100644
--- a/scene/resources/default_theme/error_icon.png
+++ b/scene/resources/default_theme/error_icon.png
Binary files differ
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index 6790c35c4b..ad22d6530c 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -82,11 +82,14 @@ void DynamicFontData::set_force_autohinter(bool p_force) {
}
void DynamicFontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &DynamicFontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &DynamicFontData::is_antialiased);
ClassDB::bind_method(D_METHOD("set_font_path", "path"), &DynamicFontData::set_font_path);
ClassDB::bind_method(D_METHOD("get_font_path"), &DynamicFontData::get_font_path);
ClassDB::bind_method(D_METHOD("set_hinting", "mode"), &DynamicFontData::set_hinting);
ClassDB::bind_method(D_METHOD("get_hinting"), &DynamicFontData::get_hinting);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
BIND_ENUM_CONSTANT(HINTING_NONE);
@@ -98,6 +101,7 @@ void DynamicFontData::_bind_methods() {
DynamicFontData::DynamicFontData() {
+ antialiased = true;
force_autohinter = false;
hinting = DynamicFontData::HINTING_NORMAL;
font_mem = NULL;
@@ -201,10 +205,10 @@ Error DynamicFontAtSize::_load() {
if (FT_HAS_COLOR(face)) {
int best_match = 0;
- int diff = ABS(id.size - face->available_sizes[0].width);
+ int diff = ABS(id.size - ((int64_t)face->available_sizes[0].width));
scale_color_font = float(id.size) / face->available_sizes[0].width;
for (int i = 1; i < face->num_fixed_sizes; i++) {
- int ndiff = ABS(id.size - face->available_sizes[i].width);
+ int ndiff = ABS(id.size - ((int64_t)face->available_sizes[i].width));
if (ndiff < diff) {
best_match = i;
diff = ndiff;
@@ -632,7 +636,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
if (id.outline_size > 0) {
character = _make_outline_char(p_char);
} else {
- error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
+ error = FT_Render_Glyph(face->glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
if (!error)
character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
}
@@ -785,6 +789,18 @@ void DynamicFont::set_use_filter(bool p_enable) {
_reload_cache();
}
+bool DynamicFontData::is_antialiased() const {
+
+ return antialiased;
+}
+
+void DynamicFontData::set_antialiased(bool p_antialiased) {
+
+ if (antialiased == p_antialiased)
+ return;
+ antialiased = p_antialiased;
+}
+
DynamicFontData::Hinting DynamicFontData::get_hinting() const {
return hinting;
@@ -1014,10 +1030,10 @@ void DynamicFont::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "get_use_mipmaps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_filter"), "set_use_filter", "get_use_filter");
ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "extra_spacing_char"), "set_spacing", "get_spacing", SPACING_CHAR);
- ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_char"), "set_spacing", "get_spacing", SPACING_CHAR);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
ADD_GROUP("Font", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font_data", PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"), "set_font_data", "get_font_data");
@@ -1029,7 +1045,7 @@ void DynamicFont::_bind_methods() {
Mutex *DynamicFont::dynamic_font_mutex = NULL;
-SelfList<DynamicFont>::List DynamicFont::dynamic_fonts;
+SelfList<DynamicFont>::List *DynamicFont::dynamic_fonts = NULL;
DynamicFont::DynamicFont() :
font_list(this) {
@@ -1041,29 +1057,31 @@ DynamicFont::DynamicFont() :
spacing_char = 0;
spacing_space = 0;
outline_color = Color(1, 1, 1);
- if (dynamic_font_mutex)
+ if (dynamic_font_mutex) {
dynamic_font_mutex->lock();
- dynamic_fonts.add(&font_list);
- if (dynamic_font_mutex)
+ dynamic_fonts->add(&font_list);
dynamic_font_mutex->unlock();
+ }
}
DynamicFont::~DynamicFont() {
-
- if (dynamic_font_mutex)
+ if (dynamic_font_mutex) {
dynamic_font_mutex->lock();
- dynamic_fonts.remove(&font_list);
- if (dynamic_font_mutex)
+ dynamic_fonts->remove(&font_list);
dynamic_font_mutex->unlock();
+ }
}
void DynamicFont::initialize_dynamic_fonts() {
+ dynamic_fonts = memnew(SelfList<DynamicFont>::List());
dynamic_font_mutex = Mutex::create();
}
void DynamicFont::finish_dynamic_fonts() {
memdelete(dynamic_font_mutex);
dynamic_font_mutex = NULL;
+ memdelete(dynamic_fonts);
+ dynamic_fonts = NULL;
}
void DynamicFont::update_oversampling() {
@@ -1073,7 +1091,7 @@ void DynamicFont::update_oversampling() {
if (dynamic_font_mutex)
dynamic_font_mutex->lock();
- SelfList<DynamicFont> *E = dynamic_fonts.first();
+ SelfList<DynamicFont> *E = dynamic_fonts->first();
while (E) {
if (E->self()->data_at_size.is_valid()) {
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
index afda48a566..96437e8982 100644
--- a/scene/resources/dynamic_font.h
+++ b/scene/resources/dynamic_font.h
@@ -71,12 +71,15 @@ public:
HINTING_NORMAL
};
+ bool is_antialiased() const;
+ void set_antialiased(bool p_antialiased);
Hinting get_hinting() const;
void set_hinting(Hinting p_hinting);
private:
const uint8_t *font_mem;
int font_mem_size;
+ bool antialiased;
bool force_autohinter;
Hinting hinting;
@@ -285,7 +288,7 @@ public:
SelfList<DynamicFont> font_list;
static Mutex *dynamic_font_mutex;
- static SelfList<DynamicFont>::List dynamic_fonts;
+ static SelfList<DynamicFont>::List *dynamic_fonts;
static void initialize_dynamic_fonts();
static void finish_dynamic_fonts();
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 7b43c33692..c9cdfe866f 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -761,7 +761,7 @@ float Environment::get_fog_sun_amount() const {
void Environment::set_fog_depth_enabled(bool p_enabled) {
fog_depth_enabled = p_enabled;
- VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
}
bool Environment::is_fog_depth_enabled() const {
@@ -771,17 +771,28 @@ bool Environment::is_fog_depth_enabled() const {
void Environment::set_fog_depth_begin(float p_distance) {
fog_depth_begin = p_distance;
- VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
}
float Environment::get_fog_depth_begin() const {
return fog_depth_begin;
}
+void Environment::set_fog_depth_end(float p_distance) {
+
+ fog_depth_end = p_distance;
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+}
+
+float Environment::get_fog_depth_end() const {
+
+ return fog_depth_end;
+}
+
void Environment::set_fog_depth_curve(float p_curve) {
fog_depth_curve = p_curve;
- VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
}
float Environment::get_fog_depth_curve() const {
@@ -791,7 +802,7 @@ float Environment::get_fog_depth_curve() const {
void Environment::set_fog_transmit_enabled(bool p_enabled) {
fog_transmit_enabled = p_enabled;
- VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
}
bool Environment::is_fog_transmit_enabled() const {
@@ -801,7 +812,7 @@ bool Environment::is_fog_transmit_enabled() const {
void Environment::set_fog_transmit_curve(float p_curve) {
fog_transmit_curve = p_curve;
- VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
+ VS::get_singleton()->environment_set_fog_depth(environment, fog_depth_enabled, fog_depth_begin, fog_depth_end, fog_depth_curve, fog_transmit_enabled, fog_transmit_curve);
}
float Environment::get_fog_transmit_curve() const {
@@ -900,6 +911,9 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fog_depth_begin", "distance"), &Environment::set_fog_depth_begin);
ClassDB::bind_method(D_METHOD("get_fog_depth_begin"), &Environment::get_fog_depth_begin);
+ ClassDB::bind_method(D_METHOD("set_fog_depth_end", "distance"), &Environment::set_fog_depth_end);
+ ClassDB::bind_method(D_METHOD("get_fog_depth_end"), &Environment::get_fog_depth_end);
+
ClassDB::bind_method(D_METHOD("set_fog_depth_curve", "curve"), &Environment::set_fog_depth_curve);
ClassDB::bind_method(D_METHOD("get_fog_depth_curve"), &Environment::get_fog_depth_curve);
@@ -928,6 +942,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_sun_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_fog_sun_amount", "get_fog_sun_amount");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_depth_enabled"), "set_fog_depth_enabled", "is_fog_depth_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_depth_begin", PROPERTY_HINT_RANGE, "0,4000,0.1"), "set_fog_depth_begin", "get_fog_depth_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_depth_end", PROPERTY_HINT_RANGE, "0,4000,0.1,or_greater"), "set_fog_depth_end", "get_fog_depth_end");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_depth_curve", PROPERTY_HINT_EXP_EASING), "set_fog_depth_curve", "get_fog_depth_curve");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_transmit_enabled"), "set_fog_transmit_enabled", "is_fog_transmit_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_transmit_curve", PROPERTY_HINT_EXP_EASING), "set_fog_transmit_curve", "get_fog_transmit_curve");
@@ -1269,6 +1284,7 @@ Environment::Environment() {
fog_depth_enabled = true;
fog_depth_begin = 10;
+ fog_depth_end = 0;
fog_depth_curve = 1;
fog_transmit_enabled = false;
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index aab37719e0..4f5d44088a 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -162,6 +162,7 @@ private:
bool fog_depth_enabled;
float fog_depth_begin;
+ float fog_depth_end;
float fog_depth_curve;
bool fog_transmit_enabled;
@@ -365,6 +366,9 @@ public:
void set_fog_depth_begin(float p_distance);
float get_fog_depth_begin() const;
+ void set_fog_depth_end(float p_distance);
+ float get_fog_depth_end() const;
+
void set_fog_depth_curve(float p_curve);
float get_fog_depth_curve() const;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 274c74a9a2..5327ed318f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -299,6 +299,7 @@ void SpatialMaterial::init_shaders() {
shader_names->particles_anim_loop = "particles_anim_loop";
shader_names->depth_min_layers = "depth_min_layers";
shader_names->depth_max_layers = "depth_max_layers";
+ shader_names->depth_flip = "depth_flip";
shader_names->grow = "grow";
@@ -532,6 +533,7 @@ void SpatialMaterial::_update_shader() {
code += "uniform float depth_scale;\n";
code += "uniform int depth_min_layers;\n";
code += "uniform int depth_max_layers;\n";
+ code += "uniform vec2 depth_flip;\n";
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
code += "varying vec3 uv1_triplanar_pos;\n";
@@ -612,9 +614,9 @@ void SpatialMaterial::_update_shader() {
//handle animation
code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
- code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n";
+ code += "\tif (!particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n";
code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n";
- code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames),particle_frame / float(particles_anim_h_frames) / float(particles_anim_v_frames));\n";
+ code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames), floor(particle_frame / float(particles_anim_h_frames)) / float(particles_anim_v_frames));\n";
} break;
}
@@ -697,7 +699,7 @@ void SpatialMaterial::_update_shader() {
if (features[FEATURE_DEPTH_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //depthmap not supported with triplanar
code += "\t{\n";
- code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT,-BINORMAL,NORMAL));\n"; //binormal is negative due to mikktpsace
+ code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*depth_flip.x,BINORMAL*depth_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace
if (deep_parallax) {
code += "\t\tfloat num_layers = mix(float(depth_max_layers),float(depth_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n";
@@ -1541,13 +1543,13 @@ int SpatialMaterial::get_particles_anim_v_frames() const {
return particles_anim_v_frames;
}
-void SpatialMaterial::set_particles_anim_loop(int p_frames) {
+void SpatialMaterial::set_particles_anim_loop(bool p_loop) {
- particles_anim_loop = p_frames;
- VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, p_frames);
+ particles_anim_loop = p_loop;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
}
-int SpatialMaterial::get_particles_anim_loop() const {
+bool SpatialMaterial::get_particles_anim_loop() const {
return particles_anim_loop;
}
@@ -1584,6 +1586,28 @@ int SpatialMaterial::get_depth_deep_parallax_max_layers() const {
return deep_parallax_max_layers;
}
+void SpatialMaterial::set_depth_deep_parallax_flip_tangent(bool p_flip) {
+
+ depth_parallax_flip_tangent = p_flip;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->depth_flip, Vector2(depth_parallax_flip_tangent ? -1 : 1, depth_parallax_flip_binormal ? -1 : 1));
+}
+
+bool SpatialMaterial::get_depth_deep_parallax_flip_tangent() const {
+
+ return depth_parallax_flip_tangent;
+}
+
+void SpatialMaterial::set_depth_deep_parallax_flip_binormal(bool p_flip) {
+
+ depth_parallax_flip_binormal = p_flip;
+ VS::get_singleton()->material_set_param(_get_material(), shader_names->depth_flip, Vector2(depth_parallax_flip_tangent ? -1 : 1, depth_parallax_flip_binormal ? -1 : 1));
+}
+
+bool SpatialMaterial::get_depth_deep_parallax_flip_binormal() const {
+
+ return depth_parallax_flip_binormal;
+}
+
void SpatialMaterial::set_grow_enabled(bool p_enable) {
grow_enabled = p_enable;
_queue_shader_change();
@@ -1898,7 +1922,7 @@ void SpatialMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &SpatialMaterial::set_particles_anim_v_frames);
ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &SpatialMaterial::get_particles_anim_v_frames);
- ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "frames"), &SpatialMaterial::set_particles_anim_loop);
+ ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &SpatialMaterial::set_particles_anim_loop);
ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &SpatialMaterial::get_particles_anim_loop);
ClassDB::bind_method(D_METHOD("set_depth_deep_parallax", "enable"), &SpatialMaterial::set_depth_deep_parallax);
@@ -1910,6 +1934,12 @@ void SpatialMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_depth_deep_parallax_max_layers", "layer"), &SpatialMaterial::set_depth_deep_parallax_max_layers);
ClassDB::bind_method(D_METHOD("get_depth_deep_parallax_max_layers"), &SpatialMaterial::get_depth_deep_parallax_max_layers);
+ ClassDB::bind_method(D_METHOD("set_depth_deep_parallax_flip_tangent", "flip"), &SpatialMaterial::set_depth_deep_parallax_flip_tangent);
+ ClassDB::bind_method(D_METHOD("get_depth_deep_parallax_flip_tangent"), &SpatialMaterial::get_depth_deep_parallax_flip_tangent);
+
+ ClassDB::bind_method(D_METHOD("set_depth_deep_parallax_flip_binormal", "flip"), &SpatialMaterial::set_depth_deep_parallax_flip_binormal);
+ ClassDB::bind_method(D_METHOD("get_depth_deep_parallax_flip_binormal"), &SpatialMaterial::get_depth_deep_parallax_flip_binormal);
+
ClassDB::bind_method(D_METHOD("set_grow", "amount"), &SpatialMaterial::set_grow);
ClassDB::bind_method(D_METHOD("get_grow"), &SpatialMaterial::get_grow);
@@ -2045,6 +2075,8 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "depth_deep_parallax"), "set_depth_deep_parallax", "is_depth_deep_parallax_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_min_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_depth_deep_parallax_min_layers", "get_depth_deep_parallax_min_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_max_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_depth_deep_parallax_max_layers", "get_depth_deep_parallax_max_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "depth_flip_tangent"), "set_depth_deep_parallax_flip_tangent", "get_depth_deep_parallax_flip_tangent");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "depth_flip_binormal"), "set_depth_deep_parallax_flip_binormal", "get_depth_deep_parallax_flip_binormal");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "depth_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_DEPTH);
ADD_GROUP("Subsurf Scatter", "subsurf_scatter_");
@@ -2244,8 +2276,11 @@ SpatialMaterial::SpatialMaterial() :
set_grow(0.0);
deep_parallax = false;
+ depth_parallax_flip_tangent = false;
+ depth_parallax_flip_binormal = false;
set_depth_deep_parallax_min_layers(8);
set_depth_deep_parallax_max_layers(32);
+ set_depth_deep_parallax_flip_tangent(false); //also sets binormal
detail_uv = DETAIL_UV_1;
blend_mode = BLEND_MODE_MIX;
diff --git a/scene/resources/material.h b/scene/resources/material.h
index cf4d19b5a7..54fceaddc1 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -338,6 +338,7 @@ private:
StringName particles_anim_loop;
StringName depth_min_layers;
StringName depth_max_layers;
+ StringName depth_flip;
StringName uv1_blend_sharpness;
StringName uv2_blend_sharpness;
StringName grow;
@@ -407,6 +408,8 @@ private:
bool deep_parallax;
int deep_parallax_min_layers;
int deep_parallax_max_layers;
+ bool depth_parallax_flip_tangent;
+ bool depth_parallax_flip_binormal;
bool proximity_fade_enabled;
float proximity_fade_distance;
@@ -501,6 +504,12 @@ public:
void set_depth_deep_parallax_max_layers(int p_layer);
int get_depth_deep_parallax_max_layers() const;
+ void set_depth_deep_parallax_flip_tangent(bool p_flip);
+ bool get_depth_deep_parallax_flip_tangent() const;
+
+ void set_depth_deep_parallax_flip_binormal(bool p_flip);
+ bool get_depth_deep_parallax_flip_binormal() const;
+
void set_subsurface_scattering_strength(float p_subsurface_scattering_strength);
float get_subsurface_scattering_strength() const;
@@ -574,8 +583,8 @@ public:
void set_particles_anim_v_frames(int p_frames);
int get_particles_anim_v_frames() const;
- void set_particles_anim_loop(int p_frames);
- int get_particles_anim_loop() const;
+ void set_particles_anim_loop(bool p_loop);
+ bool get_particles_anim_loop() const;
void set_grow_enabled(bool p_enable);
bool is_grow_enabled() const;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 6cd701eb9a..80191367ce 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -482,7 +482,7 @@ void Mesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &Mesh::set_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint);
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count);
ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays);
@@ -706,6 +706,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
Vector<AABB> skel_aabb = VS::get_singleton()->mesh_surface_get_skeleton_aabb(mesh, idx);
Array arr;
+ arr.resize(skel_aabb.size());
for (int i = 0; i < skel_aabb.size(); i++) {
arr[i] = skel_aabb[i];
}
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index a5449e1fe8..7af9086ab7 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -42,8 +42,6 @@ void MeshDataTool::clear() {
Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surface) {
ERR_FAIL_COND_V(p_mesh.is_null(), ERR_INVALID_PARAMETER);
-
- ERR_FAIL_COND_V(p_mesh.is_null(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_mesh->surface_get_primitive_type(p_surface) != Mesh::PRIMITIVE_TRIANGLES, ERR_INVALID_PARAMETER);
Array arrays = p_mesh->surface_get_arrays(p_surface);
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 086fb83af9..87483a7da4 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -264,7 +264,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) {
- //if node was not part of instance, must set it's name, parenthood and ownership
+ //if node was not part of instance, must set its name, parenthood and ownership
if (i > 0) {
if (parent) {
parent->_add_child_nocheck(node, snames[n.name]);
@@ -472,6 +472,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
+ StringName type = p_node->get_class();
for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
@@ -482,12 +483,23 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
String name = E->get().name;
Variant value = p_node->get(E->get().name);
- bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one());
+ bool isdefault = false;
+ Variant default_value = ClassDB::class_get_default_property_value(type, name);
- if (E->get().usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
- isdefault = true; //is script default value
+ if (default_value.get_type() != Variant::NIL) {
+ isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
}
+ Ref<Script> script = p_node->get_script();
+ if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) {
+ isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
+ }
+ // the version above makes more sense, because it does not rely on placeholder or usage flag
+ // in the script, just the default value function.
+ // if (E->get().usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
+ // isdefault = true; //is script default value
+ // }
+
if (pack_state_stack.size()) {
// we are on part of an instanced subscene
// or part of instanced scene.
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index ba48982fda..dae01e8d96 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -463,12 +463,6 @@ void ParticlesMaterial::_update_shader() {
code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
- if (flags[FLAG_ANIM_LOOP]) {
- code += " CUSTOM.z = mod(CUSTOM.z, 1.0);\n"; // loop
-
- } else {
- code += " CUSTOM.z = clamp(CUSTOM.z, 0.0, 1.0);\n"; // 0 to 1 only
- }
code += " }\n";
// apply color
// apply hue rotation
@@ -803,12 +797,7 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
} break;
case PARAM_SCALE: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
-
- Ref<CurveTexture> curve_tex = p_texture;
- if (curve_tex.is_valid()) {
- curve_tex->ensure_default_setup();
- }
-
+ _adjust_curve_range(p_texture, 0, 1);
} break;
case PARAM_HUE_VARIATION: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture);
@@ -1129,7 +1118,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY);
ADD_GROUP("Orbit Velocity", "orbit_");
@@ -1165,7 +1154,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
@@ -1175,7 +1164,6 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", 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_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP);
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 91fdcc0346..06ebb3c4dc 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -60,7 +60,6 @@ public:
FLAG_ALIGN_Y_TO_VELOCITY,
FLAG_ROTATE_Y,
FLAG_DISABLE_Z,
- FLAG_ANIM_LOOP,
FLAG_MAX
};
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 63aa44e1d8..dafdddd990 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -102,6 +102,9 @@ void PrimitiveMesh::_request_update() {
}
int PrimitiveMesh::get_surface_count() const {
+ if (pending_request) {
+ _update();
+ }
return 1;
}
@@ -303,7 +306,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius * w, y * radius * w, z);
points.push_back(p + Vector3(0.0, 0.0, 0.5 * mid_height));
normals.push_back(p.normalized());
- ADD_TANGENT(y, -x, 0.0, -1.0)
+ ADD_TANGENT(-y, x, 0.0, -1.0)
uvs.push_back(Vector2(u, v * onethird));
point++;
@@ -342,7 +345,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius, y * radius, z);
points.push_back(p);
normals.push_back(Vector3(x, y, 0.0));
- ADD_TANGENT(y, -x, 0.0, -1.0)
+ ADD_TANGENT(-y, x, 0.0, -1.0)
uvs.push_back(Vector2(u, onethird + (v * onethird)));
point++;
@@ -382,7 +385,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius * w, y * radius * w, z);
points.push_back(p + Vector3(0.0, 0.0, -0.5 * mid_height));
normals.push_back(p.normalized());
- ADD_TANGENT(y, -x, 0.0, -1.0)
+ ADD_TANGENT(-y, x, 0.0, -1.0)
uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird)));
point++;
@@ -511,14 +514,14 @@ void CubeMesh::_create_mesh_array(Array &p_arr) const {
// front
points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z!
normals.push_back(Vector3(0.0, 0.0, 1.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(u, v));
point++;
// back
points.push_back(Vector3(-x, -y, start_pos.z));
normals.push_back(Vector3(0.0, 0.0, -1.0));
- ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(twothirds + u, v));
point++;
@@ -565,14 +568,14 @@ void CubeMesh::_create_mesh_array(Array &p_arr) const {
// right
points.push_back(Vector3(-start_pos.x, -y, -z));
normals.push_back(Vector3(1.0, 0.0, 0.0));
- ADD_TANGENT(0.0, 0.0, 1.0, -1.0);
+ ADD_TANGENT(0.0, 0.0, -1.0, -1.0);
uvs.push_back(Vector2(onethird + u, v));
point++;
// left
points.push_back(Vector3(start_pos.x, -y, z));
normals.push_back(Vector3(-1.0, 0.0, 0.0));
- ADD_TANGENT(0.0, 0.0, -1.0, -1.0);
+ ADD_TANGENT(0.0, 0.0, 1.0, -1.0);
uvs.push_back(Vector2(u, 0.5 + v));
point++;
@@ -619,14 +622,14 @@ void CubeMesh::_create_mesh_array(Array &p_arr) const {
// top
points.push_back(Vector3(-x, -start_pos.y, -z));
normals.push_back(Vector3(0.0, 1.0, 0.0));
- ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(onethird + u, 0.5 + v));
point++;
// bottom
points.push_back(Vector3(x, start_pos.y, -z));
normals.push_back(Vector3(0.0, -1.0, 0.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(twothirds + u, 0.5 + v));
point++;
@@ -770,7 +773,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * radius, y, z * radius);
points.push_back(p);
normals.push_back(Vector3(x, 0.0, z));
- ADD_TANGENT(-z, 0.0, x, -1.0)
+ ADD_TANGENT(z, 0.0, -x, -1.0)
uvs.push_back(Vector2(u, v * 0.5));
point++;
@@ -796,7 +799,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
thisrow = point;
points.push_back(Vector3(0.0, y, 0.0));
normals.push_back(Vector3(0.0, 1.0, 0.0));
- ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0)
uvs.push_back(Vector2(0.25, 0.75));
point++;
@@ -813,7 +816,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * top_radius, y, z * top_radius);
points.push_back(p);
normals.push_back(Vector3(0.0, 1.0, 0.0));
- ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0)
uvs.push_back(Vector2(u, v));
point++;
@@ -832,7 +835,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
thisrow = point;
points.push_back(Vector3(0.0, y, 0.0));
normals.push_back(Vector3(0.0, -1.0, 0.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0)
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0)
uvs.push_back(Vector2(0.75, 0.75));
point++;
@@ -849,7 +852,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const {
Vector3 p = Vector3(x * bottom_radius, y, z * bottom_radius);
points.push_back(p);
normals.push_back(Vector3(0.0, -1.0, 0.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0)
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0)
uvs.push_back(Vector2(u, v));
point++;
@@ -980,7 +983,7 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const {
points.push_back(Vector3(-x, 0.0, -z));
normals.push_back(Vector3(0.0, 1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
- uvs.push_back(Vector2(u, v));
+ uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */
point++;
if (i > 0 && j > 0) {
@@ -1105,14 +1108,14 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
/* front */
points.push_back(Vector3(start_x + x, -y, -start_pos.z)); // double negative on the Z!
normals.push_back(Vector3(0.0, 0.0, 1.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(offset_front + u, v));
point++;
/* back */
points.push_back(Vector3(start_x + scaled_size_x - x, -y, start_pos.z));
normals.push_back(Vector3(0.0, 0.0, -1.0));
- ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(twothirds + offset_back + u, v));
point++;
@@ -1184,14 +1187,14 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
/* right */
points.push_back(Vector3(right, -y, -z));
normals.push_back(normal_right);
- ADD_TANGENT(0.0, 0.0, 1.0, -1.0);
+ ADD_TANGENT(0.0, 0.0, -1.0, -1.0);
uvs.push_back(Vector2(onethird + u, v));
point++;
/* left */
points.push_back(Vector3(left, -y, z));
normals.push_back(normal_left);
- ADD_TANGENT(0.0, 0.0, -1.0, -1.0);
+ ADD_TANGENT(0.0, 0.0, 1.0, -1.0);
uvs.push_back(Vector2(u, 0.5 + v));
point++;
@@ -1238,7 +1241,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
/* bottom */
points.push_back(Vector3(x, start_pos.y, -z));
normals.push_back(Vector3(0.0, -1.0, 0.0));
- ADD_TANGENT(-1.0, 0.0, 0.0, -1.0);
+ ADD_TANGENT(1.0, 0.0, 0.0, -1.0);
uvs.push_back(Vector2(twothirds + u, 0.5 + v));
point++;
@@ -1282,7 +1285,7 @@ void PrismMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
@@ -1379,7 +1382,7 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const {
tangents.set(i * 4 + 0, 1.0);
tangents.set(i * 4 + 1, 0.0);
tangents.set(i * 4 + 2, 0.0);
- tangents.set(i * 4 + 3, 1.0);
+ tangents.set(i * 4 + 3, -1.0);
static const Vector2 quad_uv[4] = {
Vector2(0, 1),
@@ -1465,7 +1468,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
points.push_back(p);
normals.push_back(p.normalized());
};
- ADD_TANGENT(-z, 0.0, x, -1.0)
+ ADD_TANGENT(z, 0.0, -x, -1.0)
uvs.push_back(Vector2(u, v));
point++;
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index 597ffe49ae..5ae843f2bd 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -626,14 +626,14 @@ Error ResourceInteractiveLoaderText::poll() {
if (!packed_scene.is_valid())
return error;
- error = OK;
+ error = ERR_FILE_EOF;
//get it here
resource = packed_scene;
if (!ResourceCache::has(res_path)) {
packed_scene->set_path(res_path);
}
- return ERR_FILE_EOF;
+ return error;
} else {
error_text += "Unknown tag in file: " + next_tag.name;
@@ -1600,9 +1600,11 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
String name = PE->get().name;
Variant value = res->get(name);
+ Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name);
- if ((PE->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero()) || (PE->get().usage & PROPERTY_USAGE_STORE_IF_NONONE && value.is_one()))
+ if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
continue;
+ }
if (PE->get().type == Variant::OBJECT && value.is_zero() && !(PE->get().usage & PROPERTY_USAGE_STORE_IF_NULL))
continue;
diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp
index 869f4a3a9b..214e2e8edc 100644
--- a/scene/resources/shape.cpp
+++ b/scene/resources/shape.cpp
@@ -101,7 +101,7 @@ void Shape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin);
ClassDB::bind_method(D_METHOD("get_margin"), &Shape::get_margin);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0.04,10,0.001"), "set_margin", "get_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin");
}
Shape::Shape() :
diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp
index a6a52c7bba..347bca4400 100644
--- a/scene/resources/sky_box.cpp
+++ b/scene/resources/sky_box.cpp
@@ -190,9 +190,15 @@ Ref<Image> ProceduralSky::_generate_sky() {
float c = (v_angle - (Math_PI * 0.5)) / (Math_PI * 0.5);
color = ground_horizon_linear.linear_interpolate(ground_bottom_linear, Math::ease(c, ground_curve));
+ color.r *= ground_energy;
+ color.g *= ground_energy;
+ color.b *= ground_energy;
} else {
float c = v_angle / (Math_PI * 0.5);
color = sky_horizon_linear.linear_interpolate(sky_top_linear, Math::ease(1.0 - c, sky_curve));
+ color.r *= sky_energy;
+ color.g *= sky_energy;
+ color.b *= sky_energy;
float sun_angle = Math::rad2deg(Math::acos(CLAMP(sun.dot(normal), -1.0, 1.0)));
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index 69d85eeef3..087990c308 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -310,7 +310,7 @@ void StyleBoxTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
- ADD_PROPERTYNZ(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
ADD_GROUP("Margin", "margin_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_RIGHT);
@@ -322,8 +322,8 @@ void StyleBoxTexture::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_BOTTOM);
ADD_GROUP("Axis Stretch", "axis_stretch_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
ADD_GROUP("Modulate", "modulate_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
@@ -565,8 +565,6 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
vert_offset = 0;
}
int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
- int rings = (border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0) ? 1 : 2;
- rings = 2;
int ring_corner_radius[4];
set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
@@ -592,7 +590,7 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
//calculate the vert array
for (int corner_index = 0; corner_index < 4; corner_index++) {
for (int detail = 0; detail <= adapted_corner_detail; detail++) {
- for (int inner_outer = (2 - rings); inner_outer < 2; inner_outer++) {
+ for (int inner_outer = 0; inner_outer < 2; inner_outer++) {
float radius;
Color color;
Point2 corner_point;
@@ -613,19 +611,17 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
}
- if (rings == 2) {
- int vert_count = (adapted_corner_detail + 1) * 4 * rings;
- //fill the indices and the colors for the border
- for (int i = 0; i < vert_count; i++) {
- //poly 1
- indices.push_back(vert_offset + ((i + 0) % vert_count));
- indices.push_back(vert_offset + ((i + 2) % vert_count));
- indices.push_back(vert_offset + ((i + 1) % vert_count));
- //poly 2
- indices.push_back(vert_offset + ((i + 1) % vert_count));
- indices.push_back(vert_offset + ((i + 2) % vert_count));
- indices.push_back(vert_offset + ((i + 3) % vert_count));
- }
+ int vert_count = (adapted_corner_detail + 1) * 4 * 2;
+ //fill the indices and the colors for the border
+ for (int i = 0; i < vert_count; i++) {
+ //poly 1
+ indices.push_back(vert_offset + ((i + 0) % vert_count));
+ indices.push_back(vert_offset + ((i + 2) % vert_count));
+ indices.push_back(vert_offset + ((i + 1) % vert_count));
+ //poly 2
+ indices.push_back(vert_offset + ((i + 1) % vert_count));
+ indices.push_back(vert_offset + ((i + 2) % vert_count));
+ indices.push_back(vert_offset + ((i + 3) % vert_count));
}
}
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 96b9cfa137..3d247ab7ad 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -106,7 +106,7 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualShaderNode::_get_default_input_values);
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
- ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values");
ADD_SIGNAL(MethodInfo("editor_refresh_request"));
}
diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp
index 84a87de2e2..d9b3579812 100644
--- a/servers/audio/audio_rb_resampler.cpp
+++ b/servers/audio/audio_rb_resampler.cpp
@@ -79,53 +79,27 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i
p_dest[i] = AudioFrame(v0, v1);
}
- // For now, channels higher than stereo are almost ignored
+ // This will probably never be used, but added anyway
if (C == 4) {
- // FIXME: v2 and v3 are not being used (thus were commented out to prevent
- // compilation warnings, but they should likely be uncommented *and* used).
- // See also C == 6 with similar issues.
float v0 = rb[(pos << 2) + 0];
float v1 = rb[(pos << 2) + 1];
- /*
- float v2 = rb[(pos << 2) + 2];
- float v3 = rb[(pos << 2) + 3];
- */
float v0n = rb[(pos_next << 2) + 0];
float v1n = rb[(pos_next << 2) + 1];
- /*
- float v2n = rb[(pos_next << 2) + 2];
- float v3n = rb[(pos_next << 2) + 3];
- */
-
v0 += (v0n - v0) * frac;
v1 += (v1n - v1) * frac;
- /*
- v2 += (v2n - v2) * frac;
- v3 += (v3n - v3) * frac;
- */
p_dest[i] = AudioFrame(v0, v1);
}
if (C == 6) {
- // FIXME: Lot of unused assignments here, but it seems like intermediate calculations
- // should be done as for C == 2 (C == 4 also has some unused assignments).
float v0 = rb[(pos * 6) + 0];
float v1 = rb[(pos * 6) + 1];
- /*
- float v2 = rb[(pos * 6) + 2];
- float v3 = rb[(pos * 6) + 3];
- float v4 = rb[(pos * 6) + 4];
- float v5 = rb[(pos * 6) + 5];
float v0n = rb[(pos_next * 6) + 0];
float v1n = rb[(pos_next * 6) + 1];
- float v2n = rb[(pos_next * 6) + 2];
- float v3n = rb[(pos_next * 6) + 3];
- float v4n = rb[(pos_next * 6) + 4];
- float v5n = rb[(pos_next * 6) + 5];
- */
+ v0 += (v0n - v0) * frac;
+ v1 += (v1n - v1) * frac;
p_dest[i] = AudioFrame(v0, v1);
}
}
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 7de0695e8c..02a0bed964 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -136,16 +136,20 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr
Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
+ int mix_rate = AudioDriver::get_singleton()->get_mix_rate();
+ int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);
+#ifdef DEBUG_ENABLED
+ unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
+#endif
- // p_frames is multiplied by two since an AudioFrame is stereo
- if ((p_frames + MICROPHONE_PLAYBACK_DELAY * 2) > input_size) {
+ if (playback_delay > input_size) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0.0f, 0.0f);
}
input_ofs = 0;
} else {
for (int i = 0; i < p_frames; i++) {
- if (input_size >= input_ofs) {
+ if (input_size > input_ofs) {
float l = (buf[input_ofs++] >> 16) / 32768.f;
if (input_ofs >= buf.size()) {
input_ofs = 0;
@@ -162,6 +166,12 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr
}
}
+#ifdef DEBUG_ENABLED
+ if (input_ofs > input_position && (input_ofs - input_position) < (p_frames * 2)) {
+ print_verbose(String(get_class_name()) + " buffer underrun: input_position=" + itos(input_position) + " input_ofs=" + itos(input_ofs) + " input_size=" + itos(input_size));
+ }
+#endif
+
AudioDriver::get_singleton()->unlock();
}
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 2740f86d55..f6ed45cc9c 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -122,8 +122,6 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackMicrophone, AudioStreamPlayback)
friend class AudioStreamMicrophone;
- static const int MICROPHONE_PLAYBACK_DELAY = 256;
-
bool active;
unsigned int input_ofs;
diff --git a/servers/audio/effects/audio_effect_filter.h b/servers/audio/effects/audio_effect_filter.h
index 11978882bc..e8f12e5efa 100644
--- a/servers/audio/effects/audio_effect_filter.h
+++ b/servers/audio/effects/audio_effect_filter.h
@@ -96,6 +96,11 @@ VARIANT_ENUM_CAST(AudioEffectFilter::FilterDB)
class AudioEffectLowPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectLowPassFilter, AudioEffectFilter)
+
+ void _validate_property(PropertyInfo &property) const {
+ if (property.name == "gain") property.usage = 0;
+ }
+
public:
AudioEffectLowPassFilter() :
AudioEffectFilter(AudioFilterSW::LOWPASS) {}
@@ -103,6 +108,10 @@ public:
class AudioEffectHighPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectHighPassFilter, AudioEffectFilter)
+ void _validate_property(PropertyInfo &property) const {
+ if (property.name == "gain") property.usage = 0;
+ }
+
public:
AudioEffectHighPassFilter() :
AudioEffectFilter(AudioFilterSW::HIGHPASS) {}
@@ -110,6 +119,10 @@ public:
class AudioEffectBandPassFilter : public AudioEffectFilter {
GDCLASS(AudioEffectBandPassFilter, AudioEffectFilter)
+ void _validate_property(PropertyInfo &property) const {
+ if (property.name == "gain") property.usage = 0;
+ }
+
public:
AudioEffectBandPassFilter() :
AudioEffectFilter(AudioFilterSW::BANDPASS) {}
diff --git a/servers/audio/effects/reverb.cpp b/servers/audio/effects/reverb.cpp
index ef23e4aaf3..b032c91da2 100644
--- a/servers/audio/effects/reverb.cpp
+++ b/servers/audio/effects/reverb.cpp
@@ -36,22 +36,22 @@
const float Reverb::comb_tunings[MAX_COMBS] = {
//freeverb comb tunings
- 0.025306122448979593,
- 0.026938775510204082,
- 0.028956916099773241,
- 0.03074829931972789,
- 0.032244897959183672,
- 0.03380952380952381,
- 0.035306122448979592,
- 0.036666666666666667
+ 0.025306122448979593f,
+ 0.026938775510204082f,
+ 0.028956916099773241f,
+ 0.03074829931972789f,
+ 0.032244897959183672f,
+ 0.03380952380952381f,
+ 0.035306122448979592f,
+ 0.036666666666666667f
};
const float Reverb::allpass_tunings[MAX_ALLPASS] = {
//freeverb allpass tunings
- 0.0051020408163265302,
- 0.007732426303854875,
- 0.01,
- 0.012607709750566893
+ 0.0051020408163265302f,
+ 0.007732426303854875f,
+ 0.01f,
+ 0.012607709750566893f
};
void Reverb::process(float *p_src, float *p_dst, int p_frames) {
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index d36057b465..6fd996c3d3 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -80,6 +80,14 @@ double AudioDriver::get_mix_time() const {
return total;
}
+void AudioDriver::input_buffer_init(int driver_buffer_frames) {
+
+ const int input_buffer_channels = 2;
+ input_buffer.resize(driver_buffer_frames * input_buffer_channels * 4);
+ input_position = 0;
+ input_size = 0;
+}
+
void AudioDriver::input_buffer_write(int32_t sample) {
input_buffer.write[input_position++] = sample;
@@ -452,6 +460,14 @@ void AudioServer::_mix_step() {
to_mix = buffer_size;
}
+bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const {
+ if (p_bus < 0 || p_bus >= buses.size())
+ return false;
+ if (p_buffer < 0 || p_buffer >= buses[p_bus]->channels.size())
+ return false;
+ return true;
+}
+
AudioFrame *AudioServer::thread_get_channel_mix_buffer(int p_bus, int p_buffer) {
ERR_FAIL_INDEX_V(p_bus, buses.size(), NULL);
@@ -926,6 +942,7 @@ void AudioServer::init() {
channel_disable_threshold_db = GLOBAL_DEF_RST("audio/channel_disable_threshold_db", -60.0);
channel_disable_frames = float(GLOBAL_DEF_RST("audio/channel_disable_time", 2.0)) * get_mix_rate();
+ ProjectSettings::get_singleton()->set_custom_property_info("audio/channel_disable_time", PropertyInfo(Variant::REAL, "audio/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"));
buffer_size = 1024; //hardcoded for now
init_channels_and_buffers();
@@ -1008,7 +1025,7 @@ void AudioServer::update() {
void AudioServer::load_default_bus_layout() {
- if (FileAccess::exists("res://default_bus_layout.tres")) {
+ if (ResourceLoader::exists("res://default_bus_layout.tres")) {
Ref<AudioBusLayout> default_layout = ResourceLoader::load("res://default_bus_layout.tres");
if (default_layout.is_valid()) {
set_bus_layout(default_layout);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index ba6569eb38..52fa84e3e6 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -59,6 +59,7 @@ protected:
void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true);
void update_mix_time(int p_frames);
+ void input_buffer_init(int driver_buffer_frames);
void input_buffer_write(int32_t sample);
#ifdef DEBUG_ENABLED
@@ -281,6 +282,7 @@ public:
}
//do not use from outside audio thread
+ bool thread_has_channel_mix_buffer(int p_bus, int p_buffer) const;
AudioFrame *thread_get_channel_mix_buffer(int p_bus, int p_buffer);
int thread_get_mix_buffer_size() const;
int thread_find_bus_index(const StringName &p_name);
diff --git a/servers/physics/area_pair_sw.cpp b/servers/physics/area_pair_sw.cpp
index d2fef0ab77..5e295edcd1 100644
--- a/servers/physics/area_pair_sw.cpp
+++ b/servers/physics/area_pair_sw.cpp
@@ -147,10 +147,10 @@ Area2PairSW::~Area2PairSW() {
if (colliding) {
- if (area_b->has_area_monitor_callback() && area_a->is_monitorable())
+ if (area_b->has_area_monitor_callback())
area_b->remove_area_from_query(area_a, shape_a, shape_b);
- if (area_a->has_area_monitor_callback() && area_b->is_monitorable())
+ if (area_a->has_area_monitor_callback())
area_a->remove_area_from_query(area_b, shape_b, shape_a);
}
diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp
index 087ae570fb..f17f6f7014 100644
--- a/servers/physics/collision_solver_sat.cpp
+++ b/servers/physics/collision_solver_sat.cpp
@@ -98,7 +98,6 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_
Vector3 c = rel_A.cross(rel_B).cross(rel_B);
- //if ( Math::abs(rel_A.dot(c) )<_EDGE_IS_VALID_SUPPORT_TRESHOLD ) {
if (Math::abs(rel_A.dot(c)) < CMP_EPSILON) {
// should handle somehow..
diff --git a/servers/physics/collision_solver_sw.cpp b/servers/physics/collision_solver_sw.cpp
index 2f2f6d2908..86ef719f6f 100644
--- a/servers/physics/collision_solver_sw.cpp
+++ b/servers/physics/collision_solver_sw.cpp
@@ -31,7 +31,6 @@
#include "collision_solver_sw.h"
#include "collision_solver_sat.h"
-#include "collision_solver_sat.h"
#include "gjk_epa.h"
#define collision_solver sat_calculate_penetration
diff --git a/servers/physics/joints/generic_6dof_joint_sw.cpp b/servers/physics/joints/generic_6dof_joint_sw.cpp
index 60505c08c5..3a965ff800 100644
--- a/servers/physics/joints/generic_6dof_joint_sw.cpp
+++ b/servers/physics/joints/generic_6dof_joint_sw.cpp
@@ -503,6 +503,24 @@ void Generic6DOFJointSW::set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJoi
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
// Not implemented in GodotPhysics backend
} break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: {
+ // Not implemented in GodotPhysics backend
+ } break;
case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning
}
}
@@ -585,6 +603,24 @@ real_t Generic6DOFJointSW::get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJ
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
// Not implemented in GodotPhysics backend
} break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: {
+ // Not implemented in GodotPhysics backend
+ } break;
case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning
}
return 0;
@@ -610,6 +646,12 @@ void Generic6DOFJointSW::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin
case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
// Not implemented in GodotPhysics backend
} break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING: {
+ // Not implemented in GodotPhysics backend
+ } break;
case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
}
@@ -632,6 +674,12 @@ bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin
case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
// Not implemented in GodotPhysics backend
} break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING: {
+ // Not implemented in GodotPhysics backend
+ } break;
+ case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING: {
+ // Not implemented in GodotPhysics backend
+ } break;
case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning
}
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index 76a6138817..fddb531a4f 100644
--- a/servers/physics/physics_server_sw.cpp
+++ b/servers/physics/physics_server_sw.cpp
@@ -40,6 +40,12 @@
#include "joints/pin_joint_sw.h"
#include "joints/slider_joint_sw.h"
+#define FLUSH_QUERY_CHECK \
+ if (flushing_queries) { \
+ ERR_EXPLAIN("Can't change this state while flushing queries. Use call_deferred()/set_deferred() to change monitoring state instead"); \
+ ERR_FAIL(); \
+ }
+
RID PhysicsServerSW::shape_create(ShapeType p_shape) {
ShapeSW *shape = NULL;
@@ -352,6 +358,8 @@ void PhysicsServerSW::area_clear_shapes(RID p_area) {
void PhysicsServerSW::area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) {
+ FLUSH_QUERY_CHECK
+
AreaSW *area = area_owner.get(p_area);
ERR_FAIL_COND(!area);
ERR_FAIL_INDEX(p_shape_idx, area->get_shape_count());
@@ -435,6 +443,8 @@ void PhysicsServerSW::area_set_collision_mask(RID p_area, uint32_t p_mask) {
void PhysicsServerSW::area_set_monitorable(RID p_area, bool p_monitorable) {
+ FLUSH_QUERY_CHECK
+
AreaSW *area = area_owner.get(p_area);
ERR_FAIL_COND(!area);
@@ -582,6 +592,8 @@ RID PhysicsServerSW::body_get_shape(RID p_body, int p_shape_idx) const {
void PhysicsServerSW::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
+ FLUSH_QUERY_CHECK
+
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
ERR_FAIL_INDEX(p_shape_idx, body->get_shape_count());
@@ -1459,6 +1471,8 @@ void PhysicsServerSW::flush_queries() {
doing_sync = true;
+ flushing_queries = true;
+
uint64_t time_beg = OS::get_singleton()->get_ticks_usec();
for (Set<const SpaceSW *>::Element *E = active_spaces.front(); E; E = E->next()) {
@@ -1467,6 +1481,8 @@ void PhysicsServerSW::flush_queries() {
space->call_queries();
}
+ flushing_queries = false;
+
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
uint64_t total_time[SpaceSW::ELAPSED_TIME_MAX];
@@ -1580,6 +1596,7 @@ PhysicsServerSW::PhysicsServerSW() {
collision_pairs = 0;
active = true;
+ flushing_queries = false;
};
PhysicsServerSW::~PhysicsServerSW(){
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
index 4131c5e248..b3c61403aa 100644
--- a/servers/physics/physics_server_sw.h
+++ b/servers/physics/physics_server_sw.h
@@ -51,6 +51,8 @@ class PhysicsServerSW : public PhysicsServer {
int active_objects;
int collision_pairs;
+ bool flushing_queries;
+
StepSW *stepper;
Set<const SpaceSW *> active_spaces;
@@ -365,6 +367,8 @@ public:
virtual void flush_queries();
virtual void finish();
+ virtual bool is_flushing_queries() const { return flushing_queries; }
+
int get_process_info(ProcessInfo p_info);
PhysicsServerSW();
diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp
index 731749b8ce..3b5344f020 100644
--- a/servers/physics/space_sw.cpp
+++ b/servers/physics/space_sw.cpp
@@ -544,14 +544,24 @@ int SpaceSW::test_body_ray_separation(BodySW *p_body, const Transform &p_transfo
AABB body_aabb;
+ bool shapes_found = false;
+
for (int i = 0; i < p_body->get_shape_count(); i++) {
- if (i == 0)
+ if (p_body->is_shape_set_as_disabled(i))
+ continue;
+
+ if (!shapes_found) {
body_aabb = p_body->get_shape_aabb(i);
- else
+ shapes_found = true;
+ } else {
body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
}
+ if (!shapes_found) {
+ return 0;
+ }
// Undo the currently transform the physics server is aware of and apply the provided one
body_aabb = p_transform.xform(p_body->get_inv_transform().xform(body_aabb));
body_aabb = body_aabb.grow(p_margin);
@@ -691,13 +701,23 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve
r_result->collider_shape = 0;
}
AABB body_aabb;
+ bool shapes_found = false;
for (int i = 0; i < p_body->get_shape_count(); i++) {
- if (i == 0)
+ if (p_body->is_shape_set_as_disabled(i))
+ continue;
+
+ if (!shapes_found) {
body_aabb = p_body->get_shape_aabb(i);
- else
+ shapes_found = true;
+ } else {
body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
+ }
+
+ if (!shapes_found) {
+ return false;
}
// Undo the currently transform the physics server is aware of and apply the provided one
@@ -1183,6 +1203,7 @@ SpaceSW::SpaceSW() {
body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1);
body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI));
body_time_to_sleep = GLOBAL_DEF("physics/3d/time_before_sleep", 0.5);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"));
body_angular_velocity_damp_ratio = 10;
broadphase = BroadPhaseSW::create_func();
diff --git a/servers/physics_2d/area_pair_2d_sw.cpp b/servers/physics_2d/area_pair_2d_sw.cpp
index 9d515d2183..cdd35cf657 100644
--- a/servers/physics_2d/area_pair_2d_sw.cpp
+++ b/servers/physics_2d/area_pair_2d_sw.cpp
@@ -147,10 +147,10 @@ Area2Pair2DSW::~Area2Pair2DSW() {
if (colliding) {
- if (area_b->has_area_monitor_callback() && area_a->is_monitorable())
+ if (area_b->has_area_monitor_callback())
area_b->remove_area_from_query(area_a, shape_a, shape_b);
- if (area_a->has_area_monitor_callback() && area_b->is_monitorable())
+ if (area_a->has_area_monitor_callback())
area_a->remove_area_from_query(area_b, shape_b, shape_a);
}
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
index aa063d6c1e..52362386d2 100644
--- a/servers/physics_2d/body_2d_sw.cpp
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -511,8 +511,7 @@ void Body2DSW::integrate_forces(real_t p_step) {
if (continuous_cd_mode != Physics2DServer::CCD_MODE_DISABLED) {
- motion = new_transform.get_origin() - get_transform().get_origin();
- //linear_velocity*p_step;
+ motion = linear_velocity * p_step;
do_motion = true;
}
}
diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
index 950f0f9d24..95195c8fff 100644
--- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp
+++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp
@@ -635,11 +635,15 @@ BroadPhase2DSW *BroadPhase2DHashGrid::_create() {
BroadPhase2DHashGrid::BroadPhase2DHashGrid() {
hash_table_size = GLOBAL_DEF("physics/2d/bp_hash_table_size", 4096);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/bp_hash_table_size", PropertyInfo(Variant::INT, "physics/2d/bp_hash_table_size", PROPERTY_HINT_RANGE, "0,8192,1,or_greater"));
hash_table_size = Math::larger_prime(hash_table_size);
hash_table = memnew_arr(PosBin *, hash_table_size);
cell_size = GLOBAL_DEF("physics/2d/cell_size", 128);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/cell_size", PropertyInfo(Variant::INT, "physics/2d/cell_size", PROPERTY_HINT_RANGE, "0,512,1,or_greater"));
+
large_object_min_surface = GLOBAL_DEF("physics/2d/large_object_surface_threshold_in_cells", 512);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/large_object_surface_threshold_in_cells", PropertyInfo(Variant::INT, "physics/2d/large_object_surface_threshold_in_cells", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"));
for (uint32_t i = 0; i < hash_table_size; i++)
hash_table[i] = NULL;
diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp
index 4dd5b2040f..2f5b484040 100644
--- a/servers/physics_2d/collision_object_2d_sw.cpp
+++ b/servers/physics_2d/collision_object_2d_sw.cpp
@@ -244,6 +244,7 @@ CollisionObject2DSW::CollisionObject2DSW(Type p_type) {
type = p_type;
space = NULL;
instance_id = 0;
+ canvas_instance_id = 0;
collision_mask = 1;
collision_layer = 1;
pickable = true;
diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h
index f2b2363499..f256910f52 100644
--- a/servers/physics_2d/collision_object_2d_sw.h
+++ b/servers/physics_2d/collision_object_2d_sw.h
@@ -49,6 +49,7 @@ private:
Type type;
RID self;
ObjectID instance_id;
+ ObjectID canvas_instance_id;
bool pickable;
struct Shape {
@@ -102,6 +103,9 @@ public:
_FORCE_INLINE_ void set_instance_id(const ObjectID &p_instance_id) { instance_id = p_instance_id; }
_FORCE_INLINE_ ObjectID get_instance_id() const { return instance_id; }
+ _FORCE_INLINE_ void set_canvas_instance_id(const ObjectID &p_canvas_instance_id) { canvas_instance_id = p_canvas_instance_id; }
+ _FORCE_INLINE_ ObjectID get_canvas_instance_id() const { return canvas_instance_id; }
+
void _shape_changed();
_FORCE_INLINE_ Type get_type() const { return type; }
diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp
index 000e38c4a2..45310ec4b3 100644
--- a/servers/physics_2d/physics_2d_server_sw.cpp
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -36,6 +36,12 @@
#include "core/project_settings.h"
#include "core/script_language.h"
+#define FLUSH_QUERY_CHECK \
+ if (flushing_queries) { \
+ ERR_EXPLAIN("Can't change this state while flushing queries. Use call_deferred() or set_deferred() to change monitoring state instead"); \
+ ERR_FAIL(); \
+ }
+
RID Physics2DServerSW::_shape_create(ShapeType p_shape) {
Shape2DSW *shape = NULL;
@@ -169,7 +175,9 @@ void Physics2DServerSW::_shape_col_cbk(const Vector2 &p_point_A, const Vector2 &
cbk->invalid_by_dir++;
return;
}
- if (cbk->valid_dir.dot((p_point_A - p_point_B).normalized()) < 0.7071) {
+ Vector2 rel_dir = (p_point_A - p_point_B).normalized();
+
+ if (cbk->valid_dir.dot(rel_dir) < 0.7071) { //sqrt(2)/2.0 - 45 degrees
cbk->invalid_by_dir++;
/*
@@ -399,6 +407,8 @@ void Physics2DServerSW::area_set_shape_transform(RID p_area, int p_shape_idx, co
void Physics2DServerSW::area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled) {
+ FLUSH_QUERY_CHECK
+
Area2DSW *area = area_owner.get(p_area);
ERR_FAIL_COND(!area);
@@ -469,6 +479,27 @@ ObjectID Physics2DServerSW::area_get_object_instance_id(RID p_area) const {
return area->get_instance_id();
}
+void Physics2DServerSW::area_attach_canvas_instance_id(RID p_area, ObjectID p_ID) {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space = space_owner.get(p_area);
+ p_area = space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+ area->set_canvas_instance_id(p_ID);
+}
+ObjectID Physics2DServerSW::area_get_canvas_instance_id(RID p_area) const {
+
+ if (space_owner.owns(p_area)) {
+ Space2DSW *space = space_owner.get(p_area);
+ p_area = space->get_default_area()->get_self();
+ }
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND_V(!area, 0);
+ return area->get_canvas_instance_id();
+}
+
void Physics2DServerSW::area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) {
if (space_owner.owns(p_area)) {
@@ -516,6 +547,8 @@ void Physics2DServerSW::area_set_pickable(RID p_area, bool p_pickable) {
void Physics2DServerSW::area_set_monitorable(RID p_area, bool p_monitorable) {
+ FLUSH_QUERY_CHECK
+
Area2DSW *area = area_owner.get(p_area);
ERR_FAIL_COND(!area);
@@ -594,6 +627,8 @@ RID Physics2DServerSW::body_get_space(RID p_body) const {
void Physics2DServerSW::body_set_mode(RID p_body, BodyMode p_mode) {
+ FLUSH_QUERY_CHECK
+
Body2DSW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -696,6 +731,8 @@ void Physics2DServerSW::body_clear_shapes(RID p_body) {
void Physics2DServerSW::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
+ FLUSH_QUERY_CHECK
+
Body2DSW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -744,6 +781,22 @@ uint32_t Physics2DServerSW::body_get_object_instance_id(RID p_body) const {
return body->get_instance_id();
};
+void Physics2DServerSW::body_attach_canvas_instance_id(RID p_body, uint32_t p_ID) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->set_canvas_instance_id(p_ID);
+};
+
+uint32_t Physics2DServerSW::body_get_canvas_instance_id(RID p_body) const {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+
+ return body->get_canvas_instance_id();
+};
+
void Physics2DServerSW::body_set_collision_layer(RID p_body, uint32_t p_layer) {
Body2DSW *body = body_owner.get(p_body);
@@ -1309,6 +1362,8 @@ void Physics2DServerSW::flush_queries() {
if (!active)
return;
+ flushing_queries = true;
+
uint64_t time_beg = OS::get_singleton()->get_ticks_usec();
for (Set<const Space2DSW *>::Element *E = active_spaces.front(); E; E = E->next()) {
@@ -1317,6 +1372,8 @@ void Physics2DServerSW::flush_queries() {
space->call_queries();
}
+ flushing_queries = false;
+
if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_profiling()) {
uint64_t total_time[Space2DSW::ELAPSED_TIME_MAX];
@@ -1395,6 +1452,7 @@ Physics2DServerSW::Physics2DServerSW() {
active_objects = 0;
collision_pairs = 0;
using_threads = int(ProjectSettings::get_singleton()->get("physics/2d/thread_model")) == 2;
+ flushing_queries = false;
};
Physics2DServerSW::~Physics2DServerSW(){
diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h
index d4fc44b1d7..4f33873219 100644
--- a/servers/physics_2d/physics_2d_server_sw.h
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -54,6 +54,8 @@ class Physics2DServerSW : public Physics2DServer {
bool using_threads;
+ bool flushing_queries;
+
Step2DSW *stepper;
Set<const Space2DSW *> active_spaces;
@@ -144,6 +146,9 @@ public:
virtual void area_attach_object_instance_id(RID p_area, ObjectID p_ID);
virtual ObjectID area_get_object_instance_id(RID p_area) const;
+ virtual void area_attach_canvas_instance_id(RID p_area, ObjectID p_ID);
+ virtual ObjectID area_get_canvas_instance_id(RID p_area) const;
+
virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value);
virtual void area_set_transform(RID p_area, const Transform2D &p_transform);
@@ -188,6 +193,9 @@ public:
virtual void body_attach_object_instance_id(RID p_body, uint32_t p_ID);
virtual uint32_t body_get_object_instance_id(RID p_body) const;
+ virtual void body_attach_canvas_instance_id(RID p_body, uint32_t p_ID);
+ virtual uint32_t body_get_canvas_instance_id(RID p_body) const;
+
virtual void body_set_continuous_collision_detection_mode(RID p_body, CCDMode p_mode);
virtual CCDMode body_get_continuous_collision_detection_mode(RID p_body) const;
@@ -272,6 +280,8 @@ public:
virtual void end_sync();
virtual void finish();
+ virtual bool is_flushing_queries() const { return flushing_queries; }
+
int get_process_info(ProcessInfo p_info);
Physics2DServerSW();
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index 5f5fd3866f..e736854077 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -154,6 +154,9 @@ public:
FUNC2(area_attach_object_instance_id, RID, ObjectID);
FUNC1RC(ObjectID, area_get_object_instance_id, RID);
+ FUNC2(area_attach_canvas_instance_id, RID, ObjectID);
+ FUNC1RC(ObjectID, area_get_canvas_instance_id, RID);
+
FUNC3(area_set_param, RID, AreaParameter, const Variant &);
FUNC2(area_set_transform, RID, const Transform2D &);
@@ -199,6 +202,9 @@ public:
FUNC2(body_attach_object_instance_id, RID, uint32_t);
FUNC1RC(uint32_t, body_get_object_instance_id, RID);
+ FUNC2(body_attach_canvas_instance_id, RID, uint32_t);
+ FUNC1RC(uint32_t, body_get_canvas_instance_id, RID);
+
FUNC2(body_set_continuous_collision_detection_mode, RID, CCDMode);
FUNC1RC(CCDMode, body_get_continuous_collision_detection_mode, RID);
@@ -306,6 +312,10 @@ public:
virtual void flush_queries();
virtual void finish();
+ virtual bool is_flushing_queries() const {
+ return physics_2d_server->is_flushing_queries();
+ }
+
int get_process_info(ProcessInfo p_info) {
return physics_2d_server->get_process_info(p_info);
}
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index f36328c985..720742c198 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -31,9 +31,9 @@
#include "space_2d_sw.h"
#include "collision_solver_2d_sw.h"
+#include "core/os/os.h"
#include "core/pair.h"
#include "physics_2d_server_sw.h"
-
_FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (!(p_object->get_collision_layer() & p_collision_mask)) {
@@ -49,7 +49,7 @@ _FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint
return true;
}
-int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point) {
+int Physics2DDirectSpaceStateSW::_intersect_point_impl(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point, bool p_filter_by_canvas, ObjectID p_canvas_instance_id) {
if (p_result_max <= 0)
return 0;
@@ -75,6 +75,9 @@ int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeRe
if (p_pick_point && !col_obj->is_pickable())
continue;
+ if (p_filter_by_canvas && col_obj->get_canvas_instance_id() != p_canvas_instance_id)
+ continue;
+
int shape_idx = space->intersection_query_subindex_results[i];
Shape2DSW *shape = col_obj->get_shape(shape_idx);
@@ -100,6 +103,16 @@ int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeRe
return cc;
}
+int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point) {
+
+ return _intersect_point_impl(p_point, r_results, p_result_max, p_exclude, p_collision_mask, p_collide_with_bodies, p_collide_with_areas, p_pick_point);
+}
+
+int Physics2DDirectSpaceStateSW::intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point) {
+
+ return _intersect_point_impl(p_point, r_results, p_result_max, p_exclude, p_collision_mask, p_collide_with_bodies, p_collide_with_areas, p_pick_point, true, p_canvas_instance_id);
+}
+
bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ERR_FAIL_COND_V(space->locked, false);
@@ -352,6 +365,8 @@ struct _RestCallbackData2D {
const CollisionObject2DSW *object;
const CollisionObject2DSW *best_object;
+ int local_shape;
+ int best_local_shape;
int shape;
int best_shape;
Vector2 best_contact;
@@ -382,6 +397,7 @@ static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B,
rd->best_normal = contact_rel / len;
rd->best_object = rd->object;
rd->best_shape = rd->shape;
+ rd->best_local_shape = rd->local_shape;
}
bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
@@ -415,6 +431,7 @@ bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_sh
rcd.valid_depth = 0;
rcd.object = col_obj;
rcd.shape = shape_idx;
+ rcd.local_shape = 0;
bool sc = CollisionSolver2DSW::solve(shape, p_shape_xform, p_motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), _rest_cbk_result, &rcd, NULL, p_margin);
if (!sc)
continue;
@@ -487,12 +504,23 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t
Rect2 body_aabb;
+ bool shapes_found = false;
+
for (int i = 0; i < p_body->get_shape_count(); i++) {
- if (i == 0)
+ if (p_body->is_shape_set_as_disabled(i))
+ continue;
+
+ if (!shapes_found) {
body_aabb = p_body->get_shape_aabb(i);
- else
+ shapes_found = true;
+ } else {
body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
+ }
+
+ if (!shapes_found) {
+ return 0;
}
// Undo the currently transform the physics server is aware of and apply the provided one
@@ -555,9 +583,11 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t
}
}
+ Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
+
if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
- cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
cbk.valid_depth = p_margin; //only valid depth is the collision margin
cbk.invalid_by_dir = 0;
@@ -568,7 +598,7 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t
}
Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
- if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) {
+ if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), cbkres, cbkptr, NULL, p_margin)) {
if (cbk.amount > 0) {
collided = true;
}
@@ -647,12 +677,23 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
Rect2 body_aabb;
+ bool shapes_found = false;
+
for (int i = 0; i < p_body->get_shape_count(); i++) {
- if (i == 0)
+ if (p_body->is_shape_set_as_disabled(i))
+ continue;
+
+ if (!shapes_found) {
body_aabb = p_body->get_shape_aabb(i);
- else
+ shapes_found = true;
+ } else {
body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
+ }
+
+ if (!shapes_found) {
+ return false;
}
// Undo the currently transform the physics server is aware of and apply the provided one
@@ -663,6 +704,8 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
ExcludedShapeSW excluded_shape_pairs[max_excluded_shape_pairs];
int excluded_shape_pair_count = 0;
+ float separation_margin = MIN(p_margin, MAX(0.0, p_motion.length() - CMP_EPSILON)); //don't separate by more than the intended motion
+
Transform2D body_transform = p_from;
{
@@ -710,24 +753,42 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
}
+ Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
+
if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
- cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
+
cbk.valid_depth = p_margin; //only valid depth is the collision margin
cbk.invalid_by_dir = 0;
+ if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
+ const Body2DSW *b = static_cast<const Body2DSW *>(col_obj);
+ if (b->get_mode() == Physics2DServer::BODY_MODE_KINEMATIC || b->get_mode() == Physics2DServer::BODY_MODE_RIGID) {
+ //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction
+ Vector2 lv = b->get_linear_velocity();
+ //compute displacement from linear velocity
+ Vector2 motion = lv * Physics2DDirectBodyStateSW::singleton->step;
+ float motion_len = motion.length();
+ motion.normalize();
+ cbk.valid_depth += motion_len * MAX(motion.dot(-cbk.valid_dir), 0.0);
+ }
+ }
} else {
cbk.valid_dir = Vector2();
cbk.valid_depth = 0;
cbk.invalid_by_dir = 0;
}
+ int current_collisions = cbk.amount;
+ bool did_collide = false;
+
Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
- if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) {
- collided = cbk.amount > 0;
+ if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), cbkres, cbkptr, NULL, separation_margin)) {
+ did_collide = cbk.amount > current_collisions;
}
- if (!collided && cbk.invalid_by_dir > 0) {
+ if (!did_collide && cbk.invalid_by_dir > 0) {
//this shape must be excluded
if (excluded_shape_pair_count < max_excluded_shape_pairs) {
ExcludedShapeSW esp;
@@ -737,6 +798,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
excluded_shape_pairs[excluded_shape_pair_count++] = esp;
}
}
+
+ if (did_collide) {
+ collided = true;
+ }
}
}
@@ -824,14 +889,14 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
continue;
}
- Transform2D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(col_shape_idx);
+ Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(col_shape_idx);
//test initial overlap, does it collide if going all the way?
- if (!CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion, against_shape, col_obj_xform, Vector2(), NULL, NULL, NULL, 0)) {
+ if (!CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion, against_shape, col_obj_shape_xform, Vector2(), NULL, NULL, NULL, 0)) {
continue;
}
//test initial overlap
- if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_xform, Vector2(), NULL, NULL, NULL, 0)) {
+ if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), NULL, NULL, NULL, 0)) {
if (col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {
continue;
@@ -851,7 +916,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
real_t ofs = (low + hi) * 0.5;
Vector2 sep = mnormal; //important optimization for this to work fast enough
- bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * ofs, against_shape, col_obj_xform, Vector2(), NULL, NULL, &sep, 0);
+ bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * ofs, against_shape, col_obj_shape_xform, Vector2(), NULL, NULL, &sep, 0);
if (collided) {
@@ -869,12 +934,12 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
cbk.max = 1;
cbk.amount = 0;
cbk.ptr = cd;
- cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
cbk.valid_depth = 10e20;
Vector2 sep = mnormal; //important optimization for this to work fast enough
- bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(col_shape_idx), col_obj_xform, Vector2(), Physics2DServerSW::_shape_col_cbk, &cbk, &sep, 0);
+ bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(col_shape_idx), col_obj_shape_xform, Vector2(), Physics2DServerSW::_shape_col_cbk, &cbk, &sep, 0);
if (!collided || cbk.amount == 0) {
continue;
}
@@ -907,16 +972,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
bool collided = false;
if (safe >= 1) {
- //not collided
- collided = false;
- if (r_result) {
-
- r_result->motion = p_motion;
- r_result->remainder = Vector2();
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
- }
+ best_shape = -1; //no best shape with cast, reset to -1
+ }
- } else {
+ {
//it collided, let's get the rest info in unsafe advance
Transform2D ugt = body_transform;
@@ -927,52 +986,61 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
rcd.best_object = NULL;
rcd.best_shape = 0;
- Transform2D body_shape_xform = ugt * p_body->get_shape_transform(best_shape);
- Shape2DSW *body_shape = p_body->get_shape(best_shape);
+ //optimization
+ int from_shape = best_shape != -1 ? best_shape : 0;
+ int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count();
- body_aabb.position += p_motion * unsafe;
+ for (int j = from_shape; j < to_shape; j++) {
+ Transform2D body_shape_xform = ugt * p_body->get_shape_transform(j);
+ Shape2DSW *body_shape = p_body->get_shape(j);
- int amount = _cull_aabb_for_body(p_body, body_aabb);
+ body_aabb.position += p_motion * unsafe;
+
+ int amount = _cull_aabb_for_body(p_body, body_aabb);
- for (int i = 0; i < amount; i++) {
+ for (int i = 0; i < amount; i++) {
- const CollisionObject2DSW *col_obj = intersection_query_results[i];
- int shape_idx = intersection_query_subindex_results[i];
+ const CollisionObject2DSW *col_obj = intersection_query_results[i];
+ int shape_idx = intersection_query_subindex_results[i];
- if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) {
- const Body2DSW *b = static_cast<const Body2DSW *>(col_obj);
- if (p_infinite_inertia && Physics2DServer::BODY_MODE_STATIC != b->get_mode() && Physics2DServer::BODY_MODE_KINEMATIC != b->get_mode()) {
- continue;
+ if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) {
+ const Body2DSW *b = static_cast<const Body2DSW *>(col_obj);
+ if (p_infinite_inertia && Physics2DServer::BODY_MODE_STATIC != b->get_mode() && Physics2DServer::BODY_MODE_KINEMATIC != b->get_mode()) {
+ continue;
+ }
}
- }
- Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
+ Shape2DSW *against_shape = col_obj->get_shape(shape_idx);
- bool excluded = false;
- for (int k = 0; k < excluded_shape_pair_count; k++) {
+ bool excluded = false;
+ for (int k = 0; k < excluded_shape_pair_count; k++) {
- if (excluded_shape_pairs[k].local_shape == body_shape && excluded_shape_pairs[k].against_object == col_obj && excluded_shape_pairs[k].against_shape_index == shape_idx) {
- excluded = true;
- break;
+ if (excluded_shape_pairs[k].local_shape == body_shape && excluded_shape_pairs[k].against_object == col_obj && excluded_shape_pairs[k].against_shape_index == shape_idx) {
+ excluded = true;
+ break;
+ }
}
- }
- if (excluded)
- continue;
+ if (excluded)
+ continue;
- if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
+ Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
- rcd.valid_dir = body_shape_xform.get_axis(1).normalized();
- rcd.valid_depth = 10e20;
- } else {
- rcd.valid_dir = Vector2();
- rcd.valid_depth = 0;
- }
+ if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
- rcd.object = col_obj;
- rcd.shape = shape_idx;
- bool sc = CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), _rest_cbk_result, &rcd, NULL, p_margin);
- if (!sc)
- continue;
+ rcd.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
+ rcd.valid_depth = 10e20;
+ } else {
+ rcd.valid_dir = Vector2();
+ rcd.valid_depth = 0;
+ }
+
+ rcd.object = col_obj;
+ rcd.shape = shape_idx;
+ rcd.local_shape = j;
+ bool sc = CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), _rest_cbk_result, &rcd, NULL, p_margin);
+ if (!sc)
+ continue;
+ }
}
if (rcd.best_len != 0) {
@@ -981,7 +1049,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
r_result->collider = rcd.best_object->get_self();
r_result->collider_id = rcd.best_object->get_instance_id();
r_result->collider_shape = rcd.best_shape;
- r_result->collision_local_shape = best_shape;
+ r_result->collision_local_shape = rcd.best_local_shape;
r_result->collision_normal = rcd.best_normal;
r_result->collision_point = rcd.best_contact;
r_result->collider_metadata = rcd.best_object->get_shape_metadata(rcd.best_shape);
@@ -996,16 +1064,14 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
}
collided = true;
- } else {
- if (r_result) {
+ }
+ }
- r_result->motion = p_motion;
- r_result->remainder = Vector2();
- r_result->motion += (body_transform.get_origin() - p_from.get_origin());
- }
+ if (!collided && r_result) {
- collided = false;
- }
+ r_result->motion = p_motion;
+ r_result->remainder = Vector2();
+ r_result->motion += (body_transform.get_origin() - p_from.get_origin());
}
return collided;
@@ -1233,6 +1299,7 @@ Space2DSW::Space2DSW() {
body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0);
body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI));
body_time_to_sleep = GLOBAL_DEF("physics/2d/time_before_sleep", 0.5);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"));
broadphase = BroadPhase2DSW::create_func();
broadphase->set_pair_callback(_broadphase_pair, this);
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
index c894eb9c40..bf4ea12eb5 100644
--- a/servers/physics_2d/space_2d_sw.h
+++ b/servers/physics_2d/space_2d_sw.h
@@ -45,10 +45,13 @@ class Physics2DDirectSpaceStateSW : public Physics2DDirectSpaceState {
GDCLASS(Physics2DDirectSpaceStateSW, Physics2DDirectSpaceState);
+ int _intersect_point_impl(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point, bool p_filter_by_canvas = false, ObjectID p_canvas_instance_id = 0);
+
public:
Space2DSW *space;
virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false);
+ virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false);
virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp
index 3ca6e29f0b..2ed8a4c478 100644
--- a/servers/physics_2d_server.cpp
+++ b/servers/physics_2d_server.cpp
@@ -328,7 +328,7 @@ Array Physics2DDirectSpaceState::_cast_motion(const Ref<Physics2DShapeQueryParam
return ret;
}
-Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) {
+Array Physics2DDirectSpaceState::_intersect_point_impl(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_filter_by_canvas, ObjectID p_canvas_instance_id) {
Set<RID> exclude;
for (int i = 0; i < p_exclude.size(); i++)
@@ -337,7 +337,12 @@ Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_
Vector<ShapeResult> ret;
ret.resize(p_max_results);
- int rc = intersect_point(p_point, ret.ptrw(), ret.size(), exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
+ int rc;
+ if (p_filter_by_canvas)
+ rc = intersect_point(p_point, ret.ptrw(), ret.size(), exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
+ else
+ rc = intersect_point_on_canvas(p_point, p_canvas_instance_id, ret.ptrw(), ret.size(), exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
+
if (rc == 0)
return Array();
@@ -356,6 +361,16 @@ Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_
return r;
}
+Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) {
+
+ return _intersect_point_impl(p_point, p_max_results, p_exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
+}
+
+Array Physics2DDirectSpaceState::_intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_intance_id, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) {
+
+ return _intersect_point_impl(p_point, p_max_results, p_exclude, p_layers, p_collide_with_bodies, p_collide_with_areas, true, p_canvas_intance_id);
+}
+
Array Physics2DDirectSpaceState::_collide_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results) {
ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array());
@@ -400,6 +415,7 @@ Physics2DDirectSpaceState::Physics2DDirectSpaceState() {
void Physics2DDirectSpaceState::_bind_methods() {
ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &Physics2DDirectSpaceState::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &Physics2DDirectSpaceState::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &Physics2DDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &Physics2DDirectSpaceState::_intersect_shape, DEFVAL(32));
ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &Physics2DDirectSpaceState::_cast_motion);
@@ -575,6 +591,9 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("area_attach_object_instance_id", "area", "id"), &Physics2DServer::area_attach_object_instance_id);
ClassDB::bind_method(D_METHOD("area_get_object_instance_id", "area"), &Physics2DServer::area_get_object_instance_id);
+ ClassDB::bind_method(D_METHOD("area_attach_canvas_instance_id", "area", "id"), &Physics2DServer::area_attach_canvas_instance_id);
+ ClassDB::bind_method(D_METHOD("area_get_canvas_instance_id", "area"), &Physics2DServer::area_get_canvas_instance_id);
+
ClassDB::bind_method(D_METHOD("area_set_monitor_callback", "area", "receiver", "method"), &Physics2DServer::area_set_monitor_callback);
ClassDB::bind_method(D_METHOD("area_set_area_monitor_callback", "area", "receiver", "method"), &Physics2DServer::area_set_area_monitor_callback);
ClassDB::bind_method(D_METHOD("area_set_monitorable", "area", "monitorable"), &Physics2DServer::area_set_monitorable);
@@ -606,6 +625,9 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_attach_object_instance_id", "body", "id"), &Physics2DServer::body_attach_object_instance_id);
ClassDB::bind_method(D_METHOD("body_get_object_instance_id", "body"), &Physics2DServer::body_get_object_instance_id);
+ ClassDB::bind_method(D_METHOD("body_attach_canvas_instance_id", "body", "id"), &Physics2DServer::body_attach_canvas_instance_id);
+ ClassDB::bind_method(D_METHOD("body_get_canvas_instance_id", "body"), &Physics2DServer::body_get_canvas_instance_id);
+
ClassDB::bind_method(D_METHOD("body_set_continuous_collision_detection_mode", "body", "mode"), &Physics2DServer::body_set_continuous_collision_detection_mode);
ClassDB::bind_method(D_METHOD("body_get_continuous_collision_detection_mode", "body"), &Physics2DServer::body_get_continuous_collision_detection_mode);
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index 7c23fcdaea..32e1dd1a08 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -147,8 +147,9 @@ class Physics2DDirectSpaceState : public Object {
GDCLASS(Physics2DDirectSpaceState, Object);
Dictionary _intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
-
Array _intersect_point(const Vector2 &p_point, int p_max_results = 32, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ Array _intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_intance_id, int p_max_results = 32, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ Array _intersect_point_impl(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclud, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_filter_by_canvas = false, ObjectID p_canvas_instance_id = 0);
Array _intersect_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results = 32);
Array _cast_motion(const Ref<Physics2DShapeQueryParameters> &p_shape_query);
Array _collide_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results = 32);
@@ -181,6 +182,7 @@ public:
};
virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0;
+ virtual int intersect_point_on_canvas(const Vector2 &p_point, ObjectID p_canvas_instance_id, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0;
virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
@@ -348,6 +350,9 @@ public:
virtual void area_attach_object_instance_id(RID p_area, ObjectID p_ID) = 0;
virtual ObjectID area_get_object_instance_id(RID p_area) const = 0;
+ virtual void area_attach_canvas_instance_id(RID p_area, ObjectID p_ID) = 0;
+ virtual ObjectID area_get_canvas_instance_id(RID p_area) const = 0;
+
virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) = 0;
virtual void area_set_transform(RID p_area, const Transform2D &p_transform) = 0;
@@ -401,6 +406,9 @@ public:
virtual void body_attach_object_instance_id(RID p_body, uint32_t p_ID) = 0;
virtual uint32_t body_get_object_instance_id(RID p_body) const = 0;
+ virtual void body_attach_canvas_instance_id(RID p_body, uint32_t p_ID) = 0;
+ virtual uint32_t body_get_canvas_instance_id(RID p_body) const = 0;
+
enum CCDMode {
CCD_MODE_DISABLED,
CCD_MODE_CAST_RAY,
@@ -576,6 +584,8 @@ public:
virtual void end_sync() = 0;
virtual void finish() = 0;
+ virtual bool is_flushing_queries() const = 0;
+
enum ProcessInfo {
INFO_ACTIVE_OBJECTS,
diff --git a/servers/physics_server.h b/servers/physics_server.h
index d80d76305a..15b353f768 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -697,6 +697,9 @@ public:
G6DOF_JOINT_LINEAR_DAMPING,
G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY,
G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT,
+ G6DOF_JOINT_LINEAR_SPRING_STIFFNESS,
+ G6DOF_JOINT_LINEAR_SPRING_DAMPING,
+ G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT,
G6DOF_JOINT_ANGULAR_LOWER_LIMIT,
G6DOF_JOINT_ANGULAR_UPPER_LIMIT,
G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS,
@@ -706,6 +709,9 @@ public:
G6DOF_JOINT_ANGULAR_ERP,
G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY,
G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT,
+ G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS,
+ G6DOF_JOINT_ANGULAR_SPRING_DAMPING,
+ G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT,
G6DOF_JOINT_MAX
};
@@ -713,6 +719,8 @@ public:
G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT,
G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT,
+ G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING,
+ G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING,
G6DOF_JOINT_FLAG_ENABLE_MOTOR,
G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR,
G6DOF_JOINT_FLAG_MAX
@@ -744,6 +752,8 @@ public:
virtual void flush_queries() = 0;
virtual void finish() = 0;
+ virtual bool is_flushing_queries() const = 0;
+
enum ProcessInfo {
INFO_ACTIVE_OBJECTS,
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 8c3736a1bf..4329203ccb 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -73,7 +73,7 @@ public:
virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) = 0;
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0;
- virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
+ virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
virtual bool is_environment(RID p_env) = 0;
@@ -120,9 +120,7 @@ public:
Vector<Color> lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader
virtual void base_removed() = 0;
- virtual void base_changed() = 0;
- virtual void base_material_changed() = 0;
-
+ virtual void base_changed(bool p_aabb, bool p_materials) = 0;
InstanceBase() :
dependency_item(this) {
@@ -520,6 +518,8 @@ public:
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0;
virtual void particles_restart(RID p_particles) = 0;
+ virtual bool particles_is_inactive(RID p_particles) const = 0;
+
virtual void particles_set_draw_order(RID p_particles, VS::ParticlesDrawOrder p_order) = 0;
virtual void particles_set_draw_passes(RID p_particles, int p_count) = 0;
@@ -800,8 +800,6 @@ public:
RID particles;
RID texture;
RID normal_map;
- int h_frames;
- int v_frames;
CommandParticles() { type = TYPE_PARTICLES; }
};
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 67a810bf1c..50e45963d9 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -131,7 +131,6 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"TYPE_USAMPLER3D",
"TYPE_SAMPLERCUBE",
"INTERPOLATION_FLAT",
- "INTERPOLATION_NO_PERSPECTIVE",
"INTERPOLATION_SMOOTH",
"PRECISION_LOW",
"PRECISION_MID",
@@ -271,7 +270,6 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_TYPE_USAMPLER3D, "usampler3D" },
{ TK_TYPE_SAMPLERCUBE, "samplerCube" },
{ TK_INTERPOLATION_FLAT, "flat" },
- { TK_INTERPOLATION_NO_PERSPECTIVE, "noperspective" },
{ TK_INTERPOLATION_SMOOTH, "smooth" },
{ TK_PRECISION_LOW, "lowp" },
{ TK_PRECISION_MID, "mediump" },
@@ -691,6 +689,30 @@ String ShaderLanguage::token_debug(const String &p_code) {
return output;
}
+bool ShaderLanguage::is_token_variable_datatype(TokenType p_type) {
+ return (
+ p_type == TK_TYPE_VOID ||
+ p_type == TK_TYPE_BOOL ||
+ p_type == TK_TYPE_BVEC2 ||
+ p_type == TK_TYPE_BVEC3 ||
+ p_type == TK_TYPE_BVEC4 ||
+ p_type == TK_TYPE_INT ||
+ p_type == TK_TYPE_IVEC2 ||
+ p_type == TK_TYPE_IVEC3 ||
+ p_type == TK_TYPE_IVEC4 ||
+ p_type == TK_TYPE_UINT ||
+ p_type == TK_TYPE_UVEC2 ||
+ p_type == TK_TYPE_UVEC3 ||
+ p_type == TK_TYPE_UVEC4 ||
+ p_type == TK_TYPE_FLOAT ||
+ p_type == TK_TYPE_VEC2 ||
+ p_type == TK_TYPE_VEC3 ||
+ p_type == TK_TYPE_VEC4 ||
+ p_type == TK_TYPE_MAT2 ||
+ p_type == TK_TYPE_MAT3 ||
+ p_type == TK_TYPE_MAT4);
+}
+
bool ShaderLanguage::is_token_datatype(TokenType p_type) {
return (
@@ -735,7 +757,6 @@ bool ShaderLanguage::is_token_interpolation(TokenType p_type) {
return (
p_type == TK_INTERPOLATION_FLAT ||
- p_type == TK_INTERPOLATION_NO_PERSPECTIVE ||
p_type == TK_INTERPOLATION_SMOOTH);
}
@@ -743,8 +764,6 @@ ShaderLanguage::DataInterpolation ShaderLanguage::get_token_interpolation(TokenT
if (p_type == TK_INTERPOLATION_FLAT)
return INTERPOLATION_FLAT;
- else if (p_type == TK_INTERPOLATION_NO_PERSPECTIVE)
- return INTERPOLATION_NO_PERSPECTIVE;
else
return INTERPOLATION_SMOOTH;
}
@@ -2013,6 +2032,12 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
};
+const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = {
+ //constructors
+ { "modf", 1 },
+ { NULL, 0 }
+};
+
bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type) {
ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, NULL);
@@ -2056,6 +2081,41 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p
if (!fail) {
+ //make sure its not an out argument used in the wrong way
+ int outarg_idx = 0;
+ while (builtin_func_out_args[outarg_idx].name) {
+
+ if (String(name) == builtin_func_out_args[outarg_idx].name) {
+ int arg_idx = builtin_func_out_args[outarg_idx].argument;
+
+ if (arg_idx < argcount) {
+
+ if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE) {
+ _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable");
+ return false;
+ }
+ StringName var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name;
+
+ const BlockNode *b = p_block;
+ bool valid = false;
+ while (b) {
+ if (b->variables.has(var_name)) {
+ valid = true;
+ break;
+ }
+ b = b->parent_block;
+ }
+
+ if (!valid) {
+ _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable");
+ return false;
+ }
+ }
+ }
+
+ outarg_idx++;
+ }
+
if (r_ret_type)
*r_ret_type = builtin_func_defs[idx].rettype;
@@ -2288,7 +2348,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) {
p_type == TYPE_SAMPLERCUBE;
}
-Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type) {
+Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
if (p_value.size() > 0) {
Variant value;
switch (p_type) {
@@ -2332,7 +2392,11 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C
value = Variant(Vector3(p_value[0].real, p_value[1].real, p_value[2].real));
break;
case ShaderLanguage::TYPE_VEC4:
- value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real));
+ if (p_hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) {
+ value = Variant(Color(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real));
+ } else {
+ value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real));
+ }
break;
case ShaderLanguage::TYPE_MAT2:
value = Variant(Transform2D(p_value[0].real, p_value[2].real, p_value[1].real, p_value[3].real, 0.0, 0.0));
@@ -2498,7 +2562,7 @@ bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block, CompletionT
identifier = StringName();
- TkPos pos;
+ TkPos pos = { 0, 0 };
Token tk = _get_token();
@@ -3506,6 +3570,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
nv.sint = -cn->values[i].sint;
} break;
case TYPE_UINT: {
+ // FIXME: This can't work on uint
nv.uint = -cn->values[i].uint;
} break;
case TYPE_FLOAT: {
@@ -3562,6 +3627,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
}
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for variable (samplers not allowed)");
+ return ERR_PARSE_ERROR;
+ }
+
DataType type = get_token_datatype(tk.type);
tk = _get_token();
@@ -4217,6 +4287,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for function return (samplers not allowed)");
+ return ERR_PARSE_ERROR;
+ }
+
type = get_token_datatype(tk.type);
_get_completable_identifier(NULL, COMPLETION_MAIN_FUNCTION, name);
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index 08c4d06992..2d1851928e 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -80,7 +80,6 @@ public:
TK_TYPE_USAMPLER3D,
TK_TYPE_SAMPLERCUBE,
TK_INTERPOLATION_FLAT,
- TK_INTERPOLATION_NO_PERSPECTIVE,
TK_INTERPOLATION_SMOOTH,
TK_PRECISION_LOW,
TK_PRECISION_MID,
@@ -210,7 +209,6 @@ public:
enum DataInterpolation {
INTERPOLATION_FLAT,
- INTERPOLATION_NO_PERSPECTIVE,
INTERPOLATION_SMOOTH,
};
@@ -333,6 +331,7 @@ public:
virtual DataType get_datatype() const { return datatype_cache; }
VariableNode() {
+
type = TYPE_VARIABLE;
datatype_cache = TYPE_VOID;
}
@@ -534,6 +533,7 @@ public:
static String get_token_text(Token p_token);
static bool is_token_datatype(TokenType p_type);
+ static bool is_token_variable_datatype(TokenType p_type);
static DataType get_token_datatype(TokenType p_type);
static bool is_token_interpolation(TokenType p_type);
static DataInterpolation get_token_interpolation(TokenType p_type);
@@ -548,7 +548,7 @@ public:
static int get_cardinality(DataType p_type);
static bool is_scalar_type(DataType p_type);
static bool is_sampler_type(DataType p_type);
- static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type);
+ static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
static void get_keyword_list(List<String> *r_keywords);
static void get_builtin_funcs(List<String> *r_keywords);
@@ -642,6 +642,12 @@ private:
const DataType args[MAX_ARGS];
};
+ struct BuiltinFuncOutArgs { //arguments used as out in built in funcions
+
+ const char *name;
+ int argument;
+ };
+
CompletionType completion_type;
int completion_line;
BlockNode *completion_block;
@@ -652,6 +658,7 @@ private:
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
static const BuiltinFuncDef builtin_func_defs[];
+ static const BuiltinFuncOutArgs builtin_func_out_args[];
bool _validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type);
bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = NULL);
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 69acf52e17..baafe2f8d0 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -199,6 +199,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].can_discard = false;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4);
diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp
index 16cda0326d..734c295a72 100644
--- a/servers/visual/visual_server_canvas.cpp
+++ b/servers/visual/visual_server_canvas.cpp
@@ -51,6 +51,23 @@ void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Tra
}
}
+void _collect_ysort_children(VisualServerCanvas::Item *p_canvas_item, Transform2D p_transform, VisualServerCanvas::Item **r_items, int &r_index) {
+ int child_item_count = p_canvas_item->child_items.size();
+ VisualServerCanvas::Item **child_items = p_canvas_item->child_items.ptrw();
+ for (int i = 0; i < child_item_count; i++) {
+ if (r_items) {
+ r_items[r_index] = child_items[i];
+ child_items[i]->ysort_xform = p_transform;
+ child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.elements[2]);
+ }
+
+ r_index++;
+
+ if (child_items[i]->sort_y)
+ _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, r_items, r_index);
+ }
+}
+
void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner) {
Item *ci = p_canvas_item;
@@ -58,10 +75,10 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
if (!ci->visible)
return;
- if (p_canvas_item->children_order_dirty) {
+ if (ci->children_order_dirty) {
- p_canvas_item->child_items.sort_custom<ItemIndexSort>();
- p_canvas_item->children_order_dirty = false;
+ ci->child_items.sort_custom<ItemIndexSort>();
+ ci->children_order_dirty = false;
}
Rect2 rect = ci->get_rect();
@@ -82,8 +99,7 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
return;
int child_item_count = ci->child_items.size();
- Item **child_items = (Item **)alloca(child_item_count * sizeof(Item *));
- copymem(child_items, ci->child_items.ptr(), child_item_count * sizeof(Item *));
+ Item **child_items = ci->child_items.ptrw();
if (ci->clip) {
if (p_canvas_clip != NULL) {
@@ -99,6 +115,17 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
if (ci->sort_y) {
+ if (ci->ysort_children_count == -1) {
+ ci->ysort_children_count = 0;
+ _collect_ysort_children(ci, Transform2D(), NULL, ci->ysort_children_count);
+ }
+
+ child_item_count = ci->ysort_children_count;
+ child_items = (Item **)alloca(child_item_count * sizeof(Item *));
+
+ int i = 0;
+ _collect_ysort_children(ci, Transform2D(), child_items, i);
+
SortArray<Item *, ItemPtrSort> sorter;
sorter.sort(child_items, child_item_count);
}
@@ -110,9 +137,13 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
for (int i = 0; i < child_item_count; i++) {
- if (!child_items[i]->behind)
+ if (!child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y))
continue;
- _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ if (ci->sort_y) {
+ _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ } else {
+ _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ }
}
if (ci->copy_back_buffer) {
@@ -148,9 +179,13 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
for (int i = 0; i < child_item_count; i++) {
- if (child_items[i]->behind)
+ if (child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y))
continue;
- _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ if (ci->sort_y) {
+ _render_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ } else {
+ _render_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner);
+ }
}
}
@@ -300,6 +335,12 @@ void VisualServerCanvas::canvas_item_set_parent(RID p_item, RID p_parent) {
Item *item_owner = canvas_item_owner.get(canvas_item->parent);
item_owner->child_items.erase(canvas_item);
+
+ Item *ysort_owner = item_owner;
+ while (ysort_owner && ysort_owner->sort_y) {
+ item_owner->ysort_children_count = -1;
+ ysort_owner = canvas_item_owner.getornull(ysort_owner->parent);
+ }
}
canvas_item->parent = RID();
@@ -319,6 +360,12 @@ void VisualServerCanvas::canvas_item_set_parent(RID p_item, RID p_parent) {
item_owner->child_items.push_back(canvas_item);
item_owner->children_order_dirty = true;
+ Item *ysort_owner = item_owner;
+ while (ysort_owner && ysort_owner->sort_y) {
+ item_owner->ysort_children_count = -1;
+ ysort_owner = canvas_item_owner.getornull(ysort_owner->parent);
+ }
+
} else {
ERR_EXPLAIN("Invalid parent");
@@ -689,7 +736,7 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2
if (indices.empty()) {
ERR_EXPLAIN("Bad Polygon!");
- ERR_FAIL_V();
+ ERR_FAIL();
}
Item::CommandPolygon *polygon = memnew(Item::CommandPolygon);
@@ -776,7 +823,7 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
canvas_item->commands.push_back(m);
}
-void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames) {
+void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
@@ -786,8 +833,6 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles,
part->particles = p_particles;
part->texture = p_texture;
part->normal_map = p_normal;
- part->h_frames = p_h_frames;
- part->v_frames = p_v_frames;
//take the chance and request processing for them, at least once until they become visible again
VSG::storage->particles_request_process(p_particles);
@@ -828,6 +873,7 @@ void VisualServerCanvas::canvas_item_set_sort_children_by_y(RID p_item, bool p_e
ERR_FAIL_COND(!canvas_item);
canvas_item->sort_y = p_enable;
+ canvas_item->ysort_children_count = -1;
}
void VisualServerCanvas::canvas_item_set_z_index(RID p_item, int p_z) {
@@ -1304,6 +1350,12 @@ bool VisualServerCanvas::free(RID p_rid) {
Item *item_owner = canvas_item_owner.get(canvas_item->parent);
item_owner->child_items.erase(canvas_item);
+
+ Item *ysort_owner = item_owner;
+ while (ysort_owner && ysort_owner->sort_y) {
+ item_owner->ysort_children_count = -1;
+ ysort_owner = canvas_item_owner.getornull(ysort_owner->parent);
+ }
}
}
diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h
index 966b51d341..4b7422b15a 100644
--- a/servers/visual/visual_server_canvas.h
+++ b/servers/visual/visual_server_canvas.h
@@ -48,6 +48,9 @@ public:
bool use_parent_material;
int index;
bool children_order_dirty;
+ int ysort_children_count;
+ Transform2D ysort_xform;
+ Vector2 ysort_pos;
Vector<Item *> child_items;
@@ -61,6 +64,9 @@ public:
use_parent_material = false;
z_relative = true;
index = 0;
+ ysort_children_count = -1;
+ ysort_xform = Transform2D();
+ ysort_pos = Vector2();
}
};
@@ -76,10 +82,10 @@ public:
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
- if (Math::abs(p_left->xform.elements[2].y - p_right->xform.elements[2].y) < CMP_EPSILON)
- return p_left->xform.elements[2].x < p_right->xform.elements[2].x;
+ if (Math::abs(p_left->ysort_pos.y - p_right->ysort_pos.y) < CMP_EPSILON)
+ return p_left->ysort_pos.x < p_right->ysort_pos.x;
else
- return p_left->xform.elements[2].y < p_right->xform.elements[2].y;
+ return p_left->ysort_pos.y < p_right->ysort_pos.y;
}
};
@@ -186,7 +192,7 @@ public:
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, RID p_normal_map = RID());
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
- void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
+ void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal);
void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable);
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 62ba2eab69..d37fe28ac6 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -474,7 +474,7 @@ public:
BIND2(viewport_set_transparent_background, RID, bool)
BIND2(viewport_set_global_canvas_transform, RID, const Transform2D &)
- BIND3(viewport_set_canvas_layer, RID, RID, int)
+ BIND4(viewport_set_canvas_stacking, RID, RID, int, int)
BIND2(viewport_set_shadow_atlas_size, RID, int)
BIND3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int)
BIND2(viewport_set_msaa, RID, ViewportMSAA)
@@ -511,7 +511,7 @@ public:
BIND6(environment_set_adjustment, RID, bool, float, float, float, RID)
BIND5(environment_set_fog, RID, bool, const Color &, const Color &, float)
- BIND6(environment_set_fog_depth, RID, bool, float, float, bool, float)
+ BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float)
BIND5(environment_set_fog_height, RID, bool, float, float, float)
/* SCENARIO API */
@@ -599,7 +599,7 @@ public:
BIND10(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, RID)
BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID)
BIND4(canvas_item_add_multimesh, RID, RID, RID, RID)
- BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
+ BIND4(canvas_item_add_particles, RID, RID, RID, RID)
BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
BIND2(canvas_item_add_clip_ignore, RID, bool)
BIND2(canvas_item_set_sort_children_by_y, RID, bool)
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index 654994b83f..1deca7bc66 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -601,8 +601,9 @@ void VisualServerScene::instance_set_surface_material(RID p_instance, int p_surf
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
- if (instance->update_item.in_list()) {
- _update_dirty_instance(instance);
+ if (instance->base_type == VS::INSTANCE_MESH) {
+ //may not have been updated yet
+ instance->materials.resize(VSG::storage->mesh_get_surface_count(instance->base));
}
ERR_FAIL_INDEX(p_surface, instance->materials.size());
@@ -611,7 +612,7 @@ void VisualServerScene::instance_set_surface_material(RID p_instance, int p_surf
VSG::storage->material_remove_instance_owner(instance->materials[p_surface], instance);
}
instance->materials.write[p_surface] = p_material;
- instance->base_material_changed();
+ instance->base_changed(false, true);
if (instance->materials[p_surface].is_valid()) {
VSG::storage->material_add_instance_owner(instance->materials[p_surface], instance);
@@ -839,7 +840,7 @@ void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instanc
ERR_FAIL_COND(!instance);
instance->cast_shadows = p_shadow_casting_setting;
- instance->base_material_changed(); // to actually compute if shadows are visible or not
+ instance->base_changed(false, true); // to actually compute if shadows are visible or not
}
void VisualServerScene::instance_geometry_set_material_override(RID p_instance, RID p_material) {
@@ -850,7 +851,7 @@ void VisualServerScene::instance_geometry_set_material_override(RID p_instance,
VSG::storage->material_remove_instance_owner(instance->material_override, instance);
}
instance->material_override = p_material;
- instance->base_material_changed();
+ instance->base_changed(false, true);
if (instance->material_override.is_valid()) {
VSG::storage->material_add_instance_owner(instance->material_override, instance);
@@ -1263,13 +1264,15 @@ void VisualServerScene::_update_instance_lightmap_captures(Instance *p_instance)
}
}
-void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario) {
+bool VisualServerScene::_light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario) {
InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data);
Transform light_transform = p_instance->transform;
light_transform.orthonormalize(); //scale does not count on lights
+ bool animated_material_found = false;
+
switch (VSG::storage->light_get_type(p_instance->base)) {
case VS::LIGHT_DIRECTIONAL: {
@@ -1302,6 +1305,10 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
continue;
}
+ if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) {
+ animated_material_found = true;
+ }
+
float max, min;
instance->transformed_aabb.project_range_in_plane(base, min, max);
@@ -1557,6 +1564,10 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
SWAP(instance_shadow_cull_result[j], instance_shadow_cull_result[cull_count]);
j--;
} else {
+ if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) {
+ animated_material_found = true;
+ }
+
instance->depth = near_plane.distance_to(instance->transform.origin);
instance->depth_layer = 0;
}
@@ -1608,6 +1619,9 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
SWAP(instance_shadow_cull_result[j], instance_shadow_cull_result[cull_count]);
j--;
} else {
+ if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) {
+ animated_material_found = true;
+ }
instance->depth = near_plane.distance_to(instance->transform.origin);
instance->depth_layer = 0;
}
@@ -1644,6 +1658,9 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
SWAP(instance_shadow_cull_result[j], instance_shadow_cull_result[cull_count]);
j--;
} else {
+ if (static_cast<InstanceGeometryData *>(instance->base_data)->material_is_animated) {
+ animated_material_found = true;
+ }
instance->depth = near_plane.distance_to(instance->transform.origin);
instance->depth_layer = 0;
}
@@ -1654,6 +1671,8 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
} break;
}
+
+ return animated_material_found;
}
void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) {
@@ -1893,9 +1912,14 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca
if (ins->base_type == VS::INSTANCE_PARTICLES) {
//particles visible? process them
- VSG::storage->particles_request_process(ins->base);
- //particles visible? request redraw
- VisualServerRaster::redraw_request();
+ if (VSG::storage->particles_is_inactive(ins->base)) {
+ //but if nothing is going on, don't do it.
+ keep = false;
+ } else {
+ VSG::storage->particles_request_process(ins->base);
+ //particles visible? request redraw
+ VisualServerRaster::redraw_request();
+ }
}
if (geom->lighting_dirty) {
@@ -2095,7 +2119,7 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca
if (redraw) {
//must redraw!
- _light_instance_update_shadow(ins, p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario);
+ light->shadow_dirty = _light_instance_update_shadow(ins, p_cam_transform, p_cam_projection, p_cam_orthogonal, p_shadow_atlas, scenario);
}
}
}
@@ -3243,11 +3267,13 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data);
bool can_cast_shadows = true;
+ bool is_animated = false;
if (p_instance->cast_shadows == VS::SHADOW_CASTING_SETTING_OFF) {
can_cast_shadows = false;
} else if (p_instance->material_override.is_valid()) {
can_cast_shadows = VSG::storage->material_casts_shadows(p_instance->material_override);
+ is_animated = VSG::storage->material_is_animated(p_instance->material_override);
} else {
if (p_instance->base_type == VS::INSTANCE_MESH) {
@@ -3262,12 +3288,15 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
if (!mat.is_valid()) {
cast_shadows = true;
- break;
- }
+ } else {
- if (VSG::storage->material_casts_shadows(mat)) {
- cast_shadows = true;
- break;
+ if (VSG::storage->material_casts_shadows(mat)) {
+ cast_shadows = true;
+ }
+
+ if (VSG::storage->material_is_animated(mat)) {
+ is_animated = true;
+ }
}
}
@@ -3289,12 +3318,15 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
if (!mat.is_valid()) {
cast_shadows = true;
- break;
- }
- if (VSG::storage->material_casts_shadows(mat)) {
- cast_shadows = true;
- break;
+ } else {
+
+ if (VSG::storage->material_casts_shadows(mat)) {
+ cast_shadows = true;
+ }
+ if (VSG::storage->material_is_animated(mat)) {
+ is_animated = true;
+ }
}
}
@@ -3311,6 +3343,10 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
} else {
can_cast_shadows = false;
}
+
+ if (mat.is_valid() && VSG::storage->material_is_animated(mat)) {
+ is_animated = true;
+ }
} else if (p_instance->base_type == VS::INSTANCE_PARTICLES) {
bool cast_shadows = false;
@@ -3330,12 +3366,15 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
if (!mat.is_valid()) {
cast_shadows = true;
- break;
- }
+ } else {
- if (VSG::storage->material_casts_shadows(mat)) {
- cast_shadows = true;
- break;
+ if (VSG::storage->material_casts_shadows(mat)) {
+ cast_shadows = true;
+ }
+
+ if (VSG::storage->material_is_animated(mat)) {
+ is_animated = true;
+ }
}
}
}
@@ -3355,6 +3394,8 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
geom->can_cast_shadows = can_cast_shadows;
}
+
+ geom->material_is_animated = is_animated;
}
}
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index 0d4737f268..38c5258116 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -192,14 +192,9 @@ public:
singleton->instance_set_base(self, RID());
}
- virtual void base_changed() {
+ virtual void base_changed(bool p_aabb, bool p_materials) {
- singleton->_instance_queue_update(this, true, true);
- }
-
- virtual void base_material_changed() {
-
- singleton->_instance_queue_update(this, false, true);
+ singleton->_instance_queue_update(this, p_aabb, p_materials);
}
Instance() :
@@ -247,6 +242,7 @@ public:
List<Instance *> lighting;
bool lighting_dirty;
bool can_cast_shadows;
+ bool material_is_animated;
List<Instance *> reflection_probes;
bool reflection_dirty;
@@ -261,6 +257,7 @@ public:
lighting_dirty = false;
reflection_dirty = true;
can_cast_shadows = true;
+ material_is_animated = true;
gi_probes_dirty = true;
}
};
@@ -488,7 +485,7 @@ public:
_FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance);
_FORCE_INLINE_ void _update_instance_lightmap_captures(Instance *p_instance);
- _FORCE_INLINE_ void _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
+ _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe);
void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp
index b286533590..571b71db85 100644
--- a/servers/visual/visual_server_viewport.cpp
+++ b/servers/visual/visual_server_viewport.cpp
@@ -137,7 +137,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
}
}
- canvas_map[Viewport::CanvasKey(E->key(), E->get().layer)] = &E->get();
+ canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get();
}
if (lights_with_shadow) {
@@ -176,7 +176,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
VSG::rasterizer->restore_render_target();
- if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().layer > scenario_canvas_max_layer) {
+ if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) {
Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface();
if (!can_draw_3d) {
@@ -209,7 +209,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
VSG::canvas->render_canvas(canvas, xform, canvas_lights, lights_with_mask, clip_rect);
i++;
- if (scenario_draw_canvas_bg && E->key().layer >= scenario_canvas_max_layer) {
+ if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) {
Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface();
if (!can_draw_3d) {
@@ -496,6 +496,7 @@ void VisualServerViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas)
canvas->viewports.insert(p_viewport);
viewport->canvas_map[p_canvas] = Viewport::CanvasData();
viewport->canvas_map[p_canvas].layer = 0;
+ viewport->canvas_map[p_canvas].sublayer = 0;
viewport->canvas_map[p_canvas].canvas = canvas;
}
@@ -534,13 +535,14 @@ void VisualServerViewport::viewport_set_global_canvas_transform(RID p_viewport,
viewport->global_transform = p_transform;
}
-void VisualServerViewport::viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer) {
+void VisualServerViewport::viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) {
Viewport *viewport = viewport_owner.getornull(p_viewport);
ERR_FAIL_COND(!viewport);
ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas));
viewport->canvas_map[p_canvas].layer = p_layer;
+ viewport->canvas_map[p_canvas].sublayer = p_sublayer;
}
void VisualServerViewport::viewport_set_shadow_atlas_size(RID p_viewport, int p_size) {
diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h
index cb7912d6f4..66baa48458 100644
--- a/servers/visual/visual_server_viewport.h
+++ b/servers/visual/visual_server_viewport.h
@@ -78,17 +78,21 @@ public:
struct CanvasKey {
- int layer;
+ int64_t stacking;
RID canvas;
bool operator<(const CanvasKey &p_canvas) const {
- if (layer == p_canvas.layer) return canvas < p_canvas.canvas;
- return layer < p_canvas.layer;
+ if (stacking == p_canvas.stacking)
+ return canvas < p_canvas.canvas;
+ return stacking < p_canvas.stacking;
+ }
+ CanvasKey() {
+ stacking = 0;
}
- CanvasKey() { layer = 0; }
- CanvasKey(const RID &p_canvas, int p_layer) {
+ CanvasKey(const RID &p_canvas, int p_layer, int p_sublayer) {
canvas = p_canvas;
- layer = p_layer;
+ stacking = ((int64_t)p_layer << 32) + p_sublayer;
}
+ int get_layer() const { return stacking >> 32; }
};
struct CanvasData {
@@ -96,6 +100,7 @@ public:
CanvasBase *canvas;
Transform2D transform;
int layer;
+ int sublayer;
};
Transform2D global_transform;
@@ -176,7 +181,7 @@ public:
void viewport_set_transparent_background(RID p_viewport, bool p_enabled);
void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform);
- void viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer);
+ void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer);
void viewport_set_shadow_atlas_size(RID p_viewport, int p_size);
void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv);
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index e4d69121f0..1aeb2756ba 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -400,7 +400,7 @@ public:
FUNC2(viewport_set_transparent_background, RID, bool)
FUNC2(viewport_set_global_canvas_transform, RID, const Transform2D &)
- FUNC3(viewport_set_canvas_layer, RID, RID, int)
+ FUNC4(viewport_set_canvas_stacking, RID, RID, int, int)
FUNC2(viewport_set_shadow_atlas_size, RID, int)
FUNC3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int)
FUNC2(viewport_set_msaa, RID, ViewportMSAA)
@@ -437,7 +437,7 @@ public:
FUNC6(environment_set_adjustment, RID, bool, float, float, float, RID)
FUNC5(environment_set_fog, RID, bool, const Color &, const Color &, float)
- FUNC6(environment_set_fog_depth, RID, bool, float, float, bool, float)
+ FUNC7(environment_set_fog_depth, RID, bool, float, float, float, bool, float)
FUNC5(environment_set_fog_height, RID, bool, float, float, float)
FUNCRID(scenario)
@@ -516,7 +516,7 @@ public:
FUNC10(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, RID)
FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID)
FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID)
- FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
+ FUNC4(canvas_item_add_particles, RID, RID, RID, RID)
FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
FUNC2(canvas_item_add_clip_ignore, RID, bool)
FUNC2(canvas_item_set_sort_children_by_y, RID, bool)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 9d684d33d6..e1db123f58 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -343,7 +343,7 @@ RID VisualServer::get_white_texture() {
#define SMALL_VEC2 Vector2(0.00001, 0.00001)
#define SMALL_VEC3 Vector3(0.00001, 0.00001, 0.00001)
-Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_stride, PoolVector<uint8_t> &r_vertex_array, int p_vertex_array_len, PoolVector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> r_bone_aabb) {
+Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_stride, PoolVector<uint8_t> &r_vertex_array, int p_vertex_array_len, PoolVector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb) {
PoolVector<uint8_t>::Write vw = r_vertex_array.write();
@@ -1890,7 +1890,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_canvas_transform", "viewport", "canvas", "offset"), &VisualServer::viewport_set_canvas_transform);
ClassDB::bind_method(D_METHOD("viewport_set_transparent_background", "viewport", "enabled"), &VisualServer::viewport_set_transparent_background);
ClassDB::bind_method(D_METHOD("viewport_set_global_canvas_transform", "viewport", "transform"), &VisualServer::viewport_set_global_canvas_transform);
- ClassDB::bind_method(D_METHOD("viewport_set_canvas_layer", "viewport", "canvas", "layer"), &VisualServer::viewport_set_canvas_layer);
+ ClassDB::bind_method(D_METHOD("viewport_set_canvas_stacking", "viewport", "canvas", "layer", "sublayer"), &VisualServer::viewport_set_canvas_stacking);
ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_size", "viewport", "size"), &VisualServer::viewport_set_shadow_atlas_size);
ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &VisualServer::viewport_set_shadow_atlas_quadrant_subdivision);
ClassDB::bind_method(D_METHOD("viewport_set_msaa", "viewport", "msaa"), &VisualServer::viewport_set_msaa);
@@ -1915,7 +1915,9 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance", "roughness"), &VisualServer::environment_set_ssr);
ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "radius2", "intensity2", "bias", "light_affect", "ao_channel_affect", "color", "quality", "blur", "bilateral_sharpness"), &VisualServer::environment_set_ssao);
ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "color", "sun_color", "sun_amount"), &VisualServer::environment_set_fog);
- ClassDB::bind_method(D_METHOD("environment_set_fog_depth", "env", "enable", "depth_begin", "depth_curve", "transmit", "transmit_curve"), &VisualServer::environment_set_fog_depth);
+
+ ClassDB::bind_method(D_METHOD("environment_set_fog_depth", "env", "enable", "depth_begin", "depth_end", "depth_curve", "transmit", "transmit_curve"), &VisualServer::environment_set_fog_depth);
+
ClassDB::bind_method(D_METHOD("environment_set_fog_height", "env", "enable", "min_height", "max_height", "height_curve"), &VisualServer::environment_set_fog_height);
ClassDB::bind_method(D_METHOD("scenario_create"), &VisualServer::scenario_create);
@@ -1978,7 +1980,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "bones", "weights", "texture", "count", "normal_map"), &VisualServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(Vector<int>()), DEFVAL(Vector<float>()), DEFVAL(RID()), DEFVAL(-1), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_mesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_mesh, DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("canvas_item_add_multimesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_multimesh, DEFVAL(RID()));
- ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map", "h_frames", "v_frames"), &VisualServer::canvas_item_add_particles);
+ ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map"), &VisualServer::canvas_item_add_particles);
ClassDB::bind_method(D_METHOD("canvas_item_add_set_transform", "item", "transform"), &VisualServer::canvas_item_add_set_transform);
ClassDB::bind_method(D_METHOD("canvas_item_add_clip_ignore", "item", "ignore"), &VisualServer::canvas_item_add_clip_ignore);
ClassDB::bind_method(D_METHOD("canvas_item_set_sort_children_by_y", "item", "enabled"), &VisualServer::canvas_item_set_sort_children_by_y);
@@ -2370,6 +2372,7 @@ VisualServer::VisualServer() {
GLOBAL_DEF("rendering/quality/directional_shadow/size", 4096);
GLOBAL_DEF("rendering/quality/directional_shadow/size.mobile", 2048);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/quality/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384"));
GLOBAL_DEF("rendering/quality/shadow_atlas/size", 4096);
GLOBAL_DEF("rendering/quality/shadow_atlas/size.mobile", 2048);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadow_atlas/size", PropertyInfo(Variant::INT, "rendering/quality/shadow_atlas/size", PROPERTY_HINT_RANGE, "256,16384"));
@@ -2400,6 +2403,8 @@ VisualServer::VisualServer() {
GLOBAL_DEF("rendering/quality/depth_prepass/enable", true);
GLOBAL_DEF("rendering/quality/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno");
+
+ GLOBAL_DEF("rendering/quality/filters/use_nearest_mipmap_filter", false);
}
VisualServer::~VisualServer() {
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 9d98fca21b..743e010034 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -61,7 +61,7 @@ protected:
RID white_texture;
RID test_material;
- Error _surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_stride, PoolVector<uint8_t> &r_vertex_array, int p_vertex_array_len, PoolVector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> r_bone_aabb);
+ Error _surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_stride, PoolVector<uint8_t> &r_vertex_array, int p_vertex_array_len, PoolVector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb);
static VisualServer *(*create_func)();
static void _bind_methods();
@@ -644,7 +644,7 @@ public:
virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0;
virtual void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) = 0;
- virtual void viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer) = 0;
+ virtual void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) = 0;
virtual void viewport_set_shadow_atlas_size(RID p_viewport, int p_size) = 0;
virtual void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) = 0;
@@ -763,7 +763,7 @@ public:
virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, EnvironmentSSAOQuality p_quality, EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0;
- virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
+ virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
/* SCENARIO API */
@@ -895,7 +895,7 @@ public:
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, RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
- virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
+ virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map) = 0;
virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 7fdd9e20b0..1e1fea85e5 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -264,9 +264,7 @@ File extracted from upstream source:
- From `roles/ws` exclude `ext` folder.
- From `tls` exclude `openssl` folder.
- Also copy `win32helpers/` from `win32port/` inside `thirdparty/libwebsockets`
-- A small fix has been added in `libwebsockets/libwebsockets.h` to `#include <sys/socket.h>` for the BSD family.
- This change has been PRed upstream, and should be merged before the next update. Remember to check and remove this line.
-- Another fix has been added to allow building for 32-bits UWP, replacing `GetFileSize[Ex]` and `CreateFileW` with supported functions.
+- A fix has been added to allow building for 32-bits UWP, replacing `GetFileSize[Ex]` and `CreateFileW` with supported functions.
There is a diff for this change in `thirdparty/libwebsockets/uwp_fixes.diff`
Important: `lws_config.h` and `lws_config_private.h` contains custom
diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp
index 6ba7cf0000..73a09f9b1d 100644
--- a/thirdparty/enet/godot.cpp
+++ b/thirdparty/enet/godot.cpp
@@ -33,7 +33,7 @@
*/
#include "core/io/ip.h"
-#include "core/io/packet_peer_udp.h"
+#include "core/io/net_socket.h"
#include "core/os/os.h"
// This must be last for windows to compile (tested with MinGW)
@@ -90,6 +90,16 @@ int enet_address_get_host(const ENetAddress *address, char *name, size_t nameLen
return -1;
}
+ENetSocket enet_socket_create(ENetSocketType type) {
+
+ NetSocket *socket = NetSocket::create();
+ IP::Type ip_type = IP::TYPE_ANY;
+ socket->open(NetSocket::TYPE_UDP, ip_type);
+ socket->set_blocking_enabled(false);
+
+ return socket;
+}
+
int enet_socket_bind(ENetSocket socket, const ENetAddress *address) {
IP_Address ip;
@@ -99,23 +109,15 @@ int enet_socket_bind(ENetSocket socket, const ENetAddress *address) {
ip.set_ipv6(address->host);
}
- PacketPeerUDP *sock = (PacketPeerUDP *)socket;
- if (sock->listen(address->port, ip) != OK) {
+ NetSocket *sock = (NetSocket *)socket;
+ if (sock->bind(ip, address->port) != OK) {
return -1;
}
return 0;
}
-ENetSocket enet_socket_create(ENetSocketType type) {
-
- PacketPeerUDP *socket = memnew(PacketPeerUDP);
- socket->set_blocking_mode(false);
-
- return socket;
-}
-
void enet_socket_destroy(ENetSocket socket) {
- PacketPeerUDP *sock = (PacketPeerUDP *)socket;
+ NetSocket *sock = (NetSocket *)socket;
sock->close();
memdelete(sock);
}
@@ -124,13 +126,12 @@ int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBu
ERR_FAIL_COND_V(address == NULL, -1);
- PacketPeerUDP *sock = (PacketPeerUDP *)socket;
+ NetSocket *sock = (NetSocket *)socket;
IP_Address dest;
Error err;
size_t i = 0;
dest.set_ipv6(address->host);
- sock->set_dest_address(dest, address->port);
// Create a single packet.
PoolVector<uint8_t> out;
@@ -148,7 +149,8 @@ int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBu
pos += buffers[i].dataLength;
}
- err = sock->put_packet((const uint8_t *)&w[0], size);
+ int sent = 0;
+ err = sock->sendto((const uint8_t *)&w[0], size, sent, dest, address->port);
if (err != OK) {
if (err == ERR_BUSY) { // Blocking call
@@ -159,32 +161,36 @@ int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBu
return -1;
}
- return size;
+ return sent;
}
int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buffers, size_t bufferCount) {
ERR_FAIL_COND_V(bufferCount != 1, -1);
- PacketPeerUDP *sock = (PacketPeerUDP *)socket;
+ NetSocket *sock = (NetSocket *)socket;
- int pc = sock->get_available_packet_count();
- if (pc < 1) {
- return pc;
- }
+ Error ret = sock->poll(NetSocket::POLL_TYPE_IN, 0);
- const uint8_t *buffer;
- int buffer_size;
- Error err = sock->get_packet(&buffer, buffer_size);
- if (err)
+ if (ret == ERR_BUSY)
+ return 0;
+
+ if (ret != OK)
return -1;
- copymem(buffers[0].data, buffer, buffer_size);
+ int read;
+ IP_Address ip;
- enet_address_set_ip(address, sock->get_packet_address().get_ipv6(), 16);
- address->port = sock->get_packet_port();
+ Error err = sock->recvfrom((uint8_t *)buffers[0].data, buffers[0].dataLength, read, ip, address->port);
+ if (err == ERR_BUSY)
+ return 0;
+
+ if (err != OK)
+ return -1;
+
+ enet_address_set_ip(address, ip.get_ipv6(), 16);
- return buffer_size;
+ return read;
}
// Not implemented
diff --git a/thirdparty/libwebsockets/core/context.c b/thirdparty/libwebsockets/core/context.c
index db9151b95f..7be004df33 100644
--- a/thirdparty/libwebsockets/core/context.c
+++ b/thirdparty/libwebsockets/core/context.c
@@ -134,7 +134,7 @@ lws_protocol_vh_priv_get(struct lws_vhost *vhost,
{
int n = 0;
- if (!vhost || !vhost->protocol_vh_privs)
+ if (!vhost || !vhost->protocol_vh_privs || !prot)
return NULL;
while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
@@ -808,7 +808,7 @@ lws_create_vhost(struct lws_context *context,
#ifdef LWS_WITH_ACCESS_LOG
if (info->log_filepath) {
- vh->log_fd = open(info->log_filepath,
+ vh->log_fd = lws_open(info->log_filepath,
O_CREAT | O_APPEND | O_RDWR, 0600);
if (vh->log_fd == (int)LWS_INVALID_FILE) {
lwsl_err("unable to open log filepath %s\n",
@@ -936,24 +936,29 @@ lws_create_event_pipes(struct lws_context *context)
wsi->tsi = n;
wsi->vhost = NULL;
wsi->event_pipe = 1;
+ wsi->desc.sockfd = LWS_SOCK_INVALID;
+ context->pt[n].pipe_wsi = wsi;
+ context->count_wsi_allocated++;
- if (lws_plat_pipe_create(wsi)) {
- lws_free(wsi);
+ if (lws_plat_pipe_create(wsi))
+ /*
+ * platform code returns 0 if it actually created pipes
+ * and initialized pt->dummy_pipe_fds[]. If it used
+ * some other mechanism outside of signaling in the
+ * normal event loop, we skip treating the pipe as
+ * related to dummy_pipe_fds[], adding it to the fds,
+ * etc.
+ */
continue;
- }
+
wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0];
lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd);
- context->pt[n].pipe_wsi = wsi;
-
if (context->event_loop_ops->accept)
context->event_loop_ops->accept(wsi);
if (__insert_wsi_socket_into_fds(context, wsi))
return 1;
-
- //lws_change_pollfd(context->pt[n].pipe_wsi, 0, LWS_POLLIN);
- context->count_wsi_allocated++;
}
return 0;
diff --git a/thirdparty/libwebsockets/core/libwebsockets.c b/thirdparty/libwebsockets/core/libwebsockets.c
index 0da02b17e4..58f00226f6 100644
--- a/thirdparty/libwebsockets/core/libwebsockets.c
+++ b/thirdparty/libwebsockets/core/libwebsockets.c
@@ -28,7 +28,7 @@
#ifdef LWS_WITH_IPV6
#if defined(WIN32) || defined(_WIN32)
#include <wincrypt.h>
-#include <Iphlpapi.h>
+#include <iphlpapi.h>
#else
#include <net/if.h>
#endif
@@ -58,6 +58,28 @@ static const char * const log_level_names[] = {
};
#endif
+int lws_open(const char *__file, int __oflag, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, __oflag);
+ if (((__oflag & O_CREAT) == O_CREAT)
+#if defined(O_TMPFILE)
+ || ((__oflag & O_TMPFILE) == O_TMPFILE)
+#endif
+ )
+ /* last arg is really a mode_t. But windows... */
+ n = open(__file, __oflag, va_arg(ap, uint32_t));
+ else
+ n = open(__file, __oflag);
+ va_end(ap);
+
+ lws_plat_apply_FD_CLOEXEC(n);
+
+ return n;
+}
+
#if defined (_DEBUG)
void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role)
{
@@ -826,7 +848,15 @@ just_kill_connection:
if (!wsi->protocol)
pro = &wsi->vhost->protocols[0];
- pro->callback(wsi,
+ if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi))
+ /*
+ * The network wsi for a client h2 connection shouldn't
+ * call back for its role: the child stream connections
+ * own the role. Otherwise h2 will call back closed
+ * one too many times as the children do it and then
+ * the closing network stream.
+ */
+ pro->callback(wsi,
wsi->role_ops->close_cb[lwsi_role_server(wsi)],
wsi->user_space, NULL, 0);
wsi->told_user_closed = 1;
@@ -1453,7 +1483,7 @@ lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
pf = fops->next;
while (pf) {
n = 0;
- while (n < (int)ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
+ while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) {
if (p >= vfs_path + pf->fi[n].len)
if (!strncmp(p - (pf->fi[n].len - 1),
pf->fi[n].sig,
@@ -1935,9 +1965,9 @@ static const char * const colours[] = {
"[32;1m", /* LLL_INFO */
"[34;1m", /* LLL_DEBUG */
"[33;1m", /* LLL_PARSER */
- "[33;1m", /* LLL_HEADER */
- "[33;1m", /* LLL_EXT */
- "[33;1m", /* LLL_CLIENT */
+ "[33m", /* LLL_HEADER */
+ "[33m", /* LLL_EXT */
+ "[33m", /* LLL_CLIENT */
"[33;1m", /* LLL_LATENCY */
"[30;1m", /* LLL_USER */
};
@@ -1946,14 +1976,14 @@ LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
{
char buf[50];
static char tty = 3;
- int n, m = ARRAY_SIZE(colours) - 1;
+ int n, m = LWS_ARRAY_SIZE(colours) - 1;
if (!tty)
tty = isatty(2) | 2;
lwsl_timestamp(level, buf, sizeof(buf));
if (tty == 3) {
- n = 1 << (ARRAY_SIZE(colours) - 1);
+ n = 1 << (LWS_ARRAY_SIZE(colours) - 1);
while (n) {
if (level & n)
break;
@@ -2060,7 +2090,9 @@ lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
LWS_VISIBLE void
lwsl_hexdump(const void *vbuf, size_t len)
{
+#if defined(_DEBUG)
lwsl_hexdump_level(LLL_DEBUG, vbuf, len);
+#endif
}
LWS_VISIBLE int
@@ -2091,6 +2123,8 @@ lws_partial_buffered(struct lws *wsi)
LWS_VISIBLE lws_fileofs_t
lws_get_peer_write_allowance(struct lws *wsi)
{
+ if (!wsi->role_ops->tx_credit)
+ return -1;
return wsi->role_ops->tx_credit(wsi);
}
@@ -3364,7 +3398,7 @@ lws_stats_log_dump(struct lws_context *context)
wl = pt->http.ah_wait_list;
while (wl) {
m++;
- wl = wl->ah_wait_list;
+ wl = wl->http.ah_wait_list;
}
lwsl_notice(" AH wait list count / actual: %d / %d\n",
@@ -3401,7 +3435,8 @@ lws_stats_log_dump(struct lws_context *context)
strcpy(buf, "unknown");
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n",
- buf, df->count_wsi, df->count_ah);
+ buf, df->count_wsi,
+ df->http.count_ah);
#else
lwsl_notice(" peer %s: count wsi: %d\n",
buf, df->count_wsi);
diff --git a/thirdparty/libwebsockets/core/output.c b/thirdparty/libwebsockets/core/output.c
index e2ff18ef27..11965a06b9 100644
--- a/thirdparty/libwebsockets/core/output.c
+++ b/thirdparty/libwebsockets/core/output.c
@@ -126,6 +126,18 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
lwsl_info("** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+#if !defined(LWS_WITHOUT_SERVER)
+ if (wsi->http.deferred_transaction_completed) {
+ lwsl_notice("%s: partial completed, doing "
+ "deferred transaction completed\n",
+ __func__);
+ wsi->http.deferred_transaction_completed = 0;
+ return lws_http_transaction_completed(wsi);
+ }
+#endif
+#endif
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
diff --git a/thirdparty/libwebsockets/core/private.h b/thirdparty/libwebsockets/core/private.h
index d6b494ac8c..73748b0498 100644
--- a/thirdparty/libwebsockets/core/private.h
+++ b/thirdparty/libwebsockets/core/private.h
@@ -232,7 +232,7 @@
#endif
#else /* not windows */
- static inline int compatible_close(int fd) { return close(fd); }
+ static LWS_INLINE int compatible_close(int fd) { return close(fd); }
#include <sys/stat.h>
#include <sys/time.h>
@@ -351,7 +351,15 @@ extern "C" {
#define LWS_H2_RX_SCRATCH_SIZE 512
-
+#if defined(WIN32) || defined(_WIN32)
+ // Visual studio older than 2015 and WIN_CE has only _stricmp
+ #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE)
+ #define strcasecmp _stricmp
+ #elif !defined(__MINGW32__)
+ #define strcasecmp stricmp
+ #endif
+ #define getdtablesize() 30000
+#endif
/*
* All lws_tls...() functions must return this type, converting the
@@ -863,6 +871,7 @@ struct lws_context {
unsigned int timeout_secs;
unsigned int pt_serv_buf_size;
int max_http_header_data;
+ int max_http_header_pool;
int simultaneous_ssl_restriction;
int simultaneous_ssl;
#if defined(LWS_WITH_PEER_LIMITS)
@@ -889,7 +898,6 @@ struct lws_context {
volatile int service_tid;
int service_tid_detected;
- short max_http_header_pool;
short count_threads;
short plugin_protocol_count;
short plugin_extension_count;
@@ -1216,7 +1224,7 @@ LWS_EXTERN int
lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
#ifndef LWS_LATENCY
-static inline void
+static LWS_INLINE void
lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
int ret, int completion) {
do {
@@ -1224,7 +1232,7 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
(void)completion;
} while (0);
}
-static inline void
+static LWS_INLINE void
lws_latency_pre(struct lws_context *context, struct lws *wsi) {
do { (void)context; (void)wsi; } while (0);
}
@@ -1597,6 +1605,9 @@ void lws_free(void *p);
#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0)
#endif
+char *
+lws_strdup(const char *s);
+
int
lws_plat_pipe_create(struct lws *wsi);
int
@@ -1606,6 +1617,9 @@ lws_plat_pipe_close(struct lws *wsi);
int
lws_create_event_pipes(struct lws_context *context);
+int lws_open(const char *__file, int __oflag, ...);
+void lws_plat_apply_FD_CLOEXEC(int n);
+
const struct lws_plat_file_ops *
lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
const char **vpath);
@@ -1673,10 +1687,10 @@ lws_broadcast(struct lws_context *context, int reason, void *in, size_t len);
lws_stats_atomic_max(struct lws_context * context,
struct lws_context_per_thread *pt, int index, uint64_t val);
#else
- static inline uint64_t lws_stats_atomic_bump(struct lws_context * context,
+ static LWS_INLINE uint64_t lws_stats_atomic_bump(struct lws_context * context,
struct lws_context_per_thread *pt, int index, uint64_t bump) {
(void)context; (void)pt; (void)index; (void)bump; return 0; }
- static inline uint64_t lws_stats_atomic_max(struct lws_context * context,
+ static LWS_INLINE uint64_t lws_stats_atomic_max(struct lws_context * context,
struct lws_context_per_thread *pt, int index, uint64_t val) {
(void)context; (void)pt; (void)index; (void)val; return 0; }
#endif
@@ -1703,6 +1717,11 @@ void
lws_peer_dump_from_wsi(struct lws *wsi);
#endif
+#ifdef LWS_WITH_HTTP_PROXY
+hubbub_error
+html_parser_cb(const hubbub_token *token, void *pw);
+#endif
+
void
__lws_remove_from_timeout_list(struct lws *wsi);
diff --git a/thirdparty/libwebsockets/libwebsockets.h b/thirdparty/libwebsockets/libwebsockets.h
index 7ae563d582..2c01696404 100644
--- a/thirdparty/libwebsockets/libwebsockets.h
+++ b/thirdparty/libwebsockets/libwebsockets.h
@@ -66,14 +66,6 @@ typedef unsigned long long lws_intptr_t;
#define O_RDONLY _O_RDONLY
#endif
-// Visual studio older than 2015 and WIN_CE has only _stricmp
-#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE)
-#define strcasecmp _stricmp
-#elif !defined(__MINGW32__)
-#define strcasecmp stricmp
-#endif
-#define getdtablesize() 30000
-
#define LWS_INLINE __inline
#define LWS_VISIBLE
#define LWS_WARN_UNUSED_RESULT
@@ -150,6 +142,7 @@ typedef unsigned long long lws_intptr_t;
#endif
#if defined(__ANDROID__)
+#include <netinet/in.h>
#include <unistd.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
#endif
@@ -164,6 +157,9 @@ typedef unsigned long long lws_intptr_t;
#ifdef LWS_HAVE_UV_VERSION_H
#include <uv-version.h>
#endif
+#ifdef LWS_HAVE_NEW_UV_VERSION_H
+#include <uv/version.h>
+#endif
#endif /* LWS_WITH_LIBUV */
#if defined(LWS_WITH_LIBEVENT)
#include <event2/event.h>
@@ -456,9 +452,6 @@ lwsl_visible(int level);
#endif
struct lws;
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-#endif
typedef int64_t lws_usec_t;
@@ -540,7 +533,7 @@ struct timer_mapping {
#define lws_uv_getloop(a, b) (NULL)
-static inline void uv_timer_init(void *l, uv_timer_t *t)
+static LWS_INLINE void uv_timer_init(void *l, uv_timer_t *t)
{
(void)l;
*t = NULL;
@@ -548,7 +541,7 @@ static inline void uv_timer_init(void *l, uv_timer_t *t)
extern void esp32_uvtimer_cb(TimerHandle_t t);
-static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep)
+static LWS_INLINE void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep)
{
struct timer_mapping *tm = (struct timer_mapping *)malloc(sizeof(*tm));
@@ -563,12 +556,12 @@ static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep
xTimerStart(*t, 0);
}
-static inline void uv_timer_stop(uv_timer_t *t)
+static LWS_INLINE void uv_timer_stop(uv_timer_t *t)
{
xTimerStop(*t, 0);
}
-static inline void uv_close(uv_handle_t *h, void *v)
+static LWS_INLINE void uv_close(uv_handle_t *h, void *v)
{
free(pvTimerGetTimerID((uv_timer_t)h));
xTimerDelete(*(uv_timer_t *)h, 0);
@@ -1757,7 +1750,7 @@ lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result);
* If the return is nonzero, it failed and there is nothing needing to be
* destroyed.
*/
-int
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
const uint8_t *key, size_t key_len);
@@ -1771,7 +1764,7 @@ lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
*
* If the return is nonzero, it failed and needs destroying.
*/
-int
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len);
/** lws_genhmac_destroy() - copy out the result digest and destroy the ctx
@@ -1785,7 +1778,7 @@ lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len);
* NULL result is supported so that you can destroy the ctx cleanly on error
* conditions, where there is no valid result.
*/
-int
+LWS_VISIBLE LWS_EXTERN int
lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result);
///@}
@@ -2812,7 +2805,7 @@ struct lws_context_creation_info {
/**< VHOST: pointer to optional linked list of per-vhost
* options made accessible to protocols */
int keepalive_timeout;
- /**< VHOST: (default = 0 = 60s) seconds to allow remote
+ /**< VHOST: (default = 0 = 5s) seconds to allow remote
* client to hold on to an idle HTTP/1.1 connection */
const char *log_filepath;
/**< VHOST: filepath to append logs to... this is opened before
@@ -4943,7 +4936,7 @@ lws_write(struct lws *wsi, unsigned char *buf, size_t len,
lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP)
/* helper for multi-frame ws message flags */
-static inline int
+static LWS_INLINE int
lws_write_ws_flags(int initial, int is_start, int is_end)
{
int r;
@@ -5621,13 +5614,13 @@ struct lws_dll_lws { /* typed as struct lws * */
#define lws_dll_is_null(___dll) (!(___dll)->prev && !(___dll)->next)
-static inline void
+static LWS_INLINE void
lws_dll_lws_add_front(struct lws_dll_lws *_a, struct lws_dll_lws *_head)
{
lws_dll_add_front((struct lws_dll *)_a, (struct lws_dll *)_head);
}
-static inline void
+static LWS_INLINE void
lws_dll_lws_remove(struct lws_dll_lws *_a)
{
lws_dll_remove((struct lws_dll *)_a);
@@ -7064,9 +7057,6 @@ lws_email_destroy(struct lws_email *email);
//@{
struct lejp_ctx;
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
-#endif
#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
#define LEJP_FLAG_WS_KEEP 64
#define LEJP_FLAG_WS_COMMENTLINE 32
@@ -7219,7 +7209,7 @@ typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
#endif
#ifndef LEJP_STRING_CHUNK
/* must be >= 30 to assemble floats */
-#define LEJP_STRING_CHUNK 255
+#define LEJP_STRING_CHUNK 254
#endif
enum num_flags {
@@ -7253,7 +7243,7 @@ struct lejp_ctx {
uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */
uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
char path[LEJP_MAX_PATH];
- char buf[LEJP_STRING_CHUNK];
+ char buf[LEJP_STRING_CHUNK + 1];
/* int */
diff --git a/thirdparty/libwebsockets/lws_config.h b/thirdparty/libwebsockets/lws_config.h
index e5e15cc2fd..86ce9ac38a 100644
--- a/thirdparty/libwebsockets/lws_config.h
+++ b/thirdparty/libwebsockets/lws_config.h
@@ -174,7 +174,7 @@
#define LWS_HAVE_MALLOC_H
#endif
-#if !defined(IPHONE_ENABLED) && !defined(OSX_ENABLED) && !defined(__HAIKU__)
+#if !defined(__APPLE__) && !defined(__HAIKU__)
#define LWS_HAVE_PIPE2
#endif
diff --git a/thirdparty/libwebsockets/lws_config_private.h b/thirdparty/libwebsockets/lws_config_private.h
index b26d225afa..e531777624 100644
--- a/thirdparty/libwebsockets/lws_config_private.h
+++ b/thirdparty/libwebsockets/lws_config_private.h
@@ -81,7 +81,7 @@
/* Define to 1 if you have the <sys/prctl.h> header file. */
#define LWS_HAVE_SYS_PRCTL_H
-#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
#undef LWS_HAVE_SYS_PRCTL_H
#endif
diff --git a/thirdparty/libwebsockets/misc/lejp.c b/thirdparty/libwebsockets/misc/lejp.c
index 00d350f81e..99142b9553 100644
--- a/thirdparty/libwebsockets/misc/lejp.c
+++ b/thirdparty/libwebsockets/misc/lejp.c
@@ -20,6 +20,7 @@
*/
#include <libwebsockets.h>
+#include "core/private.h"
#include <string.h>
#include <stdio.h>
@@ -30,7 +31,7 @@
* \param callback: your user callback which will received parsed tokens
* \param user: optional user data pointer untouched by lejp
* \param paths: your array of name elements you are interested in
- * \param count_paths: ARRAY_SIZE() of @paths
+ * \param count_paths: LWS_ARRAY_SIZE() of @paths
*
* Prepares your context struct for use with lejp
*/
@@ -250,7 +251,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
case LEJP_MP_STRING:
if (c == '\"') {
- if (!ctx->sp) {
+ if (!ctx->sp) { /* JSON can't end on quote */
ret = LEJP_REJECT_MP_STRING_UNDERRUN;
goto reject;
}
@@ -417,7 +418,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
}
ctx->i[ctx->ipos++] = 0;
- if (ctx->ipos > ARRAY_SIZE(ctx->i)) {
+ if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
@@ -425,17 +426,23 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
case ']':
/* pop */
+ if (!ctx->sp) { /* JSON can't end on ] */
+ ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
+ goto reject;
+ }
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
- ctx->ppos = ctx->st[ctx->sp - 1].p;
- ctx->ipos = ctx->st[ctx->sp - 1].i;
+ if (ctx->sp) {
+ ctx->ppos = ctx->st[ctx->sp - 1].p;
+ ctx->ipos = ctx->st[ctx->sp - 1].i;
+ }
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
- ctx->ppos <= ctx->path_match_len)
+ ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
@@ -603,7 +610,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
break;
}
if (c == ']') {
- if (!ctx->sp) {
+ if (!ctx->sp) { /* JSON can't end on ] */
ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
goto reject;
}
@@ -631,7 +638,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto redo_character;
}
if (c == '}') {
- if (ctx->sp == 0) {
+ if (!ctx->sp) {
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_END)) {
ret = LEJP_REJECT_CALLBACK;
@@ -716,7 +723,7 @@ add_stack_level:
ctx->st[ctx->sp].p = ctx->ppos;
ctx->st[ctx->sp].i = ctx->ipos;
- if (++ctx->sp == ARRAY_SIZE(ctx->st)) {
+ if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) {
ret = LEJP_REJECT_STACK_OVERFLOW;
goto reject;
}
diff --git a/thirdparty/libwebsockets/plat/lws-plat-unix.c b/thirdparty/libwebsockets/plat/lws-plat-unix.c
index bacc6af647..7dba3bd82f 100644
--- a/thirdparty/libwebsockets/plat/lws-plat-unix.c
+++ b/thirdparty/libwebsockets/plat/lws-plat-unix.c
@@ -30,6 +30,12 @@
#endif
#include <dirent.h>
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+ if (n != -1)
+ fcntl(n, F_SETFD, FD_CLOEXEC );
+}
+
int
lws_plat_socket_offset(void)
{
@@ -330,6 +336,8 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
struct protoent *tcp_proto;
#endif
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
if (vhost->ka_time) {
/* enable keepalive on this socket */
optval = 1;
@@ -952,7 +960,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
- int n, fd = open(filename, O_RDONLY);
+ int n, fd = lws_open(filename, O_RDONLY);
if (fd == -1)
return -1;
diff --git a/thirdparty/libwebsockets/plat/lws-plat-win.c b/thirdparty/libwebsockets/plat/lws-plat-win.c
index 8a43081ef1..1850b64250 100644
--- a/thirdparty/libwebsockets/plat/lws-plat-win.c
+++ b/thirdparty/libwebsockets/plat/lws-plat-win.c
@@ -3,6 +3,10 @@
#endif
#include "core/private.h"
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+}
+
int
lws_plat_socket_offset(void)
{
@@ -54,7 +58,7 @@ time_in_microseconds()
memcpy(&datetime, &filetime, sizeof(datetime));
/* Windows file times are in 100s of nanoseconds. */
- return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10;
+ return (datetime.QuadPart / 10) - DELTA_EPOCH_IN_MICROSECS;
}
#ifdef _WIN32_WCE
@@ -229,23 +233,21 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
continue;
wsi = wsi_from_fd(context, pfd->fd);
- if (wsi->listener)
+ if (!wsi || wsi->listener)
continue;
- if (!wsi || wsi->sock_send_blocking)
+ if (wsi->sock_send_blocking)
continue;
pfd->revents = LWS_POLLOUT;
n = lws_service_fd(context, pfd);
if (n < 0)
return -1;
+
+ /* Force WSAWaitForMultipleEvents() to check events and then return immediately. */
+ timeout_ms = 0;
+
/* if something closed, retry this slot */
if (n)
i--;
-
- /*
- * any wsi has truncated, force him signalled
- */
- if (wsi->trunc_len)
- WSASetEvent(pt->events[0]);
}
/*
@@ -261,9 +263,11 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
}
if (timeout_ms) {
+ lws_usec_t t;
+
lws_pt_lock(pt, __func__);
/* don't stay in poll wait longer than next hr timeout */
- lws_usec_t t = __lws_hrtimer_service(pt);
+ t = __lws_hrtimer_service(pt);
if ((lws_usec_t)timeout_ms * 1000 > t)
timeout_ms = (int)(t / 1000);
@@ -310,8 +314,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
if (pfd->revents & LWS_POLLHUP)
--eIdx;
- if (pfd->revents)
+ if (pfd->revents) {
+ recv(pfd->fd, NULL, 0, 0);
lws_service_fd_tsi(context, pfd, tsi);
+ }
}
}
@@ -637,7 +643,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
lws_fop_fd_t fop_fd;
FILE_STANDARD_INFO fInfo = {0};
- MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf));
+ MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, LWS_ARRAY_SIZE(buf));
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 // Windows 8 (minimum when UWP_ENABLED, but can be used in Windows builds)
CREATEFILE2_EXTENDED_PARAMETERS extParams = {0};
@@ -810,7 +816,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
{
int m, fd;
- fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
return -1;
@@ -824,7 +830,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
- int n, fd = open(filename, O_RDONLY);
+ int n, fd = lws_open(filename, O_RDONLY);
if (fd == -1)
return -1;
diff --git a/thirdparty/libwebsockets/roles/h1/ops-h1.c b/thirdparty/libwebsockets/roles/h1/ops-h1.c
index d3b16f4d1f..9001c864ea 100644
--- a/thirdparty/libwebsockets/roles/h1/ops-h1.c
+++ b/thirdparty/libwebsockets/roles/h1/ops-h1.c
@@ -195,6 +195,7 @@ postbody_completion:
}
break;
+ case LRS_RETURNED_CLOSE:
case LRS_AWAITING_CLOSE_ACK:
case LRS_WAITING_TO_SEND_CLOSE:
case LRS_SHUTDOWN:
@@ -444,6 +445,16 @@ try_pollout:
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
+ if (wsi->trunc_len) {
+ //lwsl_notice("%s: completing partial\n", __func__);
+ if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
+ wsi->trunc_len) < 0) {
+ lwsl_info("%s signalling to close\n", __func__);
+ goto fail;
+ }
+ return LWS_HPI_RET_HANDLED;
+ }
+
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_WRITEABLE_CB, 1);
#if defined(LWS_WITH_STATS)
@@ -655,6 +666,9 @@ rops_destroy_role_h1(struct lws *wsi)
ah = ah->next;
}
+#ifdef LWS_ROLE_WS
+ lws_free_set_NULL(wsi->ws);
+#endif
return 0;
}
diff --git a/thirdparty/libwebsockets/roles/http/client/client-handshake.c b/thirdparty/libwebsockets/roles/http/client/client-handshake.c
index 4830fc9eca..0095c79a69 100644
--- a/thirdparty/libwebsockets/roles/http/client/client-handshake.c
+++ b/thirdparty/libwebsockets/roles/http/client/client-handshake.c
@@ -162,7 +162,7 @@ create_new_conn:
if (!wsi->client_hostname_copy)
wsi->client_hostname_copy =
- strdup(lws_hdr_simple_ptr(wsi,
+ lws_strdup(lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS));
/*
@@ -654,13 +654,13 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
lws_ssl_close(wsi);
#endif
+ __remove_wsi_socket_from_fds(wsi);
+
if (wsi->context->event_loop_ops->close_handle_manually)
wsi->context->event_loop_ops->close_handle_manually(wsi);
else
compatible_close(wsi->desc.sockfd);
- __remove_wsi_socket_from_fds(wsi);
-
#if defined(LWS_WITH_TLS)
wsi->tls.use_ssl = ssl;
#else
@@ -717,7 +717,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
}
#ifdef LWS_WITH_HTTP_PROXY
-static hubbub_error
+hubbub_error
html_parser_cb(const hubbub_token *token, void *pw)
{
struct lws_rewrite *r = (struct lws_rewrite *)pw;
@@ -846,7 +846,7 @@ html_parser_cb(const hubbub_token *token, void *pw)
#endif
-static char *
+char *
lws_strdup(const char *s)
{
char *d = lws_malloc(strlen(s) + 1, "strdup");
diff --git a/thirdparty/libwebsockets/roles/http/client/client.c b/thirdparty/libwebsockets/roles/http/client/client.c
index ce42dc6cd3..5645fa2b7a 100644
--- a/thirdparty/libwebsockets/roles/http/client/client.c
+++ b/thirdparty/libwebsockets/roles/http/client/client.c
@@ -93,7 +93,7 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
char *sb = p;
int n = 0;
#if defined(LWS_WITH_SOCKS5)
- char conn_mode = 0, pending_timeout = 0;
+ int conn_mode = 0, pending_timeout = 0;
#endif
if ((pollfd->revents & LWS_POLLOUT) &&
@@ -252,6 +252,8 @@ socks_reply_fail:
/* clear his proxy connection timeout */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
goto start_ws_handshake;
+ default:
+ break;
}
break;
#endif
@@ -578,6 +580,8 @@ lws_http_transaction_completed_client(struct lws *wsi)
"queued client done");
}
+ _lws_header_table_reset(wsi->http.ah);
+
/* after the first one, they can only be coming from the queue */
wsi->transaction_from_pipeline_queue = 1;
@@ -629,12 +633,20 @@ lws_http_transaction_completed_client(struct lws *wsi)
}
LWS_VISIBLE LWS_EXTERN unsigned int
-lws_http_client_http_response(struct lws *wsi)
+lws_http_client_http_response(struct lws *_wsi)
{
- if (!wsi->http.ah)
- return 0;
+ struct lws *wsi;
+ unsigned int resp;
+
+ if (_wsi->http.ah && _wsi->http.ah->http_response)
+ return _wsi->http.ah->http_response;
- return wsi->http.ah->http_response;
+ lws_vhost_lock(_wsi->vhost);
+ wsi = _lws_client_wsi_master(_wsi);
+ resp = wsi->http.ah->http_response;
+ lws_vhost_unlock(_wsi->vhost);
+
+ return resp;
}
#endif
#if defined(LWS_PLAT_OPTEE)
@@ -781,7 +793,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
q = strrchr(new_path, '/');
if (q)
lws_strncpy(q + 1, p, sizeof(new_path) -
- (q - new_path));
+ (q - new_path) - 1);
else
path = p;
}
@@ -910,9 +922,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
* we seem to be good to go, give client last chance to check
* headers and OK it
*/
- if (wsi->protocol->callback(wsi,
+ if (w->protocol->callback(w,
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
- wsi->user_space, NULL, 0)) {
+ w->user_space, NULL, 0)) {
cce = "HS: disallowed by client filter";
goto bail2;
@@ -924,9 +936,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
/* call him back to inform him he is up */
- if (wsi->protocol->callback(wsi,
+ if (w->protocol->callback(w,
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
- wsi->user_space, NULL, 0)) {
+ w->user_space, NULL, 0)) {
cce = "HS: disallowed at ESTABLISHED";
goto bail3;
}
@@ -964,9 +976,9 @@ bail2:
n = 0;
if (cce)
n = (int)strlen(cce);
- wsi->protocol->callback(wsi,
+ w->protocol->callback(w,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
- wsi->user_space, (void *)cce,
+ w->user_space, (void *)cce,
(unsigned int)n);
}
wsi->already_did_cce = 1;
@@ -1228,4 +1240,4 @@ completed:
return 0;
}
-#endif \ No newline at end of file
+#endif
diff --git a/thirdparty/libwebsockets/roles/http/header.c b/thirdparty/libwebsockets/roles/http/header.c
index 99e56f7564..dbcf27cbd1 100644
--- a/thirdparty/libwebsockets/roles/http/header.c
+++ b/thirdparty/libwebsockets/roles/http/header.c
@@ -26,7 +26,7 @@
const unsigned char *
lws_token_to_string(enum lws_token_indexes token)
{
- if ((unsigned int)token >= ARRAY_SIZE(set))
+ if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
return NULL;
return (unsigned char *)set[token];
@@ -149,9 +149,17 @@ lws_add_http_common_headers(struct lws *wsi, unsigned int code,
(int)strlen(content_type), p, end))
return 1;
- if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN &&
- lws_add_http_header_content_length(wsi, content_len, p, end))
- return 1;
+ if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
+ if (lws_add_http_header_content_length(wsi, content_len, p, end))
+ return 1;
+ } else {
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
+ (unsigned char *)"close", 5,
+ p, end))
+ return 1;
+
+ wsi->http.connection_type = HTTP_CONNECTION_CLOSE;
+ }
return 0;
}
@@ -204,34 +212,40 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
#endif
#ifdef LWS_WITH_HTTP2
- if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
- return lws_add_http2_header_status(wsi, code, p, end);
+ if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi)) {
+ n = lws_add_http2_header_status(wsi, code, p, end);
+ if (n)
+ return n;
+ } else
#endif
- if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
- description = err400[code - 400];
- if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
- description = err500[code - 500];
-
- if (code == 100)
- description = "Continue";
- if (code == 200)
- description = "OK";
- if (code == 304)
- description = "Not Modified";
- else
- if (code >= 300 && code < 400)
- description = "Redirect";
-
- if (wsi->http.request_version < ARRAY_SIZE(hver))
- p1 = hver[wsi->http.request_version];
- else
- p1 = hver[0];
-
- n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description);
-
- if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end))
- return 1;
-
+ {
+ if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
+ description = err400[code - 400];
+ if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
+ description = err500[code - 500];
+
+ if (code == 100)
+ description = "Continue";
+ if (code == 200)
+ description = "OK";
+ if (code == 304)
+ description = "Not Modified";
+ else
+ if (code >= 300 && code < 400)
+ description = "Redirect";
+
+ if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
+ p1 = hver[wsi->http.request_version];
+ else
+ p1 = hver[0];
+
+ n = sprintf((char *)code_and_desc, "%s %u %s", p1, code,
+ description);
+
+ if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
+ end))
+ return 1;
+ }
headers = wsi->vhost->headers;
while (headers) {
if (lws_add_http_header_by_name(wsi,
diff --git a/thirdparty/libwebsockets/roles/http/private.h b/thirdparty/libwebsockets/roles/http/private.h
index 2aa7a92f75..5699914742 100644
--- a/thirdparty/libwebsockets/roles/http/private.h
+++ b/thirdparty/libwebsockets/roles/http/private.h
@@ -227,6 +227,7 @@ struct _lws_http_mode_related {
#if defined(LWS_WITH_HTTP_PROXY)
unsigned int perform_rewrite:1;
#endif
+ unsigned int deferred_transaction_completed:1;
};
diff --git a/thirdparty/libwebsockets/roles/http/server/lejp-conf.c b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c
index e9ce854cfc..fbf10c288e 100644
--- a/thirdparty/libwebsockets/roles/http/server/lejp-conf.c
+++ b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c
@@ -205,6 +205,7 @@ struct jpargs {
unsigned int enable_client_ssl:1;
unsigned int fresh_mount:1;
unsigned int any_vhosts:1;
+ unsigned int chunk:1;
};
static void *
@@ -213,6 +214,8 @@ lwsws_align(struct jpargs *a)
if ((lws_intptr_t)(a->p) & 15)
a->p += 16 - ((lws_intptr_t)(a->p) & 15);
+ a->chunk = 0;
+
return a->p;
}
@@ -225,7 +228,7 @@ arg_to_bool(const char *s)
if (n)
return 1;
- for (n = 0; n < (int)ARRAY_SIZE(on); n++)
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
@@ -413,25 +416,30 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
}
/* this catches, eg, vhosts[].headers[].xxx */
- if (reason == LEJPCB_VAL_STR_END &&
+ if ((reason == LEJPCB_VAL_STR_END || reason == LEJPCB_VAL_STR_CHUNK) &&
ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
- headers = lwsws_align(a);
- a->p += sizeof(*headers);
-
- n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
- /* ie, enable this protocol, no options yet */
- headers->next = a->info->headers;
- a->info->headers = headers;
- headers->name = a->p;
- // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
- a->p += n - 1;
- *(a->p++) = ':';
- if (a->p < a->end)
- *(a->p++) = '\0';
- else
- *(a->p - 1) = '\0';
- headers->value = a->p;
- headers->options = NULL;
+ if (!a->chunk) {
+ headers = lwsws_align(a);
+ a->p += sizeof(*headers);
+
+ n = lejp_get_wildcard(ctx, 0, a->p,
+ lws_ptr_diff(a->end, a->p));
+ /* ie, add this header */
+ headers->next = a->info->headers;
+ a->info->headers = headers;
+ headers->name = a->p;
+
+ lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
+ a->p += n - 1;
+ *(a->p++) = ':';
+ if (a->p < a->end)
+ *(a->p++) = '\0';
+ else
+ *(a->p - 1) = '\0';
+ headers->value = a->p;
+ headers->options = NULL;
+ }
+ a->chunk = reason == LEJPCB_VAL_STR_CHUNK;
goto dostring;
}
@@ -502,7 +510,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
if (a->last)
a->last->mount_next = m;
- for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++)
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin);
@@ -512,7 +520,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
break;
}
- if (n == (int)ARRAY_SIZE(mount_protocols)) {
+ if (n == (int)LWS_ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
@@ -750,6 +758,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
dostring:
p = ctx->buf;
+ p[LEJP_STRING_CHUNK] = '\0';
p1 = strstr(p, ESC_INSTALL_DATADIR);
if (p1) {
n = p1 - p;
@@ -762,7 +771,8 @@ dostring:
}
a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
- *(a->p)++ = '\0';
+ if (reason == LEJPCB_VAL_STR_END)
+ *(a->p)++ = '\0';
return 0;
}
@@ -779,7 +789,7 @@ lwsws_get_config(void *user, const char *f, const char * const *paths,
struct lejp_ctx ctx;
int n, m, fd;
- fd = open(f, O_RDONLY);
+ fd = lws_open(f, O_RDONLY);
if (fd < 0) {
lwsl_err("Cannot open %s\n", f);
return 2;
@@ -927,11 +937,11 @@ lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_global,
- ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
+ LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_global,
- ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
+ LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
a.plugin_dirs[a.count_plugin_dirs] = NULL;
@@ -962,11 +972,11 @@ lwsws_get_config_vhosts(struct lws_context *context,
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_vhosts,
- ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
+ LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_vhosts,
- ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
+ LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
*cs = a.p;
diff --git a/thirdparty/libwebsockets/roles/http/server/parsers.c b/thirdparty/libwebsockets/roles/http/server/parsers.c
index cb022e362b..482bdc676a 100644
--- a/thirdparty/libwebsockets/roles/http/server/parsers.c
+++ b/thirdparty/libwebsockets/roles/http/server/parsers.c
@@ -563,7 +563,7 @@ int LWS_WARN_UNUSED_RESULT
lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
{
wsi->http.ah->nfrag++;
- if (wsi->http.ah->nfrag == ARRAY_SIZE(wsi->http.ah->frags)) {
+ if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) {
lwsl_warn("More hdr frags than we can deal with, dropping\n");
return -1;
}
@@ -677,18 +677,16 @@ lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
return -1;
/* genuine delimiter */
if ((c == '&' || c == ';') && !enc) {
- if (issue_char(wsi, c) < 0)
+ if (issue_char(wsi, '\0') < 0)
return -1;
- /* swallow the terminator */
- ah->frags[ah->nfrag].len--;
/* link to next fragment */
ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+ if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
goto excessive;
/* start next fragment after the & */
ah->post_literal_equal = 0;
- ah->frags[ah->nfrag].offset = ah->pos;
+ ah->frags[ah->nfrag].offset = ++ah->pos;
ah->frags[ah->nfrag].len = 0;
ah->frags[ah->nfrag].nfrag = 0;
goto swallow;
@@ -787,9 +785,9 @@ lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
/* move to using WSI_TOKEN_HTTP_URI_ARGS */
ah->nfrag++;
- if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+ if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
goto excessive;
- ah->frags[ah->nfrag].offset = ah->pos;
+ ah->frags[ah->nfrag].offset = ++ah->pos;
ah->frags[ah->nfrag].len = 0;
ah->frags[ah->nfrag].nfrag = 0;
@@ -852,10 +850,10 @@ lws_parse(struct lws *wsi, unsigned char *buf, int *len)
c == ' ')
break;
- for (m = 0; m < ARRAY_SIZE(methods); m++)
+ for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
if (ah->parser_state == methods[m])
break;
- if (m == ARRAY_SIZE(methods))
+ if (m == LWS_ARRAY_SIZE(methods))
/* it was not any of the methods */
goto check_eol;
@@ -983,7 +981,7 @@ nope:
if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
lwsi_role_server(wsi)) {
/* this is not a header we know about */
- for (m = 0; m < ARRAY_SIZE(methods); m++)
+ for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
if (ah->frag_index[methods[m]]) {
/*
* already had the method, no idea what
@@ -996,7 +994,7 @@ nope:
* hm it's an unknown http method from a client in fact,
* it cannot be valid http
*/
- if (m == ARRAY_SIZE(methods)) {
+ if (m == LWS_ARRAY_SIZE(methods)) {
/*
* are we set up to accept raw in these cases?
*/
@@ -1025,7 +1023,7 @@ nope:
lextable[ah->lextable_pos + 1];
lwsl_parser("known hdr %d\n", n);
- for (m = 0; m < ARRAY_SIZE(methods); m++)
+ for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
if (n == methods[m] &&
ah->frag_index[methods[m]]) {
lwsl_warn("Duplicated method\n");
@@ -1061,7 +1059,7 @@ nope:
start_fragment:
ah->nfrag++;
excessive:
- if (ah->nfrag == ARRAY_SIZE(ah->frags)) {
+ if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
lwsl_warn("More hdr frags than we can deal with\n");
return -1;
}
diff --git a/thirdparty/libwebsockets/roles/http/server/server.c b/thirdparty/libwebsockets/roles/http/server/server.c
index 350af3cd7e..abd86dc9b5 100644
--- a/thirdparty/libwebsockets/roles/http/server/server.c
+++ b/thirdparty/libwebsockets/roles/http/server/server.c
@@ -131,6 +131,17 @@ done_list:
(void)n;
#if defined(__linux__)
+#ifdef LWS_WITH_UNIX_SOCK
+ /*
+ * A Unix domain sockets cannot be bound for several times, even if we set
+ * the SO_REUSE* options on.
+ * However, fortunately, each thread is able to independently listen when
+ * running on a reasonably new Linux kernel. So we can safely assume
+ * creating just one listening socket for a multi-threaded environment won't
+ * fail in most cases.
+ */
+ if (!LWS_UNIX_SOCK_ENABLED(vhost))
+#endif
limit = vhost->context->count_threads;
#endif
@@ -694,7 +705,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen)
char buf[128];
int fd, match = 0, pos = 0, n = 0, hit = 0;
- fd = open(filename, O_RDONLY);
+ fd = lws_open(filename, O_RDONLY);
if (fd < 0) {
lwsl_err("can't open auth file: %s\n", filename);
return 0;
@@ -812,7 +823,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
{
int n, count = 0;
- for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n]))
count++;
if (!count) {
@@ -827,7 +838,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
return -1;
}
- for (n = 0; n < (int)ARRAY_SIZE(methods); n++)
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
if (lws_hdr_total_length(wsi, methods[n])) {
*puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
*puri_len = lws_hdr_total_length(wsi, methods[n]);
@@ -857,7 +868,7 @@ lws_http_action(struct lws *wsi)
};
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
- if (meth < 0 || meth >= (int)ARRAY_SIZE(method_names))
+ if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
goto bail_nuke_ah;
/* we insist on absolute paths */
@@ -1128,7 +1139,7 @@ lws_http_action(struct lws *wsi)
}
if (pcolon > pslash)
pcolon = NULL;
-
+
if (pcolon)
n = pcolon - hit->origin;
else
@@ -1142,13 +1153,13 @@ lws_http_action(struct lws *wsi)
i.address = ads;
i.port = 80;
- if (hit->origin_protocol == LWSMPRO_HTTPS) {
+ if (hit->origin_protocol == LWSMPRO_HTTPS) {
i.port = 443;
i.ssl_connection = 1;
}
if (pcolon)
i.port = atoi(pcolon + 1);
-
+
lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1,
uri_ptr + hit->mountpoint_len);
lws_clean_url(rpath);
@@ -1164,7 +1175,7 @@ lws_http_action(struct lws *wsi)
p++;
}
}
-
+
i.path = rpath;
i.host = i.address;
@@ -1178,7 +1189,7 @@ lws_http_action(struct lws *wsi)
"from %s, to %s\n",
i.address, i.port, i.path, i.ssl_connection,
i.uri_replace_from, i.uri_replace_to);
-
+
if (!lws_client_connect_via_info(&i)) {
lwsl_err("proxy connect fail\n");
return 1;
@@ -1714,12 +1725,31 @@ lws_http_transaction_completed(struct lws *wsi)
{
int n = NO_PENDING_TIMEOUT;
+ if (wsi->trunc_len) {
+ /*
+ * ...so he tried to send something large as the http reply,
+ * it went as a partial, but he immediately said the
+ * transaction was completed.
+ *
+ * Defer the transaction completed until the last part of the
+ * partial is sent.
+ */
+ lwsl_notice("%s: deferring due to partial\n", __func__);
+ wsi->http.deferred_transaction_completed = 1;
+
+ return 0;
+ }
+
lwsl_info("%s: wsi %p\n", __func__, wsi);
lws_access_log(wsi);
if (!wsi->hdr_parsing_completed) {
- lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__);
+ char peer[64];
+ lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
+ peer[sizeof(peer) - 1] = '\0';
+ lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
+ __func__, peer);
return 0;
}
diff --git a/thirdparty/libwebsockets/roles/ws/client-parser-ws.c b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c
index aa561ce034..7287fb1590 100644
--- a/thirdparty/libwebsockets/roles/ws/client-parser-ws.c
+++ b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c
@@ -450,7 +450,7 @@ ping_drop:
break;
case LWSWSOPC_PONG:
- lwsl_info("client receied pong\n");
+ lwsl_info("client received pong\n");
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
@@ -488,9 +488,6 @@ ping_drop:
ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
ebuf.len = wsi->ws->rx_ubuf_head;
- if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
- goto already_done;
-
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
@@ -504,14 +501,12 @@ drain_extension:
#endif
lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
- if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
- rx_draining_ext &&
-#endif
- !ebuf.len) {
+ if (rx_draining_ext && !ebuf.len) {
lwsl_debug(" --- ending drain on 0 read result\n");
goto already_done;
}
+#endif
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
diff --git a/thirdparty/libwebsockets/roles/ws/ops-ws.c b/thirdparty/libwebsockets/roles/ws/ops-ws.c
index 5ddaba9e18..665b2c9b74 100644
--- a/thirdparty/libwebsockets/roles/ws/ops-ws.c
+++ b/thirdparty/libwebsockets/roles/ws/ops-ws.c
@@ -1246,8 +1246,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
return LWS_HP_RET_BAIL_OK;
}
- if (lwsi_role_client(wsi) && !wsi->socket_is_permanently_unusable &&
- wsi->ws->send_check_ping) {
+ if (!wsi->socket_is_permanently_unusable && wsi->ws->send_check_ping) {
lwsl_info("issuing ping on wsi %p\n", wsi);
wsi->ws->send_check_ping = 0;
@@ -1282,7 +1281,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
* payload ordering, but since they are always complete
* fragments control packets can interleave OK.
*/
- if (lwsi_role_client(wsi) && wsi->ws->tx_draining_ext) {
+ if (wsi->ws->tx_draining_ext) {
lwsl_ext("SERVICING TX EXT DRAINING\n");
if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
return LWS_HP_RET_BAIL_DIE;
@@ -1292,8 +1291,10 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
/* Priority 6: extensions
*/
- if (!wsi->ws->extension_data_pending)
+ if (!wsi->ws->extension_data_pending && !wsi->ws->tx_draining_ext) {
+ lwsl_ext("%s: !wsi->ws->extension_data_pending\n", __func__);
return LWS_HP_RET_USER_SERVICE;
+ }
/*
* check in on the active extensions, see if they
@@ -1412,15 +1413,13 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
wsi->ws->time_next_ping_check) >
context->ws_ping_pong_interval) {
- lwsl_info("req pp on wsi %p\n",
- wsi);
+ lwsl_info("req pp on wsi %p\n", wsi);
wsi->ws->send_check_ping = 1;
lws_set_timeout(wsi,
PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING,
context->timeout_secs);
lws_callback_on_writable(wsi);
- wsi->ws->time_next_ping_check =
- now;
+ wsi->ws->time_next_ping_check = now;
}
wsi = wsi->same_vh_protocol_next;
}
@@ -1466,6 +1465,9 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)
static int
rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
{
+ if (!wsi->ws)
+ return 0;
+
if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */
(reason == LWS_CLOSE_STATUS_NOSTATUS ||
reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY))
@@ -1512,7 +1514,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
if (wsi->ws->tx_draining_ext) {
struct lws **w = &pt->ws.tx_draining_ext_list;
- lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+ lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
@@ -1563,7 +1565,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
/* remove us from the list */
struct lws **w = &pt->ws.tx_draining_ext_list;
- lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
+ lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
@@ -1588,7 +1590,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
if (!(wpt & LWS_WRITE_NO_FIN) && len)
*wp &= ~LWS_WRITE_NO_FIN;
- lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
+ lwsl_ext("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
wsi->ws->tx_draining_stashed_wp, wpt);
// assert(0);
}
@@ -1644,7 +1646,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
// lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp);
if (n && ebuf.len) {
- lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
+ lwsl_ext("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
/* extension requires further draining */
wsi->ws->tx_draining_ext = 1;
wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list;
diff --git a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
index 2de6d422e3..f17c7e5494 100644
--- a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c
@@ -631,7 +631,7 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
/* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */
- for (n = 0; n < (int)ARRAY_SIZE(x5); n++) {
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(x5); n++) {
if (p != subject)
*p++ = ',';
if (elements[n])
diff --git a/thirdparty/libwebsockets/tls/mbedtls/ssl.c b/thirdparty/libwebsockets/tls/mbedtls/ssl.c
index 6ae9d2556b..f311ef50e3 100644
--- a/thirdparty/libwebsockets/tls/mbedtls/ssl.c
+++ b/thirdparty/libwebsockets/tls/mbedtls/ssl.c
@@ -121,8 +121,6 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
- lws_restart_ws_ping_pong_timer(wsi);
-
/*
* if it was our buffer that limited what we read,
* check if SSL has additional data pending inside SSL buffers.
diff --git a/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
index ba19663d9e..68ac748a28 100644
--- a/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
+++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h
@@ -37,7 +37,11 @@ typedef void RSA;
typedef void STACK;
typedef void BIO;
+#if defined(WIN32) || defined(_WIN32)
+#define ossl_inline __inline
+#else
#define ossl_inline inline
+#endif
#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__)
#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__)
diff --git a/thirdparty/libwebsockets/uwp_fixes.diff b/thirdparty/libwebsockets/uwp_fixes.diff
index 5b9d8724ed..21c3275bba 100644
--- a/thirdparty/libwebsockets/uwp_fixes.diff
+++ b/thirdparty/libwebsockets/uwp_fixes.diff
@@ -1,15 +1,15 @@
diff --git a/thirdparty/libwebsockets/plat/lws-plat-win.c b/thirdparty/libwebsockets/plat/lws-plat-win.c
-index 948db6289..511e29739 100644
+index bd513b494..1850b6425 100644
--- a/thirdparty/libwebsockets/plat/lws-plat-win.c
+++ b/thirdparty/libwebsockets/plat/lws-plat-win.c
-@@ -635,9 +635,20 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
+@@ -641,9 +641,20 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
HANDLE ret;
WCHAR buf[MAX_PATH];
lws_fop_fd_t fop_fd;
- LARGE_INTEGER llFileSize = {0};
+ FILE_STANDARD_INFO fInfo = {0};
- MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf));
+ MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, LWS_ARRAY_SIZE(buf));
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 // Windows 8 (minimum when UWP_ENABLED, but can be used in Windows builds)
+ CREATEFILE2_EXTENDED_PARAMETERS extParams = {0};
@@ -24,7 +24,7 @@ index 948db6289..511e29739 100644
if (((*flags) & 7) == _O_RDONLY) {
ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-@@ -645,6 +656,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
+@@ -651,6 +662,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
@@ -32,7 +32,7 @@ index 948db6289..511e29739 100644
if (ret == LWS_INVALID_FILE)
goto bail;
-@@ -657,9 +669,9 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
+@@ -663,9 +675,9 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
fop_fd->fd = ret;
fop_fd->filesystem_priv = NULL; /* we don't use it */
fop_fd->flags = *flags;
diff --git a/thirdparty/libwebsockets/win32helpers/getopt.c b/thirdparty/libwebsockets/win32helpers/getopt.c
index 2181f1cb12..3bb21f6f28 100644
--- a/thirdparty/libwebsockets/win32helpers/getopt.c
+++ b/thirdparty/libwebsockets/win32helpers/getopt.c
@@ -1,153 +1,153 @@
-/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
-
-/*
- * Copyright (c) 1987, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if 0
-static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-#ifdef __weak_alias
-__weak_alias(getopt,_getopt);
-#endif
-
-
-int opterr = 1, /* if error message should be printed */
- optind = 1, /* index into parent argv vector */
- optopt, /* character checked for validity */
- optreset; /* reset getopt */
-char *optarg; /* argument associated with option */
-
-static char * _progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-_progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt(nargc, nargv, ostr)
- int nargc;
- char * const nargv[];
- const char *ostr;
-{
- static char *__progname = 0;
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
- __progname = __progname?__progname:_progname(*nargv);
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-' /* found "--" */
- && place[1] == '\0') {
- ++optind;
- place = EMSG;
- return (-1);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname, optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- }
- else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if (*ostr == ':')
- return (BADARG);
- if (opterr)
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname, optopt);
- return (BADCH);
- }
- else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
+/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+#ifdef __weak_alias
+__weak_alias(getopt,_getopt);
+#endif
+
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+static char * _progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+_progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const nargv[];
+ const char *ostr;
+{
+ static char *__progname = 0;
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+ __progname = __progname?__progname:_progname(*nargv);
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-' /* found "--" */
+ && place[1] == '\0') {
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname, optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname, optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
diff --git a/thirdparty/libwebsockets/win32helpers/getopt_long.c b/thirdparty/libwebsockets/win32helpers/getopt_long.c
index 22e5fa8945..6dfccf367d 100644
--- a/thirdparty/libwebsockets/win32helpers/getopt_long.c
+++ b/thirdparty/libwebsockets/win32helpers/getopt_long.c
@@ -1,240 +1,240 @@
-
-/*
- * Copyright (c) 1987, 1993, 1994, 1996
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "getopt.h"
-
-#define lws_ptr_diff(head, tail) \
- ((int)((char *)(head) - (char *)(tail)))
-
-extern int opterr; /* if error message should be printed */
-extern int optind; /* index into parent argv vector */
-extern int optopt; /* character checked for validity */
-extern int optreset; /* reset getopt */
-extern char *optarg; /* argument associated with option */
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-static char * __progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-__progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt_internal(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-') { /* found "--" */
- /* ++optind; */
- place = EMSG;
- return (-2);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- } else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if ((opterr) && (*ostr != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname(nargv[0]), optopt);
- return (BADARG);
- } else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
-#if 0
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt2(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- int retval;
-
- if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
- retval = -1;
- ++optind;
- }
- return(retval);
-}
-#endif
-
-/*
- * getopt_long --
- * Parse argc/argv argument vector.
- */
-int
-getopt_long(nargc, nargv, options, long_options, index)
- int nargc;
- char ** nargv;
- char * options;
- struct option * long_options;
- int * index;
-{
- int retval;
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(options != NULL);
- _DIAGASSERT(long_options != NULL);
- /* index may be NULL */
-
- if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
- char *current_argv = nargv[optind++] + 2, *has_equal;
- int i, current_argv_len, match = -1;
-
- if (*current_argv == '\0') {
- return(-1);
- }
- if ((has_equal = strchr(current_argv, '=')) != NULL) {
- current_argv_len = lws_ptr_diff(has_equal, current_argv);
- has_equal++;
- } else
- current_argv_len = (int)strlen(current_argv);
-
- for (i = 0; long_options[i].name; i++) {
- if (strncmp(current_argv, long_options[i].name, current_argv_len))
- continue;
-
- if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
- match = i;
- break;
- }
- if (match == -1)
- match = i;
- }
- if (match != -1) {
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal)
- optarg = has_equal;
- else
- optarg = nargv[optind++];
- }
- if ((long_options[match].has_arg == required_argument)
- && (optarg == NULL)) {
- /*
- * Missing argument, leading :
- * indicates no error should be generated
- */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %s\n",
- __progname(nargv[0]), current_argv);
- return (BADARG);
- }
- } else { /* No matching argument */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
- return (BADCH);
- }
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- retval = 0;
- } else
- retval = long_options[match].val;
- if (index)
- *index = match;
- }
- return(retval);
-}
+
+/*
+ * Copyright (c) 1987, 1993, 1994, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+#define lws_ptr_diff(head, tail) \
+ ((int)((char *)(head) - (char *)(tail)))
+
+extern int opterr; /* if error message should be printed */
+extern int optind; /* index into parent argv vector */
+extern int optopt; /* character checked for validity */
+extern int optreset; /* reset getopt */
+extern char *optarg; /* argument associated with option */
+
+#define __P(x) x
+#define _DIAGASSERT(x) assert(x)
+
+static char * __progname __P((char *));
+int getopt_internal __P((int, char * const *, const char *));
+
+static char *
+__progname(nargv0)
+ char * nargv0;
+{
+ char * tmp;
+
+ _DIAGASSERT(nargv0 != NULL);
+
+ tmp = strrchr(nargv0, '/');
+ if (tmp)
+ tmp++;
+ else
+ tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_internal(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(ostr != NULL);
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ /* ++optind; */
+ place = EMSG;
+ return (-2);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if ((opterr) && (*ostr != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname(nargv[0]), optopt);
+ return (BADARG);
+ } else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
+#if 0
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt2(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ int retval;
+
+ if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
+ retval = -1;
+ ++optind;
+ }
+ return(retval);
+}
+#endif
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, index)
+ int nargc;
+ char ** nargv;
+ char * options;
+ struct option * long_options;
+ int * index;
+{
+ int retval;
+
+ _DIAGASSERT(nargv != NULL);
+ _DIAGASSERT(options != NULL);
+ _DIAGASSERT(long_options != NULL);
+ /* index may be NULL */
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ char *current_argv = nargv[optind++] + 2, *has_equal;
+ int i, current_argv_len, match = -1;
+
+ if (*current_argv == '\0') {
+ return(-1);
+ }
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ current_argv_len = lws_ptr_diff(has_equal, current_argv);
+ has_equal++;
+ } else
+ current_argv_len = (int)strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
+ match = i;
+ break;
+ }
+ if (match == -1)
+ match = i;
+ }
+ if (match != -1) {
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else
+ optarg = nargv[optind++];
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument, leading :
+ * indicates no error should be generated
+ */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ __progname(nargv[0]), current_argv);
+ return (BADARG);
+ }
+ } else { /* No matching argument */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
+ return (BADCH);
+ }
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ retval = 0;
+ } else
+ retval = long_options[match].val;
+ if (index)
+ *index = match;
+ }
+ return(retval);
+}
diff --git a/thirdparty/libwebsockets/win32helpers/gettimeofday.c b/thirdparty/libwebsockets/win32helpers/gettimeofday.c
index 08385c2320..35dd73531d 100644
--- a/thirdparty/libwebsockets/win32helpers/gettimeofday.c
+++ b/thirdparty/libwebsockets/win32helpers/gettimeofday.c
@@ -1,36 +1,36 @@
-#include <time.h>
-#include <windows.h> //I've omitted context line
-
-#include "gettimeofday.h"
-
-int gettimeofday(struct timeval *tv, struct timezone *tz)
-{
- FILETIME ft;
- unsigned __int64 tmpres = 0;
- static int tzflag;
-
- if (NULL != tv) {
- GetSystemTimeAsFileTime(&ft);
-
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
-
- /*converting file time to unix epoch*/
- tmpres /= 10; /*convert into microseconds*/
+#include <time.h>
+#include <windows.h> //I've omitted context line
+
+#include "gettimeofday.h"
+
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ FILETIME ft;
+ unsigned __int64 tmpres = 0;
+ static int tzflag;
+
+ if (NULL != tv) {
+ GetSystemTimeAsFileTime(&ft);
+
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+
+ /*converting file time to unix epoch*/
+ tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
- tv->tv_sec = (long)(tmpres / 1000000UL);
- tv->tv_usec = (long)(tmpres % 1000000UL);
- }
-
- if (NULL != tz) {
- if (!tzflag) {
- _tzset();
- tzflag++;
- }
- tz->tz_minuteswest = _timezone / 60;
- tz->tz_dsttime = _daylight;
- }
-
- return 0;
-}
+ tv->tv_sec = (long)(tmpres / 1000000UL);
+ tv->tv_usec = (long)(tmpres % 1000000UL);
+ }
+
+ if (NULL != tz) {
+ if (!tzflag) {
+ _tzset();
+ tzflag++;
+ }
+ tz->tz_minuteswest = _timezone / 60;
+ tz->tz_dsttime = _daylight;
+ }
+
+ return 0;
+}