diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index 9b326cb43e90..e832faaca477 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -56,7 +56,7 @@ jobs: - name: Class reference schema checks run: | - xmllint --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml + xmllint --quiet --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml - name: Run C compiler on `gdextension_interface.h` run: | diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index a8cdb6f737f5..30cdd789a448 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1563,6 +1563,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_download_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 57ac241eb229..cb43c196cda2 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -46,8 +46,8 @@ [gdscript] var box = AABB(Vector3(5, 0, 5), Vector3(-20, -10, -5)) var absolute = box.abs() - print(absolute.position) # Prints (-15, -10, 0) - print(absolute.size) # Prints (20, 10, 5) + print(absolute.position) # Prints (-15.0, -10.0, 0.0) + print(absolute.size) # Prints (20.0, 10.0, 5.0) [/gdscript] [csharp] var box = new Aabb(new Vector3(5, 0, 5), new Vector3(-20, -10, -5)); @@ -96,12 +96,12 @@ var box = AABB(Vector3(0, 0, 0), Vector3(5, 2, 5)) box = box.expand(Vector3(10, 0, 0)) - print(box.position) # Prints (0, 0, 0) - print(box.size) # Prints (10, 2, 5) + print(box.position) # Prints (0.0, 0.0, 0.0) + print(box.size) # Prints (10.0, 2.0, 5.0) box = box.expand(Vector3(-5, 0, 5)) - print(box.position) # Prints (-5, 0, 0) - print(box.size) # Prints (15, 2, 5) + print(box.position) # Prints (-5.0, 0.0, 0.0) + print(box.size) # Prints (15.0, 2.0, 5.0) [/gdscript] [csharp] var box = new Aabb(new Vector3(0, 0, 0), new Vector3(5, 2, 5)); @@ -138,15 +138,15 @@ [gdscript] var box = AABB(Vector3(0, 0, 0), Vector3(2, 4, 8)) - print(box.get_longest_axis()) # Prints (0, 0, 1) + print(box.get_longest_axis()) # Prints (0.0, 0.0, 1.0) print(box.get_longest_axis_index()) # Prints 2 - print(box.get_longest_axis_size()) # Prints 8 + print(box.get_longest_axis_size()) # Prints 8.0 [/gdscript] [csharp] var box = new Aabb(new Vector3(0, 0, 0), new Vector3(2, 4, 8)); GD.Print(box.GetLongestAxis()); // Prints (0, 0, 1) - GD.Print(box.GetLongestAxisIndex()); // Prints 2 + GD.Print(box.GetLongestAxisIndex()); // Prints Z GD.Print(box.GetLongestAxisSize()); // Prints 8 [/csharp] [/codeblocks] @@ -175,15 +175,15 @@ [gdscript] var box = AABB(Vector3(0, 0, 0), Vector3(2, 4, 8)) - print(box.get_shortest_axis()) # Prints (1, 0, 0) + print(box.get_shortest_axis()) # Prints (1.0, 0.0, 0.0) print(box.get_shortest_axis_index()) # Prints 0 - print(box.get_shortest_axis_size()) # Prints 2 + print(box.get_shortest_axis_size()) # Prints 2.0 [/gdscript] [csharp] var box = new Aabb(new Vector3(0, 0, 0), new Vector3(2, 4, 8)); GD.Print(box.GetShortestAxis()); // Prints (1, 0, 0) - GD.Print(box.GetShortestAxisIndex()); // Prints 0 + GD.Print(box.GetShortestAxisIndex()); // Prints X GD.Print(box.GetShortestAxisSize()); // Prints 2 [/csharp] [/codeblocks] @@ -225,12 +225,12 @@ [codeblocks] [gdscript] var a = AABB(Vector3(4, 4, 4), Vector3(8, 8, 8)).grow(4) - print(a.position) # Prints (0, 0, 0) - print(a.size) # Prints (16, 16, 16) + print(a.position) # Prints (0.0, 0.0, 0.0) + print(a.size) # Prints (16.0, 16.0, 16.0) var b = AABB(Vector3(0, 0, 0), Vector3(8, 4, 2)).grow(2) - print(b.position) # Prints (-2, -2, -2) - print(b.size) # Prints (12, 8, 6) + print(b.position) # Prints (-2.0, -2.0, -2.0) + print(b.size) # Prints (12.0, 8.0, 6.0) [/gdscript] [csharp] var a = new Aabb(new Vector3(4, 4, 4), new Vector3(8, 8, 8)).Grow(4); @@ -275,8 +275,8 @@ var box2 = AABB(Vector3(2, 0, 2), Vector3(8, 4, 4)) var intersection = box1.intersection(box2) - print(intersection.position) # Prints (2, 0, 2) - print(intersection.size) # Prints (3, 2, 4) + print(intersection.position) # Prints (2.0, 0.0, 2.0) + print(intersection.size) # Prints (3.0, 2.0, 4.0) [/gdscript] [csharp] var box1 = new Aabb(new Vector3(0, 0, 0), new Vector3(5, 2, 8)); diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 8e1972af116e..ac37a18a420c 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -12,8 +12,8 @@ astar_grid.region = Rect2i(0, 0, 32, 32) astar_grid.cell_size = Vector2(16, 16) astar_grid.update() - print(astar_grid.get_id_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4) - print(astar_grid.get_point_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (16, 16), (32, 32), (48, 48), (48, 64) + print(astar_grid.get_id_path(Vector2i(0, 0), Vector2i(3, 4))) # Prints [(0, 0), (1, 1), (2, 2), (3, 3), (3, 4)] + print(astar_grid.get_point_path(Vector2i(0, 0), Vector2i(3, 4))) # Prints [(0, 0), (16, 16), (32, 32), (48, 48), (48, 64)] [/gdscript] [csharp] AStarGrid2D astarGrid = new AStarGrid2D(); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 1b7dbf7c41fb..983e8fc98f5d 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -183,17 +183,17 @@ public override void _Ready() { - // Prints true (3/3 elements evaluate to true). + // Prints True (3/3 elements evaluate to true). GD.Print(new Godot.Collections.Array>int< { 6, 10, 6 }.All(GreaterThan5)); - // Prints false (1/3 elements evaluate to true). + // Prints False (1/3 elements evaluate to true). GD.Print(new Godot.Collections.Array>int< { 4, 10, 4 }.All(GreaterThan5)); - // Prints false (0/3 elements evaluate to true). + // Prints False (0/3 elements evaluate to true). GD.Print(new Godot.Collections.Array>int< { 4, 4, 4 }.All(GreaterThan5)); - // Prints true (0/0 elements evaluate to true). + // Prints True (0/0 elements evaluate to true). GD.Print(new Godot.Collections.Array>int< { }.All(GreaterThan5)); // Same as the first line above, but using a lambda function. - GD.Print(new Godot.Collections.Array>int< { 6, 10, 6 }.All(element => element > 5)); // Prints true + GD.Print(new Godot.Collections.Array>int< { 6, 10, 6 }.All(element => element > 5)); // Prints True } [/csharp] [/codeblocks] @@ -462,10 +462,10 @@ [csharp] var arr = new Godot.Collections.Array { "inside", 7 }; // By C# convention, this method is renamed to `Contains`. - GD.Print(arr.Contains("inside")); // Prints true - GD.Print(arr.Contains("outside")); // Prints false - GD.Print(arr.Contains(7)); // Prints true - GD.Print(arr.Contains("7")); // Prints false + GD.Print(arr.Contains("inside")); // Prints True + GD.Print(arr.Contains("outside")); // Prints False + GD.Print(arr.Contains(7)); // Prints True + GD.Print(arr.Contains("7")); // Prints False [/csharp] [/codeblocks] In GDScript, this is equivalent to the [code]in[/code] operator: diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index d11e070d89b3..52e99512813d 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -13,6 +13,12 @@ https://godotengine.org/asset-library/asset/2762 + + + + Override this method to return the bar beats of this stream. + + @@ -45,6 +51,12 @@ Override this method to customize the name assigned to this audio stream. Unused by the engine. + + + + Override this method to return [code]true[/code] if this stream has a loop. + + diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index b8823cf18858..81f63addd4a5 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -91,7 +91,7 @@ # Creates a Basis whose z axis points down. var my_basis = Basis.from_euler(Vector3(TAU / 4, 0, 0)) - print(my_basis.z) # Prints (0, -1, 0) + print(my_basis.z) # Prints (0.0, -1.0, 0.0) [/gdscript] [csharp] // Creates a Basis whose z axis points down. @@ -112,9 +112,9 @@ [gdscript] var my_basis = Basis.from_scale(Vector3(2, 4, 8)) - print(my_basis.x) # Prints (2, 0, 0) - print(my_basis.y) # Prints (0, 4, 0) - print(my_basis.z) # Prints (0, 0, 8) + print(my_basis.x) # Prints (2.0, 0.0, 0.0) + print(my_basis.y) # Prints (0.0, 4.0, 0.0) + print(my_basis.z) # Prints (0.0, 0.0, 8.0) [/gdscript] [csharp] var myBasis = Basis.FromScale(new Vector3(2.0f, 4.0f, 8.0f)); @@ -163,7 +163,7 @@ my_basis = my_basis.rotated(Vector3.UP, TAU / 2) my_basis = my_basis.rotated(Vector3.RIGHT, TAU / 4) - print(my_basis.get_scale()) # Prints (2, 4, 8) + print(my_basis.get_scale()) # Prints (2.0, 4.0, 8.0) [/gdscript] [csharp] var myBasis = new Basis( @@ -285,9 +285,9 @@ ) my_basis = my_basis.scaled(Vector3(0, 2, -2)) - print(my_basis.x) # Prints (0, 2, -2) - print(my_basis.y) # Prints (0, 4, -4) - print(my_basis.z) # Prints (0, 6, -6) + print(my_basis.x) # Prints (0.0, 2.0, -2.0) + print(my_basis.y) # Prints (0.0, 4.0, -4.0) + print(my_basis.z) # Prints (0.0, 6.0, -6.0) [/gdscript] [csharp] var myBasis = new Basis( @@ -360,9 +360,9 @@ ) my_basis = my_basis.transposed() - print(my_basis.x) # Prints (1, 4, 7) - print(my_basis.y) # Prints (2, 5, 8) - print(my_basis.z) # Prints (3, 6, 9) + print(my_basis.x) # Prints (1.0, 4.0, 7.0) + print(my_basis.y) # Prints (2.0, 5.0, 8.0) + print(my_basis.z) # Prints (3.0, 6.0, 9.0) [/gdscript] [csharp] var myBasis = new Basis( @@ -454,7 +454,7 @@ [gdscript] # Basis that swaps the X/Z axes and doubles the scale. var my_basis = Basis(Vector3(0, 2, 0), Vector3(2, 0, 0), Vector3(0, 0, 2)) - print(my_basis * Vector3(1, 2, 3)) # Prints (4, 2, 6) + print(my_basis * Vector3(1, 2, 3)) # Prints (4.0, 2.0, 6.0) [/gdscript] [csharp] // Basis that swaps the X/Z axes and doubles the scale. diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 5b3f86c9f57a..5103134f657f 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -47,7 +47,7 @@ If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. - When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. + If [code]true[/code], text that is too large to fit the button is clipped horizontally. If [code]false[/code], the button will always be wide enough to hold the text. The text is not vertically clipped, and the button's height is not affected by this property. When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect. See also [theme_item icon_max_width]. diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml index cf3c3e06fd51..d5978a42f6da 100644 --- a/doc/classes/Callable.xml +++ b/doc/classes/Callable.xml @@ -13,7 +13,7 @@ func test(): var callable = Callable(self, "print_args") callable.call("hello", "world") # Prints "hello world ". - callable.call(Vector2.UP, 42, callable) # Prints "(0, -1) 42 Node(node.gd)::print_args". + callable.call(Vector2.UP, 42, callable) # Prints "(0.0, -1.0) 42 Node(node.gd)::print_args". callable.call("invalid") # Invalid call, should have at least 2 arguments. [/gdscript] [csharp] diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 5c9b22fe4a1c..2e02de54a8eb 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -281,9 +281,9 @@ { 210, default }, }; - GD.Print(myDict.ContainsKey("Godot")); // Prints true - GD.Print(myDict.ContainsKey(210)); // Prints true - GD.Print(myDict.ContainsKey(4)); // Prints false + GD.Print(myDict.ContainsKey("Godot")); // Prints True + GD.Print(myDict.ContainsKey(210)); // Prints True + GD.Print(myDict.ContainsKey(4)); // Prints False [/csharp] [/codeblocks] In GDScript, this is equivalent to the [code]in[/code] operator: @@ -321,7 +321,7 @@ var dict2 = new Godot.Collections.Dictionary{{"A", 10}, {"B", 2}}; // Godot.Collections.Dictionary has no Hash() method. Use GD.Hash() instead. - GD.Print(GD.Hash(dict1) == GD.Hash(dict2)); // Prints true + GD.Print(GD.Hash(dict1) == GD.Hash(dict2)); // Prints True [/csharp] [/codeblocks] [b]Note:[/b] Dictionaries with the same entries but in a different order will not have the same hash. diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 3ae375393077..9b0879a794cb 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -209,10 +209,10 @@ print(Engine.has_singleton("Unknown")) # Prints false [/gdscript] [csharp] - GD.Print(Engine.HasSingleton("OS")); // Prints true - GD.Print(Engine.HasSingleton("Engine")); // Prints true - GD.Print(Engine.HasSingleton("AudioServer")); // Prints true - GD.Print(Engine.HasSingleton("Unknown")); // Prints false + GD.Print(Engine.HasSingleton("OS")); // Prints True + GD.Print(Engine.HasSingleton("Engine")); // Prints True + GD.Print(Engine.HasSingleton("AudioServer")); // Prints True + GD.Print(Engine.HasSingleton("Unknown")); // Prints False [/csharp] [/codeblocks] [b]Note:[/b] Global singletons are not the same as autoloaded nodes, which are configurable in the project settings. diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index 71e6cf93ae25..c86861fd130b 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -201,13 +201,13 @@ var polygon = PackedVector2Array([Vector2(0, 0), Vector2(100, 0), Vector2(100, 100), Vector2(0, 100)]) var offset = Vector2(50, 50) polygon = Transform2D(0, offset) * polygon - print(polygon) # prints [(50, 50), (150, 50), (150, 150), (50, 150)] + print(polygon) # Prints [(50.0, 50.0), (150.0, 50.0), (150.0, 150.0), (50.0, 150.0)] [/gdscript] [csharp] var polygon = new Vector2[] { new Vector2(0, 0), new Vector2(100, 0), new Vector2(100, 100), new Vector2(0, 100) }; var offset = new Vector2(50, 50); polygon = new Transform2D(0, offset) * polygon; - GD.Print((Variant)polygon); // prints [(50, 50), (150, 50), (150, 150), (50, 150)] + GD.Print((Variant)polygon); // Prints [(50, 50), (150, 50), (150, 150), (50, 150)] [/csharp] [/codeblocks] diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index 2d88876d2448..c6512e487971 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -78,6 +78,7 @@ Called each physics frame with the time since the last physics frame as argument ([param delta], in seconds). Equivalent to [method Node._physics_process]. If implemented, the method must return a boolean value. [code]true[/code] ends the main loop, while [code]false[/code] lets it proceed to the next frame. + [b]Note:[/b] [param delta] will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [param delta] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. @@ -86,6 +87,7 @@ Called each process (idle) frame with the time since the last process frame as argument (in seconds). Equivalent to [method Node._process]. If implemented, the method must return a boolean value. [code]true[/code] ends the main loop, while [code]false[/code] lets it proceed to the next frame. + [b]Note:[/b] [param delta] will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [param delta] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. diff --git a/doc/classes/NavigationPathQueryParameters2D.xml b/doc/classes/NavigationPathQueryParameters2D.xml index ce0a00bc83bb..1f9c064f930b 100644 --- a/doc/classes/NavigationPathQueryParameters2D.xml +++ b/doc/classes/NavigationPathQueryParameters2D.xml @@ -49,6 +49,9 @@ Centers every path position in the middle of the traveled navigation mesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + + Applies no postprocessing and returns the raw path corridor as found by the pathfinding algorithm. + Don't include any additional metadata about the returned path. diff --git a/doc/classes/NavigationPathQueryParameters3D.xml b/doc/classes/NavigationPathQueryParameters3D.xml index fcd913f73b59..a4c622d080d6 100644 --- a/doc/classes/NavigationPathQueryParameters3D.xml +++ b/doc/classes/NavigationPathQueryParameters3D.xml @@ -49,6 +49,9 @@ Centers every path position in the middle of the traveled navigation mesh polygon edge. This creates better paths for tile- or gridbased layouts that restrict the movement to the cells center. + + Applies no postprocessing and returns the raw path corridor as found by the pathfinding algorithm. + Don't include any additional metadata about the returned path. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 5c19a6b355db..d1fac97b93e7 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -533,7 +533,7 @@ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [param map]. - + @@ -754,12 +754,13 @@ [b]Performance:[/b] While convenient, reading data arrays from [Mesh] resources can affect the frame rate negatively. The data needs to be received from the GPU, stalling the [RenderingServer] in the process. For performance prefer the use of e.g. collision shapes or creating the data arrays entirely in code. - + + - Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters2D]. Updates the provided [NavigationPathQueryResult2D] result object with the path among other results requested by the query. + Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters2D]. Updates the provided [NavigationPathQueryResult2D] result object with the path among other results requested by the query. After the process is finished the optional [param callback] will be called. diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 66a286758bca..ab65f3f74376 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -605,7 +605,7 @@ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [param map]. - + @@ -887,12 +887,13 @@ [b]Performance:[/b] While convenient, reading data arrays from [Mesh] resources can affect the frame rate negatively. The data needs to be received from the GPU, stalling the [RenderingServer] in the process. For performance prefer the use of e.g. collision shapes or creating the data arrays entirely in code. - + + - Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query. + Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query. After the process is finished the optional [param callback] will be called. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 1e12d619e22f..ff2a440e59bd 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -71,11 +71,12 @@ - Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [param delta] variable should be constant. [param delta] is in seconds. + Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [param delta] parameter will [i]generally[/i] be constant (see exceptions below). [param delta] is in seconds. It is only called if physics processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_physics_process]. Processing happens in order of [member process_physics_priority], lower priority values are called first. Nodes with the same priority are processed in tree order, or top to bottom as seen in the editor (also known as pre-order traversal). Corresponds to the [constant NOTIFICATION_PHYSICS_PROCESS] notification in [method Object._notification]. [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan). + [b]Note:[/b] [param delta] will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [param delta] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. @@ -87,6 +88,7 @@ Processing happens in order of [member process_priority], lower priority values are called first. Nodes with the same priority are processed in tree order, or top to bottom as seen in the editor (also known as pre-order traversal). Corresponds to the [constant NOTIFICATION_PROCESS] notification in [method Object._notification]. [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan). + [b]Note:[/b] [param delta] will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [param delta] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. @@ -492,12 +494,14 @@ Returns the time elapsed (in seconds) since the last physics callback. This value is identical to [method _physics_process]'s [code]delta[/code] parameter, and is often consistent at run-time, unless [member Engine.physics_ticks_per_second] is changed. See also [constant NOTIFICATION_PHYSICS_PROCESS]. + [b]Note:[/b] The returned value will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [code]delta[/code] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. Returns the time elapsed (in seconds) since the last process callback. This value is identical to [method _process]'s [code]delta[/code] parameter, and may vary from frame to frame. See also [constant NOTIFICATION_PROCESS]. + [b]Note:[/b] The returned value will be larger than expected if running at a framerate lower than [member Engine.physics_ticks_per_second] / [member Engine.max_physics_steps_per_frame] FPS. This is done to avoid "spiral of death" scenarios where performance would plummet due to an ever-increasing number of physics steps per frame. This behavior affects both [method _process] and [method _physics_process]. As a result, avoid using [code]delta[/code] for time measurements in real-world seconds. Use the [Time] singleton's methods for this purpose instead, such as [method Time.get_ticks_usec]. diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 69d613b7cccb..669c71778d2e 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -629,10 +629,10 @@ print(OS.is_keycode_unicode(KEY_ESCAPE)) # Prints false [/gdscript] [csharp] - GD.Print(OS.IsKeycodeUnicode((long)Key.G)); // Prints true - GD.Print(OS.IsKeycodeUnicode((long)Key.Kp4)); // Prints true - GD.Print(OS.IsKeycodeUnicode((long)Key.Tab)); // Prints false - GD.Print(OS.IsKeycodeUnicode((long)Key.Escape)); // Prints false + GD.Print(OS.IsKeycodeUnicode((long)Key.G)); // Prints True + GD.Print(OS.IsKeycodeUnicode((long)Key.Kp4)); // Prints True + GD.Print(OS.IsKeycodeUnicode((long)Key.Tab)); // Prints False + GD.Print(OS.IsKeycodeUnicode((long)Key.Escape)); // Prints False [/csharp] [/codeblocks] diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index 73fd7e19439a..febc81dd81c8 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -987,12 +987,12 @@ [gdscript] var node = Node2D.new() node.set("global_scale", Vector2(8, 2.5)) - print(node.global_scale) # Prints (8, 2.5) + print(node.global_scale) # Prints (8.0, 2.5) [/gdscript] [csharp] var node = new Node2D(); - node.Set(Node2D.PropertyName.GlobalScale, new Vector2(8, 2.5)); - GD.Print(node.GlobalScale); // Prints Vector2(8, 2.5) + node.Set(Node2D.PropertyName.GlobalScale, new Vector2(8, 2.5f)); + GD.Print(node.GlobalScale); // Prints (8, 2.5) [/csharp] [/codeblocks] [b]Note:[/b] In C#, [param property] must be in snake_case when referring to built-in Godot properties. Prefer using the names exposed in the [code]PropertyName[/code] class to avoid allocating a new [StringName] on each call. @@ -1047,7 +1047,7 @@ var node = Node2D.new() node.set_indexed("position", Vector2(42, 0)) node.set_indexed("position:y", -10) - print(node.position) # Prints (42, -10) + print(node.position) # Prints (42.0, -10.0) [/gdscript] [csharp] var node = new Node2D(); diff --git a/doc/classes/PolygonPathFinder.xml b/doc/classes/PolygonPathFinder.xml index b70633883c8c..98734c3e9b05 100644 --- a/doc/classes/PolygonPathFinder.xml +++ b/doc/classes/PolygonPathFinder.xml @@ -62,8 +62,8 @@ }; var connections = new int[] { 0, 1, 1, 2, 2, 0 }; polygonPathFinder.Setup(points, connections); - GD.Print(polygonPathFinder.IsPointInside(new Vector2(0.2f, 0.2f))); // Prints true - GD.Print(polygonPathFinder.IsPointInside(new Vector2(1.0f, 1.0f))); // Prints false + GD.Print(polygonPathFinder.IsPointInside(new Vector2(0.2f, 0.2f))); // Prints True + GD.Print(polygonPathFinder.IsPointInside(new Vector2(1.0f, 1.0f))); // Prints False [/csharp] [/codeblocks] diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 0a16b159316c..76cddce3dddd 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2162,6 +2162,9 @@ If enabled, and baking would potentially lead to an engine crash, the baking will be interrupted and an error message with explanation will be raised. + + Maximum number of threads that can run pathfinding queries simultaneously on the same pathfinding graph, for example the same navigation map. Additional threads increase memory consumption and synchronization time due to the need for extra data copies prepared for each thread. A value of [code]-1[/code] means unlimited and the maximum available OS processor count is used. Defaults to [code]1[/code] when the OS does not support threads. + Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. @@ -2972,10 +2975,22 @@ Determines at which interval pipeline cache is saved to disk. The lower the value, the more often it is saved. + The size of a block allocated in the staging buffers. Staging buffers are the intermediate resources the engine uses to upload or download data to the GPU. This setting determines the max amount of data that can be transferred in a copy operation. Increasing this will result in faster data transfers at the cost of extra memory. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. + The maximum amount of memory allowed to be used by staging buffers. If the amount of data being uploaded or downloaded exceeds this amount, the GPU will stall and wait for previous frames to finish. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. + + + The region size in pixels used to download texture data from the GPU when using methods like [method RenderingDevice.texture_get_data_async]. + [b]Note:[/b] This property's upper limit is controlled by [member rendering/rendering_device/staging_buffer/block_size_kb] and whether it's possible to allocate a single block of texture data with this region size in the format that is requested. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. + The region size in pixels used to upload texture data from the GPU when using methods like [method RenderingDevice.texture_update]. + [b]Note:[/b] This property's upper limit is controlled by [member rendering/rendering_device/staging_buffer/block_size_kb] and whether it's possible to allocate a single block of texture data with this region size in the format that is requested. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. The number of frames to track on the CPU side before stalling to wait for the GPU. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 59ca06085f2f..b7f95587cda6 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -58,6 +58,27 @@ Returns a copy of the data of the specified [param buffer], optionally [param offset_bytes] and [param size_bytes] can be set to copy only a portion of the buffer. + [b]Note:[/b] This method will block the GPU from working until the data is retrieved. Refer to [method buffer_get_data_async] for an alternative that returns the data in more performant way. + + + + + + + + + + Asynchronous version of [method buffer_get_data]. RenderingDevice will call [param callback] in a certain amount of frames with the data the buffer had at the time of the request. + [b]Note:[/b] At the moment, the delay corresponds to the amount of frames specified by [member ProjectSettings.rendering/rendering_device/vsync/frame_queue_size]. + [b]Note:[/b] Downloading large buffers can have a prohibitive cost for real-time even when using the asynchronous method due to hardware bandwidth limitations. When dealing with large resources, you can adjust settings such as [member ProjectSettings.rendering/rendering_device/staging_buffer/block_size_kb] to improve the transfer speed at the cost of extra memory. + [codeblock] + func _buffer_get_data_callback(array): + value = array.decode_u32(0) + + ... + + rd.buffer_get_data_async(buffer, _buffer_get_data_callback) + [/codeblock] @@ -928,6 +949,26 @@ Returns the [param texture] data for the specified [param layer] as raw binary data. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. [b]Note:[/b] [param texture] can't be retrieved while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to retrieve this texture. Otherwise, an error is printed and a empty [PackedByteArray] is returned. [b]Note:[/b] [param texture] requires the [constant TEXTURE_USAGE_CAN_COPY_FROM_BIT] to be retrieved. Otherwise, an error is printed and a empty [PackedByteArray] is returned. + [b]Note:[/b] This method will block the GPU from working until the data is retrieved. Refer to [method texture_get_data_async] for an alternative that returns the data in more performant way. + + + + + + + + + Asynchronous version of [method texture_get_data]. RenderingDevice will call [param callback] in a certain amount of frames with the data the texture had at the time of the request. + [b]Note:[/b] At the moment, the delay corresponds to the amount of frames specified by [member ProjectSettings.rendering/rendering_device/vsync/frame_queue_size]. + [b]Note:[/b] Downloading large textures can have a prohibitive cost for real-time even when using the asynchronous method due to hardware bandwidth limitations. When dealing with large resources, you can adjust settings such as [member ProjectSettings.rendering/rendering_device/staging_buffer/texture_download_region_size_px] and [member ProjectSettings.rendering/rendering_device/staging_buffer/block_size_kb] to improve the transfer speed at the cost of extra memory. + [codeblock] + func _texture_get_data_callback(array): + value = array.decode_u32(0) + + ... + + rd.texture_get_data_async(texture, 0, _texture_get_data_callback) + [/codeblock] diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 69441e0010c6..3f25b18191c1 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -139,8 +139,8 @@ print("I" in "team") # Prints false [/gdscript] [csharp] - GD.Print("Node".Contains("de")); // Prints true - GD.Print("team".Contains("I")); // Prints false + GD.Print("Node".Contains("de")); // Prints True + GD.Print("team".Contains("I")); // Prints False [/csharp] [/codeblocks] If you need to know where [param what] is within the string, use [method find]. See also [method containsn]. diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index e561ef54faa3..074d79833b4d 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -122,8 +122,8 @@ print("I" in "team") # Prints false [/gdscript] [csharp] - GD.Print("Node".Contains("de")); // Prints true - GD.Print("team".Contains("I")); // Prints false + GD.Print("Node".Contains("de")); // Prints True + GD.Print("team".Contains("I")); // Prints False [/csharp] [/codeblocks] If you need to know where [param what] is within the string, use [method find]. See also [method containsn]. diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index bdc908d3877f..c55b5f1c9072 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -116,7 +116,7 @@ # Rotating the Transform2D in any way preserves its scale. my_transform = my_transform.rotated(TAU / 2) - print(my_transform.get_scale()) # Prints (2, 4) + print(my_transform.get_scale()) # Prints (2.0, 4.0) [/gdscript] [csharp] var myTransform = new Transform2D( diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index c03262bb33b1..e9ddf57355a5 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -213,9 +213,9 @@ Creates a unit [Vector2] rotated to the given [param angle] in radians. This is equivalent to doing [code]Vector2(cos(angle), sin(angle))[/code] or [code]Vector2.RIGHT.rotated(angle)[/code]. [codeblock] - print(Vector2.from_angle(0)) # Prints (1, 0). - print(Vector2(1, 0).angle()) # Prints 0, which is the angle used above. - print(Vector2.from_angle(PI / 2)) # Prints (0, 1). + print(Vector2.from_angle(0)) # Prints (1.0, 0.0). + print(Vector2(1, 0).angle()) # Prints 0.0, which is the angle used above. + print(Vector2.from_angle(PI / 2)) # Prints (0.0, 1.0). [/codeblock] @@ -270,7 +270,7 @@ - Returns the vector with a maximum length by limiting its length to [param length]. + Returns the vector with a maximum length by limiting its length to [param length]. If the vector is non-finite, the result is undefined. @@ -477,7 +477,7 @@ Multiplies each component of the [Vector2] by the components of the given [Vector2]. [codeblock] - print(Vector2(10, 20) * Vector2(3, 4)) # Prints "(30, 80)" + print(Vector2(10, 20) * Vector2(3, 4)) # Prints (30.0, 80.0) [/codeblock] @@ -501,7 +501,7 @@ Adds each component of the [Vector2] by the components of the given [Vector2]. [codeblock] - print(Vector2(10, 20) + Vector2(3, 4)) # Prints "(13, 24)" + print(Vector2(10, 20) + Vector2(3, 4)) # Prints (13.0, 24.0) [/codeblock] @@ -511,7 +511,7 @@ Subtracts each component of the [Vector2] by the components of the given [Vector2]. [codeblock] - print(Vector2(10, 20) - Vector2(3, 4)) # Prints "(7, 16)" + print(Vector2(10, 20) - Vector2(3, 4)) # Prints (7.0, 16.0) [/codeblock] @@ -521,7 +521,7 @@ Divides each component of the [Vector2] by the components of the given [Vector2]. [codeblock] - print(Vector2(10, 20) / Vector2(2, 5)) # Prints "(5, 4)" + print(Vector2(10, 20) / Vector2(2, 5)) # Prints (5.0, 4.0) [/codeblock] diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml index 53c7c92ca38a..74dfc3efe97a 100644 --- a/doc/classes/Vector2i.xml +++ b/doc/classes/Vector2i.xml @@ -215,7 +215,7 @@ Gets the remainder of each component of the [Vector2i] with the components of the given [Vector2i]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector2i(10, -20) % Vector2i(7, 8)) # Prints "(3, -4)" + print(Vector2i(10, -20) % Vector2i(7, 8)) # Prints (3, -4) [/codeblock] @@ -225,7 +225,7 @@ Gets the remainder of each component of the [Vector2i] with the given [int]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector2i(10, -20) % 7) # Prints "(3, -6)" + print(Vector2i(10, -20) % 7) # Prints (3, -6) [/codeblock] @@ -235,7 +235,7 @@ Multiplies each component of the [Vector2i] by the components of the given [Vector2i]. [codeblock] - print(Vector2i(10, 20) * Vector2i(3, 4)) # Prints "(30, 80)" + print(Vector2i(10, 20) * Vector2i(3, 4)) # Prints (30, 80) [/codeblock] @@ -245,7 +245,7 @@ Multiplies each component of the [Vector2i] by the given [float]. Returns a [Vector2]. [codeblock] - print(Vector2i(10, 15) * 0.9) # Prints "(9, 13.5)" + print(Vector2i(10, 15) * 0.9) # Prints (9.0, 13.5) [/codeblock] @@ -262,7 +262,7 @@ Adds each component of the [Vector2i] by the components of the given [Vector2i]. [codeblock] - print(Vector2i(10, 20) + Vector2i(3, 4)) # Prints "(13, 24)" + print(Vector2i(10, 20) + Vector2i(3, 4)) # Prints (13, 24) [/codeblock] @@ -272,7 +272,7 @@ Subtracts each component of the [Vector2i] by the components of the given [Vector2i]. [codeblock] - print(Vector2i(10, 20) - Vector2i(3, 4)) # Prints "(7, 16)" + print(Vector2i(10, 20) - Vector2i(3, 4)) # Prints (7, 16) [/codeblock] @@ -282,7 +282,7 @@ Divides each component of the [Vector2i] by the components of the given [Vector2i]. [codeblock] - print(Vector2i(10, 20) / Vector2i(2, 5)) # Prints "(5, 4)" + print(Vector2i(10, 20) / Vector2i(2, 5)) # Prints (5, 4) [/codeblock] @@ -292,7 +292,7 @@ Divides each component of the [Vector2i] by the given [float]. Returns a [Vector2]. [codeblock] - print(Vector2i(10, 20) / 2.9) # Prints "(5, 10)" + print(Vector2i(10, 20) / 2.9) # Prints (5.0, 10.0) [/codeblock] diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 4ab3140eb633..dd86c4b2874d 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -239,7 +239,7 @@ - Returns the vector with a maximum length by limiting its length to [param length]. + Returns the vector with a maximum length by limiting its length to [param length]. If the vector is non-finite, the result is undefined. @@ -518,7 +518,7 @@ Multiplies each component of the [Vector3] by the components of the given [Vector3]. [codeblock] - print(Vector3(10, 20, 30) * Vector3(3, 4, 5)) # Prints "(30, 80, 150)" + print(Vector3(10, 20, 30) * Vector3(3, 4, 5)) # Prints (30.0, 80.0, 150.0) [/codeblock] @@ -542,7 +542,7 @@ Adds each component of the [Vector3] by the components of the given [Vector3]. [codeblock] - print(Vector3(10, 20, 30) + Vector3(3, 4, 5)) # Prints "(13, 24, 35)" + print(Vector3(10, 20, 30) + Vector3(3, 4, 5)) # Prints (13.0, 24.0, 35.0) [/codeblock] @@ -552,7 +552,7 @@ Subtracts each component of the [Vector3] by the components of the given [Vector3]. [codeblock] - print(Vector3(10, 20, 30) - Vector3(3, 4, 5)) # Prints "(7, 16, 25)" + print(Vector3(10, 20, 30) - Vector3(3, 4, 5)) # Prints (7.0, 16.0, 25.0) [/codeblock] @@ -562,7 +562,7 @@ Divides each component of the [Vector3] by the components of the given [Vector3]. [codeblock] - print(Vector3(10, 20, 30) / Vector3(2, 5, 3)) # Prints "(5, 4, 10)" + print(Vector3(10, 20, 30) / Vector3(2, 5, 3)) # Prints (5.0, 4.0, 10.0) [/codeblock] diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml index 7fe469aec075..64c2137b7d83 100644 --- a/doc/classes/Vector3i.xml +++ b/doc/classes/Vector3i.xml @@ -222,7 +222,7 @@ Gets the remainder of each component of the [Vector3i] with the components of the given [Vector3i]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector3i(10, -20, 30) % Vector3i(7, 8, 9)) # Prints "(3, -4, 3)" + print(Vector3i(10, -20, 30) % Vector3i(7, 8, 9)) # Prints (3, -4, 3) [/codeblock] @@ -232,7 +232,7 @@ Gets the remainder of each component of the [Vector3i] with the given [int]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector3i(10, -20, 30) % 7) # Prints "(3, -6, 2)" + print(Vector3i(10, -20, 30) % 7) # Prints (3, -6, 2) [/codeblock] @@ -242,7 +242,7 @@ Multiplies each component of the [Vector3i] by the components of the given [Vector3i]. [codeblock] - print(Vector3i(10, 20, 30) * Vector3i(3, 4, 5)) # Prints "(30, 80, 150)" + print(Vector3i(10, 20, 30) * Vector3i(3, 4, 5)) # Prints (30, 80, 150) [/codeblock] @@ -252,7 +252,7 @@ Multiplies each component of the [Vector3i] by the given [float]. Returns a [Vector3]. [codeblock] - print(Vector3i(10, 15, 20) * 0.9) # Prints "(9, 13.5, 18)" + print(Vector3i(10, 15, 20) * 0.9) # Prints (9.0, 13.5, 18.0) [/codeblock] @@ -269,7 +269,7 @@ Adds each component of the [Vector3i] by the components of the given [Vector3i]. [codeblock] - print(Vector3i(10, 20, 30) + Vector3i(3, 4, 5)) # Prints "(13, 24, 35)" + print(Vector3i(10, 20, 30) + Vector3i(3, 4, 5)) # Prints (13, 24, 35) [/codeblock] @@ -279,7 +279,7 @@ Subtracts each component of the [Vector3i] by the components of the given [Vector3i]. [codeblock] - print(Vector3i(10, 20, 30) - Vector3i(3, 4, 5)) # Prints "(7, 16, 25)" + print(Vector3i(10, 20, 30) - Vector3i(3, 4, 5)) # Prints (7, 16, 25) [/codeblock] @@ -289,7 +289,7 @@ Divides each component of the [Vector3i] by the components of the given [Vector3i]. [codeblock] - print(Vector3i(10, 20, 30) / Vector3i(2, 5, 3)) # Prints "(5, 4, 10)" + print(Vector3i(10, 20, 30) / Vector3i(2, 5, 3)) # Prints (5, 4, 10) [/codeblock] @@ -299,7 +299,7 @@ Divides each component of the [Vector3i] by the given [float]. Returns a [Vector3]. [codeblock] - print(Vector3i(10, 20, 30) / 2.9) # Prints "(5, 10, 15)" + print(Vector3i(10, 20, 30) / 2.9) # Prints (5.0, 10.0, 15.0) [/codeblock] diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml index 8fa17b57e6cc..45780a1aedcb 100644 --- a/doc/classes/Vector4.xml +++ b/doc/classes/Vector4.xml @@ -333,7 +333,7 @@ Multiplies each component of the [Vector4] by the components of the given [Vector4]. [codeblock] - print(Vector4(10, 20, 30, 40) * Vector4(3, 4, 5, 6)) # Prints "(30, 80, 150, 240)" + print(Vector4(10, 20, 30, 40) * Vector4(3, 4, 5, 6)) # Prints (30.0, 80.0, 150.0, 240.0) [/codeblock] @@ -343,7 +343,7 @@ Multiplies each component of the [Vector4] by the given [float]. [codeblock] - print(Vector4(10, 20, 30, 40) * 2) # Prints "(20, 40, 60, 80)" + print(Vector4(10, 20, 30, 40) * 2) # Prints (20.0, 40.0, 60.0, 80.0) [/codeblock] @@ -360,7 +360,7 @@ Adds each component of the [Vector4] by the components of the given [Vector4]. [codeblock] - print(Vector4(10, 20, 30, 40) + Vector4(3, 4, 5, 6)) # Prints "(13, 24, 35, 46)" + print(Vector4(10, 20, 30, 40) + Vector4(3, 4, 5, 6)) # Prints (13.0, 24.0, 35.0, 46.0) [/codeblock] @@ -370,7 +370,7 @@ Subtracts each component of the [Vector4] by the components of the given [Vector4]. [codeblock] - print(Vector4(10, 20, 30, 40) - Vector4(3, 4, 5, 6)) # Prints "(7, 16, 25, 34)" + print(Vector4(10, 20, 30, 40) - Vector4(3, 4, 5, 6)) # Prints (7.0, 16.0, 25.0, 34.0) [/codeblock] @@ -380,7 +380,7 @@ Divides each component of the [Vector4] by the components of the given [Vector4]. [codeblock] - print(Vector4(10, 20, 30, 40) / Vector4(2, 5, 3, 4)) # Prints "(5, 4, 10, 10)" + print(Vector4(10, 20, 30, 40) / Vector4(2, 5, 3, 4)) # Prints (5.0, 4.0, 10.0, 10.0) [/codeblock] @@ -390,7 +390,7 @@ Divides each component of the [Vector4] by the given [float]. [codeblock] - print(Vector4(10, 20, 30, 40) / 2 # Prints "(5, 10, 15, 20)" + print(Vector4(10, 20, 30, 40) / 2 # Prints (5.0, 10.0, 15.0, 20.0) [/codeblock] diff --git a/doc/classes/Vector4i.xml b/doc/classes/Vector4i.xml index e1d65eb1b557..6c46f82a3bbb 100644 --- a/doc/classes/Vector4i.xml +++ b/doc/classes/Vector4i.xml @@ -208,7 +208,7 @@ Gets the remainder of each component of the [Vector4i] with the components of the given [Vector4i]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector4i(10, -20, 30, -40) % Vector4i(7, 8, 9, 10)) # Prints "(3, -4, 3, 0)" + print(Vector4i(10, -20, 30, -40) % Vector4i(7, 8, 9, 10)) # Prints (3, -4, 3, 0) [/codeblock] @@ -218,7 +218,7 @@ Gets the remainder of each component of the [Vector4i] with the given [int]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. [codeblock] - print(Vector4i(10, -20, 30, -40) % 7) # Prints "(3, -6, 2, -5)" + print(Vector4i(10, -20, 30, -40) % 7) # Prints (3, -6, 2, -5) [/codeblock] @@ -228,7 +228,7 @@ Multiplies each component of the [Vector4i] by the components of the given [Vector4i]. [codeblock] - print(Vector4i(10, 20, 30, 40) * Vector4i(3, 4, 5, 6)) # Prints "(30, 80, 150, 240)" + print(Vector4i(10, 20, 30, 40) * Vector4i(3, 4, 5, 6)) # Prints (30, 80, 150, 240) [/codeblock] @@ -239,7 +239,7 @@ Multiplies each component of the [Vector4i] by the given [float]. Returns a Vector4 value due to floating-point operations. [codeblock] - print(Vector4i(10, 20, 30, 40) * 2) # Prints "(20, 40, 60, 80)" + print(Vector4i(10, 20, 30, 40) * 2) # Prints (20.0, 40.0, 60.0, 80.0) [/codeblock] @@ -256,7 +256,7 @@ Adds each component of the [Vector4i] by the components of the given [Vector4i]. [codeblock] - print(Vector4i(10, 20, 30, 40) + Vector4i(3, 4, 5, 6)) # Prints "(13, 24, 35, 46)" + print(Vector4i(10, 20, 30, 40) + Vector4i(3, 4, 5, 6)) # Prints (13, 24, 35, 46) [/codeblock] @@ -266,7 +266,7 @@ Subtracts each component of the [Vector4i] by the components of the given [Vector4i]. [codeblock] - print(Vector4i(10, 20, 30, 40) - Vector4i(3, 4, 5, 6)) # Prints "(7, 16, 25, 34)" + print(Vector4i(10, 20, 30, 40) - Vector4i(3, 4, 5, 6)) # Prints (7, 16, 25, 34) [/codeblock] @@ -276,7 +276,7 @@ Divides each component of the [Vector4i] by the components of the given [Vector4i]. [codeblock] - print(Vector4i(10, 20, 30, 40) / Vector4i(2, 5, 3, 4)) # Prints "(5, 4, 10, 10)" + print(Vector4i(10, 20, 30, 40) / Vector4i(2, 5, 3, 4)) # Prints (5, 4, 10, 10) [/codeblock] @@ -287,7 +287,7 @@ Divides each component of the [Vector4i] by the given [float]. Returns a Vector4 value due to floating-point operations. [codeblock] - print(Vector4i(10, 20, 30, 40) / 2 # Prints "(5, 10, 15, 20)" + print(Vector4i(10, 20, 30, 40) / 2 # Prints (5.0, 10.0, 15.0, 20.0) [/codeblock] diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 56f78bdc8acc..96ac9a804973 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -97,7 +97,7 @@ Multiplies each component of the [Vector2i] by the given [float]. Returns a [Vector2]. [codeblock] - print(0.9 * Vector2i(10, 15)) # Prints "(9, 13.5)" + print(0.9 * Vector2i(10, 15)) # Prints (9.0, 13.5) [/codeblock] @@ -114,7 +114,7 @@ Multiplies each component of the [Vector3i] by the given [float]. Returns a [Vector3]. [codeblock] - print(0.9 * Vector3i(10, 15, 20)) # Prints "(9, 13.5, 18)" + print(0.9 * Vector3i(10, 15, 20)) # Prints (9.0, 13.5, 18.0) [/codeblock] @@ -131,7 +131,7 @@ Multiplies each component of the [Vector4i] by the given [float]. Returns a [Vector4]. [codeblock] - print(0.9 * Vector4i(10, 15, 20, -10)) # Prints "(9, 13.5, 18, -9)" + print(0.9 * Vector4i(10, 15, 20, -10)) # Prints (9.0, 13.5, 18.0, -9.0) [/codeblock] diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 14bbe635a4f0..bdcd9fe4849f 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1099,7 +1099,11 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { ERR_FAIL_COND_V(data.is_empty(), Ref()); image = Image::create_from_data(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, texture->real_format, data); - ERR_FAIL_COND_V(image->is_empty(), Ref()); + if (image->is_empty()) { + const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path); + ERR_FAIL_V_MSG(Ref(), vformat("Texture %s has no data.", path_str)); + } + if (texture->format != texture->real_format) { image->convert(texture->format); } @@ -1155,7 +1159,10 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { ERR_FAIL_COND_V(data.is_empty(), Ref()); image = Image::create_from_data(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data); - ERR_FAIL_COND_V(image->is_empty(), Ref()); + if (image->is_empty()) { + const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path); + ERR_FAIL_V_MSG(Ref(), vformat("Texture %s has no data.", path_str)); + } if (texture->format != Image::FORMAT_RGBA8) { image->convert(texture->format); @@ -1227,7 +1234,10 @@ Ref TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons ERR_FAIL_COND_V(data.is_empty(), Ref()); Ref image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data); - ERR_FAIL_COND_V(image->is_empty(), Ref()); + if (image->is_empty()) { + const String &path_str = texture->path.is_empty() ? "with no path" : vformat("with path '%s'", texture->path); + ERR_FAIL_V_MSG(Ref(), vformat("Texture %s has no data.", path_str)); + } if (texture->format != Image::FORMAT_RGBA8) { image->convert(texture->format); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 76a1c9b04d3f..95eb3702aed7 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -358,14 +358,18 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { log->push_color(theme_cache.error_color); Ref icon = theme_cache.error_icon; log->add_image(icon); - log->add_text(" "); + log->push_bold(); + log->add_text(" ERROR: "); + log->pop(); // bold tool_button->set_button_icon(icon); } break; case MSG_TYPE_WARNING: { log->push_color(theme_cache.warning_color); Ref icon = theme_cache.warning_icon; log->add_image(icon); - log->add_text(" "); + log->push_bold(); + log->add_text(" WARNING: "); + log->pop(); // bold tool_button->set_button_icon(icon); } break; case MSG_TYPE_EDITOR: { diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 7ef982353713..21967f3e0880 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -42,6 +42,7 @@ #include "editor/themes/editor_scale.h" #include "scene/gui/center_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" #include "scene/gui/label.h" #include "scene/gui/option_button.h" @@ -1951,30 +1952,38 @@ void EditorFileDialog::_update_option_controls() { } options_dirty = false; - while (grid_options->get_child_count() > 0) { - Node *child = grid_options->get_child(0); - grid_options->remove_child(child); + while (flow_checkbox_options->get_child_count() > 0) { + Node *child = flow_checkbox_options->get_child(0); + flow_checkbox_options->remove_child(child); child->queue_free(); } + while (grid_select_options->get_child_count() > 0) { + Node *child = grid_select_options->get_child(0); + grid_select_options->remove_child(child); + child->queue_free(); + } + selected_options.clear(); for (const EditorFileDialog::Option &opt : options) { - Label *lbl = memnew(Label); - lbl->set_text(opt.name); - grid_options->add_child(lbl); if (opt.values.is_empty()) { CheckBox *cb = memnew(CheckBox); cb->set_pressed(opt.default_idx); - grid_options->add_child(cb); + cb->set_text(opt.name); + flow_checkbox_options->add_child(cb); cb->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::_option_changed_checkbox_toggled).bind(opt.name)); selected_options[opt.name] = (bool)opt.default_idx; } else { + Label *lbl = memnew(Label); + lbl->set_text(opt.name); + grid_select_options->add_child(lbl); + OptionButton *ob = memnew(OptionButton); for (const String &val : opt.values) { ob->add_item(val); } ob->select(opt.default_idx); - grid_options->add_child(ob); + grid_select_options->add_child(ob); ob->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_option_changed_item_selected).bind(opt.name)); selected_options[opt.name] = opt.default_idx; } @@ -2266,11 +2275,13 @@ void EditorFileDialog::add_side_menu(Control *p_menu, const String &p_title) { void EditorFileDialog::_update_side_menu_visibility(bool p_native_dlg) { if (p_native_dlg) { pathhb->set_visible(false); - grid_options->set_visible(false); + flow_checkbox_options->set_visible(false); + grid_select_options->set_visible(false); list_hb->set_visible(false); } else { pathhb->set_visible(true); - grid_options->set_visible(true); + flow_checkbox_options->set_visible(true); + grid_select_options->set_visible(true); list_hb->set_visible(true); } } @@ -2372,10 +2383,15 @@ EditorFileDialog::EditorFileDialog() { body_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbc->add_child(body_hsplit); - grid_options = memnew(GridContainer); - grid_options->set_h_size_flags(Control::SIZE_SHRINK_CENTER); - grid_options->set_columns(2); - vbc->add_child(grid_options); + flow_checkbox_options = memnew(HFlowContainer); + flow_checkbox_options->set_h_size_flags(Control::SIZE_EXPAND_FILL); + flow_checkbox_options->set_alignment(FlowContainer::ALIGNMENT_CENTER); + vbc->add_child(flow_checkbox_options); + + grid_select_options = memnew(GridContainer); + grid_select_options->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + grid_select_options->set_columns(2); + vbc->add_child(grid_select_options); list_hb = memnew(HSplitContainer); list_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h index 8a07a2094372..d1b37687df85 100644 --- a/editor/gui/editor_file_dialog.h +++ b/editor/gui/editor_file_dialog.h @@ -39,6 +39,7 @@ class DependencyRemoveDialog; class GridContainer; class HSplitContainer; +class HFlowContainer; class ItemList; class MenuButton; class OptionButton; @@ -94,7 +95,8 @@ class EditorFileDialog : public ConfirmationDialog { Button *makedir = nullptr; Access access = ACCESS_RESOURCES; - GridContainer *grid_options = nullptr; + HFlowContainer *flow_checkbox_options = nullptr; + GridContainer *grid_select_options = nullptr; VBoxContainer *vbox = nullptr; FileMode mode = FILE_MODE_SAVE_FILE; bool can_create_dir = false; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 094b14722e39..82c2e3e4bd6c 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4074,6 +4074,7 @@ void CanvasItemEditor::_notification(int p_what) { case NOTIFICATION_READY: { _update_lock_and_group_button(); + SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &CanvasItemEditor::_update_lock_and_group_button)); ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed)); } break; diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 40c13ea3c4e1..7c4c3e25d2be 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -73,9 +73,17 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const { } break; case CONCAVE_POLYGON_SHAPE: { + Ref shape = node->get_shape(); + const Vector &segments = shape->get_segments(); + return segments[idx]; + } break; case CONVEX_POLYGON_SHAPE: { + Ref shape = node->get_shape(); + const Vector &points = shape->get_points(); + return points[idx]; + } break; case WORLD_BOUNDARY_SHAPE: { @@ -145,9 +153,27 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { } break; case CONCAVE_POLYGON_SHAPE: { + Ref concave_shape = node->get_shape(); + + Vector segments = concave_shape->get_segments(); + + ERR_FAIL_INDEX(idx, segments.size()); + segments.write[idx] = p_point; + + concave_shape->set_segments(segments); + } break; case CONVEX_POLYGON_SHAPE: { + Ref convex_shape = node->get_shape(); + + Vector points = convex_shape->get_points(); + + ERR_FAIL_INDEX(idx, points.size()); + points.write[idx] = p_point; + + convex_shape->set_points(points); + } break; case WORLD_BOUNDARY_SHAPE: { @@ -237,11 +263,33 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { } break; case CONCAVE_POLYGON_SHAPE: { - // Cannot be edited directly, use CollisionPolygon2D instead. + Ref concave_shape = node->get_shape(); + + Vector2 values = p_org; + + Vector undo_segments = concave_shape->get_segments(); + + ERR_FAIL_INDEX(idx, undo_segments.size()); + undo_segments.write[idx] = values; + + undo_redo->add_do_method(concave_shape.ptr(), "set_segments", concave_shape->get_segments()); + undo_redo->add_undo_method(concave_shape.ptr(), "set_segments", undo_segments); + } break; case CONVEX_POLYGON_SHAPE: { - // Cannot be edited directly, use CollisionPolygon2D instead. + Ref convex_shape = node->get_shape(); + + Vector2 values = p_org; + + Vector undo_points = convex_shape->get_points(); + + ERR_FAIL_INDEX(idx, undo_points.size()); + undo_points.write[idx] = values; + + undo_redo->add_do_method(convex_shape.ptr(), "set_points", convex_shape->get_points()); + undo_redo->add_undo_method(convex_shape.ptr(), "set_points", undo_points); + } break; case WORLD_BOUNDARY_SHAPE: { @@ -471,9 +519,29 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla } break; case CONCAVE_POLYGON_SHAPE: { + Ref shape = current_shape; + + const Vector &segments = shape->get_segments(); + + handles.resize(segments.size()); + for (int i = 0; i < handles.size(); i++) { + handles.write[i] = segments[i]; + p_overlay->draw_texture(h, gt.xform(handles[i]) - size); + } + } break; case CONVEX_POLYGON_SHAPE: { + Ref shape = current_shape; + + const Vector &points = shape->get_points(); + + handles.resize(points.size()); + for (int i = 0; i < handles.size(); i++) { + handles.write[i] = points[i]; + p_overlay->draw_texture(h, gt.xform(handles[i]) - size); + } + } break; case WORLD_BOUNDARY_SHAPE: { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 470e44d8eb13..f0008406958e 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1817,6 +1817,12 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { break; } + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_RULER) { + EditorNode::get_singleton()->get_scene_root()->add_child(ruler); + collision_reposition = true; + break; + } + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_LIST_SELECT) { _list_select(b); break; @@ -1953,6 +1959,15 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { surface->queue_redraw(); } else { + if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_RULER) { + EditorNode::get_singleton()->get_scene_root()->remove_child(ruler); + ruler_start_point->set_visible(false); + ruler_end_point->set_visible(false); + ruler_label->set_visible(false); + collision_reposition = false; + break; + } + if (_edit.gizmo.is_valid()) { _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, false); spatial_editor->get_single_selected_node()->update_gizmos(); @@ -2882,6 +2897,24 @@ void Node3DEditorViewport::_notification(int p_what) { } break; case NOTIFICATION_PROCESS: { + if (ruler->is_inside_tree()) { + Vector3 start_pos = ruler_start_point->get_global_position(); + Vector3 end_pos = ruler_end_point->get_global_position(); + + geometry->clear_surfaces(); + geometry->surface_begin(Mesh::PRIMITIVE_LINES); + geometry->surface_add_vertex(start_pos); + geometry->surface_add_vertex(end_pos); + geometry->surface_end(); + + float distance = start_pos.distance_to(end_pos); + ruler_label->set_text(TS->format_number(vformat("%.3f m", distance))); + + Vector3 center = (start_pos + end_pos) / 2; + Vector2 screen_position = camera->unproject_position(center) - (ruler_label->get_custom_minimum_size() / 2); + ruler_label->set_position(screen_position); + } + real_t delta = get_process_delta_time(); if (zoom_indicator_delay > 0) { @@ -3094,15 +3127,37 @@ void Node3DEditorViewport::_notification(int p_what) { case NOTIFICATION_PHYSICS_PROCESS: { if (collision_reposition) { - List &selection = editor_selection->get_selected_node_list(); - - if (selection.size() == 1) { - Node3D *first_selected_node = Object::cast_to(selection.front()->get()); - double snap = EDITOR_GET("interface/inspector/default_float_step"); - int snap_step_decimals = Math::range_step_decimals(snap); - set_message(TTR("Translating:") + " (" + String::num(first_selected_node->get_global_position().x, snap_step_decimals) + ", " + - String::num(first_selected_node->get_global_position().y, snap_step_decimals) + ", " + String::num(first_selected_node->get_global_position().z, snap_step_decimals) + ")"); - first_selected_node->set_global_position(spatial_editor->snap_point(_get_instance_position(_edit.mouse_pos, first_selected_node))); + Node3D *selected_node = nullptr; + + if (ruler->is_inside_tree()) { + if (ruler_start_point->is_visible()) { + selected_node = ruler_end_point; + } else { + selected_node = ruler_start_point; + } + } else { + List &selection = editor_selection->get_selected_node_list(); + if (selection.size() == 1) { + selected_node = Object::cast_to(selection.front()->get()); + } + } + + if (selected_node) { + if (!ruler->is_inside_tree()) { + double snap = EDITOR_GET("interface/inspector/default_float_step"); + int snap_step_decimals = Math::range_step_decimals(snap); + set_message(TTR("Translating:") + " (" + String::num(selected_node->get_global_position().x, snap_step_decimals) + ", " + + String::num(selected_node->get_global_position().y, snap_step_decimals) + ", " + String::num(selected_node->get_global_position().z, snap_step_decimals) + ")"); + } + + selected_node->set_global_position(spatial_editor->snap_point(_get_instance_position(_edit.mouse_pos, selected_node))); + + if (ruler->is_inside_tree() && !ruler_start_point->is_visible()) { + ruler_end_point->set_global_position(ruler_start_point->get_global_position()); + ruler_start_point->set_visible(true); + ruler_end_point->set_visible(true); + ruler_label->set_visible(true); + } } } @@ -4311,7 +4366,7 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos, Node3D HashSet rids; - if (!preview_node->is_inside_tree()) { + if (!preview_node->is_inside_tree() && !ruler->is_inside_tree()) { List &selection = editor_selection->get_selected_node_list(); Node3D *first_selected_node = Object::cast_to(selection.front()->get()); @@ -5739,6 +5794,53 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p viewport->set_as_audio_listener_3d(true); } + ruler = memnew(Node); + + ruler_start_point = memnew(Node3D); + ruler_start_point->set_visible(false); + + ruler_end_point = memnew(Node3D); + ruler_end_point->set_visible(false); + + ruler_material.instantiate(); + ruler_material->set_albedo(Color(1.0, 0.9, 0.0, 1.0)); + ruler_material->set_flag(BaseMaterial3D::FLAG_DISABLE_FOG, true); + ruler_material->set_shading_mode(BaseMaterial3D::SHADING_MODE_UNSHADED); + ruler_material->set_depth_draw_mode(BaseMaterial3D::DEPTH_DRAW_DISABLED); + + ruler_material_xray.instantiate(); + ruler_material_xray->set_albedo(Color(1.0, 0.9, 0.0, 0.15)); + ruler_material_xray->set_flag(BaseMaterial3D::FLAG_DISABLE_FOG, true); + ruler_material_xray->set_shading_mode(BaseMaterial3D::SHADING_MODE_UNSHADED); + ruler_material_xray->set_flag(BaseMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + ruler_material_xray->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); + ruler_material_xray->set_render_priority(BaseMaterial3D::RENDER_PRIORITY_MAX); + + geometry.instantiate(); + + ruler_line = memnew(MeshInstance3D); + ruler_line->set_mesh(geometry); + ruler_line->set_material_override(ruler_material); + + ruler_line_xray = memnew(MeshInstance3D); + ruler_line_xray->set_mesh(geometry); + ruler_line_xray->set_material_override(ruler_material_xray); + + ruler_label = memnew(Label); + ruler_label->add_theme_color_override(SceneStringName(font_color), Color(1.0, 0.9, 0.0, 1.0)); + ruler_label->add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 1.0)); + ruler_label->add_theme_constant_override("outline_size", 4 * EDSCALE); + ruler_label->add_theme_font_size_override(SceneStringName(font_size), 15 * EDSCALE); + ruler_label->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); + ruler_label->set_visible(false); + + ruler->add_child(ruler_start_point); + ruler->add_child(ruler_end_point); + ruler->add_child(ruler_line); + ruler->add_child(ruler_line_xray); + + viewport->add_child(ruler_label); + view_type = VIEW_TYPE_USER; _update_name(); @@ -5746,6 +5848,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p } Node3DEditorViewport::~Node3DEditorViewport() { + memdelete(ruler); memdelete(frame_time_gradient); } @@ -6860,6 +6963,14 @@ void Node3DEditor::_menu_item_pressed(int p_option) { undo_redo->add_undo_method(this, "_refresh_menu_icons"); undo_redo->commit_action(); } break; + case MENU_RULER: { + for (int i = 0; i < TOOL_MAX; i++) { + tool_button[i]->set_pressed(i == p_option); + } + tool_button[TOOL_RULER]->set_pressed(true); + tool_mode = ToolMode::TOOL_RULER; + update_transform_gizmo(); + } break; } } @@ -8024,6 +8135,7 @@ void Node3DEditor::_update_theme() { tool_button[TOOL_UNLOCK_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Unlock"))); tool_button[TOOL_GROUP_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Group"))); tool_button[TOOL_UNGROUP_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Ungroup"))); + tool_button[TOOL_RULER]->set_button_icon(get_editor_theme_icon(SNAME("Ruler"))); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_button_icon(get_editor_theme_icon(SNAME("Object"))); tool_option_button[TOOL_OPT_USE_SNAP]->set_button_icon(get_editor_theme_icon(SNAME("Snap"))); @@ -8773,6 +8885,15 @@ Node3DEditor::Node3DEditor() { // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes")); + tool_button[TOOL_RULER] = memnew(Button); + main_menu_hbox->add_child(tool_button[TOOL_RULER]); + tool_button[TOOL_RULER]->set_toggle_mode(true); + tool_button[TOOL_RULER]->set_theme_type_variation("FlatButton"); + tool_button[TOOL_RULER]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_RULER)); + tool_button[TOOL_RULER]->set_tooltip_text(TTR("LMB+Drag: Measure the distance between two points in 3D space.")); + // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. + tool_button[TOOL_RULER]->set_shortcut(ED_SHORTCUT("spatial_editor/measure", TTR("Ruler Mode"), Key::M)); + main_menu_hbox->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(Button); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 243a712c3adf..54620562f97f 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -38,6 +38,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/spin_box.h" +#include "scene/resources/immediate_mesh.h" class AcceptDialog; class CheckBox; @@ -61,6 +62,7 @@ class VSeparator; class VSplitContainer; class ViewportNavigationControl; class WorldEnvironment; +class MeshInstance3D; class ViewportRotationControl : public Control { GDCLASS(ViewportRotationControl, Control); @@ -214,6 +216,16 @@ class Node3DEditorViewport : public Control { double gpu_time_history[FRAME_TIME_HISTORY]; int gpu_time_history_index; + Node *ruler = nullptr; + Node3D *ruler_start_point = nullptr; + Node3D *ruler_end_point = nullptr; + Ref geometry; + MeshInstance3D *ruler_line = nullptr; + MeshInstance3D *ruler_line_xray = nullptr; + Label *ruler_label = nullptr; + Ref ruler_material; + Ref ruler_material_xray; + int index; ViewType view_type; void _menu_option(int p_option); @@ -621,6 +633,7 @@ class Node3DEditor : public VBoxContainer { TOOL_UNLOCK_SELECTED, TOOL_GROUP_SELECTED, TOOL_UNGROUP_SELECTED, + TOOL_RULER, TOOL_MAX }; @@ -726,7 +739,8 @@ class Node3DEditor : public VBoxContainer { MENU_UNLOCK_SELECTED, MENU_GROUP_SELECTED, MENU_UNGROUP_SELECTED, - MENU_SNAP_TO_FLOOR + MENU_SNAP_TO_FLOOR, + MENU_RULER, }; Button *tool_button[TOOL_MAX]; diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index d44a3e24cd57..6c6754d5db59 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -65,6 +65,7 @@ static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { "sampler2DArray", "sampler3D", "samplerCube", + "samplerExternalOES", }; class ShaderGlobalsEditorInterface : public Object { @@ -232,6 +233,11 @@ class ShaderGlobalsEditorInterface : public Object { pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; pinfo.hint_string = "Cubemap,CompressedCubemap"; } break; + case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "ExternalTexture"; + } break; default: { } break; } @@ -339,6 +345,9 @@ static Variant create_var(RS::GlobalShaderParameterType p_type) { case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { return ""; } + case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: { + return ""; + } default: { return Variant(); } diff --git a/main/main.cpp b/main/main.cpp index 0b3dcc3f5bd2..93551555e654 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -4441,15 +4441,20 @@ bool Main::iteration() { RenderingServer::get_singleton()->sync(); //sync if still drawing from previous frames. - if ((DisplayServer::get_singleton()->can_any_window_draw() || DisplayServer::get_singleton()->has_additional_outputs()) && - RenderingServer::get_singleton()->is_render_loop_enabled()) { + const bool has_pending_resources_for_processing = RD::get_singleton() && RD::get_singleton()->has_pending_resources_for_processing(); + bool wants_present = (DisplayServer::get_singleton()->can_any_window_draw() || + DisplayServer::get_singleton()->has_additional_outputs()) && + RenderingServer::get_singleton()->is_render_loop_enabled(); + + if (wants_present || has_pending_resources_for_processing) { + wants_present |= force_redraw_requested; if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) { if (RenderingServer::get_singleton()->has_changed()) { - RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands + RenderingServer::get_singleton()->draw(wants_present, scaled_step); // flush visual commands Engine::get_singleton()->increment_frames_drawn(); } } else { - RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands + RenderingServer::get_singleton()->draw(wants_present, scaled_step); // flush visual commands Engine::get_singleton()->increment_frames_drawn(); force_redraw_requested = false; } diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index 1bbe720eb944..8cade432e64d 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/misc/extension_api_validation/4.3-stable.expected @@ -215,3 +215,16 @@ Validate extension JSON: Error: Field 'classes/Control/properties/offset_right': Validate extension JSON: Error: Field 'classes/Control/properties/offset_top': type changed value in new API, from "int" to "float". Property type changed to float to match the actual internal API and documentation. + + +GH-100129 +--------- +Validate extension JSON: Error: Field 'classes/NavigationServer2D/methods/query_path': is_const changed value in new API, from true to false. +Validate extension JSON: Error: Field 'classes/NavigationServer3D/methods/query_path': is_const changed value in new API, from true to false. +Validate extension JSON: Error: Field 'classes/NavigationServer2D/methods/query_path/arguments': size changed value in new API, from 2 to 3. +Validate extension JSON: Error: Field 'classes/NavigationServer3D/methods/query_path/arguments': size changed value in new API, from 2 to 3. +Validate extension JSON: Error: Field 'classes/NavigationServer2D/methods/map_get_path': is_const changed value in new API, from true to false. +Validate extension JSON: Error: Field 'classes/NavigationServer3D/methods/map_get_path': is_const changed value in new API, from true to false. + +`query_path` and `map_get_path` methods changed to be non const due to internal compatibility and server changes. +Added optional callback parameters to `query_path` functions. Compatibility methods registered. diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index faf6ffb6ac1b..0c56cb2654f3 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1637,7 +1637,15 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali bool valid = true; if (!valid_annotations.has(annotation->name)) { - push_error(vformat(R"(Unrecognized annotation: "%s".)", annotation->name)); + if (annotation->name == "@deprecated") { + push_error(R"("@deprecated" annotation does not exist. Use "## @deprecated: Reason here." instead.)"); + } else if (annotation->name == "@experimental") { + push_error(R"("@experimental" annotation does not exist. Use "## @experimental: Reason here." instead.)"); + } else if (annotation->name == "@tutorial") { + push_error(R"("@tutorial" annotation does not exist. Use "## @tutorial(Title): https://example.com" instead.)"); + } else { + push_error(vformat(R"(Unrecognized annotation: "%s".)", annotation->name)); + } valid = false; } diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.gd b/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.gd new file mode 100644 index 000000000000..95569c48de70 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.gd @@ -0,0 +1,5 @@ +# This annotation should be used within a documentation comment instead: +# ## @deprecated: Reason here. + +@deprecated +var some_variable = "value" diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.out b/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.out new file mode 100644 index 000000000000..a99608890203 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_deprecated.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +"@deprecated" annotation does not exist. Use "## @deprecated: Reason here." instead. diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.gd b/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.gd new file mode 100644 index 000000000000..ce00639de6d4 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.gd @@ -0,0 +1,6 @@ +# This annotation should be used within a documentation comment instead: +# ## @experimental: Reason here. + +@experimental("This function isn't implemented yet.") +func say_hello(): + pass diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.out b/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.out new file mode 100644 index 000000000000..d7e1c2eb2f2f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_experimental.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +"@experimental" annotation does not exist. Use "## @experimental: Reason here." instead. diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.gd b/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.gd new file mode 100644 index 000000000000..ccd995bb5018 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.gd @@ -0,0 +1,5 @@ +# This annotation should be used within a documentation comment instead: +# ## @tutorial(Title): https://example.com + +@tutorial("https://example.com") +const SOME_CONSTANT = "value" diff --git a/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.out b/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.out new file mode 100644 index 000000000000..7d8be3567bf0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/annotation_tutorial.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +"@tutorial" annotation does not exist. Use "## @tutorial(Title): https://example.com" instead. diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp index 2af125d43481..de78ea5826a8 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.cpp +++ b/modules/navigation/2d/godot_navigation_server_2d.cpp @@ -81,12 +81,6 @@ return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \ } -#define FORWARD_5_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, T_4, D_4, CONV_0, CONV_1, CONV_2, CONV_3, CONV_4) \ - GodotNavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \ - const { \ - return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4))); \ - } - static RID rid_to_rid(const RID d) { return d; } @@ -277,7 +271,9 @@ real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid); void FORWARD_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real); real_t FORWARD_1_C(map_get_link_connection_radius, RID, p_map, rid_to_rid); -Vector FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32); +Vector GodotNavigationServer2D::map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers) { + return vector_v3_to_v2(NavigationServer3D::get_singleton()->map_get_path(p_map, v2_to_v3(p_origin), v2_to_v3(p_destination), p_optimize, p_navigation_layers)); +} Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); @@ -456,16 +452,48 @@ Vector GodotNavigationServer2D::obstacle_get_vertices(RID p_obstacle) c return vector_v3_to_v2(NavigationServer3D::get_singleton()->obstacle_get_vertices(p_obstacle)); } -void GodotNavigationServer2D::query_path(const Ref &p_query_parameters, Ref p_query_result) const { +void GodotNavigationServer2D::query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback) { ERR_FAIL_COND(!p_query_parameters.is_valid()); ERR_FAIL_COND(!p_query_result.is_valid()); - const NavigationUtilities::PathQueryResult _query_result = NavigationServer3D::get_singleton()->_query_path(p_query_parameters->get_parameters()); + Ref query_parameters; + query_parameters.instantiate(); + + query_parameters->set_map(p_query_parameters->get_map()); + query_parameters->set_start_position(v2_to_v3(p_query_parameters->get_start_position())); + query_parameters->set_target_position(v2_to_v3(p_query_parameters->get_target_position())); + query_parameters->set_navigation_layers(p_query_parameters->get_navigation_layers()); + query_parameters->set_pathfinding_algorithm(NavigationPathQueryParameters3D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR); + + switch (p_query_parameters->get_path_postprocessing()) { + case NavigationPathQueryParameters2D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL: { + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL); + } break; + case NavigationPathQueryParameters2D::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED: { + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED); + } break; + case NavigationPathQueryParameters2D::PathPostProcessing::PATH_POSTPROCESSING_NONE: { + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_NONE); + } break; + default: { + WARN_PRINT("No match for used PathPostProcessing - fallback to default"); + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL); + } break; + } + + query_parameters->set_metadata_flags((int64_t)p_query_parameters->get_metadata_flags()); + query_parameters->set_simplify_path(p_query_parameters->get_simplify_path()); + query_parameters->set_simplify_epsilon(p_query_parameters->get_simplify_epsilon()); + + Ref query_result; + query_result.instantiate(); + + NavigationServer3D::get_singleton()->query_path(query_parameters, query_result, p_callback); - p_query_result->set_path(vector_v3_to_v2(_query_result.path)); - p_query_result->set_path_types(_query_result.path_types); - p_query_result->set_path_rids(_query_result.path_rids); - p_query_result->set_path_owner_ids(_query_result.path_owner_ids); + p_query_result->set_path(vector_v3_to_v2(query_result->get_path())); + p_query_result->set_path_types(query_result->get_path_types()); + p_query_result->set_path_rids(query_result->get_path_rids()); + p_query_result->set_path_owner_ids(query_result->get_path_owner_ids()); } RID GodotNavigationServer2D::source_geometry_parser_create() { diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h index 1579ca290741..80fdea5ba9db 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.h +++ b/modules/navigation/2d/godot_navigation_server_2d.h @@ -68,7 +68,7 @@ class GodotNavigationServer2D : public NavigationServer2D { virtual real_t map_get_edge_connection_margin(RID p_map) const override; virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override; virtual real_t map_get_link_connection_radius(RID p_map) const override; - virtual Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override; + virtual Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) override; virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const override; virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const override; virtual TypedArray map_get_links(RID p_map) const override; @@ -242,7 +242,7 @@ class GodotNavigationServer2D : public NavigationServer2D { virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override; virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override; - virtual void query_path(const Ref &p_query_parameters, Ref p_query_result) const override; + virtual void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback) override; virtual void init() override; virtual void sync() override; diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 5dfc39f6f5e0..80e87a4b2e8d 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -237,11 +237,29 @@ real_t GodotNavigationServer3D::map_get_link_connection_radius(RID p_map) const return map->get_link_connection_radius(); } -Vector GodotNavigationServer3D::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const { +Vector GodotNavigationServer3D::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) { const NavMap *map = map_owner.get_or_null(p_map); ERR_FAIL_NULL_V(map, Vector()); - return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers, nullptr, nullptr, nullptr); + Ref query_parameters; + query_parameters.instantiate(); + + query_parameters->set_map(p_map); + query_parameters->set_start_position(p_origin); + query_parameters->set_target_position(p_destination); + query_parameters->set_navigation_layers(p_navigation_layers); + query_parameters->set_pathfinding_algorithm(NavigationPathQueryParameters3D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR); + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL); + if (!p_optimize) { + query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_EDGECENTERED); + } + + Ref query_result; + query_result.instantiate(); + + query_path(query_parameters, query_result); + + return query_result->get_path(); } Vector3 GodotNavigationServer3D::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { @@ -1384,86 +1402,14 @@ void GodotNavigationServer3D::finish() { #endif // _3D_DISABLED } -PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters &p_parameters) const { - PathQueryResult r_query_result; - - const NavMap *map = map_owner.get_or_null(p_parameters.map); - ERR_FAIL_NULL_V(map, r_query_result); - - // run the pathfinding - - if (p_parameters.pathfinding_algorithm == PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) { - // while postprocessing is still part of map.get_path() need to check and route it here for the correct "optimize" post-processing - if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) { - r_query_result.path = map->get_path( - p_parameters.start_position, - p_parameters.target_position, - true, - p_parameters.navigation_layers, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr); - } else if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) { - r_query_result.path = map->get_path( - p_parameters.start_position, - p_parameters.target_position, - false, - p_parameters.navigation_layers, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr, - p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr); - } - } else { - return r_query_result; - } - - // add path postprocessing - - if (r_query_result.path.size() > 2 && p_parameters.simplify_path) { - const LocalVector &simplified_path_indices = get_simplified_path_indices(r_query_result.path, p_parameters.simplify_epsilon); - - uint32_t indices_count = simplified_path_indices.size(); - - { - Vector3 *w = r_query_result.path.ptrw(); - const Vector3 *r = r_query_result.path.ptr(); - for (uint32_t i = 0; i < indices_count; i++) { - w[i] = r[simplified_path_indices[i]]; - } - r_query_result.path.resize(indices_count); - } - - if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { - int32_t *w = r_query_result.path_types.ptrw(); - const int32_t *r = r_query_result.path_types.ptr(); - for (uint32_t i = 0; i < indices_count; i++) { - w[i] = r[simplified_path_indices[i]]; - } - r_query_result.path_types.resize(indices_count); - } - - if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { - TypedArray simplified_path_rids; - simplified_path_rids.resize(indices_count); - for (uint32_t i = 0; i < indices_count; i++) { - simplified_path_rids[i] = r_query_result.path_rids[i]; - } - r_query_result.path_rids = simplified_path_rids; - } - - if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { - int64_t *w = r_query_result.path_owner_ids.ptrw(); - const int64_t *r = r_query_result.path_owner_ids.ptr(); - for (uint32_t i = 0; i < indices_count; i++) { - w[i] = r[simplified_path_indices[i]]; - } - r_query_result.path_owner_ids.resize(indices_count); - } - } +void GodotNavigationServer3D::query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback) { + ERR_FAIL_COND(p_query_parameters.is_null()); + ERR_FAIL_COND(p_query_result.is_null()); - // add path stats + NavMap *map = map_owner.get_or_null(p_query_parameters->get_map()); + ERR_FAIL_NULL(map); - return r_query_result; + NavMeshQueries3D::map_query_path(map, p_query_parameters, p_query_result, p_callback); } RID GodotNavigationServer3D::source_geometry_parser_create() { @@ -1490,84 +1436,30 @@ Vector GodotNavigationServer3D::simplify_path(const Vector &p_ p_epsilon = MAX(0.0, p_epsilon); - LocalVector simplified_path_indices = get_simplified_path_indices(p_path, p_epsilon); - - uint32_t indices_count = simplified_path_indices.size(); - - Vector simplified_path; - simplified_path.resize(indices_count); - - Vector3 *w = simplified_path.ptrw(); - const Vector3 *r = p_path.ptr(); - for (uint32_t i = 0; i < indices_count; i++) { - w[i] = r[simplified_path_indices[i]]; - } - - return simplified_path; -} - -LocalVector GodotNavigationServer3D::get_simplified_path_indices(const Vector &p_path, real_t p_epsilon) { - p_epsilon = MAX(0.0, p_epsilon); - real_t squared_epsilon = p_epsilon * p_epsilon; - - LocalVector valid_points; - valid_points.resize(p_path.size()); - for (uint32_t i = 0; i < valid_points.size(); i++) { - valid_points[i] = false; - } - - simplify_path_segment(0, p_path.size() - 1, p_path, squared_epsilon, valid_points); - - int valid_point_index = 0; - - for (bool valid : valid_points) { - if (valid) { - valid_point_index += 1; - } - } - - LocalVector simplified_path_indices; - simplified_path_indices.resize(valid_point_index); - valid_point_index = 0; - - for (uint32_t i = 0; i < valid_points.size(); i++) { - if (valid_points[i]) { - simplified_path_indices[valid_point_index] = i; - valid_point_index += 1; + LocalVector source_path; + { + source_path.resize(p_path.size()); + const Vector3 *r = p_path.ptr(); + for (uint32_t i = 0; i < p_path.size(); i++) { + source_path[i] = r[i]; } } - return simplified_path_indices; -} - -void GodotNavigationServer3D::simplify_path_segment(int p_start_inx, int p_end_inx, const Vector &p_points, real_t p_epsilon, LocalVector &r_valid_points) { - r_valid_points[p_start_inx] = true; - r_valid_points[p_end_inx] = true; - - const Vector3 &start_point = p_points[p_start_inx]; - const Vector3 &end_point = p_points[p_end_inx]; + LocalVector simplified_path_indices = NavMeshQueries3D::get_simplified_path_indices(source_path, p_epsilon); - Vector3 path_segment[2] = { start_point, end_point }; + uint32_t index_count = simplified_path_indices.size(); - real_t point_max_distance = 0.0; - int point_max_index = 0; - - for (int i = p_start_inx; i < p_end_inx; i++) { - const Vector3 &checked_point = p_points[i]; - - const Vector3 closest_point = Geometry3D::get_closest_point_to_segment(checked_point, path_segment); - real_t distance_squared = closest_point.distance_squared_to(checked_point); - - if (distance_squared > point_max_distance) { - point_max_index = i; - point_max_distance = distance_squared; + Vector simplified_path; + { + simplified_path.resize(index_count); + Vector3 *w = simplified_path.ptrw(); + const Vector3 *r = source_path.ptr(); + for (uint32_t i = 0; i < index_count; i++) { + w[i] = r[simplified_path_indices[i]]; } } - if (point_max_distance > p_epsilon) { - simplify_path_segment(p_start_inx, point_max_index, p_points, p_epsilon, r_valid_points); - simplify_path_segment(point_max_index, p_end_inx, p_points, p_epsilon, r_valid_points); - } + return simplified_path; } int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const { diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index eae6ea28608a..ce8d333535f2 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -40,6 +40,8 @@ #include "core/templates/local_vector.h" #include "core/templates/rid.h" #include "core/templates/rid_owner.h" +#include "servers/navigation/navigation_path_query_parameters_3d.h" +#include "servers/navigation/navigation_path_query_result_3d.h" #include "servers/navigation_server_3d.h" /// The commands are functions executed during the `sync` phase. @@ -130,7 +132,7 @@ class GodotNavigationServer3D : public NavigationServer3D { COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius); virtual real_t map_get_link_connection_radius(RID p_map) const override; - virtual Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override; + virtual Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) override; virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override; virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override; @@ -273,10 +275,6 @@ class GodotNavigationServer3D : public NavigationServer3D { virtual Vector simplify_path(const Vector &p_path, real_t p_epsilon) override; -private: - static void simplify_path_segment(int p_start_inx, int p_end_inx, const Vector &p_points, real_t p_epsilon, LocalVector &r_valid_points); - static LocalVector get_simplified_path_indices(const Vector &p_path, real_t p_epsilon); - public: COMMAND_1(free, RID, p_object); @@ -288,7 +286,7 @@ class GodotNavigationServer3D : public NavigationServer3D { virtual void sync() override; virtual void finish() override; - virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override; + virtual void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback = Callable()) override; int get_process_info(ProcessInfo p_info) const override; diff --git a/modules/navigation/3d/nav_mesh_queries_3d.cpp b/modules/navigation/3d/nav_mesh_queries_3d.cpp index fa7ccbeb701a..b60d8d073dca 100644 --- a/modules/navigation/3d/nav_mesh_queries_3d.cpp +++ b/modules/navigation/3d/nav_mesh_queries_3d.cpp @@ -33,21 +33,22 @@ #include "nav_mesh_queries_3d.h" #include "../nav_base.h" +#include "../nav_map.h" #include "core/math/geometry_3d.h" +#include "servers/navigation/navigation_utilities.h" #define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a))) -#define APPEND_METADATA(poly) \ - if (r_path_types) { \ - r_path_types->push_back(poly->owner->get_type()); \ - } \ - if (r_path_rids) { \ - r_path_rids->push_back(poly->owner->get_self()); \ - } \ - if (r_path_owners) { \ - r_path_owners->push_back(poly->owner->get_owner_id()); \ - } +bool NavMeshQueries3D::emit_callback(const Callable &p_callback) { + ERR_FAIL_COND_V(!p_callback.is_valid(), false); + + Callable::CallError ce; + Variant result; + p_callback.callp(nullptr, 0, result, ce); + + return ce.error == Callable::CallError::CALL_OK; +} Vector3 NavMeshQueries3D::polygons_get_random_point(const LocalVector &p_polygons, uint32_t p_navigation_layers, bool p_uniformly) { const LocalVector ®ion_polygons = p_polygons; @@ -127,87 +128,225 @@ Vector3 NavMeshQueries3D::polygons_get_random_point(const LocalVector NavMeshQueries3D::polygons_get_path(const LocalVector &p_polygons, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners, const Vector3 &p_map_up, uint32_t p_link_polygons_size) { - // Clear metadata outputs. - if (r_path_types) { - r_path_types->clear(); +void NavMeshQueries3D::_query_task_create_same_polygon_two_point_path(NavMeshPathQueryTask3D &p_query_task, const gd::Polygon *begin_poly, Vector3 begin_point, const gd::Polygon *end_poly, Vector3 end_point) { + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { + p_query_task.path_meta_point_types.resize(2); + p_query_task.path_meta_point_types[0] = begin_poly->owner->get_type(); + p_query_task.path_meta_point_types[1] = end_poly->owner->get_type(); } - if (r_path_rids) { - r_path_rids->clear(); + + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { + p_query_task.path_meta_point_rids.resize(2); + p_query_task.path_meta_point_rids[0] = begin_poly->owner->get_self(); + p_query_task.path_meta_point_rids[1] = end_poly->owner->get_self(); } - if (r_path_owners) { - r_path_owners->clear(); + + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { + p_query_task.path_meta_point_owners.resize(2); + p_query_task.path_meta_point_owners[0] = begin_poly->owner->get_owner_id(); + p_query_task.path_meta_point_owners[1] = end_poly->owner->get_owner_id(); } - // Find the start poly and the end poly on this map. - const gd::Polygon *begin_poly = nullptr; - const gd::Polygon *end_poly = nullptr; - Vector3 begin_point; - Vector3 end_point; - real_t begin_d = FLT_MAX; - real_t end_d = FLT_MAX; - // Find the initial poly and the end poly on this map. - for (const gd::Polygon &p : p_polygons) { - // Only consider the polygon if it in a region with compatible layers. - if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) { - continue; + p_query_task.path_points.resize(2); + p_query_task.path_points[0] = begin_point; + p_query_task.path_points[1] = end_point; +} + +void NavMeshQueries3D::_query_task_push_back_point_with_metadata(NavMeshPathQueryTask3D &p_query_task, Vector3 p_point, const gd::Polygon *p_point_polygon) { + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { + p_query_task.path_meta_point_types.push_back(p_point_polygon->owner->get_type()); + } + + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { + p_query_task.path_meta_point_rids.push_back(p_point_polygon->owner->get_self()); + } + + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { + p_query_task.path_meta_point_owners.push_back(p_point_polygon->owner->get_owner_id()); + } + + p_query_task.path_points.push_back(p_point); +} + +void NavMeshQueries3D::map_query_path(NavMap *map, const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback) { + ERR_FAIL_NULL(map); + ERR_FAIL_COND(p_query_parameters.is_null()); + ERR_FAIL_COND(p_query_result.is_null()); + + using namespace NavigationUtilities; + + NavMeshQueries3D::NavMeshPathQueryTask3D query_task; + query_task.start_position = p_query_parameters->get_start_position(); + query_task.target_position = p_query_parameters->get_target_position(); + query_task.navigation_layers = p_query_parameters->get_navigation_layers(); + query_task.callback = p_callback; + + switch (p_query_parameters->get_pathfinding_algorithm()) { + case NavigationPathQueryParameters3D::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR: { + query_task.pathfinding_algorithm = PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; + } break; + default: { + WARN_PRINT("No match for used PathfindingAlgorithm - fallback to default"); + query_task.pathfinding_algorithm = PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; + } break; + } + + switch (p_query_parameters->get_path_postprocessing()) { + case NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL: { + query_task.path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; + } break; + case NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED: { + query_task.path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED; + } break; + case NavigationPathQueryParameters3D::PathPostProcessing::PATH_POSTPROCESSING_NONE: { + query_task.path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_NONE; + } break; + default: { + WARN_PRINT("No match for used PathPostProcessing - fallback to default"); + query_task.path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; + } break; + } + + query_task.metadata_flags = (int64_t)p_query_parameters->get_metadata_flags(); + query_task.simplify_path = p_query_parameters->get_simplify_path(); + query_task.simplify_epsilon = p_query_parameters->get_simplify_epsilon(); + query_task.status = NavMeshPathQueryTask3D::TaskStatus::QUERY_STARTED; + + map->query_path(query_task); + + const uint32_t path_point_size = query_task.path_points.size(); + + Vector path_points; + Vector path_meta_point_types; + TypedArray path_meta_point_rids; + Vector path_meta_point_owners; + + { + path_points.resize(path_point_size); + Vector3 *w = path_points.ptrw(); + const Vector3 *r = query_task.path_points.ptr(); + for (uint32_t i = 0; i < path_point_size; i++) { + w[i] = r[i]; } + } - // For each face check the distance between the origin/destination - for (size_t point_id = 2; point_id < p.points.size(); point_id++) { - const Face3 face(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); + if (query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { + path_meta_point_types.resize(path_point_size); + int32_t *w = path_meta_point_types.ptrw(); + const int32_t *r = query_task.path_meta_point_types.ptr(); + for (uint32_t i = 0; i < path_point_size; i++) { + w[i] = r[i]; + } + } + if (query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { + path_meta_point_rids.resize(path_point_size); + for (uint32_t i = 0; i < path_point_size; i++) { + path_meta_point_rids[i] = query_task.path_meta_point_rids[i]; + } + } + if (query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { + path_meta_point_owners.resize(path_point_size); + int64_t *w = path_meta_point_owners.ptrw(); + const int64_t *r = query_task.path_meta_point_owners.ptr(); + for (uint32_t i = 0; i < path_point_size; i++) { + w[i] = r[i]; + } + } - Vector3 point = face.get_closest_point_to(p_origin); - real_t distance_to_point = point.distance_to(p_origin); - if (distance_to_point < begin_d) { - begin_d = distance_to_point; - begin_poly = &p; - begin_point = point; - } + p_query_result->set_path(path_points); + p_query_result->set_path_types(path_meta_point_types); + p_query_result->set_path_rids(path_meta_point_rids); + p_query_result->set_path_owner_ids(path_meta_point_owners); - point = face.get_closest_point_to(p_destination); - distance_to_point = point.distance_to(p_destination); - if (distance_to_point < end_d) { - end_d = distance_to_point; - end_poly = &p; - end_point = point; - } + if (query_task.callback.is_valid()) { + if (emit_callback(query_task.callback)) { + query_task.status = NavMeshPathQueryTask3D::TaskStatus::CALLBACK_DISPATCHED; + } else { + query_task.status = NavMeshPathQueryTask3D::TaskStatus::CALLBACK_FAILED; } } +} + +void NavMeshQueries3D::query_task_polygons_get_path(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const Vector3 &p_map_up, uint32_t p_link_polygons_size) { + p_query_task.path_points.clear(); + p_query_task.path_meta_point_types.clear(); + p_query_task.path_meta_point_rids.clear(); + p_query_task.path_meta_point_owners.clear(); + + // Find begin polyon and begin position closest to start position and + // end polyon and end position closest to target position on the map. + const gd::Polygon *begin_poly = nullptr; + const gd::Polygon *end_poly = nullptr; + Vector3 begin_point; + Vector3 end_point; + + _query_task_find_start_end_positions(p_query_task, p_polygons, &begin_poly, begin_point, &end_poly, end_point); // Check for trivial cases if (!begin_poly || !end_poly) { - return Vector(); + p_query_task.status = NavMeshPathQueryTask3D::TaskStatus::QUERY_FAILED; + return; } + if (begin_poly == end_poly) { - if (r_path_types) { - r_path_types->resize(2); - r_path_types->write[0] = begin_poly->owner->get_type(); - r_path_types->write[1] = end_poly->owner->get_type(); - } + _query_task_create_same_polygon_two_point_path(p_query_task, begin_poly, begin_point, end_poly, end_point); + return; + } - if (r_path_rids) { - r_path_rids->resize(2); - (*r_path_rids)[0] = begin_poly->owner->get_self(); - (*r_path_rids)[1] = end_poly->owner->get_self(); - } + _query_task_build_path_corridor(p_query_task, p_polygons, p_map_up, p_link_polygons_size, begin_poly, begin_point, end_poly, end_point); + + // Post-Process path. + switch (p_query_task.path_postprocessing) { + case PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL: { + _path_corridor_post_process_corridorfunnel(p_query_task, p_query_task.least_cost_id, begin_poly, begin_point, end_poly, end_point, p_map_up); + } break; + case PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED: { + _path_corridor_post_process_edgecentered(p_query_task, p_query_task.least_cost_id, begin_poly, begin_point, end_poly, end_point); + } break; + case PathPostProcessing::PATH_POSTPROCESSING_NONE: { + _path_corridor_post_process_nopostprocessing(p_query_task, p_query_task.least_cost_id, begin_poly, begin_point, end_poly, end_point); + } break; + default: { + WARN_PRINT("No match for used PathPostProcessing - fallback to default"); + _path_corridor_post_process_corridorfunnel(p_query_task, p_query_task.least_cost_id, begin_poly, begin_point, end_poly, end_point, p_map_up); + } break; + } - if (r_path_owners) { - r_path_owners->resize(2); - r_path_owners->write[0] = begin_poly->owner->get_owner_id(); - r_path_owners->write[1] = end_poly->owner->get_owner_id(); - } + p_query_task.path_points.invert(); + p_query_task.path_meta_point_types.invert(); + p_query_task.path_meta_point_rids.invert(); + p_query_task.path_meta_point_owners.invert(); + + if (p_query_task.simplify_path) { + _query_task_simplified_path_points(p_query_task); + } + +#ifdef DEBUG_ENABLED + // Ensure post conditions as path meta arrays if used MUST match in array size with the path points. + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { + DEV_ASSERT(p_query_task.path_points.size() == p_query_task.path_meta_point_types.size()); + } - Vector path; - path.resize(2); - path.write[0] = begin_point; - path.write[1] = end_point; - return path; + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { + DEV_ASSERT(p_query_task.path_points.size() == p_query_task.path_meta_point_rids.size()); } + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { + DEV_ASSERT(p_query_task.path_points.size() == p_query_task.path_meta_point_owners.size()); + } +#endif // DEBUG_ENABLED + + p_query_task.status = NavMeshPathQueryTask3D::TaskStatus::QUERY_FINISHED; +} + +void NavMeshQueries3D::_query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const Vector3 &p_map_up, uint32_t p_link_polygons_size, const gd::Polygon *begin_poly, Vector3 begin_point, const gd::Polygon *end_poly, Vector3 end_point) { // List of all reachable navigation polys. - LocalVector navigation_polys; - navigation_polys.resize(p_polygons.size() + p_link_polygons_size); + LocalVector &navigation_polys = p_query_task.path_query_slot->path_corridor; + for (gd::NavigationPoly &polygon : navigation_polys) { + polygon.reset(); + } + + DEV_ASSERT(navigation_polys.size() == p_polygons.size() + p_link_polygons_size); // Initialize the matching navigation polygon. gd::NavigationPoly &begin_navigation_poly = navigation_polys[begin_poly->id]; @@ -218,11 +357,12 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVector - traversable_polys; + &traversable_polys = p_query_task.path_query_slot->traversable_polys; + traversable_polys.clear(); traversable_polys.reserve(p_polygons.size() * 0.25); // This is an implementation of the A* algorithm. - int least_cost_id = begin_poly->id; + p_query_task.least_cost_id = begin_poly->id; int prev_least_cost_id = -1; bool found_route = false; @@ -232,24 +372,24 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectoredges) { + for (const gd::Edge &edge : navigation_polys[p_query_task.least_cost_id].poly->edges) { // Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon. for (uint32_t connection_index = 0; connection_index < edge.connections.size(); connection_index++) { const gd::Edge::Connection &connection = edge.connections[connection_index]; // Only consider the connection to another polygon if this polygon is in a region with compatible layers. - if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) { + if ((p_query_task.navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) { continue; } - const gd::NavigationPoly &least_cost_poly = navigation_polys[least_cost_id]; + const gd::NavigationPoly &least_cost_poly = navigation_polys[p_query_task.least_cost_id]; real_t poly_enter_cost = 0.0; real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost(); if (prev_least_cost_id != -1 && navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) { poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost(); } - prev_least_cost_id = least_cost_id; + prev_least_cost_id = p_query_task.least_cost_id; Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end }; const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, pathway); @@ -262,7 +402,7 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVector NavMeshQueries3D::polygons_get_path(const LocalVector NavMeshQueries3D::polygons_get_path(const LocalVectorpoints.size(); point_id++) { Face3 f(end_poly->points[0].pos, end_poly->points[point_id - 1].pos, end_poly->points[point_id].pos); - Vector3 spoint = f.get_closest_point_to(p_destination); - real_t dpoint = spoint.distance_to(p_destination); + Vector3 spoint = f.get_closest_point_to(p_query_task.target_position); + real_t dpoint = spoint.distance_to(p_query_task.target_position); if (dpoint < end_d) { end_point = spoint; end_d = dpoint; @@ -322,8 +462,8 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectorpoints.size(); point_id++) { Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos); - Vector3 spoint = f.get_closest_point_to(p_destination); - real_t dpoint = spoint.distance_to(p_destination); + Vector3 spoint = f.get_closest_point_to(p_query_task.target_position); + real_t dpoint = spoint.distance_to(p_query_task.target_position); if (dpoint < end_d) { end_point = spoint; end_d = dpoint; @@ -332,30 +472,8 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectorresize(2); - r_path_types->write[0] = begin_poly->owner->get_type(); - r_path_types->write[1] = begin_poly->owner->get_type(); - } - - if (r_path_rids) { - r_path_rids->resize(2); - (*r_path_rids)[0] = begin_poly->owner->get_self(); - (*r_path_rids)[1] = begin_poly->owner->get_self(); - } - - if (r_path_owners) { - r_path_owners->resize(2); - r_path_owners->write[0] = begin_poly->owner->get_owner_id(); - r_path_owners->write[1] = begin_poly->owner->get_owner_id(); - } - - Vector path; - path.resize(2); - path.write[0] = begin_point; - path.write[1] = end_point; - return path; + _query_task_create_same_polygon_two_point_path(p_query_task, begin_poly, begin_point, end_poly, end_point); + return; } for (gd::NavigationPoly &nav_poly : navigation_polys) { @@ -363,7 +481,7 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectorid].poly = begin_poly; - least_cost_id = begin_poly->id; + p_query_task.least_cost_id = begin_poly->id; prev_least_cost_id = -1; reachable_end = nullptr; @@ -372,19 +490,19 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectorpoly->id; + p_query_task.least_cost_id = traversable_polys.pop()->poly->id; // Store the farthest reachable end polygon in case our goal is not reachable. if (is_reachable) { - real_t distance = navigation_polys[least_cost_id].entry.distance_to(p_destination); + real_t distance = navigation_polys[p_query_task.least_cost_id].entry.distance_to(p_query_task.target_position); if (distance_to_reachable_end > distance) { distance_to_reachable_end = distance; - reachable_end = navigation_polys[least_cost_id].poly; + reachable_end = navigation_polys[p_query_task.least_cost_id].poly; } } // Check if we reached the end - if (navigation_polys[least_cost_id].poly == end_poly) { + if (navigation_polys[p_query_task.least_cost_id].poly == end_poly) { found_route = true; break; } @@ -393,190 +511,227 @@ Vector NavMeshQueries3D::polygons_get_path(const LocalVectorpoints.size(); point_id++) { Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos); - Vector3 spoint = f.get_closest_point_to(p_destination); - real_t dpoint = spoint.distance_to(p_destination); + Vector3 spoint = f.get_closest_point_to(p_query_task.target_position); + real_t dpoint = spoint.distance_to(p_query_task.target_position); if (dpoint < end_d) { end_point = spoint; end_d = dpoint; } } + _query_task_create_same_polygon_two_point_path(p_query_task, begin_poly, begin_point, begin_poly, end_point); + return; + } +} + +void NavMeshQueries3D::_query_task_simplified_path_points(NavMeshPathQueryTask3D &p_query_task) { + if (!p_query_task.simplify_path || p_query_task.path_points.size() <= 2) { + return; + } + + const LocalVector &simplified_path_indices = NavMeshQueries3D::get_simplified_path_indices(p_query_task.path_points, p_query_task.simplify_epsilon); - if (r_path_types) { - r_path_types->resize(2); - r_path_types->write[0] = begin_poly->owner->get_type(); - r_path_types->write[1] = begin_poly->owner->get_type(); + uint32_t index_count = simplified_path_indices.size(); + + { + Vector3 *points_ptr = p_query_task.path_points.ptr(); + for (uint32_t i = 0; i < index_count; i++) { + points_ptr[i] = points_ptr[simplified_path_indices[i]]; } + p_query_task.path_points.resize(index_count); + } - if (r_path_rids) { - r_path_rids->resize(2); - (*r_path_rids)[0] = begin_poly->owner->get_self(); - (*r_path_rids)[1] = begin_poly->owner->get_self(); + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) { + int32_t *types_ptr = p_query_task.path_meta_point_types.ptr(); + for (uint32_t i = 0; i < index_count; i++) { + types_ptr[i] = types_ptr[simplified_path_indices[i]]; } + p_query_task.path_meta_point_types.resize(index_count); + } - if (r_path_owners) { - r_path_owners->resize(2); - r_path_owners->write[0] = begin_poly->owner->get_owner_id(); - r_path_owners->write[1] = begin_poly->owner->get_owner_id(); + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) { + RID *rids_ptr = p_query_task.path_meta_point_rids.ptr(); + for (uint32_t i = 0; i < index_count; i++) { + rids_ptr[i] = rids_ptr[simplified_path_indices[i]]; } + p_query_task.path_meta_point_rids.resize(index_count); + } - Vector path; - path.resize(2); - path.write[0] = begin_point; - path.write[1] = end_point; - return path; + if (p_query_task.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) { + int64_t *owners_ptr = p_query_task.path_meta_point_owners.ptr(); + for (uint32_t i = 0; i < index_count; i++) { + owners_ptr[i] = owners_ptr[simplified_path_indices[i]]; + } + p_query_task.path_meta_point_owners.resize(index_count); } +} - Vector path; - // Optimize the path. - if (p_optimize) { - // Set the apex poly/point to the end point - gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id]; +void NavMeshQueries3D::_path_corridor_post_process_corridorfunnel(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point, const Vector3 &p_map_up) { + LocalVector &p_path_corridor = p_query_task.path_query_slot->path_corridor; - Vector3 back_pathway[2] = { apex_poly->back_navigation_edge_pathway_start, apex_poly->back_navigation_edge_pathway_end }; - const Vector3 back_edge_closest_point = Geometry3D::get_closest_point_to_segment(end_point, back_pathway); - if (end_point.is_equal_approx(back_edge_closest_point)) { - // The end point is basically on top of the last crossed edge, funneling around the corners would at best do nothing. - // At worst it would add an unwanted path point before the last point due to precision issues so skip to the next polygon. - if (apex_poly->back_navigation_poly_id != -1) { - apex_poly = &navigation_polys[apex_poly->back_navigation_poly_id]; - } + // Set the apex poly/point to the end point + gd::NavigationPoly *apex_poly = &p_path_corridor[p_least_cost_id]; + + Vector3 back_pathway[2] = { apex_poly->back_navigation_edge_pathway_start, apex_poly->back_navigation_edge_pathway_end }; + const Vector3 back_edge_closest_point = Geometry3D::get_closest_point_to_segment(p_end_point, back_pathway); + if (p_end_point.is_equal_approx(back_edge_closest_point)) { + // The end point is basically on top of the last crossed edge, funneling around the corners would at best do nothing. + // At worst it would add an unwanted path point before the last point due to precision issues so skip to the next polygon. + if (apex_poly->back_navigation_poly_id != -1) { + apex_poly = &p_path_corridor[apex_poly->back_navigation_poly_id]; } + } - Vector3 apex_point = end_point; + Vector3 apex_point = p_end_point; - gd::NavigationPoly *left_poly = apex_poly; - Vector3 left_portal = apex_point; - gd::NavigationPoly *right_poly = apex_poly; - Vector3 right_portal = apex_point; + gd::NavigationPoly *left_poly = apex_poly; + Vector3 left_portal = apex_point; + gd::NavigationPoly *right_poly = apex_poly; + Vector3 right_portal = apex_point; - gd::NavigationPoly *p = apex_poly; + gd::NavigationPoly *p = apex_poly; - path.push_back(end_point); - APPEND_METADATA(end_poly); + _query_task_push_back_point_with_metadata(p_query_task, p_end_point, p_end_polygon); - while (p) { - // Set left and right points of the pathway between polygons. - Vector3 left = p->back_navigation_edge_pathway_start; - Vector3 right = p->back_navigation_edge_pathway_end; - if (THREE_POINTS_CROSS_PRODUCT(apex_point, left, right).dot(p_map_up) < 0) { - SWAP(left, right); - } + while (p) { + // Set left and right points of the pathway between polygons. + Vector3 left = p->back_navigation_edge_pathway_start; + Vector3 right = p->back_navigation_edge_pathway_end; + if (THREE_POINTS_CROSS_PRODUCT(apex_point, left, right).dot(p_map_up) < 0) { + SWAP(left, right); + } - bool skip = false; - if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(p_map_up) >= 0) { - //process - if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(p_map_up) > 0) { - left_poly = p; - left_portal = left; - } else { - clip_path(navigation_polys, path, apex_poly, right_portal, right_poly, r_path_types, r_path_rids, r_path_owners, p_map_up); - - apex_point = right_portal; - p = right_poly; - left_poly = p; - apex_poly = p; - left_portal = apex_point; - right_portal = apex_point; - - path.push_back(apex_point); - APPEND_METADATA(apex_poly->poly); - skip = true; - } - } + bool skip = false; + if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(p_map_up) >= 0) { + //process + if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(p_map_up) > 0) { + left_poly = p; + left_portal = left; + } else { + clip_path(p_query_task, p_path_corridor, apex_poly, right_portal, right_poly, p_map_up); - if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(p_map_up) <= 0) { - //process - if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(p_map_up) < 0) { - right_poly = p; - right_portal = right; - } else { - clip_path(navigation_polys, path, apex_poly, left_portal, left_poly, r_path_types, r_path_rids, r_path_owners, p_map_up); + apex_point = right_portal; + p = right_poly; + left_poly = p; + apex_poly = p; + left_portal = apex_point; + right_portal = apex_point; - apex_point = left_portal; - p = left_poly; - right_poly = p; - apex_poly = p; - right_portal = apex_point; - left_portal = apex_point; + _query_task_push_back_point_with_metadata(p_query_task, apex_point, apex_poly->poly); - path.push_back(apex_point); - APPEND_METADATA(apex_poly->poly); - } + skip = true; } + } - // Go to the previous polygon. - if (p->back_navigation_poly_id != -1) { - p = &navigation_polys[p->back_navigation_poly_id]; + if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(p_map_up) <= 0) { + //process + if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(p_map_up) < 0) { + right_poly = p; + right_portal = right; } else { - // The end - p = nullptr; + clip_path(p_query_task, p_path_corridor, apex_poly, left_portal, left_poly, p_map_up); + + apex_point = left_portal; + p = left_poly; + right_poly = p; + apex_poly = p; + right_portal = apex_point; + left_portal = apex_point; + + _query_task_push_back_point_with_metadata(p_query_task, apex_point, apex_poly->poly); } } - // If the last point is not the begin point, add it to the list. - if (path[path.size() - 1] != begin_point) { - path.push_back(begin_point); - APPEND_METADATA(begin_poly); + // Go to the previous polygon. + if (p->back_navigation_poly_id != -1) { + p = &p_path_corridor[p->back_navigation_poly_id]; + } else { + // The end + p = nullptr; } + } - path.reverse(); - if (r_path_types) { - r_path_types->reverse(); - } - if (r_path_rids) { - r_path_rids->reverse(); - } - if (r_path_owners) { - r_path_owners->reverse(); - } + // If the last point is not the begin point, add it to the list. + if (p_query_task.path_points[p_query_task.path_points.size() - 1] != p_begin_point) { + _query_task_push_back_point_with_metadata(p_query_task, p_begin_point, p_begin_poly); + } +} - } else { - path.push_back(end_point); - APPEND_METADATA(end_poly); - - // Add mid points - int np_id = least_cost_id; - while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -1) { - if (navigation_polys[np_id].back_navigation_edge != -1) { - int prev = navigation_polys[np_id].back_navigation_edge; - int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); - Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; - - path.push_back(point); - APPEND_METADATA(navigation_polys[np_id].poly); - } else { - path.push_back(navigation_polys[np_id].entry); - APPEND_METADATA(navigation_polys[np_id].poly); - } +void NavMeshQueries3D::_path_corridor_post_process_edgecentered(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point) { + LocalVector &p_path_corridor = p_query_task.path_query_slot->path_corridor; - np_id = navigation_polys[np_id].back_navigation_poly_id; - } + _query_task_push_back_point_with_metadata(p_query_task, p_end_point, p_end_polygon); - path.push_back(begin_point); - APPEND_METADATA(begin_poly); + // Add mid points. + int np_id = p_least_cost_id; + while (np_id != -1 && p_path_corridor[np_id].back_navigation_poly_id != -1) { + if (p_path_corridor[np_id].back_navigation_edge != -1) { + int prev = p_path_corridor[np_id].back_navigation_edge; + int prev_n = (p_path_corridor[np_id].back_navigation_edge + 1) % p_path_corridor[np_id].poly->points.size(); + Vector3 point = (p_path_corridor[np_id].poly->points[prev].pos + p_path_corridor[np_id].poly->points[prev_n].pos) * 0.5; - path.reverse(); - if (r_path_types) { - r_path_types->reverse(); - } - if (r_path_rids) { - r_path_rids->reverse(); - } - if (r_path_owners) { - r_path_owners->reverse(); + _query_task_push_back_point_with_metadata(p_query_task, point, p_path_corridor[np_id].poly); + } else { + _query_task_push_back_point_with_metadata(p_query_task, p_path_corridor[np_id].entry, p_path_corridor[np_id].poly); } + + np_id = p_path_corridor[np_id].back_navigation_poly_id; } - // Ensure post conditions (path arrays MUST match in size). - CRASH_COND(r_path_types && path.size() != r_path_types->size()); - CRASH_COND(r_path_rids && path.size() != r_path_rids->size()); - CRASH_COND(r_path_owners && path.size() != r_path_owners->size()); + _query_task_push_back_point_with_metadata(p_query_task, p_begin_point, p_begin_poly); +} + +void NavMeshQueries3D::_path_corridor_post_process_nopostprocessing(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point) { + LocalVector &p_path_corridor = p_query_task.path_query_slot->path_corridor; - return path; + _query_task_push_back_point_with_metadata(p_query_task, p_end_point, p_end_polygon); + + // Add mid points. + int np_id = p_least_cost_id; + while (np_id != -1 && p_path_corridor[np_id].back_navigation_poly_id != -1) { + _query_task_push_back_point_with_metadata(p_query_task, p_path_corridor[np_id].entry, p_path_corridor[np_id].poly); + + np_id = p_path_corridor[np_id].back_navigation_poly_id; + } + + _query_task_push_back_point_with_metadata(p_query_task, p_begin_point, p_begin_poly); +} + +void NavMeshQueries3D::_query_task_find_start_end_positions(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const gd::Polygon **r_begin_poly, Vector3 &r_begin_point, const gd::Polygon **r_end_poly, Vector3 &r_end_point) { + real_t begin_d = FLT_MAX; + real_t end_d = FLT_MAX; + + // Find the initial poly and the end poly on this map. + for (const gd::Polygon &p : p_polygons) { + // Only consider the polygon if it in a region with compatible layers. + if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) { + continue; + } + + // For each face check the distance between the origin/destination. + for (size_t point_id = 2; point_id < p.points.size(); point_id++) { + const Face3 face(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); + + Vector3 point = face.get_closest_point_to(p_query_task.start_position); + real_t distance_to_point = point.distance_to(p_query_task.start_position); + if (distance_to_point < begin_d) { + begin_d = distance_to_point; + *r_begin_poly = &p; + r_begin_point = point; + } + + point = face.get_closest_point_to(p_query_task.target_position); + distance_to_point = point.distance_to(p_query_task.target_position); + if (distance_to_point < end_d) { + end_d = distance_to_point; + *r_end_poly = &p; + r_end_point = point; + } + } + } } Vector3 NavMeshQueries3D::polygons_get_closest_point_to_segment(const LocalVector &p_polygons, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) { @@ -728,8 +883,8 @@ RID NavMeshQueries3D::polygons_get_closest_point_owner(const LocalVector &p_navigation_polys, Vector &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners, const Vector3 &p_map_up) { - Vector3 from = path[path.size() - 1]; +void NavMeshQueries3D::clip_path(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_navigation_polys, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, const Vector3 &p_map_up) { + Vector3 from = p_query_task.path_points[p_query_task.path_points.size() - 1]; if (from.is_equal_approx(p_to_point)) { return; @@ -753,13 +908,73 @@ void NavMeshQueries3D::clip_path(const LocalVector &p_naviga if (!pathway_start.is_equal_approx(pathway_end)) { Vector3 inters; if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) { - if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) { - path.push_back(inters); - APPEND_METADATA(from_poly->poly); + if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(p_query_task.path_points[p_query_task.path_points.size() - 1])) { + _query_task_push_back_point_with_metadata(p_query_task, inters, from_poly->poly); } } } } } +LocalVector NavMeshQueries3D::get_simplified_path_indices(const LocalVector &p_path, real_t p_epsilon) { + p_epsilon = MAX(0.0, p_epsilon); + real_t squared_epsilon = p_epsilon * p_epsilon; + + LocalVector valid_points; + valid_points.resize(p_path.size()); + for (uint32_t i = 0; i < valid_points.size(); i++) { + valid_points[i] = false; + } + + simplify_path_segment(0, p_path.size() - 1, p_path, squared_epsilon, valid_points); + + int valid_point_index = 0; + + for (bool valid : valid_points) { + if (valid) { + valid_point_index += 1; + } + } + + LocalVector simplified_path_indices; + simplified_path_indices.resize(valid_point_index); + valid_point_index = 0; + + for (uint32_t i = 0; i < valid_points.size(); i++) { + if (valid_points[i]) { + simplified_path_indices[valid_point_index] = i; + valid_point_index += 1; + } + } + + return simplified_path_indices; +} + +void NavMeshQueries3D::simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector &p_points, real_t p_epsilon, LocalVector &r_valid_points) { + r_valid_points[p_start_inx] = true; + r_valid_points[p_end_inx] = true; + + Vector3 path_segment[2] = { p_points[p_start_inx], p_points[p_end_inx] }; + + real_t point_max_distance = 0.0; + int point_max_index = 0; + + for (int i = p_start_inx; i < p_end_inx; i++) { + const Vector3 &checked_point = p_points[i]; + + const Vector3 closest_point = Geometry3D::get_closest_point_to_segment(checked_point, path_segment); + real_t distance_squared = closest_point.distance_squared_to(checked_point); + + if (distance_squared > point_max_distance) { + point_max_index = i; + point_max_distance = distance_squared; + } + } + + if (point_max_distance > p_epsilon) { + simplify_path_segment(p_start_inx, point_max_index, p_points, p_epsilon, r_valid_points); + simplify_path_segment(point_max_index, p_end_inx, p_points, p_epsilon, r_valid_points); + } +} + #endif // _3D_DISABLED diff --git a/modules/navigation/3d/nav_mesh_queries_3d.h b/modules/navigation/3d/nav_mesh_queries_3d.h index 109bb2f97123..a92520f9444e 100644 --- a/modules/navigation/3d/nav_mesh_queries_3d.h +++ b/modules/navigation/3d/nav_mesh_queries_3d.h @@ -33,20 +33,91 @@ #ifndef _3D_DISABLED -#include "../nav_map.h" +#include "../nav_utils.h" + +#include "servers/navigation/navigation_path_query_parameters_3d.h" +#include "servers/navigation/navigation_path_query_result_3d.h" +#include "servers/navigation/navigation_utilities.h" + +using namespace NavigationUtilities; + +class NavMap; class NavMeshQueries3D { public: + struct PathQuerySlot { + LocalVector path_corridor; + gd::Heap traversable_polys; + bool in_use = false; + uint32_t slot_index = 0; + }; + + struct NavMeshPathQueryTask3D { + enum TaskStatus { + QUERY_STARTED, + QUERY_FINISHED, + QUERY_FAILED, + CALLBACK_DISPATCHED, + CALLBACK_FAILED, + }; + + // Parameters. + Vector3 start_position; + Vector3 target_position; + uint32_t navigation_layers; + BitField metadata_flags = PathMetadataFlags::PATH_INCLUDE_ALL; + PathfindingAlgorithm pathfinding_algorithm = PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; + PathPostProcessing path_postprocessing = PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; + bool simplify_path = false; + real_t simplify_epsilon = 0.0; + + // Path building. + Vector3 begin_position; + Vector3 end_position; + uint32_t least_cost_id = 0; + Vector3 map_up; + NavMap *map = nullptr; + PathQuerySlot *path_query_slot = nullptr; + + // Path points. + LocalVector path_points; + LocalVector path_meta_point_types; + LocalVector path_meta_point_rids; + LocalVector path_meta_point_owners; + + Ref query_parameters; + Ref query_result; + Callable callback; + NavMeshPathQueryTask3D::TaskStatus status = NavMeshPathQueryTask3D::TaskStatus::QUERY_STARTED; + }; + + static bool emit_callback(const Callable &p_callback); + static Vector3 polygons_get_random_point(const LocalVector &p_polygons, uint32_t p_navigation_layers, bool p_uniformly); - static Vector polygons_get_path(const LocalVector &p_polygons, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners, const Vector3 &p_map_up, uint32_t p_link_polygons_size); static Vector3 polygons_get_closest_point_to_segment(const LocalVector &p_polygons, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision); static Vector3 polygons_get_closest_point(const LocalVector &p_polygons, const Vector3 &p_point); static Vector3 polygons_get_closest_point_normal(const LocalVector &p_polygons, const Vector3 &p_point); static gd::ClosestPointQueryResult polygons_get_closest_point_info(const LocalVector &p_polygons, const Vector3 &p_point); static RID polygons_get_closest_point_owner(const LocalVector &p_polygons, const Vector3 &p_point); - static void clip_path(const LocalVector &p_navigation_polys, Vector &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners, const Vector3 &p_map_up); + static void map_query_path(NavMap *map, const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback); + + static void query_task_polygons_get_path(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const Vector3 &p_map_up, uint32_t p_link_polygons_size); + + static void _query_task_create_same_polygon_two_point_path(NavMeshPathQueryTask3D &p_query_task, const gd::Polygon *begin_poly, Vector3 begin_point, const gd::Polygon *end_poly, Vector3 end_point); + static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask3D &p_query_task, Vector3 p_point, const gd::Polygon *p_point_polygon); + static void _query_task_find_start_end_positions(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const gd::Polygon **r_begin_poly, Vector3 &r_begin_point, const gd::Polygon **r_end_poly, Vector3 &r_end_point); + static void _query_task_build_path_corridor(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_polygons, const Vector3 &p_map_up, uint32_t p_link_polygons_size, const gd::Polygon *begin_poly, Vector3 begin_point, const gd::Polygon *end_polygon, Vector3 end_point); + static void _path_corridor_post_process_corridorfunnel(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point, const Vector3 &p_map_up); + static void _path_corridor_post_process_edgecentered(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point); + static void _path_corridor_post_process_nopostprocessing(NavMeshPathQueryTask3D &p_query_task, int p_least_cost_id, const gd::Polygon *p_begin_poly, Vector3 p_begin_point, const gd::Polygon *p_end_polygon, Vector3 p_end_point); + + static void clip_path(NavMeshPathQueryTask3D &p_query_task, const LocalVector &p_navigation_polys, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, const Vector3 &p_map_up); + static void _query_task_simplified_path_points(NavMeshPathQueryTask3D &p_query_task); + + static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector &p_points, real_t p_epsilon, LocalVector &r_valid_points); + static LocalVector get_simplified_path_indices(const LocalVector &p_path, real_t p_epsilon); }; #endif // _3D_DISABLED diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 739b031d061d..07ee96dc321f 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -35,8 +35,6 @@ #include "nav_obstacle.h" #include "nav_region.h" -#include "3d/nav_mesh_queries_3d.h" - #include "core/config/project_settings.h" #include "core/object/worker_thread_pool.h" @@ -123,16 +121,40 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const { return p; } -Vector NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners) const { +void NavMap::query_path(NavMeshQueries3D::NavMeshPathQueryTask3D &p_query_task) { RWLockRead read_lock(map_rwlock); if (iteration_id == 0) { - NAVMAP_ITERATION_ZERO_ERROR_MSG(); - return Vector(); + return; } - return NavMeshQueries3D::polygons_get_path( - polygons, p_origin, p_destination, p_optimize, p_navigation_layers, - r_path_types, r_path_rids, r_path_owners, up, link_polygons.size()); + path_query_slots_semaphore.wait(); + + path_query_slots_mutex.lock(); + for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : path_query_slots) { + if (!p_path_query_slot.in_use) { + p_path_query_slot.in_use = true; + p_query_task.path_query_slot = &p_path_query_slot; + break; + } + } + path_query_slots_mutex.unlock(); + + if (p_query_task.path_query_slot == nullptr) { + path_query_slots_semaphore.post(); + ERR_FAIL_NULL_MSG(p_query_task.path_query_slot, "No unused NavMap path query slot found! This should never happen :(."); + } + + p_query_task.map_up = get_up(); + + NavMeshQueries3D::query_task_polygons_get_path(p_query_task, polygons, up, link_polygons.size()); + + path_query_slots_mutex.lock(); + uint32_t used_slot_index = p_query_task.path_query_slot->slot_index; + path_query_slots[used_slot_index].in_use = false; + p_query_task.path_query_slot = nullptr; + path_query_slots_mutex.unlock(); + + path_query_slots_semaphore.post(); } Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { @@ -639,6 +661,15 @@ void NavMap::sync() { // Some code treats 0 as a failure case, so we avoid returning 0 and modulo wrap UINT32_MAX manually. iteration_id = iteration_id % UINT32_MAX + 1; + + path_query_slots_mutex.lock(); + for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : path_query_slots) { + p_path_query_slot.path_corridor.clear(); + p_path_query_slot.path_corridor.resize(polygons.size() + link_polygons.size()); + p_path_query_slot.traversable_polys.clear(); + p_path_query_slot.traversable_polys.reserve(polygons.size() * 0.25); + } + path_query_slots_mutex.unlock(); } map_settings_dirty = false; @@ -969,6 +1000,26 @@ void NavMap::_sync_dirty_avoidance_update_requests() { NavMap::NavMap() { avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads"); avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads"); + + path_query_slots_max = GLOBAL_GET("navigation/pathfinding/max_threads"); + + int processor_count = OS::get_singleton()->get_processor_count(); + if (path_query_slots_max < 0) { + path_query_slots_max = processor_count; + } + if (processor_count < path_query_slots_max) { + path_query_slots_max = processor_count; + } + if (path_query_slots_max < 1) { + path_query_slots_max = 1; + } + + path_query_slots.resize(path_query_slots_max); + for (uint32_t i = 0; i < path_query_slots.size(); i++) { + path_query_slots[i].slot_index = i; + } + + path_query_slots_semaphore.post(path_query_slots_max); } NavMap::~NavMap() { diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index feeb2cc54834..ce247b83c164 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -31,6 +31,7 @@ #ifndef NAV_MAP_H #define NAV_MAP_H +#include "3d/nav_mesh_queries_3d.h" #include "nav_rid.h" #include "nav_utils.h" @@ -135,6 +136,11 @@ class NavMap : public NavRid { SelfList::List obstacles; } sync_dirty_requests; + LocalVector path_query_slots; + int path_query_slots_max = 4; + Mutex path_query_slots_mutex; + Semaphore path_query_slots_semaphore; + public: NavMap(); ~NavMap(); @@ -176,7 +182,8 @@ class NavMap : public NavRid { gd::PointKey get_point_key(const Vector3 &p_pos) const; - Vector get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector *r_path_types, TypedArray *r_path_rids, Vector *r_path_owners) const; + void query_path(NavMeshQueries3D::NavMeshPathQueryTask3D &p_query_task); + Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const; Vector3 get_closest_point(const Vector3 &p_point) const; Vector3 get_closest_point_normal(const Vector3 &p_point) const; diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 993a97638dd7..ea22fb0bab3d 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -145,6 +145,15 @@ struct NavigationPoly { bool operator!=(const NavigationPoly &p_other) const { return !(*this == p_other); } + + void reset() { + poly = nullptr; + traversable_poly_index = UINT32_MAX; + back_navigation_poly_id = -1; + back_navigation_edge = -1; + traveled_distance = 0.0; + distance_to_destination = 0.0; + } }; struct NavPolyTravelCostGreaterThan { diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub index f3a554a58db8..c5d8c00c6d77 100644 --- a/platform/linuxbsd/wayland/SCsub +++ b/platform/linuxbsd/wayland/SCsub @@ -163,15 +163,25 @@ env.WAYLAND_API_CODE( ) env.WAYLAND_API_HEADER( - target="protocol/xdg_foreign.gen.h", + target="protocol/xdg_foreign_v1.gen.h", source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml", ) env.WAYLAND_API_CODE( - target="protocol/xdg_foreign.gen.c", + target="protocol/xdg_foreign_v1.gen.c", source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml", ) +env.WAYLAND_API_HEADER( + target="protocol/xdg_foreign_v2.gen.h", + source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml", +) + +env.WAYLAND_API_CODE( + target="protocol/xdg_foreign_v2.gen.c", + source="#thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml", +) + env.WAYLAND_API_HEADER( target="protocol/xdg_system_bell.gen.h", source="#thirdparty/wayland-protocols/staging/xdg-system-bell/xdg-system-bell-v1.xml", @@ -188,7 +198,8 @@ source_files = [ "protocol/fractional_scale.gen.c", "protocol/xdg_shell.gen.c", "protocol/xdg_system_bell.gen.c", - "protocol/xdg_foreign.gen.c", + "protocol/xdg_foreign_v1.gen.c", + "protocol/xdg_foreign_v2.gen.c", "protocol/xdg_decoration.gen.c", "protocol/xdg_activation.gen.c", "protocol/relative_pointer.gen.c", diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index 4eaa932337d2..1130472e9472 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -377,9 +377,16 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re return; } + // NOTE: Deprecated. if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) { - registry->xdg_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1); - registry->xdg_exporter_name = name; + registry->xdg_exporter_v1 = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1); + registry->xdg_exporter_v1_name = name; + return; + } + + if (strcmp(interface, zxdg_exporter_v2_interface.name) == 0) { + registry->xdg_exporter_v2 = (struct zxdg_exporter_v2 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v2_interface, 1); + registry->xdg_exporter_v2_name = name; return; } @@ -599,13 +606,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry return; } - if (name == registry->xdg_exporter_name) { - if (registry->xdg_exporter) { - zxdg_exporter_v1_destroy(registry->xdg_exporter); - registry->xdg_exporter = nullptr; + // NOTE: Deprecated. + if (name == registry->xdg_exporter_v1_name) { + if (registry->xdg_exporter_v1) { + zxdg_exporter_v1_destroy(registry->xdg_exporter_v1); + registry->xdg_exporter_v1 = nullptr; + } + + registry->xdg_exporter_v1_name = 0; + + return; + } + + if (name == registry->xdg_exporter_v2_name) { + if (registry->xdg_exporter_v2) { + zxdg_exporter_v2_destroy(registry->xdg_exporter_v2); + registry->xdg_exporter_v2 = nullptr; } - registry->xdg_exporter_name = 0; + registry->xdg_exporter_v2_name = 0; return; } @@ -1186,7 +1205,15 @@ void WaylandThread::_xdg_toplevel_on_wm_capabilities(void *data, struct xdg_topl } } -void WaylandThread::_xdg_exported_on_exported(void *data, zxdg_exported_v1 *exported, const char *handle) { +// NOTE: Deprecated. +void WaylandThread::_xdg_exported_v1_on_handle(void *data, zxdg_exported_v1 *exported, const char *handle) { + WindowState *ws = (WindowState *)data; + ERR_FAIL_NULL(ws); + + ws->exported_handle = vformat("wayland:%s", String::utf8(handle)); +} + +void WaylandThread::_xdg_exported_v2_on_handle(void *data, zxdg_exported_v2 *exported, const char *handle) { WindowState *ws = (WindowState *)data; ERR_FAIL_NULL(ws); @@ -3284,9 +3311,12 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid ws.frame_callback = wl_surface_frame(ws.wl_surface); wl_callback_add_listener(ws.frame_callback, &frame_wl_callback_listener, &ws); - if (registry.xdg_exporter) { - ws.xdg_exported = zxdg_exporter_v1_export(registry.xdg_exporter, ws.wl_surface); - zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws); + if (registry.xdg_exporter_v2) { + ws.xdg_exported_v2 = zxdg_exporter_v2_export_toplevel(registry.xdg_exporter_v2, ws.wl_surface); + zxdg_exported_v2_add_listener(ws.xdg_exported_v2, &xdg_exported_v2_listener, &ws); + } else if (registry.xdg_exporter_v1) { + ws.xdg_exported_v1 = zxdg_exporter_v1_export(registry.xdg_exporter_v1, ws.wl_surface); + zxdg_exported_v1_add_listener(ws.xdg_exported_v1, &xdg_exported_v1_listener, &ws); } wl_surface_commit(ws.wl_surface); @@ -4410,10 +4440,14 @@ void WaylandThread::destroy() { xdg_wm_base_destroy(registry.xdg_wm_base); } - if (registry.xdg_exporter) { - zxdg_exporter_v1_destroy(registry.xdg_exporter); + // NOTE: Deprecated. + if (registry.xdg_exporter_v1) { + zxdg_exporter_v1_destroy(registry.xdg_exporter_v1); } + if (registry.xdg_exporter_v2) { + zxdg_exporter_v2_destroy(registry.xdg_exporter_v2); + } if (registry.wl_shm) { wl_shm_destroy(registry.wl_shm); } diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index c07694b52ae8..8f1605b82027 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -67,10 +67,13 @@ #include "wayland/protocol/wayland.gen.h" #include "wayland/protocol/xdg_activation.gen.h" #include "wayland/protocol/xdg_decoration.gen.h" -#include "wayland/protocol/xdg_foreign.gen.h" +#include "wayland/protocol/xdg_foreign_v2.gen.h" #include "wayland/protocol/xdg_shell.gen.h" #include "wayland/protocol/xdg_system_bell.gen.h" +// NOTE: Deprecated. +#include "wayland/protocol/xdg_foreign_v1.gen.h" + #ifdef LIBDECOR_ENABLED #ifdef SOWRAP_ENABLED #include "dynwrappers/libdecor-so_wrap.h" @@ -149,8 +152,12 @@ class WaylandThread { struct xdg_wm_base *xdg_wm_base = nullptr; uint32_t xdg_wm_base_name = 0; - struct zxdg_exporter_v1 *xdg_exporter = nullptr; - uint32_t xdg_exporter_name = 0; + // NOTE: Deprecated. + struct zxdg_exporter_v1 *xdg_exporter_v1 = nullptr; + uint32_t xdg_exporter_v1_name = 0; + + uint32_t xdg_exporter_v2_name = 0; + struct zxdg_exporter_v2 *xdg_exporter_v2 = nullptr; // wayland-protocols globals. @@ -224,7 +231,11 @@ class WaylandThread { struct wp_viewport *wp_viewport = nullptr; struct wp_fractional_scale_v1 *wp_fractional_scale = nullptr; - struct zxdg_exported_v1 *xdg_exported = nullptr; + + // NOTE: Deprecated. + struct zxdg_exported_v1 *xdg_exported_v1 = nullptr; + + struct zxdg_exported_v2 *xdg_exported_v2 = nullptr; String exported_handle; @@ -652,7 +663,10 @@ class WaylandThread { static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode); - static void _xdg_exported_on_exported(void *data, zxdg_exported_v1 *exported, const char *handle); + // NOTE: Deprecated. + static void _xdg_exported_v1_on_handle(void *data, zxdg_exported_v1 *exported, const char *handle); + + static void _xdg_exported_v2_on_handle(void *data, zxdg_exported_v2 *exported, const char *handle); static void _xdg_activation_token_on_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token, const char *token); @@ -820,8 +834,13 @@ class WaylandThread { .done = _wp_text_input_on_done, }; - static constexpr struct zxdg_exported_v1_listener xdg_exported_listener = { - .handle = _xdg_exported_on_exported + // NOTE: Deprecated. + static constexpr struct zxdg_exported_v1_listener xdg_exported_v1_listener = { + .handle = _xdg_exported_v1_on_handle, + }; + + static constexpr struct zxdg_exported_v2_listener xdg_exported_v2_listener = { + .handle = _xdg_exported_v2_on_handle, }; static constexpr struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 9f1f7d21e227..9519ca5270a2 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -216,7 +216,7 @@ class DisplayServerMacOS : public DisplayServer { Callable system_theme_changed; WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); - void _update_window_style(WindowData p_wd); + void _update_window_style(WindowData p_wd, WindowID p_window); void _update_displays_arrangement() const; Point2i _get_native_screen_position(int p_screen) const; @@ -266,6 +266,8 @@ class DisplayServerMacOS : public DisplayServer { void mouse_exit_window(WindowID p_window); void update_presentation_mode(); + bool is_always_on_top_recursive(WindowID p_window) const; + void window_destroy(WindowID p_window); void window_resize(WindowID p_window, int p_width, int p_height); void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled); diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 472d54ae62e1..ee9692f5d74a 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -213,7 +213,7 @@ return id; } -void DisplayServerMacOS::_update_window_style(WindowData p_wd) { +void DisplayServerMacOS::_update_window_style(WindowData p_wd, WindowID p_window) { bool borderless_full = false; if (p_wd.borderless) { @@ -233,7 +233,7 @@ [(NSWindow *)p_wd.window_object setHidesOnDeactivate:YES]; } else { // Reset these when our window is not a borderless window that covers up the screen. - if (p_wd.on_top && !p_wd.fullscreen) { + if (is_always_on_top_recursive(p_window) && !p_wd.fullscreen) { [(NSWindow *)p_wd.window_object setLevel:NSFloatingWindowLevel]; } else { [(NSWindow *)p_wd.window_object setLevel:NSNormalWindowLevel]; @@ -242,6 +242,21 @@ } } +bool DisplayServerMacOS::is_always_on_top_recursive(WindowID p_window) const { + ERR_FAIL_COND_V(!windows.has(p_window), false); + + const WindowData &wd = windows[p_window]; + if (wd.on_top) { + return true; + } + + if (wd.transient_parent != INVALID_WINDOW_ID) { + return is_always_on_top_recursive(wd.transient_parent); + } + + return false; +} + void DisplayServerMacOS::set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) { ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; @@ -2112,7 +2127,7 @@ [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)]; - _update_window_style(wd); + _update_window_style(wd, p_window); update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); } @@ -2250,7 +2265,7 @@ [wd.window_object setFrame:new_frame display:YES]; - _update_window_style(wd); + _update_window_style(wd, p_window); } Size2i DisplayServerMacOS::window_get_size(WindowID p_window) const { @@ -2547,7 +2562,7 @@ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; [wd.window_object setFrame:frameRect display:NO]; } - _update_window_style(wd); + _update_window_style(wd, p_window); if (was_visible || [wd.window_object isVisible]) { if ([wd.window_object isMiniaturized]) { return; @@ -2632,11 +2647,7 @@ return [wd.window_object styleMask] == NSWindowStyleMaskBorderless; } break; case WINDOW_FLAG_ALWAYS_ON_TOP: { - if (wd.fullscreen) { - return wd.on_top; - } else { - return [(NSWindow *)wd.window_object level] == NSFloatingWindowLevel; - } + return wd.on_top; } break; case WINDOW_FLAG_TRANSPARENT: { return wd.layered_window; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 7749debfd6d4..35c053dca5db 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -194,7 +194,7 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { } // Restore on-top state. - if (wd.on_top) { + if (ds->is_always_on_top_recursive(window_id)) { [wd.window_object setLevel:NSFloatingWindowLevel]; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 0769603d8eb2..a0d10549338d 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1606,6 +1606,21 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod return window_id; } +bool DisplayServerWindows::_is_always_on_top_recursive(WindowID p_window) const { + ERR_FAIL_COND_V(!windows.has(p_window), false); + + const WindowData &wd = windows[p_window]; + if (wd.always_on_top) { + return true; + } + + if (wd.transient_parent != INVALID_WINDOW_ID) { + return _is_always_on_top_recursive(wd.transient_parent); + } + + return false; +} + void DisplayServerWindows::show_window(WindowID p_id) { ERR_FAIL_COND(!windows.has(p_id)); @@ -1633,7 +1648,7 @@ void DisplayServerWindows::show_window(WindowID p_id) { SetForegroundWindow(wd.hWnd); // Slightly higher priority. SetFocus(wd.hWnd); // Set keyboard focus. } - if (wd.always_on_top) { + if (_is_always_on_top_recursive(p_id)) { SetWindowPos(wd.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0)); } } @@ -2256,7 +2271,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain set_icon(icon); } - SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0)); + SetWindowPos(wd.hWnd, _is_always_on_top_recursive(p_window) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0)); if (p_repaint) { RECT rect; diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index af5243a14951..52a180984ba0 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -635,6 +635,8 @@ class DisplayServerWindows : public DisplayServer { void _drag_event(WindowID p_window, float p_x, float p_y, int idx); void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx); + bool _is_always_on_top_recursive(WindowID p_window) const; + void _update_window_style(WindowID p_window, bool p_repaint = true); void _update_window_mouse_passthrough(WindowID p_window); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index f030473c4b74..786598f01a3a 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -133,7 +133,7 @@ void NavigationAgent2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,1000,1,or_greater,suffix:px"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered,None"), "set_path_postprocessing", "get_path_postprocessing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon", PROPERTY_HINT_RANGE, "0.0,10.0,0.001,or_greater,suffix:px"), "set_simplify_epsilon", "get_simplify_epsilon"); diff --git a/scene/2d/physics/collision_shape_2d.cpp b/scene/2d/physics/collision_shape_2d.cpp index bdd0d06b5e2a..a6ddff4563ae 100644 --- a/scene/2d/physics/collision_shape_2d.cpp +++ b/scene/2d/physics/collision_shape_2d.cpp @@ -190,7 +190,7 @@ PackedStringArray CollisionShape2D::get_configuration_warnings() const { Ref convex = shape; Ref concave = shape; if (convex.is_valid() || concave.is_valid()) { - warnings.push_back(RTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead.")); + warnings.push_back(RTR("The CollisionShape2D node has limited editing options for polygon-based shapes. Consider using a CollisionPolygon2D node instead.")); } return warnings; diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index faf138896a52..d5e162d41691 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -144,7 +144,7 @@ void NavigationAgent3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,or_greater,suffix:m"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered,None"), "set_path_postprocessing", "get_path_postprocessing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon", PROPERTY_HINT_RANGE, "0.0,10.0,0.001,or_greater,suffix:m"), "set_simplify_epsilon", "get_simplify_epsilon"); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ea0b8cd80864..be54ef7c94ed 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1370,6 +1370,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } // Finish lines and boxes. + if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) { + if (last_color.a > 0.0) { + Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding); + Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color); + } + } if (step == DRAW_STEP_BACKGROUND) { if (sel_start != -1) { Color selection_bg = theme_cache.selection_color; @@ -1380,13 +1387,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } } - if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) { - if (last_color.a > 0.0) { - Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding); - Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color); - } - } if (step == DRAW_STEP_TEXT) { if (ul_started) { ul_started = false; diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 41e0aa739ee2..4007cd58e94c 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -197,6 +197,11 @@ void ShaderGlobalsOverride::_get_property_list(List *p_list) const pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; pinfo.hint_string = "Cubemap"; } break; + case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: { + pinfo.type = Variant::OBJECT; + pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; + pinfo.hint_string = "ExternalTexture"; + } break; default: { } break; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1a047dfa2765..06724b461fa5 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1742,6 +1742,11 @@ void Viewport::_gui_input_event(Ref p_event) { if (mb.is_valid()) { Point2 mpos = mb->get_position(); if (mb->is_pressed()) { + if (gui.dragging && mb->get_button_index() == MouseButton::RIGHT) { + _perform_drop(); + set_input_as_handled(); + return; + } MouseButtonMask button_mask = mouse_button_to_mask(mb->get_button_index()); if (!gui.mouse_focus_mask.is_empty() && !gui.mouse_focus_mask.has_flag(button_mask)) { // Do not steal mouse focus and stuff while a focus mask without the current mouse button exists. diff --git a/scene/resources/compositor.cpp b/scene/resources/compositor.cpp index 60b7ed0563fa..d9fdb93c5d32 100644 --- a/scene/resources/compositor.cpp +++ b/scene/resources/compositor.cpp @@ -85,6 +85,10 @@ void CompositorEffect::_validate_property(PropertyInfo &p_property) const { } } +void CompositorEffect::_call_render_callback(int p_effect_callback_type, const RenderData *p_render_data) { + GDVIRTUAL_CALL(_render_callback, p_effect_callback_type, p_render_data); +} + void CompositorEffect::set_enabled(bool p_enabled) { enabled = p_enabled; if (rid.is_valid()) { @@ -105,7 +109,7 @@ void CompositorEffect::set_effect_callback_type(EffectCallbackType p_callback_ty if (rid.is_valid()) { RenderingServer *rs = RenderingServer::get_singleton(); ERR_FAIL_NULL(rs); - rs->compositor_effect_set_callback(rid, RenderingServer::CompositorEffectCallbackType(effect_callback_type), Callable(this, "_render_callback")); + rs->compositor_effect_set_callback(rid, RenderingServer::CompositorEffectCallbackType(effect_callback_type), callable_mp(this, &CompositorEffect::_call_render_callback)); } } diff --git a/scene/resources/compositor.h b/scene/resources/compositor.h index ff840c88789f..e7d56878df94 100644 --- a/scene/resources/compositor.h +++ b/scene/resources/compositor.h @@ -65,6 +65,8 @@ class CompositorEffect : public Resource { static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; + void _call_render_callback(int p_effect_callback_type, const RenderData *p_render_data); + GDVIRTUAL2(_render_callback, int, const RenderData *) public: diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 77ac0569ff35..5cdb7602d47d 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1515,7 +1515,7 @@ void fragment() {)"; vec3(1.0 + 0.055) * pow(albedo_tex.rgb, vec3(1.0 / 2.4)) - vec3(0.055), vec3(12.92) * albedo_tex.rgb, lessThan(albedo_tex.rgb, vec3(0.0031308))); - vec2 msdf_size = vec2(msdf_pixel_range) / vec2(textureSize(texture_albedo, 0)); + vec2 msdf_size = vec2(msdf_pixel_range) / vec2(albedo_texture_size)); )"; if (flags[FLAG_USE_POINT_SIZE]) { code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n"; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index d5c37c2acca4..70f005930bbf 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -352,6 +352,8 @@ void AudioStream::_bind_methods() { GDVIRTUAL_BIND(_get_bpm) GDVIRTUAL_BIND(_get_beat_count) GDVIRTUAL_BIND(_get_parameter_list) + GDVIRTUAL_BIND(_has_loop); + GDVIRTUAL_BIND(_get_bar_beats); ADD_SIGNAL(MethodInfo("parameter_list_changed")); } diff --git a/servers/navigation/navigation_path_query_parameters_2d.cpp b/servers/navigation/navigation_path_query_parameters_2d.cpp index 6c1f88e3490c..74aaf64b4e09 100644 --- a/servers/navigation/navigation_path_query_parameters_2d.cpp +++ b/servers/navigation/navigation_path_query_parameters_2d.cpp @@ -31,108 +31,75 @@ #include "navigation_path_query_parameters_2d.h" void NavigationPathQueryParameters2D::set_pathfinding_algorithm(const NavigationPathQueryParameters2D::PathfindingAlgorithm p_pathfinding_algorithm) { - switch (p_pathfinding_algorithm) { - case PATHFINDING_ALGORITHM_ASTAR: { - parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; - } break; - default: { - WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default"); - parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; - } break; - } + pathfinding_algorithm = p_pathfinding_algorithm; } NavigationPathQueryParameters2D::PathfindingAlgorithm NavigationPathQueryParameters2D::get_pathfinding_algorithm() const { - switch (parameters.pathfinding_algorithm) { - case NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR: - return PATHFINDING_ALGORITHM_ASTAR; - default: - WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default"); - return PATHFINDING_ALGORITHM_ASTAR; - } + return pathfinding_algorithm; } void NavigationPathQueryParameters2D::set_path_postprocessing(const NavigationPathQueryParameters2D::PathPostProcessing p_path_postprocessing) { - switch (p_path_postprocessing) { - case PATH_POSTPROCESSING_CORRIDORFUNNEL: { - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; - } break; - case PATH_POSTPROCESSING_EDGECENTERED: { - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED; - } break; - default: { - WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default"); - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; - } break; - } + path_postprocessing = p_path_postprocessing; } NavigationPathQueryParameters2D::PathPostProcessing NavigationPathQueryParameters2D::get_path_postprocessing() const { - switch (parameters.path_postprocessing) { - case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL: - return PATH_POSTPROCESSING_CORRIDORFUNNEL; - case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED: - return PATH_POSTPROCESSING_EDGECENTERED; - default: - WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default"); - return PATH_POSTPROCESSING_CORRIDORFUNNEL; - } + return path_postprocessing; } -void NavigationPathQueryParameters2D::set_map(const RID &p_map) { - parameters.map = p_map; +void NavigationPathQueryParameters2D::set_map(RID p_map) { + map = p_map; } -const RID &NavigationPathQueryParameters2D::get_map() const { - return parameters.map; +RID NavigationPathQueryParameters2D::get_map() const { + return map; } -void NavigationPathQueryParameters2D::set_start_position(const Vector2 p_start_position) { - parameters.start_position = Vector3(p_start_position.x, 0.0, p_start_position.y); +void NavigationPathQueryParameters2D::set_start_position(Vector2 p_start_position) { + start_position = p_start_position; } Vector2 NavigationPathQueryParameters2D::get_start_position() const { - return Vector2(parameters.start_position.x, parameters.start_position.z); + return start_position; } -void NavigationPathQueryParameters2D::set_target_position(const Vector2 p_target_position) { - parameters.target_position = Vector3(p_target_position.x, 0.0, p_target_position.y); +void NavigationPathQueryParameters2D::set_target_position(Vector2 p_target_position) { + target_position = p_target_position; } Vector2 NavigationPathQueryParameters2D::get_target_position() const { - return Vector2(parameters.target_position.x, parameters.target_position.z); + return target_position; } void NavigationPathQueryParameters2D::set_navigation_layers(uint32_t p_navigation_layers) { - parameters.navigation_layers = p_navigation_layers; + navigation_layers = p_navigation_layers; } uint32_t NavigationPathQueryParameters2D::get_navigation_layers() const { - return parameters.navigation_layers; + return navigation_layers; } void NavigationPathQueryParameters2D::set_metadata_flags(BitField p_flags) { - parameters.metadata_flags = (int64_t)p_flags; + metadata_flags = (int64_t)p_flags; } BitField NavigationPathQueryParameters2D::get_metadata_flags() const { - return (int64_t)parameters.metadata_flags; + return (int64_t)metadata_flags; } void NavigationPathQueryParameters2D::set_simplify_path(bool p_enabled) { - parameters.simplify_path = p_enabled; + simplify_path = p_enabled; } bool NavigationPathQueryParameters2D::get_simplify_path() const { - return parameters.simplify_path; + return simplify_path; } void NavigationPathQueryParameters2D::set_simplify_epsilon(real_t p_epsilon) { - parameters.simplify_epsilon = MAX(0.0, p_epsilon); + simplify_epsilon = MAX(0.0, p_epsilon); } real_t NavigationPathQueryParameters2D::get_simplify_epsilon() const { - return parameters.simplify_epsilon; + return simplify_epsilon; } void NavigationPathQueryParameters2D::_bind_methods() { @@ -168,7 +135,7 @@ void NavigationPathQueryParameters2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered,None"), "set_path_postprocessing", "get_path_postprocessing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon"); @@ -177,6 +144,7 @@ void NavigationPathQueryParameters2D::_bind_methods() { BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED); + BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_NONE); BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE); BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES); diff --git a/servers/navigation/navigation_path_query_parameters_2d.h b/servers/navigation/navigation_path_query_parameters_2d.h index a1d5f2d109a4..91031bfc180f 100644 --- a/servers/navigation/navigation_path_query_parameters_2d.h +++ b/servers/navigation/navigation_path_query_parameters_2d.h @@ -37,19 +37,18 @@ class NavigationPathQueryParameters2D : public RefCounted { GDCLASS(NavigationPathQueryParameters2D, RefCounted); - NavigationUtilities::PathQueryParameters parameters; - protected: static void _bind_methods(); public: enum PathfindingAlgorithm { - PATHFINDING_ALGORITHM_ASTAR = 0, + PATHFINDING_ALGORITHM_ASTAR = NavigationUtilities::PATHFINDING_ALGORITHM_ASTAR, }; enum PathPostProcessing { - PATH_POSTPROCESSING_CORRIDORFUNNEL = 0, - PATH_POSTPROCESSING_EDGECENTERED, + PATH_POSTPROCESSING_CORRIDORFUNNEL = NavigationUtilities::PATH_POSTPROCESSING_CORRIDORFUNNEL, + PATH_POSTPROCESSING_EDGECENTERED = NavigationUtilities::PATH_POSTPROCESSING_EDGECENTERED, + PATH_POSTPROCESSING_NONE = NavigationUtilities::PATH_POSTPROCESSING_NONE, }; enum PathMetadataFlags { @@ -60,16 +59,26 @@ class NavigationPathQueryParameters2D : public RefCounted { PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL }; - const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; } +private: + PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR; + PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL; + RID map; + Vector2 start_position; + Vector2 target_position; + uint32_t navigation_layers = 1; + BitField metadata_flags = PATH_METADATA_INCLUDE_ALL; + bool simplify_path = false; + real_t simplify_epsilon = 0.0; +public: void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm); PathfindingAlgorithm get_pathfinding_algorithm() const; void set_path_postprocessing(const PathPostProcessing p_path_postprocessing); PathPostProcessing get_path_postprocessing() const; - void set_map(const RID &p_map); - const RID &get_map() const; + void set_map(RID p_map); + RID get_map() const; void set_start_position(const Vector2 p_start_position); Vector2 get_start_position() const; diff --git a/servers/navigation/navigation_path_query_parameters_3d.cpp b/servers/navigation/navigation_path_query_parameters_3d.cpp index b0a5b0ad82b1..99c5318bed2d 100644 --- a/servers/navigation/navigation_path_query_parameters_3d.cpp +++ b/servers/navigation/navigation_path_query_parameters_3d.cpp @@ -31,108 +31,75 @@ #include "navigation_path_query_parameters_3d.h" void NavigationPathQueryParameters3D::set_pathfinding_algorithm(const NavigationPathQueryParameters3D::PathfindingAlgorithm p_pathfinding_algorithm) { - switch (p_pathfinding_algorithm) { - case PATHFINDING_ALGORITHM_ASTAR: { - parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; - } break; - default: { - WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default"); - parameters.pathfinding_algorithm = NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR; - } break; - } + pathfinding_algorithm = p_pathfinding_algorithm; } NavigationPathQueryParameters3D::PathfindingAlgorithm NavigationPathQueryParameters3D::get_pathfinding_algorithm() const { - switch (parameters.pathfinding_algorithm) { - case NavigationUtilities::PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR: - return PATHFINDING_ALGORITHM_ASTAR; - default: - WARN_PRINT_ONCE("No match for used PathfindingAlgorithm - fallback to default"); - return PATHFINDING_ALGORITHM_ASTAR; - } + return pathfinding_algorithm; } void NavigationPathQueryParameters3D::set_path_postprocessing(const NavigationPathQueryParameters3D::PathPostProcessing p_path_postprocessing) { - switch (p_path_postprocessing) { - case PATH_POSTPROCESSING_CORRIDORFUNNEL: { - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; - } break; - case PATH_POSTPROCESSING_EDGECENTERED: { - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED; - } break; - default: { - WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default"); - parameters.path_postprocessing = NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL; - } break; - } + path_postprocessing = p_path_postprocessing; } NavigationPathQueryParameters3D::PathPostProcessing NavigationPathQueryParameters3D::get_path_postprocessing() const { - switch (parameters.path_postprocessing) { - case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL: - return PATH_POSTPROCESSING_CORRIDORFUNNEL; - case NavigationUtilities::PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED: - return PATH_POSTPROCESSING_EDGECENTERED; - default: - WARN_PRINT_ONCE("No match for used PathPostProcessing - fallback to default"); - return PATH_POSTPROCESSING_CORRIDORFUNNEL; - } + return path_postprocessing; } -void NavigationPathQueryParameters3D::set_map(const RID &p_map) { - parameters.map = p_map; +void NavigationPathQueryParameters3D::set_map(RID p_map) { + map = p_map; } -const RID &NavigationPathQueryParameters3D::get_map() const { - return parameters.map; +RID NavigationPathQueryParameters3D::get_map() const { + return map; } -void NavigationPathQueryParameters3D::set_start_position(const Vector3 &p_start_position) { - parameters.start_position = p_start_position; +void NavigationPathQueryParameters3D::set_start_position(Vector3 p_start_position) { + start_position = p_start_position; } -const Vector3 &NavigationPathQueryParameters3D::get_start_position() const { - return parameters.start_position; +Vector3 NavigationPathQueryParameters3D::get_start_position() const { + return start_position; } -void NavigationPathQueryParameters3D::set_target_position(const Vector3 &p_target_position) { - parameters.target_position = p_target_position; +void NavigationPathQueryParameters3D::set_target_position(Vector3 p_target_position) { + target_position = p_target_position; } -const Vector3 &NavigationPathQueryParameters3D::get_target_position() const { - return parameters.target_position; +Vector3 NavigationPathQueryParameters3D::get_target_position() const { + return target_position; } void NavigationPathQueryParameters3D::set_navigation_layers(uint32_t p_navigation_layers) { - parameters.navigation_layers = p_navigation_layers; + navigation_layers = p_navigation_layers; } uint32_t NavigationPathQueryParameters3D::get_navigation_layers() const { - return parameters.navigation_layers; + return navigation_layers; } void NavigationPathQueryParameters3D::set_metadata_flags(BitField p_flags) { - parameters.metadata_flags = (int64_t)p_flags; + metadata_flags = (int64_t)p_flags; } BitField NavigationPathQueryParameters3D::get_metadata_flags() const { - return (int64_t)parameters.metadata_flags; + return (int64_t)metadata_flags; } void NavigationPathQueryParameters3D::set_simplify_path(bool p_enabled) { - parameters.simplify_path = p_enabled; + simplify_path = p_enabled; } bool NavigationPathQueryParameters3D::get_simplify_path() const { - return parameters.simplify_path; + return simplify_path; } void NavigationPathQueryParameters3D::set_simplify_epsilon(real_t p_epsilon) { - parameters.simplify_epsilon = MAX(0.0, p_epsilon); + simplify_epsilon = MAX(0.0, p_epsilon); } real_t NavigationPathQueryParameters3D::get_simplify_epsilon() const { - return parameters.simplify_epsilon; + return simplify_epsilon; } void NavigationPathQueryParameters3D::_bind_methods() { @@ -168,7 +135,7 @@ void NavigationPathQueryParameters3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered,None"), "set_path_postprocessing", "get_path_postprocessing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon"); @@ -177,6 +144,7 @@ void NavigationPathQueryParameters3D::_bind_methods() { BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_CORRIDORFUNNEL); BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_EDGECENTERED); + BIND_ENUM_CONSTANT(PATH_POSTPROCESSING_NONE); BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_NONE); BIND_BITFIELD_FLAG(PATH_METADATA_INCLUDE_TYPES); diff --git a/servers/navigation/navigation_path_query_parameters_3d.h b/servers/navigation/navigation_path_query_parameters_3d.h index 2eb85db78757..66cad8dcd543 100644 --- a/servers/navigation/navigation_path_query_parameters_3d.h +++ b/servers/navigation/navigation_path_query_parameters_3d.h @@ -37,19 +37,18 @@ class NavigationPathQueryParameters3D : public RefCounted { GDCLASS(NavigationPathQueryParameters3D, RefCounted); - NavigationUtilities::PathQueryParameters parameters; - protected: static void _bind_methods(); public: enum PathfindingAlgorithm { - PATHFINDING_ALGORITHM_ASTAR = 0, + PATHFINDING_ALGORITHM_ASTAR = NavigationUtilities::PATHFINDING_ALGORITHM_ASTAR, }; enum PathPostProcessing { - PATH_POSTPROCESSING_CORRIDORFUNNEL = 0, - PATH_POSTPROCESSING_EDGECENTERED, + PATH_POSTPROCESSING_CORRIDORFUNNEL = NavigationUtilities::PATH_POSTPROCESSING_CORRIDORFUNNEL, + PATH_POSTPROCESSING_EDGECENTERED = NavigationUtilities::PATH_POSTPROCESSING_EDGECENTERED, + PATH_POSTPROCESSING_NONE = NavigationUtilities::PATH_POSTPROCESSING_NONE, }; enum PathMetadataFlags { @@ -60,22 +59,32 @@ class NavigationPathQueryParameters3D : public RefCounted { PATH_METADATA_INCLUDE_ALL = NavigationUtilities::PathMetadataFlags::PATH_INCLUDE_ALL }; - const NavigationUtilities::PathQueryParameters &get_parameters() const { return parameters; } +private: + PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR; + PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL; + RID map; + Vector3 start_position; + Vector3 target_position; + uint32_t navigation_layers = 1; + BitField metadata_flags = PATH_METADATA_INCLUDE_ALL; + bool simplify_path = false; + real_t simplify_epsilon = 0.0; +public: void set_pathfinding_algorithm(const PathfindingAlgorithm p_pathfinding_algorithm); PathfindingAlgorithm get_pathfinding_algorithm() const; void set_path_postprocessing(const PathPostProcessing p_path_postprocessing); PathPostProcessing get_path_postprocessing() const; - void set_map(const RID &p_map); - const RID &get_map() const; + void set_map(RID p_map); + RID get_map() const; - void set_start_position(const Vector3 &p_start_position); - const Vector3 &get_start_position() const; + void set_start_position(Vector3 p_start_position); + Vector3 get_start_position() const; - void set_target_position(const Vector3 &p_target_position); - const Vector3 &get_target_position() const; + void set_target_position(Vector3 p_target_position); + Vector3 get_target_position() const; void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; diff --git a/servers/navigation/navigation_path_query_result_2d.h b/servers/navigation/navigation_path_query_result_2d.h index 856219f998d2..1320d15ea2e1 100644 --- a/servers/navigation/navigation_path_query_result_2d.h +++ b/servers/navigation/navigation_path_query_result_2d.h @@ -47,8 +47,8 @@ class NavigationPathQueryResult2D : public RefCounted { public: enum PathSegmentType { - PATH_SEGMENT_TYPE_REGION = 0, - PATH_SEGMENT_TYPE_LINK = 1, + PATH_SEGMENT_TYPE_REGION = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION, + PATH_SEGMENT_TYPE_LINK = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK, }; void set_path(const Vector &p_path); diff --git a/servers/navigation/navigation_path_query_result_3d.h b/servers/navigation/navigation_path_query_result_3d.h index fd8545a0c933..8c4e89b9b017 100644 --- a/servers/navigation/navigation_path_query_result_3d.h +++ b/servers/navigation/navigation_path_query_result_3d.h @@ -48,8 +48,8 @@ class NavigationPathQueryResult3D : public RefCounted { public: enum PathSegmentType { - PATH_SEGMENT_TYPE_REGION = 0, - PATH_SEGMENT_TYPE_LINK = 1, + PATH_SEGMENT_TYPE_REGION = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION, + PATH_SEGMENT_TYPE_LINK = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK, }; void set_path(const Vector &p_path); diff --git a/servers/navigation/navigation_utilities.h b/servers/navigation/navigation_utilities.h index 7ae22b1d3a28..36192c140fc0 100644 --- a/servers/navigation/navigation_utilities.h +++ b/servers/navigation/navigation_utilities.h @@ -43,6 +43,7 @@ enum PathfindingAlgorithm { enum PathPostProcessing { PATH_POSTPROCESSING_CORRIDORFUNNEL = 0, PATH_POSTPROCESSING_EDGECENTERED, + PATH_POSTPROCESSING_NONE, }; enum PathSegmentType { @@ -58,25 +59,6 @@ enum PathMetadataFlags { PATH_INCLUDE_ALL = PATH_INCLUDE_TYPES | PATH_INCLUDE_RIDS | PATH_INCLUDE_OWNERS }; -struct PathQueryParameters { - PathfindingAlgorithm pathfinding_algorithm = PATHFINDING_ALGORITHM_ASTAR; - PathPostProcessing path_postprocessing = PATH_POSTPROCESSING_CORRIDORFUNNEL; - RID map; - Vector3 start_position; - Vector3 target_position; - uint32_t navigation_layers = 1; - BitField metadata_flags = PATH_INCLUDE_ALL; - bool simplify_path = false; - real_t simplify_epsilon = 0.0; -}; - -struct PathQueryResult { - PackedVector3Array path; - PackedInt32Array path_types; - TypedArray path_rids; - PackedInt64Array path_owner_ids; -}; - } //namespace NavigationUtilities #endif // NAVIGATION_UTILITIES_H diff --git a/servers/navigation_server_2d.compat.inc b/servers/navigation_server_2d.compat.inc new file mode 100644 index 000000000000..14205ef536b0 --- /dev/null +++ b/servers/navigation_server_2d.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* navigation_server_2d.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +Vector NavigationServer2D::_map_get_path_bind_compat_100129(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers) const { + return const_cast(this)->map_get_path(p_map, p_origin, p_destination, p_optimize, p_navigation_layers); +} + +void NavigationServer2D::_query_path_bind_compat_100129(const Ref &p_query_parameters, Ref p_query_result) const { + return const_cast(this)->query_path(p_query_parameters, p_query_result, Callable()); +} + +void NavigationServer2D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::_map_get_path_bind_compat_100129, DEFVAL(1)); + ClassDB::bind_compatibility_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::_query_path_bind_compat_100129); +} + +#endif // DISABLE_DEPRECATED diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index ceb5e909a3b9..cd0dd7e3a042 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "navigation_server_2d.h" +#include "navigation_server_2d.compat.inc" #include "servers/navigation_server_3d.h" @@ -62,7 +63,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer2D::map_get_random_point); - ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result", "callback"), &NavigationServer2D::query_path, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create); ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 250183300f1d..e7c7cf065331 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -91,7 +91,7 @@ class NavigationServer2D : public Object { virtual real_t map_get_link_connection_radius(RID p_map) const = 0; /// Returns the navigation path to reach the destination from the origin. - virtual Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0; + virtual Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) = 0; virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const = 0; virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const = 0; @@ -293,7 +293,7 @@ class NavigationServer2D : public Object { virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0; /// Returns a customized navigation path using a query parameters object - virtual void query_path(const Ref &p_query_parameters, Ref p_query_result) const = 0; + virtual void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback = Callable()) = 0; virtual void init() = 0; virtual void sync() = 0; @@ -318,6 +318,14 @@ class NavigationServer2D : public Object { void set_debug_enabled(bool p_enabled); bool get_debug_enabled() const; +protected: +#ifndef DISABLE_DEPRECATED + Vector _map_get_path_bind_compat_100129(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; + void _query_path_bind_compat_100129(const Ref &p_query_parameters, Ref p_query_result) const; + static void _bind_compatibility_methods(); +#endif + +public: #ifdef DEBUG_ENABLED void set_debug_navigation_enabled(bool p_enabled); bool get_debug_navigation_enabled() const; diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index 5bc91830e642..c54e62f80e6d 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -50,7 +50,7 @@ class NavigationServer2DDummy : public NavigationServer2D { real_t map_get_edge_connection_margin(RID p_map) const override { return 0; } void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {} real_t map_get_link_connection_radius(RID p_map) const override { return 0; } - Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override { return Vector(); } + Vector map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) override { return Vector(); } Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const override { return Vector2(); } RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const override { return RID(); } TypedArray map_get_links(RID p_map) const override { return TypedArray(); } @@ -158,7 +158,7 @@ class NavigationServer2DDummy : public NavigationServer2D { void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} uint32_t obstacle_get_avoidance_layers(RID p_agent) const override { return 0; } - void query_path(const Ref &p_query_parameters, Ref p_query_result) const override {} + void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback = Callable()) override {} void init() override {} void sync() override {} diff --git a/servers/navigation_server_3d.compat.inc b/servers/navigation_server_3d.compat.inc new file mode 100644 index 000000000000..633e4d5ddff0 --- /dev/null +++ b/servers/navigation_server_3d.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* navigation_server_3d.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +Vector NavigationServer3D::_map_get_path_bind_compat_100129(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const { + return const_cast(this)->map_get_path(p_map, p_origin, p_destination, p_optimize, p_navigation_layers); +} + +void NavigationServer3D::_query_path_bind_compat_100129(const Ref &p_query_parameters, Ref p_query_result) const { + return const_cast(this)->query_path(p_query_parameters, p_query_result, Callable()); +} + +void NavigationServer3D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer3D::_map_get_path_bind_compat_100129, DEFVAL(1)); + ClassDB::bind_compatibility_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::_query_path_bind_compat_100129); +} + +#endif // DISABLE_DEPRECATED diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 572309c42963..4c36df83f334 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "navigation_server_3d.h" +#include "navigation_server_3d.compat.inc" #include "core/config/project_settings.h" #include "scene/main/node.h" @@ -72,7 +73,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer3D::map_get_random_point); - ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::query_path); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result", "callback"), &NavigationServer3D::query_path, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create); ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer3D::region_set_enabled); @@ -247,6 +248,8 @@ NavigationServer3D::NavigationServer3D() { GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true); GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true); + GLOBAL_DEF("navigation/pathfinding/max_threads", 4); + GLOBAL_DEF("navigation/baking/use_crash_prevention_checks", true); GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true); GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true); @@ -935,18 +938,6 @@ bool NavigationServer3D::get_debug_avoidance_enabled() const { #endif // DEBUG_ENABLED -void NavigationServer3D::query_path(const Ref &p_query_parameters, Ref p_query_result) const { - ERR_FAIL_COND(!p_query_parameters.is_valid()); - ERR_FAIL_COND(!p_query_result.is_valid()); - - const NavigationUtilities::PathQueryResult _query_result = _query_path(p_query_parameters->get_parameters()); - - p_query_result->set_path(_query_result.path); - p_query_result->set_path_types(_query_result.path_types); - p_query_result->set_path_rids(_query_result.path_rids); - p_query_result->set_path_owner_ids(_query_result.path_owner_ids); -} - /////////////////////////////////////////////////////// NavigationServer3DCallback NavigationServer3DManager::create_callback = nullptr; diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 6dbbd3564810..df5f7658bb2a 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -103,7 +103,7 @@ class NavigationServer3D : public Object { virtual real_t map_get_link_connection_radius(RID p_map) const = 0; /// Returns the navigation path to reach the destination from the origin. - virtual Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0; + virtual Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) = 0; virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const = 0; virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const = 0; @@ -343,9 +343,7 @@ class NavigationServer3D : public Object { virtual void finish() = 0; /// Returns a customized navigation path using a query parameters object - virtual void query_path(const Ref &p_query_parameters, Ref p_query_result) const; - - virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0; + virtual void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback = Callable()) = 0; #ifndef _3D_DISABLED virtual void parse_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0; @@ -380,6 +378,13 @@ class NavigationServer3D : public Object { void set_debug_enabled(bool p_enabled); bool get_debug_enabled() const; +protected: +#ifndef DISABLE_DEPRECATED + Vector _map_get_path_bind_compat_100129(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; + void _query_path_bind_compat_100129(const Ref &p_query_parameters, Ref p_query_result) const; + static void _bind_compatibility_methods(); +#endif + private: bool debug_enabled = false; diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 210c404365a2..418f7b373d25 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -55,7 +55,7 @@ class NavigationServer3DDummy : public NavigationServer3D { real_t map_get_edge_connection_margin(RID p_map) const override { return 0; } void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) override {} real_t map_get_link_connection_radius(RID p_map) const override { return 0; } - Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const override { return Vector(); } + Vector map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) override { return Vector(); } Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const override { return Vector3(); } Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override { return Vector3(); } Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override { return Vector3(); } @@ -178,6 +178,8 @@ class NavigationServer3DDummy : public NavigationServer3D { void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; } + virtual void query_path(const Ref &p_query_parameters, Ref p_query_result, const Callable &p_callback = Callable()) override {} + #ifndef _3D_DISABLED void parse_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override {} @@ -197,7 +199,6 @@ class NavigationServer3DDummy : public NavigationServer3D { void sync() override {} void finish() override {} - NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); } int get_process_info(ProcessInfo p_info) const override { return 0; } void set_debug_enabled(bool p_enabled) {} diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h index 6205193d9a99..33b4c38018b4 100644 --- a/servers/rendering/dummy/rasterizer_dummy.h +++ b/servers/rendering/dummy/rasterizer_dummy.h @@ -90,8 +90,8 @@ class RasterizerDummy : public RendererCompositor { void gl_end_frame(bool p_swap_buffers) override {} - void end_frame(bool p_swap_buffers) override { - if (p_swap_buffers) { + void end_frame(bool p_present) override { + if (p_present) { DisplayServer::get_singleton()->swap_buffers(); } } diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index dbc8f155bfbd..e52cab740e01 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -99,7 +99,7 @@ class RendererCompositor { virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; virtual void gl_end_frame(bool p_swap_buffers) = 0; - virtual void end_frame(bool p_swap_buffers) = 0; + virtual void end_frame(bool p_present) = 0; virtual void finalize() = 0; virtual uint64_t get_frame_number() const = 0; virtual double get_frame_delta_time() const = 0; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index dbaa6ac7b2eb..fae7f11d9156 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -112,10 +112,8 @@ void RendererCompositorRD::begin_frame(double frame_step) { scene->set_time(time, frame_step); } -void RendererCompositorRD::end_frame(bool p_swap_buffers) { - if (p_swap_buffers) { - RD::get_singleton()->swap_buffers(); - } +void RendererCompositorRD::end_frame(bool p_present) { + RD::get_singleton()->swap_buffers(p_present); } void RendererCompositorRD::initialize() { @@ -267,7 +265,7 @@ void RendererCompositorRD::set_boot_image(const Ref &p_image, const Color RD::get_singleton()->draw_list_end(); - RD::get_singleton()->swap_buffers(); + RD::get_singleton()->swap_buffers(true); texture_storage->texture_free(texture); RD::get_singleton()->free(sampler); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 6821fa737e4e..41c11113fefa 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -128,7 +128,7 @@ class RendererCompositorRD : public RendererCompositor { void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); void gl_end_frame(bool p_swap_buffers) {} - void end_frame(bool p_swap_buffers); + void end_frame(bool p_present); void finalize(); _ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 697d9490158e..d664f26d3403 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1446,7 +1446,11 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); } - ERR_FAIL_COND_V(image->is_empty(), Ref()); + if (image->is_empty()) { + const String &path_str = tex->path.is_empty() ? "with no path" : vformat("with path '%s'", tex->path); + ERR_FAIL_V_MSG(Ref(), vformat("Texture %s has no data.", path_str)); + } + if (tex->format != tex->validated_format) { image->convert(tex->format); } @@ -1467,7 +1471,10 @@ Ref TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons Vector data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer); ERR_FAIL_COND_V(data.is_empty(), Ref()); Ref image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); - ERR_FAIL_COND_V(image->is_empty(), Ref()); + if (image->is_empty()) { + const String &path_str = tex->path.is_empty() ? "with no path" : vformat("with path '%s'", tex->path); + ERR_FAIL_V_MSG(Ref(), vformat("Texture %s has no data.", path_str)); + } if (tex->format != tex->validated_format) { image->convert(tex->format); } @@ -1494,6 +1501,10 @@ Vector> TextureStorage::texture_3d_get(RID p_texture) const { Ref img = Image::create_from_data(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); ERR_FAIL_COND_V(img->is_empty(), Vector>()); + if (img->is_empty()) { + const String &path_str = tex->path.is_empty() ? "with no path" : vformat("with path '%s'", tex->path); + ERR_FAIL_V_MSG(Vector>(), vformat("Texture %s has no data.", path_str)); + } if (tex->format != tex->validated_format) { img->convert(tex->format); } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 2fbafcbda37b..263e808ebd4b 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -282,20 +282,20 @@ Error RenderingDevice::_buffer_initialize(Buffer *p_buffer, const uint8_t *p_dat return OK; } -Error RenderingDevice::_insert_staging_block() { +Error RenderingDevice::_insert_staging_block(StagingBuffers &p_staging_buffers) { StagingBufferBlock block; - block.driver_id = driver->buffer_create(staging_buffer_block_size, RDD::BUFFER_USAGE_TRANSFER_FROM_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU); + block.driver_id = driver->buffer_create(p_staging_buffers.block_size, p_staging_buffers.usage_bits, RDD::MEMORY_ALLOCATION_TYPE_CPU); ERR_FAIL_COND_V(!block.driver_id, ERR_CANT_CREATE); block.frame_used = 0; block.fill_amount = 0; - staging_buffer_blocks.insert(staging_buffer_current, block); + p_staging_buffers.blocks.insert(p_staging_buffers.current, block); return OK; } -Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment) { +Error RenderingDevice::_staging_buffer_allocate(StagingBuffers &p_staging_buffers, uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment) { // Determine a block to use. r_alloc_size = p_amount; @@ -305,10 +305,10 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re r_alloc_offset = 0; // See if we can use current block. - if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { + if (p_staging_buffers.blocks[p_staging_buffers.current].frame_used == frames_drawn) { // We used this block this frame, let's see if there is still room. - uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; + uint32_t write_from = p_staging_buffers.blocks[p_staging_buffers.current].fill_amount; { uint32_t align_remainder = write_from % p_required_align; @@ -317,7 +317,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re } } - int32_t available_bytes = int32_t(staging_buffer_block_size) - int32_t(write_from); + int32_t available_bytes = int32_t(p_staging_buffers.block_size) - int32_t(write_from); if ((int32_t)p_amount < available_bytes) { // All is good, we should be ok, all will fit. @@ -332,20 +332,20 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re // Can't fit it into this buffer. // Will need to try next buffer. - staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); + p_staging_buffers.current = (p_staging_buffers.current + 1) % p_staging_buffers.blocks.size(); // Before doing anything, though, let's check that we didn't manage to fill all blocks. // Possible in a single frame. - if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { + if (p_staging_buffers.blocks[p_staging_buffers.current].frame_used == frames_drawn) { // Guess we did.. ok, let's see if we can insert a new block. - if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)p_staging_buffers.blocks.size() * p_staging_buffers.block_size < p_staging_buffers.max_size) { // We can, so we are safe. - Error err = _insert_staging_block(); + Error err = _insert_staging_block(p_staging_buffers); if (err) { return err; } // Claim for this frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + p_staging_buffers.blocks.write[p_staging_buffers.current].frame_used = frames_drawn; } else { // Ok, worst case scenario, all the staging buffers belong to this frame // and this frame is not even done. @@ -360,20 +360,20 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re } } - } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frames.size()) { + } else if (p_staging_buffers.blocks[p_staging_buffers.current].frame_used <= frames_drawn - frames.size()) { // This is an old block, which was already processed, let's reuse. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; + p_staging_buffers.blocks.write[p_staging_buffers.current].frame_used = frames_drawn; + p_staging_buffers.blocks.write[p_staging_buffers.current].fill_amount = 0; } else { // This block may still be in use, let's not touch it unless we have to, so.. can we create a new one? - if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { + if ((uint64_t)p_staging_buffers.blocks.size() * p_staging_buffers.block_size < p_staging_buffers.max_size) { // We are still allowed to create a new block, so let's do that and insert it for current pos. - Error err = _insert_staging_block(); + Error err = _insert_staging_block(p_staging_buffers); if (err) { return err; } // Claim for this frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + p_staging_buffers.blocks.write[p_staging_buffers.current].frame_used = frames_drawn; } else { // Oops, we are out of room and we can't create more. // Let's flush older frames. @@ -387,12 +387,12 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re break; } - staging_buffer_used = true; + p_staging_buffers.used = true; return OK; } -void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAction p_required_action) { +void RenderingDevice::_staging_buffer_execute_required_action(StagingBuffers &p_staging_buffers, StagingRequiredAction p_required_action) { switch (p_required_action) { case STAGING_REQUIRED_ACTION_NONE: { // Do nothing. @@ -401,30 +401,30 @@ void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAct _flush_and_stall_for_all_frames(); // Clear the whole staging buffer. - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - staging_buffer_blocks.write[i].frame_used = 0; - staging_buffer_blocks.write[i].fill_amount = 0; + for (int i = 0; i < p_staging_buffers.blocks.size(); i++) { + p_staging_buffers.blocks.write[i].frame_used = 0; + p_staging_buffers.blocks.write[i].fill_amount = 0; } // Claim for current frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + p_staging_buffers.blocks.write[p_staging_buffers.current].frame_used = frames_drawn; } break; case STAGING_REQUIRED_ACTION_STALL_PREVIOUS: { _stall_for_previous_frames(); - for (int i = 0; i < staging_buffer_blocks.size(); i++) { + for (int i = 0; i < p_staging_buffers.blocks.size(); i++) { // Clear all blocks but the ones from this frame. - int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); - if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { + int block_idx = (i + p_staging_buffers.current) % p_staging_buffers.blocks.size(); + if (p_staging_buffers.blocks[block_idx].frame_used == frames_drawn) { break; // Ok, we reached something from this frame, abort. } - staging_buffer_blocks.write[block_idx].frame_used = 0; - staging_buffer_blocks.write[block_idx].fill_amount = 0; + p_staging_buffers.blocks.write[block_idx].frame_used = 0; + p_staging_buffers.blocks.write[block_idx].fill_amount = 0; } // Claim for current frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + p_staging_buffers.blocks.write[p_staging_buffers.current].frame_used = frames_drawn; } break; default: { DEV_ASSERT(false && "Unknown required action."); @@ -503,7 +503,7 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p uint32_t block_write_amount; StagingRequiredAction required_action; - Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), required_align, block_write_offset, block_write_amount, required_action); + Error err = _staging_buffer_allocate(upload_staging_buffers, MIN(to_submit, upload_staging_buffers.block_size), required_align, block_write_offset, block_write_amount, required_action); if (err) { return err; } @@ -518,17 +518,17 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p command_buffer_copies_vector.clear(); } - _staging_buffer_execute_required_action(required_action); + _staging_buffer_execute_required_action(upload_staging_buffers, required_action); // Map staging buffer (It's CPU and coherent). - uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); + uint8_t *data_ptr = driver->buffer_map(upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id); ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); // Copy to staging buffer. memcpy(data_ptr + block_write_offset, src_data + submit_from, block_write_amount); // Unmap. - driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id); + driver->buffer_unmap(upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id); // Insert a command to copy this. RDD::BufferCopyRegion region; @@ -537,11 +537,11 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p region.size = block_write_amount; RDG::RecordedBufferCopy buffer_copy; - buffer_copy.source = staging_buffer_blocks[staging_buffer_current].driver_id; + buffer_copy.source = upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id; buffer_copy.region = region; command_buffer_copies_vector.push_back(buffer_copy); - staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount; + upload_staging_buffers.blocks.write[upload_staging_buffers.current].fill_amount = block_write_offset + block_write_amount; to_submit -= block_write_amount; submit_from += block_write_amount; @@ -611,7 +611,7 @@ Vector RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset Buffer *buffer = _get_buffer_from_owner(p_buffer); if (!buffer) { - ERR_FAIL_V_MSG(Vector(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); + ERR_FAIL_V_MSG(Vector(), "Buffer is either invalid or this type of buffer can't be retrieved."); } // Size of buffer to retrieve. @@ -653,6 +653,89 @@ Vector RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset return buffer_data; } +Error RenderingDevice::buffer_get_data_async(RID p_buffer, const Callable &p_callback, uint32_t p_offset, uint32_t p_size) { + ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE); + + Buffer *buffer = _get_buffer_from_owner(p_buffer); + if (buffer == nullptr) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer is either invalid or this type of buffer can't be retrieved."); + } + + if (p_size == 0) { + p_size = buffer->size; + } + + ERR_FAIL_COND_V_MSG(p_size + p_offset > buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the buffer."); + ERR_FAIL_COND_V_MSG(!p_callback.is_valid(), ERR_INVALID_PARAMETER, "Callback must be valid."); + + _check_transfer_worker_buffer(buffer); + + BufferGetDataRequest get_data_request; + uint32_t flushed_copies = 0; + get_data_request.callback = p_callback; + get_data_request.frame_local_index = frames[frame].download_buffer_copy_regions.size(); + get_data_request.size = p_size; + + const uint32_t required_align = 32; + uint32_t block_write_offset; + uint32_t block_write_amount; + StagingRequiredAction required_action; + uint32_t to_submit = p_size; + uint32_t submit_from = 0; + while (to_submit > 0) { + Error err = _staging_buffer_allocate(download_staging_buffers, MIN(to_submit, download_staging_buffers.block_size), required_align, block_write_offset, block_write_amount, required_action); + if (err) { + return err; + } + + if ((get_data_request.frame_local_count > 0) && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) { + if (_buffer_make_mutable(buffer, p_buffer)) { + // The buffer must be mutable to be used as a copy source. + draw_graph.add_synchronization(); + } + + for (uint32_t i = flushed_copies; i < get_data_request.frame_local_count; i++) { + uint32_t local_index = get_data_request.frame_local_index + i; + draw_graph.add_buffer_get_data(buffer->driver_id, buffer->draw_tracker, frames[frame].download_buffer_staging_buffers[local_index], frames[frame].download_buffer_copy_regions[local_index]); + } + + flushed_copies = get_data_request.frame_local_count; + } + + _staging_buffer_execute_required_action(download_staging_buffers, required_action); + + RDD::BufferCopyRegion region; + region.src_offset = submit_from + p_offset; + region.dst_offset = block_write_offset; + region.size = block_write_amount; + + frames[frame].download_buffer_staging_buffers.push_back(download_staging_buffers.blocks[download_staging_buffers.current].driver_id); + frames[frame].download_buffer_copy_regions.push_back(region); + get_data_request.frame_local_count++; + + download_staging_buffers.blocks.write[download_staging_buffers.current].fill_amount = block_write_offset + block_write_amount; + + to_submit -= block_write_amount; + submit_from += block_write_amount; + } + + if (get_data_request.frame_local_count > 0) { + if (_buffer_make_mutable(buffer, p_buffer)) { + // The buffer must be mutable to be used as a copy source. + draw_graph.add_synchronization(); + } + + for (uint32_t i = flushed_copies; i < get_data_request.frame_local_count; i++) { + uint32_t local_index = get_data_request.frame_local_index + i; + draw_graph.add_buffer_get_data(buffer->driver_id, buffer->draw_tracker, frames[frame].download_buffer_staging_buffers[local_index], frames[frame].download_buffer_copy_regions[local_index]); + } + + frames[frame].download_buffer_get_data_requests.push_back(get_data_request); + } + + return OK; +} + RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector &p_data, BitField p_usage) { ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); @@ -1461,7 +1544,7 @@ Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vec uint32_t to_allocate = region_pitch * region_h; uint32_t alloc_offset = 0, alloc_size = 0; StagingRequiredAction required_action; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, required_action, false); + Error err = _staging_buffer_allocate(upload_staging_buffers, to_allocate, required_align, alloc_offset, alloc_size, required_action, false); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); if (!command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) { @@ -1475,12 +1558,12 @@ Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vec command_buffer_to_texture_copies_vector.clear(); } - _staging_buffer_execute_required_action(required_action); + _staging_buffer_execute_required_action(upload_staging_buffers, required_action); uint8_t *write_ptr; { // Map. - uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); + uint8_t *data_ptr = driver->buffer_map(upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id); ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); write_ptr = data_ptr; write_ptr += alloc_offset; @@ -1492,7 +1575,7 @@ Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vec _copy_region_block_or_regular(read_ptr_mipmap_layer, write_ptr, x, y, width, region_w, region_h, block_w, block_h, region_pitch, pixel_size, block_size); { // Unmap. - driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id); + driver->buffer_unmap(upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id); } RDD::BufferTextureCopyRegion copy_region; @@ -1505,11 +1588,11 @@ Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vec copy_region.texture_region_size = Vector3i(region_logic_w, region_logic_h, 1); RDG::RecordedBufferToTextureCopy buffer_to_texture_copy; - buffer_to_texture_copy.from_buffer = staging_buffer_blocks[staging_buffer_current].driver_id; + buffer_to_texture_copy.from_buffer = upload_staging_buffers.blocks[upload_staging_buffers.current].driver_id; buffer_to_texture_copy.region = copy_region; command_buffer_to_texture_copies_vector.push_back(buffer_to_texture_copy); - staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; + upload_staging_buffers.blocks.write[upload_staging_buffers.current].fill_amount = alloc_offset + alloc_size; } } } @@ -1890,6 +1973,131 @@ Vector RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye } } +Error RenderingDevice::texture_get_data_async(RID p_texture, uint32_t p_layer, const Callable &p_callback) { + ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE); + + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(tex, ERR_INVALID_PARAMETER); + + ERR_FAIL_COND_V_MSG(tex->bound, ERR_INVALID_PARAMETER, "Texture can't be retrieved while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to retrieve this texture."); + ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); + ERR_FAIL_COND_V(p_layer >= tex->layers, ERR_INVALID_PARAMETER); + + _check_transfer_worker_texture(tex); + + thread_local LocalVector mip_layouts; + mip_layouts.resize(tex->mipmaps); + for (uint32_t i = 0; i < tex->mipmaps; i++) { + RDD::TextureSubresource subres; + subres.aspect = RDD::TEXTURE_ASPECT_COLOR; + subres.layer = p_layer; + subres.mipmap = i; + driver->texture_get_copyable_layout(tex->driver_id, subres, &mip_layouts[i]); + + // Assuming layers are tightly packed. If this is not true on some driver, we must modify the copy algorithm. + DEV_ASSERT(mip_layouts[i].layer_pitch == mip_layouts[i].size / tex->layers); + } + + ERR_FAIL_COND_V(mip_layouts.is_empty(), ERR_INVALID_PARAMETER); + + if (_texture_make_mutable(tex, p_texture)) { + // The texture must be mutable to be used as a copy source due to layout transitions. + draw_graph.add_synchronization(); + } + + TextureGetDataRequest get_data_request; + get_data_request.callback = p_callback; + get_data_request.frame_local_index = frames[frame].download_buffer_texture_copy_regions.size(); + get_data_request.width = tex->width; + get_data_request.height = tex->height; + get_data_request.depth = tex->depth; + get_data_request.format = tex->format; + get_data_request.mipmaps = tex->mipmaps; + + uint32_t block_w, block_h; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + + uint32_t pixel_size = get_image_format_pixel_size(tex->format); + uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(tex->format); + + uint32_t w, h, d; + uint32_t required_align = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT); + uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP); + uint32_t region_size = texture_download_region_size_px; + uint32_t logic_w = tex->width; + uint32_t logic_h = tex->height; + uint32_t mipmap_offset = 0; + uint32_t block_write_offset; + uint32_t block_write_amount; + StagingRequiredAction required_action; + uint32_t flushed_copies = 0; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + uint32_t image_total = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, i + 1, &w, &h, &d); + uint32_t tight_mip_size = image_total - mipmap_offset; + for (uint32_t z = 0; z < d; z++) { + for (uint32_t y = 0; y < h; y += region_size) { + for (uint32_t x = 0; x < w; x += region_size) { + uint32_t region_w = MIN(region_size, w - x); + uint32_t region_h = MIN(region_size, h - y); + ERR_FAIL_COND_V(region_w % block_w, ERR_BUG); + ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); + + uint32_t region_logic_w = MIN(region_size, logic_w - x); + uint32_t region_logic_h = MIN(region_size, logic_h - y); + uint32_t region_pitch = (region_w * pixel_size * block_w) >> pixel_rshift; + region_pitch = STEPIFY(region_pitch, pitch_step); + + uint32_t to_allocate = region_pitch * region_h; + Error err = _staging_buffer_allocate(download_staging_buffers, to_allocate, required_align, block_write_offset, block_write_amount, required_action, false); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + + if ((get_data_request.frame_local_count > 0) && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) { + for (uint32_t j = flushed_copies; j < get_data_request.frame_local_count; j++) { + uint32_t local_index = get_data_request.frame_local_index + j; + draw_graph.add_texture_get_data(tex->driver_id, tex->draw_tracker, frames[frame].download_texture_staging_buffers[local_index], frames[frame].download_buffer_texture_copy_regions[local_index]); + } + + flushed_copies = get_data_request.frame_local_count; + } + + _staging_buffer_execute_required_action(download_staging_buffers, required_action); + + RDD::BufferTextureCopyRegion copy_region; + copy_region.buffer_offset = block_write_offset; + copy_region.texture_subresources.aspect = tex->read_aspect_flags; + copy_region.texture_subresources.mipmap = i; + copy_region.texture_subresources.base_layer = p_layer; + copy_region.texture_subresources.layer_count = 1; + copy_region.texture_offset = Vector3i(x, y, z); + copy_region.texture_region_size = Vector3i(region_logic_w, region_logic_h, 1); + frames[frame].download_texture_staging_buffers.push_back(download_staging_buffers.blocks[download_staging_buffers.current].driver_id); + frames[frame].download_buffer_texture_copy_regions.push_back(copy_region); + frames[frame].download_texture_mipmap_offsets.push_back(mipmap_offset + (tight_mip_size / d) * z); + get_data_request.frame_local_count++; + + download_staging_buffers.blocks.write[download_staging_buffers.current].fill_amount = block_write_offset + block_write_amount; + } + } + } + + mipmap_offset = image_total; + logic_w = MAX(1u, logic_w >> 1); + logic_h = MAX(1u, logic_h >> 1); + } + + if (get_data_request.frame_local_count > 0) { + for (uint32_t i = flushed_copies; i < get_data_request.frame_local_count; i++) { + uint32_t local_index = get_data_request.frame_local_index + i; + draw_graph.add_texture_get_data(tex->driver_id, tex->draw_tracker, frames[frame].download_texture_staging_buffers[local_index], frames[frame].download_buffer_texture_copy_regions[local_index]); + } + + flushed_copies = get_data_request.frame_local_count; + frames[frame].download_texture_get_data_requests.push_back(get_data_request); + } + + return OK; +} + bool RenderingDevice::texture_is_shared(RID p_texture) { ERR_RENDER_THREAD_GUARD_V(false); @@ -5821,6 +6029,8 @@ void RenderingDevice::_free_internal(RID p_id) { ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); #endif } + + frames_pending_resources_for_processing = uint32_t(frames.size()); } // The full list of resources that can be named is in the VkObjectType enum. @@ -5923,11 +6133,11 @@ String RenderingDevice::get_device_pipeline_cache_uuid() const { return driver->get_pipeline_cache_uuid(); } -void RenderingDevice::swap_buffers() { +void RenderingDevice::swap_buffers(bool p_present) { ERR_RENDER_THREAD_GUARD(); _end_frame(); - _execute_frame(true); + _execute_frame(p_present); // Advance to the next frame and begin recording again. frame = (frame + 1) % frames.size(); @@ -6030,6 +6240,10 @@ void RenderingDevice::_free_pending_resources(int p_frame) { frames[p_frame].buffers_to_dispose_of.pop_front(); } + + if (frames_pending_resources_for_processing > 0u) { + --frames_pending_resources_for_processing; + } } uint32_t RenderingDevice::get_frame_delay() const { @@ -6055,11 +6269,8 @@ uint64_t RenderingDevice::get_memory_usage(MemoryType p_type) const { } void RenderingDevice::_begin_frame(bool p_presented) { - // Before beginning this frame, wait on the fence if it was signaled to make sure its work is finished. - if (frames[frame].fence_signaled) { - driver->fence_wait(frames[frame].fence); - frames[frame].fence_signaled = false; - } + // Before writing to this frame, wait for it to be finished. + _stall_for_frame(frame); if (command_pool_reset_enabled) { bool reset = driver->command_pool_reset(frames[frame].command_pool); @@ -6081,10 +6292,15 @@ void RenderingDevice::_begin_frame(bool p_presented) { // Erase pending resources. _free_pending_resources(frame); - // Advance staging buffer if used. - if (staging_buffer_used) { - staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); - staging_buffer_used = false; + // Advance staging buffers if used. + if (upload_staging_buffers.used) { + upload_staging_buffers.current = (upload_staging_buffers.current + 1) % upload_staging_buffers.blocks.size(); + upload_staging_buffers.used = false; + } + + if (download_staging_buffers.used) { + download_staging_buffers.current = (download_staging_buffers.current + 1) % download_staging_buffers.blocks.size(); + download_staging_buffers.used = false; } if (frames[frame].timestamp_count) { @@ -6202,12 +6418,97 @@ void RenderingDevice::_execute_frame(bool p_present) { } } +void RenderingDevice::_stall_for_frame(uint32_t p_frame) { + thread_local PackedByteArray packed_byte_array; + + if (frames[p_frame].fence_signaled) { + driver->fence_wait(frames[p_frame].fence); + frames[p_frame].fence_signaled = false; + + // Flush any pending requests for asynchronous buffer downloads. + if (!frames[p_frame].download_buffer_get_data_requests.is_empty()) { + for (uint32_t i = 0; i < frames[p_frame].download_buffer_get_data_requests.size(); i++) { + const BufferGetDataRequest &request = frames[p_frame].download_buffer_get_data_requests[i]; + packed_byte_array.resize(request.size); + + uint32_t array_offset = 0; + for (uint32_t j = 0; j < request.frame_local_count; j++) { + uint32_t local_index = request.frame_local_index + j; + const RDD::BufferCopyRegion ®ion = frames[p_frame].download_buffer_copy_regions[local_index]; + uint8_t *buffer_data = driver->buffer_map(frames[p_frame].download_buffer_staging_buffers[local_index]); + memcpy(&packed_byte_array.write[array_offset], &buffer_data[region.dst_offset], region.size); + driver->buffer_unmap(frames[p_frame].download_buffer_staging_buffers[local_index]); + array_offset += region.size; + } + + request.callback.call(packed_byte_array); + } + + frames[p_frame].download_buffer_staging_buffers.clear(); + frames[p_frame].download_buffer_copy_regions.clear(); + frames[p_frame].download_buffer_get_data_requests.clear(); + } + + // Flush any pending requests for asynchronous texture downloads. + if (!frames[p_frame].download_texture_get_data_requests.is_empty()) { + uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP); + for (uint32_t i = 0; i < frames[p_frame].download_texture_get_data_requests.size(); i++) { + const TextureGetDataRequest &request = frames[p_frame].download_texture_get_data_requests[i]; + uint32_t texture_size = get_image_format_required_size(request.format, request.width, request.height, request.depth, request.mipmaps); + packed_byte_array.resize(texture_size); + + // Find the block size of the texture's format. + uint32_t block_w = 0; + uint32_t block_h = 0; + get_compressed_image_format_block_dimensions(request.format, block_w, block_h); + + uint32_t block_size = get_compressed_image_format_block_byte_size(request.format); + uint32_t pixel_size = get_image_format_pixel_size(request.format); + uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(request.format); + uint32_t region_size = texture_download_region_size_px; + + for (uint32_t j = 0; j < request.frame_local_count; j++) { + uint32_t local_index = request.frame_local_index + j; + const RDD::BufferTextureCopyRegion ®ion = frames[p_frame].download_buffer_texture_copy_regions[local_index]; + uint32_t w = STEPIFY(request.width >> region.texture_subresources.mipmap, block_w); + uint32_t h = STEPIFY(request.height >> region.texture_subresources.mipmap, block_h); + uint32_t region_w = MIN(region_size, w - region.texture_offset.x); + uint32_t region_h = MIN(region_size, h - region.texture_offset.y); + uint32_t region_pitch = (region_w * pixel_size * block_w) >> pixel_rshift; + region_pitch = STEPIFY(region_pitch, pitch_step); + + uint8_t *buffer_data = driver->buffer_map(frames[p_frame].download_texture_staging_buffers[local_index]); + const uint8_t *read_ptr = buffer_data + region.buffer_offset; + uint8_t *write_ptr = packed_byte_array.ptrw() + frames[p_frame].download_texture_mipmap_offsets[local_index]; + uint32_t unit_size = pixel_size; + if (block_w != 1 || block_h != 1) { + unit_size = block_size; + } + + write_ptr += ((region.texture_offset.y / block_h) * (w / block_w) + (region.texture_offset.x / block_w)) * unit_size; + for (uint32_t y = region_h / block_h; y > 0; y--) { + memcpy(write_ptr, read_ptr, (region_w / block_w) * unit_size); + write_ptr += (w / block_w) * unit_size; + read_ptr += region_pitch; + } + + driver->buffer_unmap(frames[p_frame].download_texture_staging_buffers[local_index]); + } + + request.callback.call(packed_byte_array); + } + + frames[p_frame].download_texture_staging_buffers.clear(); + frames[p_frame].download_buffer_texture_copy_regions.clear(); + frames[p_frame].download_texture_mipmap_offsets.clear(); + frames[p_frame].download_texture_get_data_requests.clear(); + } + } +} + void RenderingDevice::_stall_for_previous_frames() { for (uint32_t i = 0; i < frames.size(); i++) { - if (frames[i].fence_signaled) { - driver->fence_wait(frames[i].fence); - frames[i].fence_signaled = false; - } + _stall_for_frame(i); } } @@ -6386,30 +6687,41 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ } // Convert block size from KB. - staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); - staging_buffer_block_size = MAX(4u, staging_buffer_block_size); - staging_buffer_block_size *= 1024; + upload_staging_buffers.block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); + upload_staging_buffers.block_size = MAX(4u, upload_staging_buffers.block_size); + upload_staging_buffers.block_size *= 1024; // Convert staging buffer size from MB. - staging_buffer_max_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/max_size_mb"); - staging_buffer_max_size = MAX(1u, staging_buffer_max_size); - staging_buffer_max_size *= 1024 * 1024; + upload_staging_buffers.max_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/max_size_mb"); + upload_staging_buffers.max_size = MAX(1u, upload_staging_buffers.max_size); + upload_staging_buffers.max_size *= 1024 * 1024; + upload_staging_buffers.max_size = MAX(upload_staging_buffers.max_size, upload_staging_buffers.block_size * 4); - if (staging_buffer_max_size < staging_buffer_block_size * 4) { - // Validate enough blocks. - staging_buffer_max_size = staging_buffer_block_size * 4; - } + // Copy the sizes to the download staging buffers. + download_staging_buffers.block_size = upload_staging_buffers.block_size; + download_staging_buffers.max_size = upload_staging_buffers.max_size; texture_upload_region_size_px = GLOBAL_GET("rendering/rendering_device/staging_buffer/texture_upload_region_size_px"); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); + texture_download_region_size_px = GLOBAL_GET("rendering/rendering_device/staging_buffer/texture_download_region_size_px"); + texture_download_region_size_px = nearest_power_of_2_templated(texture_download_region_size_px); + // Ensure current staging block is valid and at least one per frame exists. - staging_buffer_current = 0; - staging_buffer_used = false; + upload_staging_buffers.current = 0; + upload_staging_buffers.used = false; + upload_staging_buffers.usage_bits = RDD::BUFFER_USAGE_TRANSFER_FROM_BIT; + + download_staging_buffers.current = 0; + download_staging_buffers.used = false; + download_staging_buffers.usage_bits = RDD::BUFFER_USAGE_TRANSFER_TO_BIT; for (uint32_t i = 0; i < frames.size(); i++) { - // Staging was never used, create a block. - err = _insert_staging_block(); + // Staging was never used, create the blocks. + err = _insert_staging_block(upload_staging_buffers); + ERR_FAIL_COND_V(err, FAILED); + + err = _insert_staging_block(download_staging_buffers); ERR_FAIL_COND_V(err, FAILED); } @@ -6788,8 +7100,12 @@ void RenderingDevice::finalize() { frames.clear(); - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - driver->buffer_free(staging_buffer_blocks[i].driver_id); + for (int i = 0; i < upload_staging_buffers.blocks.size(); i++) { + driver->buffer_free(upload_staging_buffers.blocks[i].driver_id); + } + + for (int i = 0; i < download_staging_buffers.blocks.size(); i++) { + driver->buffer_free(download_staging_buffers.blocks[i].driver_id); } while (vertex_formats.size()) { @@ -6869,6 +7185,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_update", "texture", "layer", "data"), &RenderingDevice::texture_update); ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "layer"), &RenderingDevice::texture_get_data); + ClassDB::bind_method(D_METHOD("texture_get_data_async", "texture", "layer", "callback"), &RenderingDevice::texture_get_data_async); ClassDB::bind_method(D_METHOD("texture_is_format_supported_for_usage", "format", "usage_flags"), &RenderingDevice::texture_is_format_supported_for_usage); @@ -6926,6 +7243,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data"), &RenderingDevice::_buffer_update_bind); ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes"), &RenderingDevice::buffer_clear); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("buffer_get_data_async", "buffer", "callback", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data_async, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass", "specialization_constants"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0), DEFVAL(TypedArray())); ClassDB::bind_method(D_METHOD("render_pipeline_is_valid", "render_pipeline"), &RenderingDevice::render_pipeline_is_valid); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 92045bd0d841..bbfd0abd7158 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -156,27 +156,33 @@ class RenderingDevice : public RenderingDeviceCommons { // // See the comments in the code to understand better how it works. + enum StagingRequiredAction { + STAGING_REQUIRED_ACTION_NONE, + STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL, + STAGING_REQUIRED_ACTION_STALL_PREVIOUS, + }; + struct StagingBufferBlock { RDD::BufferID driver_id; uint64_t frame_used = 0; uint32_t fill_amount = 0; }; - Vector staging_buffer_blocks; - int staging_buffer_current = 0; - uint32_t staging_buffer_block_size = 0; - uint64_t staging_buffer_max_size = 0; - bool staging_buffer_used = false; - - enum StagingRequiredAction { - STAGING_REQUIRED_ACTION_NONE, - STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL, - STAGING_REQUIRED_ACTION_STALL_PREVIOUS + struct StagingBuffers { + Vector blocks; + int current = 0; + uint32_t block_size = 0; + uint64_t max_size = 0; + BitField usage_bits; + bool used = false; }; - Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment = true); - void _staging_buffer_execute_required_action(StagingRequiredAction p_required_action); - Error _insert_staging_block(); + Error _staging_buffer_allocate(StagingBuffers &p_staging_buffers, uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment = true); + void _staging_buffer_execute_required_action(StagingBuffers &p_staging_buffers, StagingRequiredAction p_required_action); + Error _insert_staging_block(StagingBuffers &p_staging_buffers); + + StagingBuffers upload_staging_buffers; + StagingBuffers download_staging_buffers; struct Buffer { RDD::BufferID driver_id; @@ -205,11 +211,19 @@ class RenderingDevice : public RenderingDeviceCommons { RID_Owner storage_buffer_owner; RID_Owner texture_buffer_owner; + struct BufferGetDataRequest { + uint32_t frame_local_index = 0; + uint32_t frame_local_count = 0; + Callable callback; + uint32_t size = 0; + }; + public: Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size); Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data); Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size); Vector buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); // This causes stall, only use to retrieve large buffers for saving. + Error buffer_get_data_async(RID p_buffer, const Callable &p_callback, uint32_t p_offset = 0, uint32_t p_size = 0); /*****************/ /**** TEXTURE ****/ @@ -300,6 +314,7 @@ class RenderingDevice : public RenderingDeviceCommons { RID_Owner texture_owner; uint32_t texture_upload_region_size_px = 0; + uint32_t texture_download_region_size_px = 0; Vector _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false); uint32_t _texture_layer_count(Texture *p_texture) const; @@ -311,6 +326,17 @@ class RenderingDevice : public RenderingDeviceCommons { void _texture_copy_shared(RID p_src_texture_rid, Texture *p_src_texture, RID p_dst_texture_rid, Texture *p_dst_texture); void _texture_create_reinterpret_buffer(Texture *p_texture); + struct TextureGetDataRequest { + uint32_t frame_local_index = 0; + uint32_t frame_local_count = 0; + Callable callback; + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 0; + uint32_t mipmaps = 0; + RDD::DataFormat format = RDD::DATA_FORMAT_MAX; + }; + public: struct TextureView { DataFormat format_override = DATA_FORMAT_MAX; // // Means, use same as format. @@ -342,6 +368,7 @@ class RenderingDevice : public RenderingDeviceCommons { RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0); Error texture_update(RID p_texture, uint32_t p_layer, const Vector &p_data); Vector texture_get_data(RID p_texture, uint32_t p_layer); // CPU textures will return immediately, while GPU textures will most likely force a flush + Error texture_get_data_async(RID p_texture, uint32_t p_layer, const Callable &p_callback); bool texture_is_format_supported_for_usage(DataFormat p_format, BitField p_usage) const; bool texture_is_shared(RID p_texture); @@ -1381,6 +1408,17 @@ class RenderingDevice : public RenderingDeviceCommons { List render_pipelines_to_dispose_of; List compute_pipelines_to_dispose_of; + // Pending asynchronous data transfer for buffers. + LocalVector download_buffer_staging_buffers; + LocalVector download_buffer_copy_regions; + LocalVector download_buffer_get_data_requests; + + // Pending asynchronous data transfer for textures. + LocalVector download_texture_staging_buffers; + LocalVector download_buffer_texture_copy_regions; + LocalVector download_texture_mipmap_offsets; + LocalVector download_texture_get_data_requests; + // The command pool used by the command buffer. RDD::CommandPoolID command_pool; @@ -1431,6 +1469,17 @@ class RenderingDevice : public RenderingDeviceCommons { TightLocalVector frames; uint64_t frames_drawn = 0; + // Whenever logic/physics request a graphics operation (not just deleting a resource) that requires + // us to flush all graphics commands, we must set frames_pending_resources_for_processing = frames.size(). + // This is important for when the user requested for the logic loop to still be updated while + // graphics should not (e.g. headless Multiplayer servers, minimized windows that need to still + // process something on the background). + uint32_t frames_pending_resources_for_processing = 0u; + +public: + bool has_pending_resources_for_processing() const { return frames_pending_resources_for_processing != 0u; } + +private: void _free_pending_resources(int p_frame); uint64_t texture_memory = 0; @@ -1446,6 +1495,7 @@ class RenderingDevice : public RenderingDeviceCommons { void _begin_frame(bool p_presented = false); void _end_frame(); void _execute_frame(bool p_present); + void _stall_for_frame(uint32_t p_frame); void _stall_for_previous_frames(); void _flush_and_stall_for_all_frames(); @@ -1481,7 +1531,7 @@ class RenderingDevice : public RenderingDeviceCommons { uint64_t limit_get(Limit p_limit) const; - void swap_buffers(); + void swap_buffers(bool p_present); uint32_t get_frame_delay() const; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 2ec693cbbfe7..b5d0d014791f 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -406,15 +406,15 @@ void RenderingServerDefault::sync() { } } -void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { +void RenderingServerDefault::draw(bool p_present, double frame_step) { ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred()."); // Needs to be done before changes is reset to 0, to not force the editor to redraw. RS::get_singleton()->emit_signal(SNAME("frame_pre_draw")); changes = 0; if (create_thread) { - command_queue.push(this, &RenderingServerDefault::_draw, p_swap_buffers, frame_step); + command_queue.push(this, &RenderingServerDefault::_draw, p_present, frame_step); } else { - _draw(p_swap_buffers, frame_step); + _draw(p_present, frame_step); } } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 29b1e163c78a..4a0df4a0cf3b 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -1124,7 +1124,7 @@ class RenderingServerDefault : public RenderingServer { virtual void request_frame_drawn_callback(const Callable &p_callable) override; - virtual void draw(bool p_swap_buffers, double frame_step) override; + virtual void draw(bool p_present, double frame_step) override; virtual void sync() override; virtual bool has_changed() const override; virtual void init() override; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 585a323a4df6..e5e095c7a193 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -674,7 +674,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene const StringName &varying_name = varying_names[k]; const SL::ShaderNode::Varying &varying = pnode->varyings[varying_name]; - if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) { + if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) { var_frag_to_light.push_back(Pair(varying_name, varying)); fragment_varyings.insert(varying_name); continue; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 764d420a2394..4e800112aca1 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -128,6 +128,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "TYPE_USAMPLER3D", "TYPE_SAMPLERCUBE", "TYPE_SAMPLERCUBEARRAY", + "TYPE_SAMPLEREXT", "INTERPOLATION_FLAT", "INTERPOLATION_SMOOTH", "CONST", @@ -5062,7 +5063,7 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St case ShaderNode::Varying::STAGE_UNKNOWN: // first assign if (current_function == varying_function_names.vertex) { if (p_varying.type < TYPE_INT) { - *r_message = vformat(RTR("Varying with '%s' data type may only be assigned in the 'fragment' function."), get_datatype_name(p_varying.type)); + *r_message = vformat(RTR("Varying with '%s' data type may only be assigned in the '%s' function."), get_datatype_name(p_varying.type), "fragment"); return false; } p_varying.stage = ShaderNode::Varying::STAGE_VERTEX; @@ -5070,17 +5071,15 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT; } break; - case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: case ShaderNode::Varying::STAGE_VERTEX: if (current_function == varying_function_names.fragment) { - *r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'."); + *r_message = vformat(RTR("Varyings which assigned in '%s' function may not be reassigned in '%s' or '%s'."), "vertex", "fragment", "light"); return false; } break; - case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT: case ShaderNode::Varying::STAGE_FRAGMENT: if (current_function == varying_function_names.vertex) { - *r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'."); + *r_message = vformat(RTR("Varyings which assigned in '%s' function may not be reassigned in '%s' or '%s'."), "fragment", "vertex", "light"); return false; } break; @@ -6038,15 +6037,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons error = true; } break; - case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: - [[fallthrough]]; case ShaderNode::Varying::STAGE_VERTEX: if (is_out_arg && current_function != varying_function_names.vertex) { // inout/out error = true; } break; - case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT: - [[fallthrough]]; case ShaderNode::Varying::STAGE_FRAGMENT: if (!is_out_arg) { if (current_function != varying_function_names.fragment && current_function != varying_function_names.light) { @@ -6246,37 +6241,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } } else { - switch (var.stage) { - case ShaderNode::Varying::STAGE_UNKNOWN: { - if (var.type < TYPE_INT) { - if (current_function == varying_function_names.vertex) { - _set_error(vformat(RTR("Varying with '%s' data type may only be used in the 'fragment' function."), get_datatype_name(var.type))); - } else { - _set_error(vformat(RTR("Varying '%s' must be assigned in the 'fragment' function first."), identifier)); - } - return nullptr; - } - } break; - case ShaderNode::Varying::STAGE_VERTEX: - if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) { - var.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT; - } - break; - case ShaderNode::Varying::STAGE_FRAGMENT: - if (current_function == varying_function_names.light) { - var.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT; - } - break; - default: - break; + if (var.stage == ShaderNode::Varying::STAGE_UNKNOWN && var.type < TYPE_INT) { + if (current_function == varying_function_names.vertex) { + _set_error(vformat(RTR("Varying with '%s' data type may only be used in the '%s' function."), get_datatype_name(var.type), "fragment")); + } else { + _set_error(vformat(RTR("Varying '%s' must be assigned in the '%s' function first."), identifier, "fragment")); + } + return nullptr; } } - - if ((var.stage != ShaderNode::Varying::STAGE_FRAGMENT && var.stage != ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT) && var.type < TYPE_FLOAT && var.interpolation != INTERPOLATION_FLAT) { - _set_tkpos(var.tkpos); - _set_error(RTR("Varying with integer data type must be declared with `flat` interpolation qualifier.")); - return nullptr; - } } if (ident_type == IDENTIFIER_FUNCTION) { @@ -10697,6 +10670,14 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f tk = _get_token(); } + for (const KeyValue &kv : shader->varyings) { + if (kv.value.stage != ShaderNode::Varying::STAGE_FRAGMENT && (kv.value.type > TYPE_BVEC4 && kv.value.type < TYPE_FLOAT) && kv.value.interpolation != INTERPOLATION_FLAT) { + _set_tkpos(kv.value.tkpos); + _set_error(vformat(RTR("Varying with integer data type must be declared with `%s` interpolation qualifier."), "flat")); + return ERR_PARSE_ERROR; + } + } + #ifdef DEBUG_ENABLED if (check_device_limit_warnings && uniform_buffer_exceeded_line != -1) { _add_warning(ShaderWarning::DEVICE_LIMIT_EXCEEDED, uniform_buffer_exceeded_line, RTR("uniform buffer"), { uniform_buffer_size, max_uniform_buffer_size }); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 4e7642a7818c..61a3f1fa0281 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -620,10 +620,8 @@ class ShaderLanguage { struct Varying { enum Stage { STAGE_UNKNOWN, - STAGE_VERTEX, // transition stage to STAGE_VERTEX_TO_FRAGMENT_LIGHT, emits warning if it's not used - STAGE_FRAGMENT, // transition stage to STAGE_FRAGMENT_TO_LIGHT, emits warning if it's not used - STAGE_VERTEX_TO_FRAGMENT_LIGHT, - STAGE_FRAGMENT_TO_LIGHT, + STAGE_VERTEX, + STAGE_FRAGMENT, }; Stage stage = STAGE_UNKNOWN; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 5e0a1daf2e9a..16e05c73d3c3 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1868,6 +1868,8 @@ int RenderingServer::global_shader_uniform_type_get_shader_datatype(GlobalShader return ShaderLanguage::TYPE_SAMPLER3D; case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: return ShaderLanguage::TYPE_SAMPLERCUBE; + case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: + return ShaderLanguage::TYPE_SAMPLEREXT; default: return ShaderLanguage::TYPE_MAX; // Invalid or not found. } diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index 06a38f234b02..dbf348ba9620 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -1341,8 +1341,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE); // Force Drop doesn't get triggered by mouse Buttons other than LMB. - SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); - SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::RIGHT, MouseButtonMask::NONE, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::MIDDLE, MouseButtonMask::MIDDLE, Key::NONE); + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::NONE, Key::NONE); CHECK(root->gui_is_dragging()); // Force Drop with LMB-Down. @@ -1351,6 +1351,15 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { CHECK(root->gui_is_drag_successful()); SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + + node_a->force_drag(SNAME("Drag Data"), nullptr); + CHECK(root->gui_is_dragging()); + + // Cancel with RMB. + SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); + CHECK_FALSE(root->gui_is_dragging()); + CHECK_FALSE(root->gui_is_drag_successful()); + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::RIGHT, MouseButtonMask::NONE, Key::NONE); } } diff --git a/thirdparty/README.md b/thirdparty/README.md index 258a12b840eb..57701e94ee38 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -577,7 +577,7 @@ File extracted from upstream source: ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls -- Version: 3.6.1 (71c569d44bf3a8bd53d874c81ee8ac644dd6e9e3, 2024) +- Version: 3.6.2 (107ea89daaefb9867ea9121002fbbdf926780e98, 2024) - License: Apache 2.0 File extracted from upstream release tarball: diff --git a/thirdparty/mbedtls/include/mbedtls/build_info.h b/thirdparty/mbedtls/include/mbedtls/build_info.h index 8242ec68281b..d91d2964b6ae 100644 --- a/thirdparty/mbedtls/include/mbedtls/build_info.h +++ b/thirdparty/mbedtls/include/mbedtls/build_info.h @@ -26,16 +26,16 @@ */ #define MBEDTLS_VERSION_MAJOR 3 #define MBEDTLS_VERSION_MINOR 6 -#define MBEDTLS_VERSION_PATCH 1 +#define MBEDTLS_VERSION_PATCH 2 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x03060100 -#define MBEDTLS_VERSION_STRING "3.6.1" -#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 3.6.1" +#define MBEDTLS_VERSION_NUMBER 0x03060200 +#define MBEDTLS_VERSION_STRING "3.6.2" +#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 3.6.2" /* Macros for build-time platform detection */ diff --git a/thirdparty/mbedtls/library/pkwrite.c b/thirdparty/mbedtls/library/pkwrite.c index 5e009c565ea4..2a698448bee6 100644 --- a/thirdparty/mbedtls/library/pkwrite.c +++ b/thirdparty/mbedtls/library/pkwrite.c @@ -65,17 +65,21 @@ static int pk_write_rsa_der(unsigned char **p, unsigned char *buf, #if defined(MBEDTLS_USE_PSA_CRYPTO) if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_OPAQUE) { uint8_t tmp[PSA_EXPORT_KEY_PAIR_MAX_SIZE]; - size_t len = 0, tmp_len = 0; + size_t tmp_len = 0; if (psa_export_key(pk->priv_id, tmp, sizeof(tmp), &tmp_len) != PSA_SUCCESS) { return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } + /* Ensure there's enough space in the provided buffer before copying data into it. */ + if (tmp_len > (size_t) (*p - buf)) { + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } *p -= tmp_len; memcpy(*p, tmp, tmp_len); - len += tmp_len; mbedtls_platform_zeroize(tmp, sizeof(tmp)); - return (int) len; + return (int) tmp_len; } #endif /* MBEDTLS_USE_PSA_CRYPTO */ return mbedtls_rsa_write_key(mbedtls_pk_rsa(*pk), buf, p); @@ -125,6 +129,10 @@ static int pk_write_ec_pubkey(unsigned char **p, unsigned char *start, if (psa_export_public_key(pk->priv_id, buf, sizeof(buf), &len) != PSA_SUCCESS) { return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } + /* Ensure there's enough space in the provided buffer before copying data into it. */ + if (len > (size_t) (*p - start)) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } *p -= len; memcpy(*p, buf, len); return (int) len; diff --git a/thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml b/thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml new file mode 100644 index 000000000000..cc3271dca4d6 --- /dev/null +++ b/thirdparty/wayland-protocols/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml @@ -0,0 +1,200 @@ + + + + + Copyright © 2015-2016 Red Hat Inc. + + 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 (including the next + paragraph) 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. + + + + This protocol specifies a way for making it possible to reference a surface + of a different client. With such a reference, a client can, by using the + interfaces provided by this protocol, manipulate the relationship between + its own surfaces and the surface of some other client. For example, stack + some of its own surface above the other clients surface. + + In order for a client A to get a reference of a surface of client B, client + B must first export its surface using xdg_exporter.export_toplevel. Upon + doing this, client B will receive a handle (a unique string) that it may + share with client A in some way (for example D-Bus). After client A has + received the handle from client B, it may use xdg_importer.import_toplevel + to create a reference to the surface client B just exported. See the + corresponding requests for details. + + A possible use case for this is out-of-process dialogs. For example when a + sandboxed client without file system access needs the user to select a file + on the file system, given sandbox environment support, it can export its + surface, passing the exported surface handle to an unsandboxed process that + can show a file browser dialog and stack it above the sandboxed client's + surface. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + A global interface used for exporting surfaces that can later be imported + using xdg_importer. + + + + + Notify the compositor that the xdg_exporter object will no longer be + used. + + + + + + These errors can be emitted in response to invalid xdg_exporter + requests. + + + + + + + The export_toplevel request exports the passed surface so that it can later be + imported via xdg_importer. When called, a new xdg_exported object will + be created and xdg_exported.handle will be sent immediately. See the + corresponding interface and event for details. + + A surface may be exported multiple times, and each exported handle may + be used to create an xdg_imported multiple times. Only xdg_toplevel + equivalent surfaces may be exported, otherwise an invalid_surface + protocol error is sent. + + + + + + + + + A global interface used for importing surfaces exported by xdg_exporter. + With this interface, a client can create a reference to a surface of + another client. + + + + + Notify the compositor that the xdg_importer object will no longer be + used. + + + + + + The import_toplevel request imports a surface from any client given a handle + retrieved by exporting said surface using xdg_exporter.export_toplevel. + When called, a new xdg_imported object will be created. This new object + represents the imported surface, and the importing client can + manipulate its relationship using it. See xdg_imported for details. + + + + + + + + + An xdg_exported object represents an exported reference to a surface. The + exported surface may be referenced as long as the xdg_exported object not + destroyed. Destroying the xdg_exported invalidates any relationship the + importer may have established using xdg_imported. + + + + + Revoke the previously exported surface. This invalidates any + relationship the importer may have set up using the xdg_imported created + given the handle sent via xdg_exported.handle. + + + + + + The handle event contains the unique handle of this exported surface + reference. It may be shared with any client, which then can use it to + import the surface by calling xdg_importer.import_toplevel. A handle + may be used to import the surface multiple times. + + + + + + + + An xdg_imported object represents an imported reference to surface exported + by some client. A client can use this interface to manipulate + relationships between its own surfaces and the imported surface. + + + + + These errors can be emitted in response to invalid xdg_imported + requests. + + + + + + + Notify the compositor that it will no longer use the xdg_imported + object. Any relationship that may have been set up will at this point + be invalidated. + + + + + + Set the imported surface as the parent of some surface of the client. + The passed surface must be an xdg_toplevel equivalent, otherwise an + invalid_surface protocol error is sent. Calling this function sets up + a surface to surface relation with the same stacking and positioning + semantics as xdg_toplevel.set_parent. + + + + + + + The imported surface handle has been destroyed and any relationship set + up has been invalidated. This may happen for various reasons, for + example if the exported surface or the exported surface handle has been + destroyed, if the handle used for importing was invalid. + + + + +