[3.5] Update NavigationServer backport

Backports features and bugfixes from current Godot 4.0 to 3.5 and brings functions and codebase of both version largely in sync to make tutorials more compatible and future backports easier.
This commit is contained in:
smix8 2022-06-13 15:51:23 +02:00
parent 0d2be435ea
commit 8bd7c6188b
60 changed files with 1859 additions and 749 deletions

View file

@ -592,8 +592,10 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_RENDER); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_RENDER);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_PHYSICS); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_PHYSICS);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_NAVIGATION);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_NAVIGATION);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FILE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FILE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_DIR); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_DIR);

View file

@ -67,8 +67,10 @@ enum PropertyHint {
PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
PROPERTY_HINT_LAYERS_2D_RENDER, PROPERTY_HINT_LAYERS_2D_RENDER,
PROPERTY_HINT_LAYERS_2D_PHYSICS, PROPERTY_HINT_LAYERS_2D_PHYSICS,
PROPERTY_HINT_LAYERS_2D_NAVIGATION,
PROPERTY_HINT_LAYERS_3D_RENDER, PROPERTY_HINT_LAYERS_3D_RENDER,
PROPERTY_HINT_LAYERS_3D_PHYSICS, PROPERTY_HINT_LAYERS_3D_PHYSICS,
PROPERTY_HINT_LAYERS_3D_NAVIGATION,
PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
PROPERTY_HINT_DIR, ///< a directory path must be passed PROPERTY_HINT_DIR, ///< a directory path must be passed
PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"

View file

@ -1421,7 +1421,7 @@
Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string. Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string.
The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code]. The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code].
</constant> </constant>
<constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="37" enum="PropertyHint"> <constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="39" enum="PropertyHint">
Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code].
Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values. Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values.
</constant> </constant>
@ -1443,40 +1443,46 @@
<constant name="PROPERTY_HINT_LAYERS_2D_PHYSICS" value="10" enum="PropertyHint"> <constant name="PROPERTY_HINT_LAYERS_2D_PHYSICS" value="10" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 2D physics layers. Hints that an integer property is a bitmask using the optionally named 2D physics layers.
</constant> </constant>
<constant name="PROPERTY_HINT_LAYERS_3D_RENDER" value="11" enum="PropertyHint"> <constant name="PROPERTY_HINT_LAYERS_2D_NAVIGATION" value="11" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 2D navigation layers.
</constant>
<constant name="PROPERTY_HINT_LAYERS_3D_RENDER" value="12" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 3D render layers. Hints that an integer property is a bitmask using the optionally named 3D render layers.
</constant> </constant>
<constant name="PROPERTY_HINT_LAYERS_3D_PHYSICS" value="12" enum="PropertyHint"> <constant name="PROPERTY_HINT_LAYERS_3D_PHYSICS" value="13" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 3D physics layers. Hints that an integer property is a bitmask using the optionally named 3D physics layers.
</constant> </constant>
<constant name="PROPERTY_HINT_FILE" value="13" enum="PropertyHint"> <constant name="PROPERTY_HINT_LAYERS_3D_NAVIGATION" value="14" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 2D navigation layers.
</constant>
<constant name="PROPERTY_HINT_FILE" value="15" enum="PropertyHint">
Hints that a string property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. Hints that a string property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code].
</constant> </constant>
<constant name="PROPERTY_HINT_DIR" value="14" enum="PropertyHint"> <constant name="PROPERTY_HINT_DIR" value="16" enum="PropertyHint">
Hints that a string property is a path to a directory. Editing it will show a file dialog for picking the path. Hints that a string property is a path to a directory. Editing it will show a file dialog for picking the path.
</constant> </constant>
<constant name="PROPERTY_HINT_GLOBAL_FILE" value="15" enum="PropertyHint"> <constant name="PROPERTY_HINT_GLOBAL_FILE" value="17" enum="PropertyHint">
Hints that a string property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. Hints that a string property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code].
</constant> </constant>
<constant name="PROPERTY_HINT_GLOBAL_DIR" value="16" enum="PropertyHint"> <constant name="PROPERTY_HINT_GLOBAL_DIR" value="18" enum="PropertyHint">
Hints that a string property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path. Hints that a string property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path.
</constant> </constant>
<constant name="PROPERTY_HINT_RESOURCE_TYPE" value="17" enum="PropertyHint"> <constant name="PROPERTY_HINT_RESOURCE_TYPE" value="19" enum="PropertyHint">
Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture"[/code]). Editing it will show a popup menu of valid resource types to instantiate. Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture"[/code]). Editing it will show a popup menu of valid resource types to instantiate.
</constant> </constant>
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="18" enum="PropertyHint"> <constant name="PROPERTY_HINT_MULTILINE_TEXT" value="20" enum="PropertyHint">
Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed. Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed.
</constant> </constant>
<constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="19" enum="PropertyHint"> <constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="21" enum="PropertyHint">
Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use. Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use.
</constant> </constant>
<constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="20" enum="PropertyHint"> <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="22" enum="PropertyHint">
Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited. Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited.
</constant> </constant>
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="21" enum="PropertyHint"> <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="23" enum="PropertyHint">
Hints that an image is compressed using lossy compression. Hints that an image is compressed using lossy compression.
</constant> </constant>
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="22" enum="PropertyHint"> <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="24" enum="PropertyHint">
Hints that an image is compressed using lossless compression. Hints that an image is compressed using lossless compression.
</constant> </constant>
<constant name="PROPERTY_USAGE_STORAGE" value="1" enum="PropertyUsageFlags"> <constant name="PROPERTY_USAGE_STORAGE" value="1" enum="PropertyUsageFlags">

View file

@ -43,7 +43,7 @@
<method name="get_rid" qualifiers="const"> <method name="get_rid" qualifiers="const">
<return type="RID" /> <return type="RID" />
<description> <description>
Returns the object's [RID]. Returns the [RID] of the navigation map on the [NavigationServer].
</description> </description>
</method> </method>
<method name="get_simple_path" qualifiers="const"> <method name="get_simple_path" qualifiers="const">
@ -57,19 +57,30 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="cell_height" type="float" setter="set_cell_height" getter="get_cell_height" default="0.2"> <member name="cell_height" type="float" setter="set_cell_height" getter="get_cell_height" default="0.25">
The cell height to use for fields. The cell height to use for fields.
</member> </member>
<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="0.3"> <member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="0.25">
The XZ plane cell size to use for fields. The XZ plane cell size to use for fields.
</member> </member>
<member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="5.0"> <member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="0.25">
This value is used to detect the near edges to connect compatible regions. This value is used to detect the near edges to connect compatible regions.
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the navigation can use on a [method Navigation.get_simple_path] path query.
</member>
<member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector" default="Vector3( 0, 1, 0 )"> <member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector" default="Vector3( 0, 1, 0 )">
Defines which direction is up. By default, this is [code](0, 1, 0)[/code], which is the world's "up" direction. Defines which direction is up. By default, this is [code](0, 1, 0)[/code], which is the world's "up" direction.
</member> </member>
</members> </members>
<signals>
<signal name="map_changed">
<argument index="0" name="map" type="RID" />
<description>
Emitted when a navigation map is updated, when a region moves or is modified.
</description>
</signal>
</signals>
<constants> <constants>
</constants> </constants>
</class> </class>

View file

@ -47,6 +47,9 @@
<member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="1.0"> <member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="1.0">
This value is used to detect the near edges to connect compatible regions. This value is used to detect the near edges to connect compatible regions.
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the navigation can use on a [method Navigation2D.get_simple_path] path query.
</member>
</members> </members>
<constants> <constants>
</constants> </constants>

View file

@ -7,7 +7,8 @@
Navigation2DServer is the server responsible for all 2D navigation. It handles several objects, namely maps, regions and agents. Navigation2DServer is the server responsible for all 2D navigation. It handles several objects, namely maps, regions and agents.
Maps are made up of regions, which are made of navigation polygons. Together, they define the navigable areas in the 2D world. Maps are made up of regions, which are made of navigation polygons. Together, they define the navigable areas in the 2D world.
[b]Note:[/b] Most NavigationServer changes take effect after the next physics frame and not immediately. This includes all changes made to maps, regions or agents by navigation related Nodes in the SceneTree or made through scripts. [b]Note:[/b] Most NavigationServer changes take effect after the next physics frame and not immediately. This includes all changes made to maps, regions or agents by navigation related Nodes in the SceneTree or made through scripts.
For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [member Navigation.edge_connection_margin] to the respective other edge's vertex. For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than navigation map [code]edge_connection_margin[/code] to the respective other edge's vertex.
You may assign navigation layers to regions with [method Navigation2DServer.region_set_navigation_layers], which then can be checked upon when requesting a path with [method Navigation2DServer.map_get_path]. This allows allowing or forbidding some areas to 2D objects.
To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity. To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity.
[b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine. [b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine.
This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying. This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying.
@ -138,6 +139,13 @@
Returns all navigation agents [RID]s that are currently assigned to the requested navigation [code]map[/code]. Returns all navigation agents [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description> </description>
</method> </method>
<method name="map_get_cell_height" qualifiers="const">
<return type="float" />
<argument index="0" name="map" type="RID" />
<description>
Returns the map cell height. [b]Note:[/b] Currently not implemented.
</description>
</method>
<method name="map_get_cell_size" qualifiers="const"> <method name="map_get_cell_size" qualifiers="const">
<return type="float" /> <return type="float" />
<argument index="0" name="map" type="RID" /> <argument index="0" name="map" type="RID" />
@ -174,8 +182,9 @@
<argument index="1" name="origin" type="Vector2" /> <argument index="1" name="origin" type="Vector2" />
<argument index="2" name="destination" type="Vector2" /> <argument index="2" name="destination" type="Vector2" />
<argument index="3" name="optimize" type="bool" /> <argument index="3" name="optimize" type="bool" />
<argument index="4" name="navigation_layers" type="int" default="1" />
<description> <description>
Returns the navigation path to reach the destination from the origin. Returns the navigation path to reach the destination from the origin. [code]navigation_layers[/code] is a bitmask of all region layers that are allowed to be in the path.
</description> </description>
</method> </method>
<method name="map_get_regions" qualifiers="const"> <method name="map_get_regions" qualifiers="const">
@ -200,6 +209,14 @@
Sets the map active. Sets the map active.
</description> </description>
</method> </method>
<method name="map_set_cell_height" qualifiers="const">
<return type="void" />
<argument index="0" name="map" type="RID" />
<argument index="1" name="cell_height" type="float" />
<description>
Set the map cell height used to weld the navigation mesh polygons. [b]Note:[/b] Currently not implemented.
</description>
</method>
<method name="map_set_cell_size" qualifiers="const"> <method name="map_set_cell_size" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="map" type="RID" /> <argument index="0" name="map" type="RID" />
@ -222,6 +239,36 @@
Creates a new region. Creates a new region.
</description> </description>
</method> </method>
<method name="region_get_connection_pathway_end" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="connection" type="int" />
<description>
Returns the ending point of a connection door. [code]connection[/code] is an index between 0 and the return value of [method region_get_connections_count].
</description>
</method>
<method name="region_get_connection_pathway_start" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="connection" type="int" />
<description>
Returns the starting point of a connection door. [code]connection[/code] is an index between 0 and the return value of [method region_get_connections_count].
</description>
</method>
<method name="region_get_connections_count" qualifiers="const">
<return type="int" />
<argument index="0" name="region" type="RID" />
<description>
Returns how many connections this [code]region[/code] has with other regions in the map.
</description>
</method>
<method name="region_get_enter_cost" qualifiers="const">
<return type="float" />
<argument index="0" name="region" type="RID" />
<description>
Returns the [code]enter_cost[/code] of this [code]region[/code].
</description>
</method>
<method name="region_get_map" qualifiers="const"> <method name="region_get_map" qualifiers="const">
<return type="RID" /> <return type="RID" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -229,6 +276,28 @@
Returns the navigation map [RID] the requested [code]region[/code] is currently assigned to. Returns the navigation map [RID] the requested [code]region[/code] is currently assigned to.
</description> </description>
</method> </method>
<method name="region_get_navigation_layers" qualifiers="const">
<return type="int" />
<argument index="0" name="region" type="RID" />
<description>
Returns the region's navigation layers.
</description>
</method>
<method name="region_get_travel_cost" qualifiers="const">
<return type="float" />
<argument index="0" name="region" type="RID" />
<description>
Returns the [code]travel_cost[/code] of this [code]region[/code].
</description>
</method>
<method name="region_set_enter_cost" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="enter_cost" type="float" />
<description>
Sets the [code]enter_cost[/code] for this [code]region[/code].
</description>
</method>
<method name="region_set_map" qualifiers="const"> <method name="region_set_map" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -237,6 +306,14 @@
Sets the map for the region. Sets the map for the region.
</description> </description>
</method> </method>
<method name="region_set_navigation_layers" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="navigation_layers" type="int" />
<description>
Set the region's navigation layers. This allows selecting regions from a path request (when using [method Navigation2DServer.map_get_path]).
</description>
</method>
<method name="region_set_navpoly" qualifiers="const"> <method name="region_set_navpoly" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -253,7 +330,23 @@
Sets the global transformation for the region. Sets the global transformation for the region.
</description> </description>
</method> </method>
<method name="region_set_travel_cost" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="travel_cost" type="float" />
<description>
Sets the [code]travel_cost[/code] for this [code]region[/code].
</description>
</method>
</methods> </methods>
<signals>
<signal name="map_changed">
<argument index="0" name="map" type="RID" />
<description>
Emitted when a navigation map is updated, when a region moves or is modified.
</description>
</signal>
</signals>
<constants> <constants>
</constants> </constants>
</class> </class>

View file

@ -4,7 +4,7 @@
3D agent used in navigation for collision avoidance. 3D agent used in navigation for collision avoidance.
</brief_description> </brief_description>
<description> <description>
3D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation] node, or using [method set_navigation]. [NavigationAgent] is physics safe. 3D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World] navigation map. If this node is a child of a [Navigation] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent] is physics safe.
[b]Note:[/b] After [method set_target_location] is used it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. [b]Note:[/b] After [method set_target_location] is used it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
</description> </description>
<tutorials> <tutorials>
@ -114,6 +114,9 @@
<member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="10.0"> <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="10.0">
The maximum speed that an agent can move. The maximum speed that an agent can move.
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the [NavigationAgent] belongs to. On path requests the agent will ignore navmeshes without at least one matching layer.
</member>
<member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="50.0"> <member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="50.0">
The distance to search for other agents. The distance to search for other agents.
</member> </member>

View file

@ -4,7 +4,7 @@
2D agent used in navigation for collision avoidance. 2D agent used in navigation for collision avoidance.
</brief_description> </brief_description>
<description> <description>
2D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationAgent2D] is physics safe. 2D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World2D] navigation map. If this node is a child of a [Navigation2D] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent2D] is physics safe.
[b]Note:[/b] After [method set_target_location] is used it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. [b]Note:[/b] After [method set_target_location] is used it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
</description> </description>
<tutorials> <tutorials>
@ -108,6 +108,9 @@
<member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="200.0"> <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="200.0">
The maximum speed that an agent can move. The maximum speed that an agent can move.
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the [NavigationAgent2D] belongs to. On path requests the agent will ignore navmeshes without at least one matching layer.
</member>
<member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="500.0"> <member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="500.0">
The distance to search for other agents. The distance to search for other agents.
</member> </member>

View file

@ -98,7 +98,7 @@
<member name="detail_sample_distance" type="float" setter="set_detail_sample_distance" getter="get_detail_sample_distance" default="6.0"> <member name="detail_sample_distance" type="float" setter="set_detail_sample_distance" getter="get_detail_sample_distance" default="6.0">
The sampling distance to use when generating the detail mesh, in cell unit. The sampling distance to use when generating the detail mesh, in cell unit.
</member> </member>
<member name="detail_sample_max_error" type="float" setter="set_detail_sample_max_error" getter="get_detail_sample_max_error" default="5.0"> <member name="detail_sample_max_error" type="float" setter="set_detail_sample_max_error" getter="get_detail_sample_max_error" default="1.0">
The maximum distance the detail mesh surface should deviate from heightfield, in cell unit. The maximum distance the detail mesh surface should deviate from heightfield, in cell unit.
</member> </member>
<member name="edge_max_error" type="float" setter="set_edge_max_error" getter="get_edge_max_error" default="1.3"> <member name="edge_max_error" type="float" setter="set_edge_max_error" getter="get_edge_max_error" default="1.3">

View file

@ -4,7 +4,13 @@
An instance of a [NavigationMesh]. An instance of a [NavigationMesh].
</brief_description> </brief_description>
<description> <description>
An instance of a [NavigationMesh]. It tells the [Navigation] node what can be navigated and what cannot, based on the [NavigationMesh] resource. This should be a child of a [Navigation] node. An instance of a [NavigationMesh]. It tells the [Navigation] node what can be navigated and what cannot, based on the [NavigationMesh] resource.
By default this node will register to the default [World] navigation map. If this node is a child of a [Navigation] node it will register to the navigation map of the navigation node.
Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method NavigationServer.map_set_edge_connection_margin].
[b]Note:[/b] Overlapping two regions' navmeshes is not enough for connecting two regions. They must share a similar edge.
The cost of entering this region from another region can be controlled with the [member enter_cost] value.
[b]Note[/b]: This value is not added to the path cost when the start position is already inside this region.
The cost of traveling distances inside this region can be controlled with the [member travel_cost] multiplier.
</description> </description>
<tutorials> <tutorials>
</tutorials> </tutorials>
@ -27,9 +33,18 @@
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationMeshInstance] is enabled or disabled. Determines if the [NavigationMeshInstance] is enabled or disabled.
</member> </member>
<member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0">
When pathfinding enters this regions navmesh from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path.
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the [NavigationMesh] belongs to. On path requests with [method NavigationServer.map_get_path] navmeshes without matching layers will be ignored and the navigation map will only proximity merge different navmeshes with matching layers.
</member>
<member name="navmesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh"> <member name="navmesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh">
The [NavigationMesh] resource to use. The [NavigationMesh] resource to use.
</member> </member>
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
When pathfinding moves inside this regions navmesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path.
</member>
</members> </members>
<signals> <signals>
<signal name="bake_finished"> <signal name="bake_finished">

View file

@ -1,8 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationPolygonInstance" inherits="Node2D" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <class name="NavigationPolygonInstance" inherits="Node2D" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description> <brief_description>
A region of the 2D navigation map.
</brief_description> </brief_description>
<description> <description>
A region of the navigation map. It tells the [Navigation2DServer] what can be navigated and what cannot, based on its [NavigationPolygon] resource.
By default this node will register to the default [World2D] navigation map. If this node is a child of a [Navigation2D] node it will register to the navigation map of the navigation node.
Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method Navigation2DServer.map_set_edge_connection_margin].
[b]Note:[/b] Overlapping two regions' polygons is not enough for connecting two regions. They must share a similar edge.
The pathfinding cost of entering this region from another region can be controlled with the [member enter_cost] value.
[b]Note[/b]: This value is not added to the path cost when the start position is already inside this region.
The pathfinding cost of traveling distances inside this region can be controlled with the [member travel_cost] multiplier.
</description> </description>
<tutorials> <tutorials>
</tutorials> </tutorials>
@ -16,8 +24,19 @@
</methods> </methods>
<members> <members>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationPolygonInstance] is enabled or disabled.
</member>
<member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0">
When pathfinding enters this regions navmesh from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path.
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
A bitfield determining all navigation map layers the [NavigationPolygon] belongs to. On path requests with [method Navigation2DServer.map_get_path] navmeshes without matching layers will be ignored and the navigation map will only proximity merge different navmeshes with matching layers.
</member> </member>
<member name="navpoly" type="NavigationPolygon" setter="set_navigation_polygon" getter="get_navigation_polygon"> <member name="navpoly" type="NavigationPolygon" setter="set_navigation_polygon" getter="get_navigation_polygon">
The [NavigationPolygon] resource to use.
</member>
<member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0">
When pathfinding moves inside this regions navmesh the traveled distances are multiplied with [code]travel_cost[/code] for determining the shortest path.
</member> </member>
</members> </members>
<constants> <constants>

View file

@ -199,8 +199,9 @@
<argument index="1" name="origin" type="Vector3" /> <argument index="1" name="origin" type="Vector3" />
<argument index="2" name="destination" type="Vector3" /> <argument index="2" name="destination" type="Vector3" />
<argument index="3" name="optimize" type="bool" /> <argument index="3" name="optimize" type="bool" />
<argument index="4" name="navigation_layers" type="int" default="1" />
<description> <description>
Returns the navigation path to reach the destination from the origin. Returns the navigation path to reach the destination from the origin. [code]navigation_layers[/code] is a bitmask of all region layers that are allowed to be in the path.
</description> </description>
</method> </method>
<method name="map_get_regions" qualifiers="const"> <method name="map_get_regions" qualifiers="const">
@ -287,6 +288,36 @@
Creates a new region. Creates a new region.
</description> </description>
</method> </method>
<method name="region_get_connection_pathway_end" qualifiers="const">
<return type="Vector3" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="connection" type="int" />
<description>
Returns the ending point of a connection door. [code]connection[/code] is an index between 0 and the return value of [method region_get_connections_count].
</description>
</method>
<method name="region_get_connection_pathway_start" qualifiers="const">
<return type="Vector3" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="connection" type="int" />
<description>
Returns the starting point of a connection door. [code]connection[/code] is an index between 0 and the return value of [method region_get_connections_count].
</description>
</method>
<method name="region_get_connections_count" qualifiers="const">
<return type="int" />
<argument index="0" name="region" type="RID" />
<description>
Returns how many connections this [code]region[/code] has with other regions in the map.
</description>
</method>
<method name="region_get_enter_cost" qualifiers="const">
<return type="float" />
<argument index="0" name="region" type="RID" />
<description>
Returns the [code]enter_cost[/code] of this [code]region[/code].
</description>
</method>
<method name="region_get_map" qualifiers="const"> <method name="region_get_map" qualifiers="const">
<return type="RID" /> <return type="RID" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -294,6 +325,28 @@
Returns the navigation map [RID] the requested [code]region[/code] is currently assigned to. Returns the navigation map [RID] the requested [code]region[/code] is currently assigned to.
</description> </description>
</method> </method>
<method name="region_get_navigation_layers" qualifiers="const">
<return type="int" />
<argument index="0" name="region" type="RID" />
<description>
Returns the region's navigation layers.
</description>
</method>
<method name="region_get_travel_cost" qualifiers="const">
<return type="float" />
<argument index="0" name="region" type="RID" />
<description>
Returns the [code]travel_cost[/code] of this [code]region[/code].
</description>
</method>
<method name="region_set_enter_cost" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="enter_cost" type="float" />
<description>
Sets the [code]enter_cost[/code] for this [code]region[/code].
</description>
</method>
<method name="region_set_map" qualifiers="const"> <method name="region_set_map" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -302,6 +355,14 @@
Sets the map for the region. Sets the map for the region.
</description> </description>
</method> </method>
<method name="region_set_navigation_layers" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="navigation_layers" type="int" />
<description>
Set the region's navigation layers. This allows selecting regions from a path request (when using [method NavigationServer.map_get_path]).
</description>
</method>
<method name="region_set_navmesh" qualifiers="const"> <method name="region_set_navmesh" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="region" type="RID" /> <argument index="0" name="region" type="RID" />
@ -318,6 +379,14 @@
Sets the global transformation for the region. Sets the global transformation for the region.
</description> </description>
</method> </method>
<method name="region_set_travel_cost" qualifiers="const">
<return type="void" />
<argument index="0" name="region" type="RID" />
<argument index="1" name="travel_cost" type="float" />
<description>
Sets the [code]travel_cost[/code] for this [code]region[/code].
</description>
</method>
<method name="set_active" qualifiers="const"> <method name="set_active" qualifiers="const">
<return type="void" /> <return type="void" />
<argument index="0" name="active" type="bool" /> <argument index="0" name="active" type="bool" />
@ -326,6 +395,14 @@
</description> </description>
</method> </method>
</methods> </methods>
<signals>
<signal name="map_changed">
<argument index="0" name="map" type="RID" />
<description>
Emitted when a navigation map is updated, when a region moves or is modified.
</description>
</signal>
</signals>
<constants> <constants>
</constants> </constants>
</class> </class>

View file

@ -638,6 +638,102 @@
<member name="input_devices/pointing/ios/touch_delay" type="float" setter="" getter="" default="0.15"> <member name="input_devices/pointing/ios/touch_delay" type="float" setter="" getter="" default="0.15">
Default delay for touch events. This only affects iOS devices. Default delay for touch events. This only affects iOS devices.
</member> </member>
<member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1".
</member>
<member name="layer_names/2d_navigation/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 10. If left empty, the layer will display as "Layer 10".
</member>
<member name="layer_names/2d_navigation/layer_11" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 11. If left empty, the layer will display as "Layer 11".
</member>
<member name="layer_names/2d_navigation/layer_12" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 12. If left empty, the layer will display as "Layer 12".
</member>
<member name="layer_names/2d_navigation/layer_13" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 13. If left empty, the layer will display as "Layer 13".
</member>
<member name="layer_names/2d_navigation/layer_14" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 14. If left empty, the layer will display as "Layer 14".
</member>
<member name="layer_names/2d_navigation/layer_15" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 15. If left empty, the layer will display as "Layer 15".
</member>
<member name="layer_names/2d_navigation/layer_16" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 16. If left empty, the layer will display as "Layer 16".
</member>
<member name="layer_names/2d_navigation/layer_17" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 17. If left empty, the layer will display as "Layer 17".
</member>
<member name="layer_names/2d_navigation/layer_18" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 18. If left empty, the layer will display as "Layer 18".
</member>
<member name="layer_names/2d_navigation/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 19. If left empty, the layer will display as "Layer 19".
</member>
<member name="layer_names/2d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 2. If left empty, the layer will display as "Layer 2".
</member>
<member name="layer_names/2d_navigation/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 20. If left empty, the layer will display as "Layer 20".
</member>
<member name="layer_names/2d_navigation/layer_21" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 21. If left empty, the layer will display as "Layer 21".
</member>
<member name="layer_names/2d_navigation/layer_22" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 22. If left empty, the layer will display as "Layer 22".
</member>
<member name="layer_names/2d_navigation/layer_23" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 23. If left empty, the layer will display as "Layer 23".
</member>
<member name="layer_names/2d_navigation/layer_24" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 24. If left empty, the layer will display as "Layer 24".
</member>
<member name="layer_names/2d_navigation/layer_25" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 25. If left empty, the layer will display as "Layer 25".
</member>
<member name="layer_names/2d_navigation/layer_26" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 26. If left empty, the layer will display as "Layer 26".
</member>
<member name="layer_names/2d_navigation/layer_27" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 27. If left empty, the layer will display as "Layer 27".
</member>
<member name="layer_names/2d_navigation/layer_28" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 28. If left empty, the layer will display as "Layer 28".
</member>
<member name="layer_names/2d_navigation/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 29. If left empty, the layer will display as "Layer 29".
</member>
<member name="layer_names/2d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 3. If left empty, the layer will display as "Layer 3".
</member>
<member name="layer_names/2d_navigation/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 30. If left empty, the layer will display as "Layer 30".
</member>
<member name="layer_names/2d_navigation/layer_31" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 31. If left empty, the layer will display as "Layer 31".
</member>
<member name="layer_names/2d_navigation/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 32. If left empty, the layer will display as "Layer 32".
</member>
<member name="layer_names/2d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4".
</member>
<member name="layer_names/2d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 5. If left empty, the layer will display as "Layer 5".
</member>
<member name="layer_names/2d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 6. If left empty, the layer will display as "Layer 6".
</member>
<member name="layer_names/2d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 7. If left empty, the layer will display as "Layer 7".
</member>
<member name="layer_names/2d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 8. If left empty, the layer will display as "Layer 8".
</member>
<member name="layer_names/2d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;"> <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D physics layer 1. Optional name for the 2D physics layer 1.
</member> </member>
@ -794,6 +890,102 @@
<member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;"> <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D render layer 9. Optional name for the 2D render layer 9.
</member> </member>
<member name="layer_names/3d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 1. If left empty, the layer will display as "Layer 1".
</member>
<member name="layer_names/3d_navigation/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 10. If left empty, the layer will display as "Layer 10".
</member>
<member name="layer_names/3d_navigation/layer_11" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 11. If left empty, the layer will display as "Layer 11".
</member>
<member name="layer_names/3d_navigation/layer_12" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 12. If left empty, the layer will display as "Layer 12".
</member>
<member name="layer_names/3d_navigation/layer_13" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 13. If left empty, the layer will display as "Layer 13".
</member>
<member name="layer_names/3d_navigation/layer_14" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 14. If left empty, the layer will display as "Layer 14".
</member>
<member name="layer_names/3d_navigation/layer_15" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 15. If left empty, the layer will display as "Layer 15".
</member>
<member name="layer_names/3d_navigation/layer_16" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 16. If left empty, the layer will display as "Layer 16".
</member>
<member name="layer_names/3d_navigation/layer_17" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 17. If left empty, the layer will display as "Layer 17".
</member>
<member name="layer_names/3d_navigation/layer_18" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 18. If left empty, the layer will display as "Layer 18".
</member>
<member name="layer_names/3d_navigation/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 19. If left empty, the layer will display as "Layer 19".
</member>
<member name="layer_names/3d_navigation/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 2. If left empty, the layer will display as "Layer 2".
</member>
<member name="layer_names/3d_navigation/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 20. If left empty, the layer will display as "Layer 20".
</member>
<member name="layer_names/3d_navigation/layer_21" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 21. If left empty, the layer will display as "Layer 21".
</member>
<member name="layer_names/3d_navigation/layer_22" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 22. If left empty, the layer will display as "Layer 22".
</member>
<member name="layer_names/3d_navigation/layer_23" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 23. If left empty, the layer will display as "Layer 23".
</member>
<member name="layer_names/3d_navigation/layer_24" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 24. If left empty, the layer will display as "Layer 24".
</member>
<member name="layer_names/3d_navigation/layer_25" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 25. If left empty, the layer will display as "Layer 25".
</member>
<member name="layer_names/3d_navigation/layer_26" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 26. If left empty, the layer will display as "Layer 26".
</member>
<member name="layer_names/3d_navigation/layer_27" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 27. If left empty, the layer will display as "Layer 27".
</member>
<member name="layer_names/3d_navigation/layer_28" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 28. If left empty, the layer will display as "Layer 28".
</member>
<member name="layer_names/3d_navigation/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 29. If left empty, the layer will display as "Layer 29".
</member>
<member name="layer_names/3d_navigation/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 3. If left empty, the layer will display as "Layer 3".
</member>
<member name="layer_names/3d_navigation/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 30. If left empty, the layer will display as "Layer 30".
</member>
<member name="layer_names/3d_navigation/layer_31" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 31. If left empty, the layer will display as "Layer 31".
</member>
<member name="layer_names/3d_navigation/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 32. If left empty, the layer will display as "Layer 32".
</member>
<member name="layer_names/3d_navigation/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4".
</member>
<member name="layer_names/3d_navigation/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 5. If left empty, the layer will display as "Layer 5".
</member>
<member name="layer_names/3d_navigation/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 6. If left empty, the layer will display as "Layer 6".
</member>
<member name="layer_names/3d_navigation/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 7. If left empty, the layer will display as "Layer 7".
</member>
<member name="layer_names/3d_navigation/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 8. If left empty, the layer will display as "Layer 8".
</member>
<member name="layer_names/3d_navigation/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D navigation layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;"> <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D physics layer 1. Optional name for the 3D physics layer 1.
</member> </member>
@ -990,18 +1182,28 @@
The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown. The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown.
[b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code. [b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code.
</member> </member>
<member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1"> <member name="navigation/2d/default_cell_height" type="float" setter="" getter="" default="1.0">
Default cell height for 2D navigation maps. See [method Navigation2DServer.map_set_cell_height].
[b]Note:[/b] Currently not implemented.
</member>
<member name="navigation/2d/default_cell_size" type="float" setter="" getter="" default="1.0">
Default cell size for 2D navigation maps. See [method Navigation2DServer.map_set_cell_size]. Default cell size for 2D navigation maps. See [method Navigation2DServer.map_set_cell_size].
</member> </member>
<member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="1"> <member name="navigation/2d/default_edge_connection_margin" type="float" setter="" getter="" default="1.0">
Default edge connection margin for 2D navigation maps. See [method Navigation2DServer.map_set_edge_connection_margin]. Default edge connection margin for 2D navigation maps. See [method Navigation2DServer.map_set_edge_connection_margin].
</member> </member>
<member name="navigation/3d/default_cell_height" type="float" setter="" getter="" default="0.25">
Default cell height for 3D navigation maps. See [method NavigationServer.map_set_cell_height].
</member>
<member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.25"> <member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.25">
Default cell size for 3D navigation maps. See [method NavigationServer.map_set_cell_size]. Default cell size for 3D navigation maps. See [method NavigationServer.map_set_cell_size].
</member> </member>
<member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="0.25"> <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="0.25">
Default edge connection margin for 3D navigation maps. See [method NavigationServer.map_set_edge_connection_margin]. Default edge connection margin for 3D navigation maps. See [method NavigationServer.map_set_edge_connection_margin].
</member> </member>
<member name="navigation/3d/default_map_up" type="Vector3" setter="" getter="" default="Vector3( 0, 1, 0 )">
Default map up vector for 3D navigation maps. See [method NavigationServer.map_set_up].
</member>
<member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter="" default="2048"> <member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter="" default="2048">
Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
</member> </member>

View file

@ -215,6 +215,9 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
If [code]true[/code], this TileMap bakes a navigation region.
</member>
<member name="cell_clip_uv" type="bool" setter="set_clip_uv" getter="get_clip_uv" default="false"> <member name="cell_clip_uv" type="bool" setter="set_clip_uv" getter="get_clip_uv" default="false">
If [code]true[/code], the cell's UVs will be clipped. If [code]true[/code], the cell's UVs will be clipped.
</member> </member>
@ -266,6 +269,9 @@
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="TileMap.Mode" default="0"> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="TileMap.Mode" default="0">
The TileMap orientation mode. See [enum Mode] for possible values. The TileMap orientation mode. See [enum Mode] for possible values.
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
The navigation layers the TileMap generates its navigation regions in.
</member>
<member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask" default="1"> <member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask" default="1">
The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s). The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s).
</member> </member>

View file

@ -4,7 +4,7 @@
Class that has everything pertaining to a world. Class that has everything pertaining to a world.
</brief_description> </brief_description>
<description> <description>
Class that has everything pertaining to a world. A physics space, a visual scenario and a sound space. Spatial nodes register their resources into the current world. Class that has everything pertaining to a world. A physics space, a visual scenario, a navigation map and a sound space. Spatial nodes register their resources into the current world.
</description> </description>
<tutorials> <tutorials>
<link>$DOCS_URL/tutorials/physics/ray-casting.html</link> <link>$DOCS_URL/tutorials/physics/ray-casting.html</link>

View file

@ -4,7 +4,7 @@
Class that has everything pertaining to a 2D world. Class that has everything pertaining to a 2D world.
</brief_description> </brief_description>
<description> <description>
Class that has everything pertaining to a 2D world. A physics space, a visual scenario and a sound space. 2D nodes register their resources into the current 2D world. Class that has everything pertaining to a 2D world. A physics space, a visual scenario, a navigation map and a sound space. 2D nodes register their resources into the current 2D world.
</description> </description>
<tutorials> <tutorials>
<link>$DOCS_URL/tutorials/physics/ray-casting.html</link> <link>$DOCS_URL/tutorials/physics/ray-casting.html</link>

View file

@ -971,6 +971,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
layer_count = 32; layer_count = 32;
} break; } break;
case LAYER_NAVIGATION_2D: {
basename = "layer_names/2d_navigation";
layer_group_size = 4;
layer_count = 32;
} break;
case LAYER_RENDER_3D: { case LAYER_RENDER_3D: {
basename = "layer_names/3d_render"; basename = "layer_names/3d_render";
layer_group_size = 5; layer_group_size = 5;
@ -982,6 +988,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
layer_group_size = 4; layer_group_size = 4;
layer_count = 32; layer_count = 32;
} break; } break;
case LAYER_NAVIGATION_3D: {
basename = "layer_names/3d_navigation";
layer_group_size = 4;
layer_count = 32;
} break;
} }
Vector<String> names; Vector<String> names;
@ -2734,7 +2746,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
editor->setup(options); editor->setup(options);
add_property_editor(p_path, editor); add_property_editor(p_path, editor);
} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) { } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D; EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;
switch (p_hint) { switch (p_hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER: case PROPERTY_HINT_LAYERS_2D_RENDER:
@ -2743,12 +2755,18 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case PROPERTY_HINT_LAYERS_2D_PHYSICS: case PROPERTY_HINT_LAYERS_2D_PHYSICS:
lt = EditorPropertyLayers::LAYER_PHYSICS_2D; lt = EditorPropertyLayers::LAYER_PHYSICS_2D;
break; break;
case PROPERTY_HINT_LAYERS_2D_NAVIGATION:
lt = EditorPropertyLayers::LAYER_NAVIGATION_2D;
break;
case PROPERTY_HINT_LAYERS_3D_RENDER: case PROPERTY_HINT_LAYERS_3D_RENDER:
lt = EditorPropertyLayers::LAYER_RENDER_3D; lt = EditorPropertyLayers::LAYER_RENDER_3D;
break; break;
case PROPERTY_HINT_LAYERS_3D_PHYSICS: case PROPERTY_HINT_LAYERS_3D_PHYSICS:
lt = EditorPropertyLayers::LAYER_PHYSICS_3D; lt = EditorPropertyLayers::LAYER_PHYSICS_3D;
break; break;
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;
break;
default: { default: {
} //compiler could be smarter here and realize this can't happen } //compiler could be smarter here and realize this can't happen
} }

View file

@ -256,8 +256,10 @@ public:
enum LayerType { enum LayerType {
LAYER_PHYSICS_2D, LAYER_PHYSICS_2D,
LAYER_RENDER_2D, LAYER_RENDER_2D,
LAYER_NAVIGATION_2D,
LAYER_PHYSICS_3D, LAYER_PHYSICS_3D,
LAYER_RENDER_3D, LAYER_RENDER_3D,
LAYER_NAVIGATION_3D,
}; };
private: private:

View file

@ -419,7 +419,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
updating = false; updating = false;
return false; return false;
} else if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER) { } else if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
String basename; String basename;
switch (hint) { switch (hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER: case PROPERTY_HINT_LAYERS_2D_RENDER:
@ -428,12 +428,18 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
case PROPERTY_HINT_LAYERS_2D_PHYSICS: case PROPERTY_HINT_LAYERS_2D_PHYSICS:
basename = "layer_names/2d_physics"; basename = "layer_names/2d_physics";
break; break;
case PROPERTY_HINT_LAYERS_2D_NAVIGATION:
basename = "layer_names/2d_navigation";
break;
case PROPERTY_HINT_LAYERS_3D_RENDER: case PROPERTY_HINT_LAYERS_3D_RENDER:
basename = "layer_names/3d_render"; basename = "layer_names/3d_render";
break; break;
case PROPERTY_HINT_LAYERS_3D_PHYSICS: case PROPERTY_HINT_LAYERS_3D_PHYSICS:
basename = "layer_names/3d_physics"; basename = "layer_names/3d_physics";
break; break;
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
basename = "layer_names/3d_navigation";
break;
} }
checks20gc->show(); checks20gc->show();
@ -1166,7 +1172,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
emit_signal("variant_changed"); emit_signal("variant_changed");
} break; } break;
case Variant::INT: { case Variant::INT: {
if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER) { if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
uint32_t f = v; uint32_t f = v;
if (checks20[p_which]->is_pressed()) { if (checks20[p_which]->is_pressed()) {
f |= (1 << p_which); f |= (1 << p_which);

View file

@ -61,8 +61,10 @@ typedef enum {
GODOT_PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) GODOT_PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
GODOT_PROPERTY_HINT_LAYERS_2D_RENDER, GODOT_PROPERTY_HINT_LAYERS_2D_RENDER,
GODOT_PROPERTY_HINT_LAYERS_2D_PHYSICS, GODOT_PROPERTY_HINT_LAYERS_2D_PHYSICS,
GODOT_PROPERTY_HINT_LAYERS_2D_NAVIGATION,
GODOT_PROPERTY_HINT_LAYERS_3D_RENDER, GODOT_PROPERTY_HINT_LAYERS_3D_RENDER,
GODOT_PROPERTY_HINT_LAYERS_3D_PHYSICS, GODOT_PROPERTY_HINT_LAYERS_3D_PHYSICS,
GODOT_PROPERTY_HINT_LAYERS_3D_NAVIGATION,
GODOT_PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," GODOT_PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
GODOT_PROPERTY_HINT_DIR, ///< a directory path must be passed GODOT_PROPERTY_HINT_DIR, ///< a directory path must be passed
GODOT_PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," GODOT_PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"

View file

@ -4304,6 +4304,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
break; break;
} }
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_NAVIGATION") {
_ADVANCE_AND_CONSUME_NEWLINES;
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected \")\" in the layers 2D navigation hint.");
return;
}
current_export.hint = PROPERTY_HINT_LAYERS_2D_PHYSICS;
break;
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") {
_ADVANCE_AND_CONSUME_NEWLINES; _ADVANCE_AND_CONSUME_NEWLINES;
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
@ -4324,6 +4334,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
break; break;
} }
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_NAVIGATION") {
_ADVANCE_AND_CONSUME_NEWLINES;
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected \")\" in the layers 3D navigation hint.");
return;
}
current_export.hint = PROPERTY_HINT_LAYERS_3D_NAVIGATION;
break;
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) { if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration //enumeration
current_export.hint = PROPERTY_HINT_ENUM; current_export.hint = PROPERTY_HINT_ENUM;

View file

@ -160,6 +160,9 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
If [code]true[/code], this GridMap uses cell navmesh resources to create navigation regions.
</member>
<member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true"> <member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true">
If [code]true[/code], grid items are centered on the X axis. If [code]true[/code], grid items are centered on the X axis.
</member> </member>
@ -190,6 +193,9 @@
<member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library"> <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary]. The assigned [MeshLibrary].
</member> </member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
The navigation layers the GridMap generates its navigation regions in.
</member>
<member name="physics_material" type="PhysicsMaterial" setter="set_physics_material" getter="get_physics_material"> <member name="physics_material" type="PhysicsMaterial" setter="set_physics_material" getter="get_physics_material">
Overrides the default friction and bounce physics properties for the whole [GridMap]. Overrides the default friction and bounce physics properties for the whole [GridMap].
</member> </member>

View file

@ -213,6 +213,24 @@ Array GridMap::get_collision_shapes() const {
return shapes; return shapes;
} }
void GridMap::set_bake_navigation(bool p_bake_navigation) {
bake_navigation = p_bake_navigation;
_recreate_octant_data();
}
bool GridMap::is_baking_navigation() {
return bake_navigation;
}
void GridMap::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
_recreate_octant_data();
}
uint32_t GridMap::get_navigation_layers() {
return navigation_layers;
}
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) { void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
if (!mesh_library.is_null()) { if (!mesh_library.is_null()) {
mesh_library->unregister_owner(this); mesh_library->unregister_owner(this);
@ -421,6 +439,18 @@ void GridMap::_octant_transform(const OctantKey &p_key) {
VS::get_singleton()->instance_set_transform(g.collision_debug_instance, get_global_transform()); VS::get_singleton()->instance_set_transform(g.collision_debug_instance, get_global_transform());
} }
// update transform for NavigationServer regions and navigation debugmesh instances
if (bake_navigation) {
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
if (E->get().region.is_valid()) {
NavigationServer::get_singleton()->region_set_transform(E->get().region, get_global_transform() * E->get().xform);
}
if (E->get().navmesh_debug_instance.is_valid()) {
VS::get_singleton()->instance_set_transform(E->get().navmesh_debug_instance, get_global_transform() * E->get().xform);
}
}
}
for (int i = 0; i < g.multimesh_instances.size(); i++) { for (int i = 0; i < g.multimesh_instances.size(); i++) {
VS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform()); VS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
} }
@ -444,6 +474,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
//erase navigation //erase navigation
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) { for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
NavigationServer::get_singleton()->free(E->get().region); NavigationServer::get_singleton()->free(E->get().region);
if (E->get().navmesh_debug_instance.is_valid()) {
VS::get_singleton()->free(E->get().navmesh_debug_instance);
}
} }
g.navmesh_ids.clear(); g.navmesh_ids.clear();
@ -523,12 +556,32 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Octant::NavMesh nm; Octant::NavMesh nm;
nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item); nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item);
if (navigation) { if (bake_navigation) {
RID region = NavigationServer::get_singleton()->region_create(); RID region = NavigationServer::get_singleton()->region_create();
NavigationServer::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer::get_singleton()->region_set_navmesh(region, navmesh); NavigationServer::get_singleton()->region_set_navmesh(region, navmesh);
NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * nm.xform); NavigationServer::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform);
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); if (navigation) {
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
} else {
NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map());
}
nm.region = region; nm.region = region;
// add navigation debugmesh visual instances if debug is enabled
SceneTree *st = SceneTree::get_singleton();
if (st && st->is_debugging_navigation_hint()) {
if (!nm.navmesh_debug_instance.is_valid()) {
RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid();
nm.navmesh_debug_instance = VS::get_singleton()->instance_create();
VS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid);
VS::get_singleton()->mesh_surface_set_material(navmesh_debug_rid, 0, st->get_debug_navigation_material()->get_rid());
}
if (is_inside_tree()) {
VS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world()->get_scenario());
VS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform);
}
}
} }
g.navmesh_ids[E->get()] = nm; g.navmesh_ids[E->get()] = nm;
} }
@ -614,16 +667,21 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
VS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform()); VS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
} }
if (navigation && mesh_library.is_valid()) { if (bake_navigation && mesh_library.is_valid()) {
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) { for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
if (cell_map.has(F->key()) && F->get().region.is_valid() == false) { if (cell_map.has(E->key()) && E->get().region.is_valid() == false) {
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F->key()].item); Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[E->key()].item);
if (nm.is_valid()) { if (nm.is_valid()) {
RID region = NavigationServer::get_singleton()->region_create(); RID region = NavigationServer::get_singleton()->region_create();
NavigationServer::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer::get_singleton()->region_set_navmesh(region, nm); NavigationServer::get_singleton()->region_set_navmesh(region, nm);
NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * F->get().xform); NavigationServer::get_singleton()->region_set_transform(region, get_global_transform() * E->get().xform);
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); if (navigation) {
F->get().region = region; NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
} else {
NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map());
}
E->get().region = region;
} }
} }
} }
@ -644,12 +702,14 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
VS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID()); VS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID());
} }
if (navigation) { for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) { if (E->get().region.is_valid()) {
if (F->get().region.is_valid()) { NavigationServer::get_singleton()->free(E->get().region);
NavigationServer::get_singleton()->free(F->get().region); E->get().region = RID();
F->get().region = RID(); }
} if (E->get().navmesh_debug_instance.is_valid()) {
VS::get_singleton()->free(E->get().navmesh_debug_instance);
E->get().navmesh_debug_instance = RID();
} }
} }
} }
@ -675,7 +735,12 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
// Erase navigation // Erase navigation
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) { for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
NavigationServer::get_singleton()->free(E->get().region); if (E->get().region.is_valid()) {
NavigationServer::get_singleton()->free(E->get().region);
}
if (E->get().navmesh_debug_instance.is_valid()) {
VS::get_singleton()->free(E->get().navmesh_debug_instance);
}
} }
g.navmesh_ids.clear(); g.navmesh_ids.clear();
@ -855,6 +920,12 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &GridMap::set_physics_material); ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &GridMap::set_physics_material);
ClassDB::bind_method(D_METHOD("get_physics_material"), &GridMap::get_physics_material); ClassDB::bind_method(D_METHOD("get_physics_material"), &GridMap::get_physics_material);
ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation);
ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &GridMap::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library); ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library);
ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library); ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library);
@ -914,6 +985,8 @@ void GridMap::_bind_methods() {
ADD_GROUP("Collision", "collision_"); ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
BIND_CONSTANT(INVALID_CELL_ITEM); BIND_CONSTANT(INVALID_CELL_ITEM);

View file

@ -90,6 +90,7 @@ class GridMap : public Spatial {
struct NavMesh { struct NavMesh {
RID region; RID region;
Transform xform; Transform xform;
RID navmesh_debug_instance;
}; };
struct MultimeshInstance { struct MultimeshInstance {
@ -135,6 +136,8 @@ class GridMap : public Spatial {
uint32_t collision_layer; uint32_t collision_layer;
uint32_t collision_mask; uint32_t collision_mask;
Ref<PhysicsMaterial> physics_material; Ref<PhysicsMaterial> physics_material;
bool bake_navigation = false;
uint32_t navigation_layers = 1;
Transform last_transform; Transform last_transform;
@ -227,6 +230,12 @@ public:
Array get_collision_shapes() const; Array get_collision_shapes() const;
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers();
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library); void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
Ref<MeshLibrary> get_mesh_library() const; Ref<MeshLibrary> get_mesh_library() const;

View file

@ -36,10 +36,6 @@
#include "navigation_mesh_generator.h" #include "navigation_mesh_generator.h"
#endif #endif
/**
@author AndreaCatania
*/
/// Creates a struct for each function and a function that once called creates /// Creates a struct for each function and a function that once called creates
/// an instance of that struct with the submitted parameters. /// an instance of that struct with the submitted parameters.
/// Then, that struct is stored in an array; the `sync` function consume that array. /// Then, that struct is stored in an array; the `sync` function consume that array.
@ -113,17 +109,14 @@
} \ } \
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
GodotNavigationServer::GodotNavigationServer() : GodotNavigationServer::GodotNavigationServer() {}
NavigationServer(),
active(true) {
}
GodotNavigationServer::~GodotNavigationServer() { GodotNavigationServer::~GodotNavigationServer() {
flush_queries(); flush_queries();
} }
void GodotNavigationServer::add_command(SetCommand *command) const { void GodotNavigationServer::add_command(SetCommand *command) const {
auto mut_this = const_cast<GodotNavigationServer *>(this); GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
{ {
MutexLock lock(commands_mutex); MutexLock lock(commands_mutex);
mut_this->commands.push_back(command); mut_this->commands.push_back(command);
@ -131,7 +124,7 @@ void GodotNavigationServer::add_command(SetCommand *command) const {
} }
RID GodotNavigationServer::map_create() const { RID GodotNavigationServer::map_create() const {
auto mut_this = const_cast<GodotNavigationServer *>(this); GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex); MutexLock lock(mut_this->operations_mutex);
NavMap *space = memnew(NavMap); NavMap *space = memnew(NavMap);
RID rid = map_owner.make_rid(space); RID rid = map_owner.make_rid(space);
@ -140,27 +133,31 @@ RID GodotNavigationServer::map_create() const {
} }
COMMAND_2(map_set_active, RID, p_map, bool, p_active) { COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
if (p_active) { if (p_active) {
if (!map_is_active(p_map)) { if (!map_is_active(p_map)) {
active_maps.push_back(map); active_maps.push_back(map);
active_maps_update_id.push_back(map->get_map_update_id());
} }
} else { } else {
active_maps.erase(map); int map_index = active_maps.find(map);
ERR_FAIL_COND(map_index < 0);
active_maps.remove(map_index);
active_maps_update_id.remove(map_index);
} }
} }
bool GodotNavigationServer::map_is_active(RID p_map) const { bool GodotNavigationServer::map_is_active(RID p_map) const {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND_V(map == nullptr, false); ERR_FAIL_COND_V(map == nullptr, false);
return active_maps.find(map) >= 0; return active_maps.find(map) >= 0;
} }
COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) { COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
map->set_up(p_up); map->set_up(p_up);
@ -174,7 +171,7 @@ Vector3 GodotNavigationServer::map_get_up(RID p_map) const {
} }
COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) { COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
map->set_cell_size(p_cell_size); map->set_cell_size(p_cell_size);
@ -188,7 +185,7 @@ real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
} }
COMMAND_2(map_set_cell_height, RID, p_map, real_t, p_cell_height) { COMMAND_2(map_set_cell_height, RID, p_map, real_t, p_cell_height) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
map->set_cell_height(p_cell_height); map->set_cell_height(p_cell_height);
@ -202,7 +199,7 @@ real_t GodotNavigationServer::map_get_cell_height(RID p_map) const {
} }
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) { COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
map->set_edge_connection_margin(p_connection_margin); map->set_edge_connection_margin(p_connection_margin);
@ -215,11 +212,11 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
return map->get_edge_connection_margin(); return map->get_edge_connection_margin();
} }
Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const { Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
NavMap *map = map_owner.get(p_map); const NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>()); ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
return map->get_path(p_origin, p_destination, p_optimize); return map->get_path(p_origin, p_destination, p_optimize, p_layers);
} }
Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@ -289,7 +286,7 @@ RID GodotNavigationServer::agent_get_map(RID p_agent) const {
} }
RID GodotNavigationServer::region_create() const { RID GodotNavigationServer::region_create() const {
auto mut_this = const_cast<GodotNavigationServer *>(this); GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex); MutexLock lock(mut_this->operations_mutex);
NavRegion *reg = memnew(NavRegion); NavRegion *reg = memnew(NavRegion);
RID rid = region_owner.make_rid(reg); RID rid = region_owner.make_rid(reg);
@ -298,7 +295,7 @@ RID GodotNavigationServer::region_create() const {
} }
COMMAND_2(region_set_map, RID, p_region, RID, p_map) { COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
NavRegion *region = region_owner.get(p_region); NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr); ERR_FAIL_COND(region == nullptr);
if (region->get_map() != nullptr) { if (region->get_map() != nullptr) {
@ -311,7 +308,7 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
} }
if (p_map.is_valid()) { if (p_map.is_valid()) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
map->add_region(region); map->add_region(region);
@ -320,14 +317,58 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
} }
COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform) { COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform) {
NavRegion *region = region_owner.get(p_region); NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr); ERR_FAIL_COND(region == nullptr);
region->set_transform(p_transform); region->set_transform(p_transform);
} }
COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr);
ERR_FAIL_COND(p_enter_cost < 0.0);
region->set_enter_cost(p_enter_cost);
}
real_t GodotNavigationServer::region_get_enter_cost(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
return region->get_enter_cost();
}
COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost) {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr);
ERR_FAIL_COND(p_travel_cost < 0.0);
region->set_travel_cost(p_travel_cost);
}
real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
return region->get_travel_cost();
}
COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers) {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr);
region->set_navigation_layers(p_navigation_layers);
}
uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
return region->get_navigation_layers();
}
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) { COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
NavRegion *region = region_owner.get(p_region); NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND(region == nullptr); ERR_FAIL_COND(region == nullptr);
region->set_mesh(p_nav_mesh); region->set_mesh(p_nav_mesh);
@ -343,8 +384,29 @@ void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node
#endif #endif
} }
int GodotNavigationServer::region_get_connections_count(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(!region, 0);
return region->get_connections_count();
}
Vector3 GodotNavigationServer::region_get_connection_pathway_start(RID p_region, int p_connection_id) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(!region, Vector3());
return region->get_connection_pathway_start(p_connection_id);
}
Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, int p_connection_id) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(!region, Vector3());
return region->get_connection_pathway_end(p_connection_id);
}
RID GodotNavigationServer::agent_create() const { RID GodotNavigationServer::agent_create() const {
auto mut_this = const_cast<GodotNavigationServer *>(this); GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex); MutexLock lock(mut_this->operations_mutex);
RvoAgent *agent = memnew(RvoAgent()); RvoAgent *agent = memnew(RvoAgent());
RID rid = agent_owner.make_rid(agent); RID rid = agent_owner.make_rid(agent);
@ -353,7 +415,7 @@ RID GodotNavigationServer::agent_create() const {
} }
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) { COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
if (agent->get_map()) { if (agent->get_map()) {
@ -367,7 +429,7 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
agent->set_map(nullptr); agent->set_map(nullptr);
if (p_map.is_valid()) { if (p_map.is_valid()) {
NavMap *map = map_owner.get(p_map); NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr); ERR_FAIL_COND(map == nullptr);
agent->set_map(map); agent->set_map(map);
@ -380,77 +442,77 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
} }
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) { COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->neighborDist_ = p_dist; agent->get_agent()->neighborDist_ = p_dist;
} }
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) { COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxNeighbors_ = p_count; agent->get_agent()->maxNeighbors_ = p_count;
} }
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) { COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->timeHorizon_ = p_time; agent->get_agent()->timeHorizon_ = p_time;
} }
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) { COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->radius_ = p_radius; agent->get_agent()->radius_ = p_radius;
} }
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) { COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxSpeed_ = p_max_speed; agent->get_agent()->maxSpeed_ = p_max_speed;
} }
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) { COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z); agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
} }
COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) { COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z); agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
} }
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) { COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z); agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z);
} }
COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) { COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->ignore_y_ = p_ignore; agent->get_agent()->ignore_y_ = p_ignore;
} }
bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const { bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false); ERR_FAIL_COND_V(agent == nullptr, false);
return agent->is_map_changed(); return agent->is_map_changed();
} }
COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) { COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) {
RvoAgent *agent = agent_owner.get(p_agent); RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr); ERR_FAIL_COND(agent == nullptr);
agent->set_callback(p_receiver == nullptr ? 0 : p_receiver->get_instance_id(), p_method, p_udata); agent->set_callback(p_receiver == nullptr ? 0 : p_receiver->get_instance_id(), p_method, p_udata);
@ -466,7 +528,7 @@ COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_
COMMAND_1(free, RID, p_object) { COMMAND_1(free, RID, p_object) {
if (map_owner.owns(p_object)) { if (map_owner.owns(p_object)) {
NavMap *map = map_owner.get(p_object); NavMap *map = map_owner.getornull(p_object);
// Removes any assigned region // Removes any assigned region
std::vector<NavRegion *> regions = map->get_regions(); std::vector<NavRegion *> regions = map->get_regions();
@ -482,12 +544,14 @@ COMMAND_1(free, RID, p_object) {
agents[i]->set_map(nullptr); agents[i]->set_map(nullptr);
} }
active_maps.erase(map); int map_index = active_maps.find(map);
active_maps.remove(map_index);
active_maps_update_id.remove(map_index);
map_owner.free(p_object); map_owner.free(p_object);
memdelete(map); memdelete(map);
} else if (region_owner.owns(p_object)) { } else if (region_owner.owns(p_object)) {
NavRegion *region = region_owner.get(p_object); NavRegion *region = region_owner.getornull(p_object);
// Removes this region from the map if assigned // Removes this region from the map if assigned
if (region->get_map() != nullptr) { if (region->get_map() != nullptr) {
@ -499,7 +563,7 @@ COMMAND_1(free, RID, p_object) {
memdelete(region); memdelete(region);
} else if (agent_owner.owns(p_object)) { } else if (agent_owner.owns(p_object)) {
RvoAgent *agent = agent_owner.get(p_object); RvoAgent *agent = agent_owner.getornull(p_object);
// Removes this agent from the map if assigned // Removes this agent from the map if assigned
if (agent->get_map() != nullptr) { if (agent->get_map() != nullptr) {
@ -516,7 +580,7 @@ COMMAND_1(free, RID, p_object) {
} }
void GodotNavigationServer::set_active(bool p_active) const { void GodotNavigationServer::set_active(bool p_active) const {
auto mut_this = const_cast<GodotNavigationServer *>(this); GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex); MutexLock lock(mut_this->operations_mutex);
mut_this->active = p_active; mut_this->active = p_active;
} }
@ -543,10 +607,18 @@ void GodotNavigationServer::process(real_t p_delta_time) {
// In c++ we can't be sure that this is performed in the main thread // In c++ we can't be sure that this is performed in the main thread
// even with mutable functions. // even with mutable functions.
MutexLock lock(operations_mutex); MutexLock lock(operations_mutex);
for (int i(0); i < active_maps.size(); i++) {
for (uint32_t i(0); i < active_maps.size(); i++) {
active_maps[i]->sync(); active_maps[i]->sync();
active_maps[i]->step(p_delta_time); active_maps[i]->step(p_delta_time);
active_maps[i]->dispatch_callbacks(); active_maps[i]->dispatch_callbacks();
// Emit a signal if a map changed.
const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
if (new_map_update_id != active_maps_update_id[i]) {
emit_signal("map_changed", active_maps[i]->get_self());
active_maps_update_id[i] = new_map_update_id;
}
} }
} }

View file

@ -31,16 +31,13 @@
#ifndef GODOT_NAVIGATION_SERVER_H #ifndef GODOT_NAVIGATION_SERVER_H
#define GODOT_NAVIGATION_SERVER_H #define GODOT_NAVIGATION_SERVER_H
#include "core/rid.h"
#include "servers/navigation_server.h" #include "servers/navigation_server.h"
#include "nav_map.h" #include "nav_map.h"
#include "nav_region.h" #include "nav_region.h"
#include "rvo_agent.h" #include "rvo_agent.h"
/**
@author AndreaCatania
*/
/// The commands are functions executed during the `sync` phase. /// The commands are functions executed during the `sync` phase.
#define MERGE_INTERNAL(A, B) A##B #define MERGE_INTERNAL(A, B) A##B
@ -76,8 +73,9 @@ class GodotNavigationServer : public NavigationServer {
mutable RID_Owner<NavRegion> region_owner; mutable RID_Owner<NavRegion> region_owner;
mutable RID_Owner<RvoAgent> agent_owner; mutable RID_Owner<RvoAgent> agent_owner;
bool active; bool active = true;
Vector<NavMap *> active_maps; LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
public: public:
GodotNavigationServer(); GodotNavigationServer();
@ -101,7 +99,7 @@ public:
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin); COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const; virtual real_t map_get_edge_connection_margin(RID p_map) const;
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const; virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
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; 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;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const; virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const;
@ -112,12 +110,24 @@ public:
virtual Array map_get_agents(RID p_map) const; virtual Array map_get_agents(RID p_map) const;
virtual RID region_create() const; virtual RID region_create() const;
COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost);
virtual real_t region_get_enter_cost(RID p_region) const;
COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost);
virtual real_t region_get_travel_cost(RID p_region) const;
COMMAND_2(region_set_map, RID, p_region, RID, p_map); COMMAND_2(region_set_map, RID, p_region, RID, p_map);
virtual RID region_get_map(RID p_region) const; virtual RID region_get_map(RID p_region) const;
COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers);
virtual uint32_t region_get_navigation_layers(RID p_region) const;
COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform); COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform);
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh); COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const; virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const;
virtual int region_get_connections_count(RID p_region) const;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const;
virtual RID agent_create() const; virtual RID agent_create() const;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map); COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
virtual RID agent_get_map(RID p_agent) const; virtual RID agent_get_map(RID p_agent) const;
@ -136,6 +146,7 @@ public:
COMMAND_1(free, RID, p_object); COMMAND_1(free, RID, p_object);
virtual void set_active(bool p_active) const; virtual void set_active(bool p_active) const;
void flush_queries(); void flush_queries();
virtual void process(real_t p_delta_time); virtual void process(real_t p_delta_time);
}; };

View file

@ -32,28 +32,10 @@
#include "nav_region.h" #include "nav_region.h"
#include "rvo_agent.h" #include "rvo_agent.h"
#include <algorithm> #include <algorithm>
/** #define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
@author AndreaCatania
*/
#define USE_ENTRY_POINT
NavMap::NavMap() :
up(0, 1, 0),
cell_size(0.3),
cell_height(0.2),
edge_connection_margin(5.0),
regenerate_polygons(true),
regenerate_links(true),
agents_dirty(false),
deltatime(0.0),
map_update_id(0) {}
NavMap::~NavMap() {
step_work_pool.finish();
}
void NavMap::set_up(Vector3 p_up) { void NavMap::set_up(Vector3 p_up) {
up = p_up; up = p_up;
@ -88,9 +70,10 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
return p; return p;
} }
Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const { Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
const gd::Polygon *begin_poly = NULL; // Find the start poly and the end poly on this map.
const gd::Polygon *end_poly = NULL; const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
Vector3 begin_point; Vector3 begin_point;
Vector3 end_point; Vector3 end_point;
float begin_d = 1e20; float begin_d = 1e20;
@ -100,30 +83,35 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
for (size_t i(0); i < polygons.size(); i++) { for (size_t i(0); i < polygons.size(); i++) {
const gd::Polygon &p = polygons[i]; const gd::Polygon &p = polygons[i];
// Only consider the polygon if it in a region with compatible layers.
if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) {
continue;
}
// For each face check the distance between the origin/destination // For each face check the distance between the origin/destination
for (size_t point_id = 2; point_id < p.points.size(); point_id++) { 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); const Face3 face(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
Vector3 spoint = face.get_closest_point_to(p_origin); Vector3 point = face.get_closest_point_to(p_origin);
float dpoint = spoint.distance_to(p_origin); float distance_to_point = point.distance_to(p_origin);
if (dpoint < begin_d) { if (distance_to_point < begin_d) {
begin_d = dpoint; begin_d = distance_to_point;
begin_poly = &p; begin_poly = &p;
begin_point = spoint; begin_point = point;
} }
spoint = face.get_closest_point_to(p_destination); point = face.get_closest_point_to(p_destination);
dpoint = spoint.distance_to(p_destination); distance_to_point = point.distance_to(p_destination);
if (dpoint < end_d) { if (distance_to_point < end_d) {
end_d = dpoint; end_d = distance_to_point;
end_poly = &p; end_poly = &p;
end_point = spoint; end_point = point;
} }
} }
} }
// Check for trivial cases
if (!begin_poly || !end_poly) { if (!begin_poly || !end_poly) {
// No path
return Vector<Vector3>(); return Vector<Vector3>();
} }
@ -135,92 +123,102 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
return path; return path;
} }
// List of all reachable navigation polys.
std::vector<gd::NavigationPoly> navigation_polys; std::vector<gd::NavigationPoly> navigation_polys;
navigation_polys.reserve(polygons.size() * 0.75); navigation_polys.reserve(polygons.size() * 0.75);
// The elements indices in the `navigation_polys`. // Add the start polygon to the reachable navigation polygons.
int least_cost_id(-1); gd::NavigationPoly begin_navigation_poly = gd::NavigationPoly(begin_poly);
List<uint32_t> open_list; begin_navigation_poly.self_id = 0;
begin_navigation_poly.entry = begin_point;
begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
navigation_polys.push_back(begin_navigation_poly);
// List of polygon IDs to visit.
List<uint32_t> to_visit;
to_visit.push_back(0);
// This is an implementation of the A* algorithm.
int least_cost_id = 0;
bool found_route = false; bool found_route = false;
navigation_polys.push_back(gd::NavigationPoly(begin_poly)); const gd::Polygon *reachable_end = nullptr;
{
least_cost_id = 0;
gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
least_cost_poly->self_id = least_cost_id;
least_cost_poly->entry = begin_point;
}
open_list.push_back(0);
const gd::Polygon *reachable_end = NULL;
float reachable_d = 1e30; float reachable_d = 1e30;
bool is_reachable = true; bool is_reachable = true;
while (found_route == false) { gd::NavigationPoly *prev_least_cost_poly = nullptr;
{
// Takes the current least_cost_poly neighbors and compute the traveled_distance of each
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
const gd::Edge &edge = least_cost_poly->poly->edges[i]; while (true) {
if (!edge.other_polygon) // Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
const gd::Edge &edge = least_cost_poly->poly->edges[i];
// Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
for (int 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) {
continue; continue;
}
#ifdef USE_ENTRY_POINT float region_enter_cost = 0.0;
Vector3 edge_line[2] = { float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost();
least_cost_poly->poly->points[i].pos,
least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos
};
const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, edge_line); if (prev_least_cost_poly != nullptr && !(prev_least_cost_poly->poly->owner->get_self() == least_cost_poly->poly->owner->get_self())) {
const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance; region_enter_cost = least_cost_poly->poly->owner->get_enter_cost();
#else }
const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance; prev_least_cost_poly = least_cost_poly;
#endif
auto it = std::find( Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, pathway);
const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance;
const std::vector<gd::NavigationPoly>::iterator it = std::find(
navigation_polys.begin(), navigation_polys.begin(),
navigation_polys.end(), navigation_polys.end(),
gd::NavigationPoly(edge.other_polygon)); gd::NavigationPoly(connection.polygon));
if (it != navigation_polys.end()) { if (it != navigation_polys.end()) {
// Oh this was visited already, can we win the cost? // Polygon already visited, check if we can reduce the travel cost.
if (it->traveled_distance > new_distance) { if (new_distance < it->traveled_distance) {
it->prev_navigation_poly_id = least_cost_id; it->back_navigation_poly_id = least_cost_id;
it->back_navigation_edge = edge.other_edge; it->back_navigation_edge = connection.edge;
it->back_navigation_edge_pathway_start = connection.pathway_start;
it->back_navigation_edge_pathway_end = connection.pathway_end;
it->traveled_distance = new_distance; it->traveled_distance = new_distance;
#ifdef USE_ENTRY_POINT
it->entry = new_entry; it->entry = new_entry;
#endif
} }
} else { } else {
// Add to open neighbours // Add the neighbour polygon to the reachable ones.
gd::NavigationPoly new_navigation_poly = gd::NavigationPoly(connection.polygon);
new_navigation_poly.self_id = navigation_polys.size();
new_navigation_poly.back_navigation_poly_id = least_cost_id;
new_navigation_poly.back_navigation_edge = connection.edge;
new_navigation_poly.back_navigation_edge_pathway_start = connection.pathway_start;
new_navigation_poly.back_navigation_edge_pathway_end = connection.pathway_end;
new_navigation_poly.traveled_distance = new_distance;
new_navigation_poly.entry = new_entry;
navigation_polys.push_back(new_navigation_poly);
navigation_polys.push_back(gd::NavigationPoly(edge.other_polygon)); // Add the neighbour polygon to the polygons to visit.
gd::NavigationPoly *np = &navigation_polys[navigation_polys.size() - 1]; to_visit.push_back(navigation_polys.size() - 1);
np->self_id = navigation_polys.size() - 1;
np->prev_navigation_poly_id = least_cost_id;
np->back_navigation_edge = edge.other_edge;
np->traveled_distance = new_distance;
#ifdef USE_ENTRY_POINT
np->entry = new_entry;
#endif
open_list.push_back(navigation_polys.size() - 1);
} }
} }
} }
// Removes the least cost polygon from the open list so we can advance. // Removes the least cost polygon from the list of polygons to visit so we can advance.
open_list.erase(least_cost_id); to_visit.erase(least_cost_id);
if (open_list.size() == 0) { // When the list of polygons to visit is empty at this point it means the End Polygon is not reachable
// When the open list is empty at this point the End Polygon is not reachable if (to_visit.size() == 0) {
// so use the further reachable polygon // Thus use the further reachable polygon
ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons"); ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons");
is_reachable = false; is_reachable = false;
if (reachable_end == NULL) { if (reachable_end == nullptr) {
// The path is not found and there is not a way out. // The path is not found and there is not a way out.
break; break;
} }
@ -242,27 +240,22 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
gd::NavigationPoly np = navigation_polys[0]; gd::NavigationPoly np = navigation_polys[0];
navigation_polys.clear(); navigation_polys.clear();
navigation_polys.push_back(np); navigation_polys.push_back(np);
open_list.clear(); to_visit.clear();
open_list.push_back(0); to_visit.push_back(0);
least_cost_id = 0; least_cost_id = 0;
reachable_end = NULL; reachable_end = nullptr;
continue; continue;
} }
// Now take the new least_cost_poly from the open list. // Find the polygon with the minimum cost from the list of polygons to visit.
least_cost_id = -1; least_cost_id = -1;
float least_cost = 1e30; float least_cost = 1e30;
for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
for (auto element = open_list.front(); element != NULL; element = element->next()) {
gd::NavigationPoly *np = &navigation_polys[element->get()]; gd::NavigationPoly *np = &navigation_polys[element->get()];
float cost = np->traveled_distance; float cost = np->traveled_distance;
#ifdef USE_ENTRY_POINT cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost());
cost += np->entry.distance_to(end_point);
#else
cost += np->poly->center.distance_to(end_point);
#endif
if (cost < least_cost) { if (cost < least_cost) {
least_cost_id = np->self_id; least_cost_id = np->self_id;
least_cost = cost; least_cost = cost;
@ -273,7 +266,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Stores the further reachable end polygon, in case our goal is not reachable. // Stores the further reachable end polygon, in case our goal is not reachable.
if (is_reachable) { if (is_reachable) {
float d = navigation_polys[least_cost_id].entry.distance_to(p_destination); float d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost();
if (reachable_d > d) { if (reachable_d > d) {
reachable_d = d; reachable_d = d;
reachable_end = navigation_polys[least_cost_id].poly; reachable_end = navigation_polys[least_cost_id].poly;
@ -282,120 +275,112 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Check if we reached the end // Check if we reached the end
if (navigation_polys[least_cost_id].poly == end_poly) { if (navigation_polys[least_cost_id].poly == end_poly) {
// Yep, done!!
found_route = true; found_route = true;
break; break;
} }
} }
if (found_route) { // If we did not find a route, return an empty path.
Vector<Vector3> path; if (!found_route) {
if (p_optimize) { return Vector<Vector3>();
// String pulling }
gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id]; Vector<Vector3> path;
Vector3 apex_point = end_point; // Optimize the path.
Vector3 portal_left = apex_point; if (p_optimize) {
Vector3 portal_right = apex_point; // Set the apex poly/point to the end point
gd::NavigationPoly *left_poly = apex_poly; gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id];
gd::NavigationPoly *right_poly = apex_poly; Vector3 apex_point = end_point;
gd::NavigationPoly *p = apex_poly;
path.push_back(end_point); gd::NavigationPoly *left_poly = apex_poly;
Vector3 left_portal = apex_point;
gd::NavigationPoly *right_poly = apex_poly;
Vector3 right_portal = apex_point;
while (p) { gd::NavigationPoly *p = apex_poly;
Vector3 left;
Vector3 right;
#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b))) path.push_back(end_point);
if (p->poly == begin_poly) { while (p) {
left = begin_point; // Set left and right points of the pathway between polygons.
right = begin_point; 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(up) < 0) {
SWAP(left, right);
}
bool skip = false;
if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(up) >= 0) {
//process
if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(up) > 0) {
left_poly = p;
left_portal = left;
} else { } else {
int prev = p->back_navigation_edge; clip_path(navigation_polys, path, apex_poly, right_portal, right_poly);
int prev_n = (p->back_navigation_edge + 1) % p->poly->points.size();
left = p->poly->points[prev].pos;
right = p->poly->points[prev_n].pos;
//if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){ apex_point = right_portal;
if (p->poly->clockwise) { p = right_poly;
SWAP(left, right); left_poly = p;
} apex_poly = p;
left_portal = apex_point;
right_portal = apex_point;
path.push_back(apex_point);
skip = true;
} }
bool skip = false;
if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
//process
if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
left_poly = p;
portal_left = left;
} else {
clip_path(navigation_polys, path, apex_poly, portal_right, right_poly);
apex_point = portal_right;
p = right_poly;
left_poly = p;
apex_poly = p;
portal_left = apex_point;
portal_right = apex_point;
path.push_back(apex_point);
skip = true;
}
}
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
//process
if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
right_poly = p;
portal_right = right;
} else {
clip_path(navigation_polys, path, apex_poly, portal_left, left_poly);
apex_point = portal_left;
p = left_poly;
right_poly = p;
apex_poly = p;
portal_right = apex_point;
portal_left = apex_point;
path.push_back(apex_point);
}
}
if (p->prev_navigation_poly_id != -1)
p = &navigation_polys[p->prev_navigation_poly_id];
else
// The end
p = NULL;
} }
if (path[path.size() - 1] != begin_point) if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(up) <= 0) {
path.push_back(begin_point); //process
if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(up) < 0) {
right_poly = p;
right_portal = right;
} else {
clip_path(navigation_polys, path, apex_poly, left_portal, left_poly);
path.invert(); apex_point = left_portal;
p = left_poly;
} else { right_poly = p;
path.push_back(end_point); apex_poly = p;
right_portal = apex_point;
// Add mid points left_portal = apex_point;
int np_id = least_cost_id; path.push_back(apex_point);
while (np_id != -1 && navigation_polys[np_id].prev_navigation_poly_id != -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);
np_id = navigation_polys[np_id].prev_navigation_poly_id;
} }
path.push_back(begin_point); // Go to the previous polygon.
if (p->back_navigation_poly_id != -1) {
path.invert(); p = &navigation_polys[p->back_navigation_poly_id];
} else {
// The end
p = nullptr;
}
} }
return path; // 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);
}
path.invert();
} else {
path.push_back(end_point);
// Add mid points
int np_id = least_cost_id;
while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -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);
np_id = navigation_polys[np_id].back_navigation_poly_id;
}
path.push_back(begin_point);
path.invert();
} }
return Vector<Vector3>();
return path;
} }
Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@ -492,7 +477,7 @@ void NavMap::add_region(NavRegion *p_region) {
} }
void NavMap::remove_region(NavRegion *p_region) { void NavMap::remove_region(NavRegion *p_region) {
std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region); const std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region);
if (it != regions.end()) { if (it != regions.end()) {
regions.erase(it); regions.erase(it);
regenerate_links = true; regenerate_links = true;
@ -512,7 +497,7 @@ void NavMap::add_agent(RvoAgent *agent) {
void NavMap::remove_agent(RvoAgent *agent) { void NavMap::remove_agent(RvoAgent *agent) {
remove_agent_as_controlled(agent); remove_agent_as_controlled(agent);
auto it = std::find(agents.begin(), agents.end(), agent); const std::vector<RvoAgent *>::iterator it = std::find(agents.begin(), agents.end(), agent);
if (it != agents.end()) { if (it != agents.end()) {
agents.erase(it); agents.erase(it);
agents_dirty = true; agents_dirty = true;
@ -528,13 +513,14 @@ void NavMap::set_agent_as_controlled(RvoAgent *agent) {
} }
void NavMap::remove_agent_as_controlled(RvoAgent *agent) { void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
auto it = std::find(controlled_agents.begin(), controlled_agents.end(), agent); const std::vector<RvoAgent *>::iterator it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
if (it != controlled_agents.end()) { if (it != controlled_agents.end()) {
controlled_agents.erase(it); controlled_agents.erase(it);
} }
} }
void NavMap::sync() { void NavMap::sync() {
// Check if we need to update the links.
if (regenerate_polygons) { if (regenerate_polygons) {
for (size_t r(0); r < regions.size(); r++) { for (size_t r(0); r < regions.size(); r++) {
regions[r]->scratch_polygons(); regions[r]->scratch_polygons();
@ -549,27 +535,30 @@ void NavMap::sync() {
} }
if (regenerate_links) { if (regenerate_links) {
// Copy all region polygons in the map. // Remove regions connections.
for (size_t r(0); r < regions.size(); r++) {
regions[r]->get_connections().clear();
}
// Resize the polygon count.
int count = 0; int count = 0;
for (size_t r(0); r < regions.size(); r++) { for (size_t r(0); r < regions.size(); r++) {
count += regions[r]->get_polygons().size(); count += regions[r]->get_polygons().size();
} }
polygons.resize(count); polygons.resize(count);
count = 0;
// Copy all region polygons in the map.
count = 0;
for (size_t r(0); r < regions.size(); r++) { for (size_t r(0); r < regions.size(); r++) {
std::copy( std::copy(
regions[r]->get_polygons().data(), regions[r]->get_polygons().data(),
regions[r]->get_polygons().data() + regions[r]->get_polygons().size(), regions[r]->get_polygons().data() + regions[r]->get_polygons().size(),
polygons.begin() + count); polygons.begin() + count);
count += regions[r]->get_polygons().size(); count += regions[r]->get_polygons().size();
} }
// Connects the `Edges` of all the `Polygons` of all `Regions` each other. // Group all edges per key.
Map<gd::EdgeKey, gd::Connection> connections; Map<gd::EdgeKey, Vector<gd::Edge::Connection>> connections;
for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) { for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
gd::Polygon &poly(polygons[poly_id]); gd::Polygon &poly(polygons[poly_id]);
@ -577,69 +566,40 @@ void NavMap::sync() {
int next_point = (p + 1) % poly.points.size(); int next_point = (p + 1) % poly.points.size();
gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key); gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
Map<gd::EdgeKey, gd::Connection>::Element *connection = connections.find(ek); Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *connection = connections.find(ek);
if (!connection) { if (!connection) {
// Nothing yet connections[ek] = Vector<gd::Edge::Connection>();
gd::Connection c; }
c.A = &poly; if (connections[ek].size() <= 1) {
c.A_edge = p; // Add the polygon/edge tuple to this key.
c.B = NULL; gd::Edge::Connection new_connection;
c.B_edge = -1; new_connection.polygon = &poly;
connections[ek] = c; new_connection.edge = p;
new_connection.pathway_start = poly.points[p].pos;
} else if (connection->get().B == NULL) { new_connection.pathway_end = poly.points[next_point].pos;
CRASH_COND(connection->get().A == NULL); // Unreachable connections[ek].push_back(new_connection);
// Connect the two Polygons by this edge
connection->get().B = &poly;
connection->get().B_edge = p;
connection->get().A->edges[connection->get().A_edge].this_edge = connection->get().A_edge;
connection->get().A->edges[connection->get().A_edge].other_polygon = connection->get().B;
connection->get().A->edges[connection->get().A_edge].other_edge = connection->get().B_edge;
connection->get().B->edges[connection->get().B_edge].this_edge = connection->get().B_edge;
connection->get().B->edges[connection->get().B_edge].other_polygon = connection->get().A;
connection->get().B->edges[connection->get().B_edge].other_edge = connection->get().A_edge;
} else { } else {
// The edge is already connected with another edge, skip. // The edge is already connected with another edge, skip.
ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. Either the Navigation's `cell_size` is different from the one used to generate the navigation mesh or `detail/sample_max_error` is too small. This will cause navigation problem."); ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
} }
} }
} }
// Takes all the free edges. Vector<gd::Edge::Connection> free_edges;
std::vector<gd::FreeEdge> free_edges; for (Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *E = connections.front(); E; E = E->next()) {
free_edges.reserve(connections.size()); if (E->get().size() == 2) {
// Connect edge that are shared in different polygons.
for (auto connection_element = connections.front(); connection_element; connection_element = connection_element->next()) { gd::Edge::Connection &c1 = E->get().write[0];
if (connection_element->get().B == NULL) { gd::Edge::Connection &c2 = E->get().write[1];
CRASH_COND(connection_element->get().A == NULL); // Unreachable c1.polygon->edges[c1.edge].connections.push_back(c2);
CRASH_COND(connection_element->get().A_edge < 0); // Unreachable c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
// This is a free edge } else {
uint32_t id(free_edges.size()); CRASH_COND_MSG(E->get().size() != 1, vformat("Number of connection != 1. Found: %d", E->get().size()));
free_edges.push_back(gd::FreeEdge()); free_edges.push_back(E->get()[0]);
free_edges[id].is_free = true;
free_edges[id].poly = connection_element->get().A;
free_edges[id].edge_id = connection_element->get().A_edge;
uint32_t point_0(free_edges[id].edge_id);
uint32_t point_1((free_edges[id].edge_id + 1) % free_edges[id].poly->points.size());
Vector3 pos_0 = free_edges[id].poly->points[point_0].pos;
Vector3 pos_1 = free_edges[id].poly->points[point_1].pos;
Vector3 relative = pos_1 - pos_0;
free_edges[id].edge_center = (pos_0 + pos_1) / 2.0;
free_edges[id].edge_dir = relative.normalized();
free_edges[id].edge_len_squared = relative.length_squared();
} }
} }
const float ecm_squared(edge_connection_margin * edge_connection_margin);
#define LEN_TOLERANCE 0.1
#define DIR_TOLERANCE 0.9
// In front of tolerance
#define IFO_TOLERANCE 0.5
// Find the compatible near edges. // Find the compatible near edges.
// //
// Note: // Note:
@ -647,48 +607,73 @@ void NavMap::sync() {
// to be connected, create new polygons to remove that small gap is // to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during // not really useful and would result in wasteful computation during
// connection, integration and path finding. // connection, integration and path finding.
for (size_t i(0); i < free_edges.size(); i++) { for (int i = 0; i < free_edges.size(); i++) {
if (!free_edges[i].is_free) { const gd::Edge::Connection &free_edge = free_edges[i];
continue; Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
} Vector3 edge_p2 = free_edge.polygon->points[(free_edge.edge + 1) % free_edge.polygon->points.size()].pos;
gd::FreeEdge &edge = free_edges[i];
for (size_t y(0); y < free_edges.size(); y++) { for (int j = 0; j < free_edges.size(); j++) {
gd::FreeEdge &other_edge = free_edges[y]; const gd::Edge::Connection &other_edge = free_edges[j];
if (i == y || !other_edge.is_free || edge.poly->owner == other_edge.poly->owner) { if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
continue; continue;
} }
Vector3 rel_centers = other_edge.edge_center - edge.edge_center; Vector3 other_edge_p1 = other_edge.polygon->points[other_edge.edge].pos;
if (ecm_squared > rel_centers.length_squared() // Are enough closer? Vector3 other_edge_p2 = other_edge.polygon->points[(other_edge.edge + 1) % other_edge.polygon->points.size()].pos;
&& ABS(edge.edge_len_squared - other_edge.edge_len_squared) < LEN_TOLERANCE // Are the same length?
&& ABS(edge.edge_dir.dot(other_edge.edge_dir)) > DIR_TOLERANCE // Are aligned?
&& ABS(rel_centers.normalized().dot(edge.edge_dir)) < IFO_TOLERANCE // Are one in front the other?
) {
// The edges can be connected
edge.is_free = false;
other_edge.is_free = false;
edge.poly->edges[edge.edge_id].this_edge = edge.edge_id; // Compute the projection of the opposite edge on the current one
edge.poly->edges[edge.edge_id].other_edge = other_edge.edge_id; Vector3 edge_vector = edge_p2 - edge_p1;
edge.poly->edges[edge.edge_id].other_polygon = other_edge.poly; float projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());
float projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());
other_edge.poly->edges[other_edge.edge_id].this_edge = other_edge.edge_id; if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
other_edge.poly->edges[other_edge.edge_id].other_edge = edge.edge_id; continue;
other_edge.poly->edges[other_edge.edge_id].other_polygon = edge.poly;
} }
// Check if the two edges are close to each other enough and compute a pathway between the two regions.
Vector3 self1 = edge_vector * CLAMP(projected_p1_ratio, 0.0, 1.0) + edge_p1;
Vector3 other1;
if (projected_p1_ratio >= 0.0 && projected_p1_ratio <= 1.0) {
other1 = other_edge_p1;
} else {
other1 = other_edge_p1.linear_interpolate(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
if (other1.distance_to(self1) > edge_connection_margin) {
continue;
}
Vector3 self2 = edge_vector * CLAMP(projected_p2_ratio, 0.0, 1.0) + edge_p1;
Vector3 other2;
if (projected_p2_ratio >= 0.0 && projected_p2_ratio <= 1.0) {
other2 = other_edge_p2;
} else {
other2 = other_edge_p1.linear_interpolate(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
if (other2.distance_to(self2) > edge_connection_margin) {
continue;
}
// The edges can now be connected.
gd::Edge::Connection new_connection = other_edge;
new_connection.pathway_start = (self1 + other1) / 2.0;
new_connection.pathway_end = (self2 + other2) / 2.0;
free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
// Add the connection to the region_connection map.
free_edge.polygon->owner->get_connections().push_back(new_connection);
} }
} }
// Update the update ID.
map_update_id = (map_update_id + 1) % 9999999;
} }
if (regenerate_links) { // Update agents tree.
map_update_id = map_update_id + 1 % 9999999;
}
if (agents_dirty) { if (agents_dirty) {
std::vector<RVO::Agent *> raw_agents; std::vector<RVO::Agent *> raw_agents;
raw_agents.reserve(agents.size()); raw_agents.reserve(agents.size());
for (size_t i(0); i < agents.size(); i++) for (size_t i(0); i < agents.size(); i++) {
raw_agents.push_back(agents[i]->get_agent()); raw_agents.push_back(agents[i]->get_agent());
}
rvo.buildAgentTree(raw_agents); rvo.buildAgentTree(raw_agents);
} }
@ -725,30 +710,38 @@ void NavMap::dispatch_callbacks() {
void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const { void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
Vector3 from = path[path.size() - 1]; Vector3 from = path[path.size() - 1];
if (from.distance_to(p_to_point) < CMP_EPSILON) if (from.is_equal_approx(p_to_point)) {
return; return;
}
Plane cut_plane; Plane cut_plane;
cut_plane.normal = (from - p_to_point).cross(up); cut_plane.normal = (from - p_to_point).cross(up);
if (cut_plane.normal == Vector3()) if (cut_plane.normal == Vector3()) {
return; return;
}
cut_plane.normal.normalize(); cut_plane.normal.normalize();
cut_plane.d = cut_plane.normal.dot(from); cut_plane.d = cut_plane.normal.dot(from);
while (from_poly != p_to_poly) { while (from_poly != p_to_poly) {
int back_nav_edge = from_poly->back_navigation_edge; Vector3 pathway_start = from_poly->back_navigation_edge_pathway_start;
Vector3 a = from_poly->poly->points[back_nav_edge].pos; Vector3 pathway_end = from_poly->back_navigation_edge_pathway_end;
Vector3 b = from_poly->poly->points[(back_nav_edge + 1) % from_poly->poly->points.size()].pos;
ERR_FAIL_COND(from_poly->prev_navigation_poly_id == -1); ERR_FAIL_COND(from_poly->back_navigation_poly_id == -1);
from_poly = &p_navigation_polys[from_poly->prev_navigation_poly_id]; from_poly = &p_navigation_polys[from_poly->back_navigation_poly_id];
if (a.distance_to(b) > CMP_EPSILON) { if (!pathway_start.is_equal_approx(pathway_end)) {
Vector3 inters; Vector3 inters;
if (cut_plane.intersects_segment(a, b, &inters)) { if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) {
if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) { if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) {
path.push_back(inters); path.push_back(inters);
} }
} }
} }
} }
} }
NavMap::NavMap() {
}
NavMap::~NavMap() {
step_work_pool.finish();
}

View file

@ -33,14 +33,12 @@
#include "nav_rid.h" #include "nav_rid.h"
#include "core/map.h"
#include "core/math/math_defs.h" #include "core/math/math_defs.h"
#include "core/os/thread_work_pool.h" #include "core/os/thread_work_pool.h"
#include "nav_utils.h" #include "nav_utils.h"
#include <KdTree.h>
/** #include <KdTree.h>
@author AndreaCatania
*/
class NavRegion; class NavRegion;
class RvoAgent; class RvoAgent;
@ -48,18 +46,18 @@ class NavRegion;
class NavMap : public NavRid { class NavMap : public NavRid {
/// Map Up /// Map Up
Vector3 up; Vector3 up = Vector3(0, 1, 0);
/// To find the polygons edges the vertices are displaced in a grid where /// To find the polygons edges the vertices are displaced in a grid where
/// each cell has the following cell_size and cell_height. /// each cell has the following cell_size and cell_height.
real_t cell_size; real_t cell_size = 0.25;
real_t cell_height; real_t cell_height = 0.25;
/// This value is used to detect the near edges to connect. /// This value is used to detect the near edges to connect.
real_t edge_connection_margin; real_t edge_connection_margin = 0.25;
bool regenerate_polygons; bool regenerate_polygons = true;
bool regenerate_links; bool regenerate_links = true;
std::vector<NavRegion *> regions; std::vector<NavRegion *> regions;
@ -70,7 +68,7 @@ class NavMap : public NavRid {
RVO::KdTree rvo; RVO::KdTree rvo;
/// Is agent array modified? /// Is agent array modified?
bool agents_dirty; bool agents_dirty = false;
/// All the Agents (even the controlled one) /// All the Agents (even the controlled one)
std::vector<RvoAgent *> agents; std::vector<RvoAgent *> agents;
@ -79,10 +77,10 @@ class NavMap : public NavRid {
std::vector<RvoAgent *> controlled_agents; std::vector<RvoAgent *> controlled_agents;
/// Physics delta time /// Physics delta time
real_t deltatime; real_t deltatime = 0.0;
/// Change the id each time the map is updated. /// Change the id each time the map is updated.
uint32_t map_update_id; uint32_t map_update_id = 0;
/// Pooled threads for computing steps /// Pooled threads for computing steps
ThreadWorkPool step_work_pool; ThreadWorkPool step_work_pool;
@ -113,7 +111,7 @@ public:
gd::PointKey get_point_key(const Vector3 &p_pos) const; gd::PointKey get_point_key(const Vector3 &p_pos) const;
Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const; Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const; 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(const Vector3 &p_point) const;
Vector3 get_closest_point_normal(const Vector3 &p_point) const; Vector3 get_closest_point_normal(const Vector3 &p_point) const;

View file

@ -32,18 +32,20 @@
#include "nav_map.h" #include "nav_map.h"
/**
@author AndreaCatania
*/
NavRegion::NavRegion() :
map(NULL),
polygons_dirty(true) {
}
void NavRegion::set_map(NavMap *p_map) { void NavRegion::set_map(NavMap *p_map) {
map = p_map; map = p_map;
polygons_dirty = true; polygons_dirty = true;
if (!map) {
connections.clear();
}
}
void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
}
uint32_t NavRegion::get_navigation_layers() const {
return navigation_layers;
} }
void NavRegion::set_transform(Transform p_transform) { void NavRegion::set_transform(Transform p_transform) {
@ -56,6 +58,25 @@ void NavRegion::set_mesh(Ref<NavigationMesh> p_mesh) {
polygons_dirty = true; polygons_dirty = true;
} }
int NavRegion::get_connections_count() const {
if (!map) {
return 0;
}
return connections.size();
}
Vector3 NavRegion::get_connection_pathway_start(int p_connection_id) const {
ERR_FAIL_COND_V(!map, Vector3());
ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
return connections[p_connection_id].pathway_start;
}
Vector3 NavRegion::get_connection_pathway_end(int p_connection_id) const {
ERR_FAIL_COND_V(!map, Vector3());
ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
return connections[p_connection_id].pathway_end;
}
bool NavRegion::sync() { bool NavRegion::sync() {
bool something_changed = polygons_dirty /* || something_dirty? */; bool something_changed = polygons_dirty /* || something_dirty? */;
@ -71,17 +92,19 @@ void NavRegion::update_polygons() {
polygons.clear(); polygons.clear();
polygons_dirty = false; polygons_dirty = false;
if (map == NULL) { if (map == nullptr) {
return; return;
} }
if (mesh.is_null()) if (mesh.is_null()) {
return; return;
}
PoolVector<Vector3> vertices = mesh->get_vertices(); PoolVector<Vector3> vertices = mesh->get_vertices();
int len = vertices.size(); int len = vertices.size();
if (len == 0) if (len == 0) {
return; return;
}
PoolVector<Vector3>::Read vertices_r = vertices.read(); PoolVector<Vector3>::Read vertices_r = vertices.read();
@ -132,3 +155,7 @@ void NavRegion::update_polygons() {
} }
} }
} }
NavRegion::NavRegion() {}
NavRegion::~NavRegion() {}

View file

@ -31,32 +31,31 @@
#ifndef NAV_REGION_H #ifndef NAV_REGION_H
#define NAV_REGION_H #define NAV_REGION_H
#include "nav_rid.h"
#include "nav_utils.h"
#include "scene/3d/navigation.h" #include "scene/3d/navigation.h"
#include <vector>
/** #include "nav_rid.h"
@author AndreaCatania #include "nav_utils.h"
*/
#include <vector>
class NavMap; class NavMap;
class NavRegion; class NavRegion;
class NavRegion : public NavRid { class NavRegion : public NavRid {
NavMap *map; NavMap *map = nullptr;
Transform transform; Transform transform;
Ref<NavigationMesh> mesh; Ref<NavigationMesh> mesh;
uint32_t navigation_layers = 1;
float enter_cost = 0.0;
float travel_cost = 1.0;
Vector<gd::Edge::Connection> connections;
bool polygons_dirty; bool polygons_dirty = true;
/// Cache /// Cache
std::vector<gd::Polygon> polygons; std::vector<gd::Polygon> polygons;
public: public:
NavRegion();
void scratch_polygons() { void scratch_polygons() {
polygons_dirty = true; polygons_dirty = true;
} }
@ -66,6 +65,15 @@ public:
return map; return map;
} }
void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); }
float get_enter_cost() const { return enter_cost; }
void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); }
float get_travel_cost() const { return travel_cost; }
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_transform(Transform transform); void set_transform(Transform transform);
const Transform &get_transform() const { const Transform &get_transform() const {
return transform; return transform;
@ -76,12 +84,22 @@ public:
return mesh; return mesh;
} }
Vector<gd::Edge::Connection> &get_connections() {
return connections;
}
int get_connections_count() const;
Vector3 get_connection_pathway_start(int p_connection_id) const;
Vector3 get_connection_pathway_end(int p_connection_id) const;
std::vector<gd::Polygon> const &get_polygons() const { std::vector<gd::Polygon> const &get_polygons() const {
return polygons; return polygons;
} }
bool sync(); bool sync();
NavRegion();
~NavRegion();
private: private:
void update_polygons(); void update_polygons();
}; };

View file

@ -33,10 +33,6 @@
#include "core/rid.h" #include "core/rid.h"
/**
@author AndreaCatania
*/
class NavRid : public RID_Data { class NavRid : public RID_Data {
RID self; RID self;

View file

@ -32,11 +32,8 @@
#define NAV_UTILS_H #define NAV_UTILS_H
#include "core/math/vector3.h" #include "core/math/vector3.h"
#include <vector>
/** #include <vector>
@author AndreaCatania
*/
class NavRegion; class NavRegion;
@ -50,8 +47,7 @@ union PointKey {
int64_t z : 21; int64_t z : 21;
}; };
uint64_t key; uint64_t key = 0;
bool operator<(const PointKey &p_key) const { return key < p_key.key; }
}; };
struct EdgeKey { struct EdgeKey {
@ -78,23 +74,20 @@ struct Point {
struct Edge { struct Edge {
/// This edge ID /// This edge ID
int this_edge; int this_edge = -1;
/// Other Polygon /// The gateway in the edge, as, in some case, the whole edge might not be navigable.
Polygon *other_polygon; struct Connection {
Polygon *polygon = nullptr;
/// The other `Polygon` at this edge id has this `Polygon`. int edge = -1;
int other_edge; Vector3 pathway_start;
Vector3 pathway_end;
Edge() { };
this_edge = -1; Vector<Connection> connections;
other_polygon = NULL;
other_edge = -1;
}
}; };
struct Polygon { struct Polygon {
NavRegion *owner; NavRegion *owner = nullptr;
/// The points of this `Polygon` /// The points of this `Polygon`
std::vector<Point> points; std::vector<Point> points;
@ -109,40 +102,24 @@ struct Polygon {
Vector3 center; Vector3 center;
}; };
struct Connection {
Polygon *A;
int A_edge;
Polygon *B;
int B_edge;
Connection() {
A = NULL;
B = NULL;
A_edge = -1;
B_edge = -1;
}
};
struct NavigationPoly { struct NavigationPoly {
uint32_t self_id; uint32_t self_id = 0;
/// This poly. /// This poly.
const Polygon *poly; const Polygon *poly;
/// The previous navigation poly (id in the `navigation_poly` array).
int prev_navigation_poly_id; /// Those 4 variables are used to travel the path backwards.
/// The edge id in this `Poly` to reach the `prev_navigation_poly_id`. int back_navigation_poly_id = -1;
uint32_t back_navigation_edge; uint32_t back_navigation_edge = UINT32_MAX;
Vector3 back_navigation_edge_pathway_start;
Vector3 back_navigation_edge_pathway_end;
/// The entry location of this poly. /// The entry location of this poly.
Vector3 entry; Vector3 entry;
/// The distance to the destination. /// The distance to the destination.
float traveled_distance; float traveled_distance = 0.0;
NavigationPoly(const Polygon *p_poly) : NavigationPoly(const Polygon *p_poly) :
self_id(0), poly(p_poly) {}
poly(p_poly),
prev_navigation_poly_id(-1),
back_navigation_edge(0),
traveled_distance(0.0) {
}
bool operator==(const NavigationPoly &other) const { bool operator==(const NavigationPoly &other) const {
return this->poly == other.poly; return this->poly == other.poly;
@ -153,15 +130,6 @@ struct NavigationPoly {
} }
}; };
struct FreeEdge {
bool is_free;
Polygon *poly;
uint32_t edge_id;
Vector3 edge_center;
Vector3 edge_dir;
float edge_len_squared;
};
struct ClosestPointQueryResult { struct ClosestPointQueryResult {
Vector3 point; Vector3 point;
Vector3 normal; Vector3 normal;

View file

@ -42,10 +42,6 @@
#include "navigation_mesh_editor_plugin.h" #include "navigation_mesh_editor_plugin.h"
#endif #endif
/**
@author AndreaCatania
*/
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
NavigationMeshGenerator *_nav_mesh_generator = nullptr; NavigationMeshGenerator *_nav_mesh_generator = nullptr;
#endif #endif

View file

@ -28,9 +28,5 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/ /*************************************************************************/
/**
@author AndreaCatania
*/
void register_navigation_types(); void register_navigation_types();
void unregister_navigation_types(); void unregister_navigation_types();

View file

@ -32,12 +32,7 @@
#include "nav_map.h" #include "nav_map.h"
/** RvoAgent::RvoAgent() {
@author AndreaCatania
*/
RvoAgent::RvoAgent() :
map(NULL) {
callback.id = ObjectID(0); callback.id = ObjectID(0);
} }
@ -70,7 +65,7 @@ void RvoAgent::dispatch_callback() {
return; return;
} }
Object *obj = ObjectDB::get_instance(callback.id); Object *obj = ObjectDB::get_instance(callback.id);
if (obj == NULL) { if (obj == nullptr) {
callback.id = ObjectID(0); callback.id = ObjectID(0);
} }

View file

@ -33,11 +33,8 @@
#include "core/object.h" #include "core/object.h"
#include "nav_rid.h" #include "nav_rid.h"
#include <Agent.h>
/** #include <Agent.h>
@author AndreaCatania
*/
class NavMap; class NavMap;
@ -49,10 +46,10 @@ class RvoAgent : public NavRid {
Variant new_velocity; Variant new_velocity;
}; };
NavMap *map; NavMap *map = nullptr;
RVO::Agent agent; RVO::Agent agent;
AvoidanceComputedCallback callback; AvoidanceComputedCallback callback;
uint32_t map_update_id; uint32_t map_update_id = 0;
public: public:
RvoAgent(); RvoAgent();

View file

@ -45,8 +45,12 @@ void Navigation2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation2D::set_edge_connection_margin); ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation2D::set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation2D::get_edge_connection_margin); ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation2D::get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &Navigation2D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &Navigation2D::get_navigation_layers);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
} }
void Navigation2D::_notification(int p_what) { void Navigation2D::_notification(int p_what) {
@ -54,9 +58,12 @@ void Navigation2D::_notification(int p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
Navigation2DServer::get_singleton()->map_set_active(map, true); Navigation2DServer::get_singleton()->map_set_active(map, true);
} break; } break;
case NOTIFICATION_EXIT_TREE: { // FIXME 3.5 with this old navigation 2d node only
Navigation2DServer::get_singleton()->map_set_active(map, false); // if the node gets deleted this exit causes annoying error prints in debug
} break; // It tries to deactive a map that itself has send a free command to the server.
//case NOTIFICATION_EXIT_TREE: {
// Navigation2DServer::get_singleton()->map_set_active(map, false);
//} break;
} }
} }
@ -71,7 +78,7 @@ void Navigation2D::set_edge_connection_margin(float p_edge_connection_margin) {
} }
Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) const { Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) const {
return Navigation2DServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize); return Navigation2DServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize, navigation_layers);
} }
Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) const { Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) const {
@ -82,10 +89,19 @@ RID Navigation2D::get_closest_point_owner(const Vector2 &p_point) const {
return Navigation2DServer::get_singleton()->map_get_closest_point_owner(map, p_point); return Navigation2DServer::get_singleton()->map_get_closest_point_owner(map, p_point);
} }
void Navigation2D::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
}
uint32_t Navigation2D::get_navigation_layers() const {
return navigation_layers;
}
Navigation2D::Navigation2D() { Navigation2D::Navigation2D() {
map = Navigation2DServer::get_singleton()->map_create(); map = Navigation2DServer::get_singleton()->map_create();
set_cell_size(1); // One pixel Navigation2DServer::get_singleton()->map_set_active(map, true);
set_edge_connection_margin(1); Navigation2DServer::get_singleton()->map_set_cell_size(map, get_cell_size());
Navigation2DServer::get_singleton()->map_set_edge_connection_margin(map, get_edge_connection_margin());
} }
Navigation2D::~Navigation2D() { Navigation2D::~Navigation2D() {

View file

@ -38,8 +38,9 @@ class Navigation2D : public Node2D {
GDCLASS(Navigation2D, Node2D); GDCLASS(Navigation2D, Node2D);
RID map; RID map;
real_t cell_size; real_t cell_size = 1.0;
real_t edge_connection_margin; real_t edge_connection_margin = 1.0;
uint32_t navigation_layers = 1;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -55,6 +56,9 @@ public:
return cell_size; return cell_size;
} }
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_edge_connection_margin(float p_edge_connection_margin); void set_edge_connection_margin(float p_edge_connection_margin);
float get_edge_connection_margin() const { float get_edge_connection_margin() const {
return edge_connection_margin; return edge_connection_margin;

View file

@ -64,6 +64,9 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance); ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance); ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent2D::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent2D::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location); ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location); ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location); ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location);
@ -86,6 +89,7 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached")); ADD_SIGNAL(MethodInfo("target_reached"));
@ -95,11 +99,7 @@ void NavigationAgent2D::_bind_methods() {
void NavigationAgent2D::_notification(int p_what) { void NavigationAgent2D::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_POST_ENTER_TREE: {
agent_parent = Object::cast_to<Node2D>(get_parent());
set_avoidance_enabled(avoidance_enabled);
// Search the navigation node and set it // Search the navigation node and set it
{ {
Navigation2D *nav = nullptr; Navigation2D *nav = nullptr;
@ -112,12 +112,31 @@ void NavigationAgent2D::_notification(int p_what) {
p = p->get_parent(); p = p->get_parent();
} }
} }
set_navigation(nav); set_navigation(nav);
} }
// need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready.
// cannot use READY as ready does not get called if Node is readded to SceneTree
set_agent_parent(get_parent());
set_physics_process_internal(true); set_physics_process_internal(true);
} break; } break;
case NOTIFICATION_PARENTED: {
if (is_inside_tree() && (get_parent() != agent_parent)) {
// only react to PARENTED notifications when already inside_tree and parent changed, e.g. users switch nodes around
// PARENTED notification fires also when Node is added in scripts to a parent
// this would spam transforms fails and world fails while Node is outside SceneTree
// when node gets reparented when joining the tree POST_ENTER_TREE takes care of this
set_agent_parent(get_parent());
set_physics_process_internal(true);
}
} break;
case NOTIFICATION_UNPARENTED: {
// if agent has no parent no point in processing it until reparented
set_agent_parent(nullptr);
set_physics_process_internal(false);
} break;
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (agent_parent && !agent_parent->can_process()) { if (agent_parent && !agent_parent->can_process()) {
map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid()); map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid());
@ -146,7 +165,11 @@ void NavigationAgent2D::_notification(int p_what) {
} break; } break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) { if (agent_parent) {
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().get_origin()); if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
}
_check_distance_to_target(); _check_distance_to_target();
} }
} break; } break;
@ -189,6 +212,26 @@ bool NavigationAgent2D::get_avoidance_enabled() const {
return avoidance_enabled; return avoidance_enabled;
} }
void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
Navigation2DServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done");
if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Node2D>(p_agent_parent);
if (navigation == nullptr) {
// no navigation node found in parent nodes, use default navigation map from world resource
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map());
} else {
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid());
}
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
agent_parent = nullptr;
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
}
}
void NavigationAgent2D::set_navigation(Navigation2D *p_nav) { void NavigationAgent2D::set_navigation(Navigation2D *p_nav) {
if (navigation == p_nav) { if (navigation == p_nav) {
return; // Pointless return; // Pointless
@ -208,6 +251,18 @@ Node *NavigationAgent2D::get_navigation_node() const {
return Object::cast_to<Node>(navigation); return Object::cast_to<Node>(navigation);
} }
void NavigationAgent2D::set_navigation_layers(uint32_t p_navigation_layers) {
bool _navigation_layers_changed = navigation_layers != p_navigation_layers;
navigation_layers = p_navigation_layers;
if (_navigation_layers_changed) {
_request_repath();
}
}
uint32_t NavigationAgent2D::get_navigation_layers() const {
return navigation_layers;
}
void NavigationAgent2D::set_target_desired_distance(real_t p_dd) { void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
target_desired_distance = p_dd; target_desired_distance = p_dd;
} }
@ -247,10 +302,7 @@ real_t NavigationAgent2D::get_path_max_distance() {
void NavigationAgent2D::set_target_location(Vector2 p_location) { void NavigationAgent2D::set_target_location(Vector2 p_location) {
target_location = p_location; target_location = p_location;
navigation_path.clear(); _request_repath();
target_reached = false;
navigation_finished = false;
update_frame_id = 0;
} }
Vector2 NavigationAgent2D::get_target_location() const { Vector2 NavigationAgent2D::get_target_location() const {
@ -357,7 +409,7 @@ void NavigationAgent2D::update_navigation() {
} }
if (reload_path) { if (reload_path) {
navigation_path = Navigation2DServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true); navigation_path = Navigation2DServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true, navigation_layers);
navigation_finished = false; navigation_finished = false;
nav_path_index = 0; nav_path_index = 0;
emit_signal("path_changed"); emit_signal("path_changed");
@ -383,6 +435,13 @@ void NavigationAgent2D::update_navigation() {
} }
} }
void NavigationAgent2D::_request_repath() {
navigation_path.clear();
target_reached = false;
navigation_finished = false;
update_frame_id = 0;
}
void NavigationAgent2D::_check_distance_to_target() { void NavigationAgent2D::_check_distance_to_target() {
if (!target_reached) { if (!target_reached) {
if (distance_to_target() < target_desired_distance) { if (distance_to_target() < target_desired_distance) {

View file

@ -40,34 +40,35 @@ class Navigation2D;
class NavigationAgent2D : public Node { class NavigationAgent2D : public Node {
GDCLASS(NavigationAgent2D, Node); GDCLASS(NavigationAgent2D, Node);
Node2D *agent_parent; Node2D *agent_parent = nullptr;
Navigation2D *navigation; Navigation2D *navigation = nullptr;
RID agent; RID agent;
RID map_before_pause; RID map_before_pause;
bool avoidance_enabled; bool avoidance_enabled = false;
uint32_t navigation_layers = 1;
real_t target_desired_distance; real_t target_desired_distance = 1.0;
real_t radius; real_t radius = 0.0;
real_t neighbor_dist; real_t neighbor_dist = 0.0;
int max_neighbors; int max_neighbors = 0;
real_t time_horizon; real_t time_horizon = 0.0;
real_t max_speed; real_t max_speed = 0.0;
real_t path_max_distance; real_t path_max_distance = 3.0;
Vector2 target_location; Vector2 target_location;
Vector<Vector2> navigation_path; Vector<Vector2> navigation_path;
int nav_path_index; int nav_path_index = 0;
bool velocity_submitted; bool velocity_submitted = false;
Vector2 prev_safe_velocity; Vector2 prev_safe_velocity;
/// The submitted target velocity /// The submitted target velocity
Vector2 target_velocity; Vector2 target_velocity;
bool target_reached; bool target_reached = false;
bool navigation_finished; bool navigation_finished = true;
// No initialized on purpose // No initialized on purpose
uint32_t update_frame_id; uint32_t update_frame_id = 0;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -92,6 +93,11 @@ public:
void set_avoidance_enabled(bool p_enabled); void set_avoidance_enabled(bool p_enabled);
bool get_avoidance_enabled() const; bool get_avoidance_enabled() const;
void set_agent_parent(Node *p_agent_parent);
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_target_desired_distance(real_t p_dd); void set_target_desired_distance(real_t p_dd);
real_t get_target_desired_distance() const { real_t get_target_desired_distance() const {
return target_desired_distance; return target_desired_distance;
@ -151,6 +157,7 @@ public:
private: private:
void update_navigation(); void update_navigation();
void _request_repath();
void _check_distance_to_target(); void _check_distance_to_target();
}; };

View file

@ -151,10 +151,12 @@ void NavigationPolygon::add_outline_at_index(const PoolVector<Vector2> &p_outlin
int NavigationPolygon::get_polygon_count() const { int NavigationPolygon::get_polygon_count() const {
return polygons.size(); return polygons.size();
} }
Vector<int> NavigationPolygon::get_polygon(int p_idx) { Vector<int> NavigationPolygon::get_polygon(int p_idx) {
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
return polygons[p_idx].indices; return polygons[p_idx].indices;
} }
void NavigationPolygon::clear_polygons() { void NavigationPolygon::clear_polygons() {
polygons.clear(); polygons.clear();
{ {
@ -165,6 +167,7 @@ void NavigationPolygon::clear_polygons() {
Ref<NavigationMesh> NavigationPolygon::get_mesh() { Ref<NavigationMesh> NavigationPolygon::get_mesh() {
MutexLock lock(navmesh_generation); MutexLock lock(navmesh_generation);
if (navmesh.is_null()) { if (navmesh.is_null()) {
navmesh.instance(); navmesh.instance();
PoolVector<Vector3> verts; PoolVector<Vector3> verts;
@ -184,6 +187,7 @@ Ref<NavigationMesh> NavigationPolygon::get_mesh() {
navmesh->add_polygon(get_polygon(i)); navmesh->add_polygon(get_polygon(i));
} }
} }
return navmesh; return navmesh;
} }
@ -217,6 +221,7 @@ void NavigationPolygon::clear_outlines() {
outlines.clear(); outlines.clear();
rect_cache_dirty = true; rect_cache_dirty = true;
} }
void NavigationPolygon::make_polygons_from_outlines() { void NavigationPolygon::make_polygons_from_outlines() {
{ {
MutexLock lock(navmesh_generation); MutexLock lock(navmesh_generation);
@ -348,8 +353,7 @@ void NavigationPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
} }
NavigationPolygon::NavigationPolygon() : NavigationPolygon::NavigationPolygon() {
rect_cache_dirty(true) {
} }
NavigationPolygon::~NavigationPolygon() { NavigationPolygon::~NavigationPolygon() {
@ -367,10 +371,14 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
if (!enabled) { if (!enabled) {
Navigation2DServer::get_singleton()->region_set_map(region, RID()); Navigation2DServer::get_singleton()->region_set_map(region, RID());
Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed");
} else { } else {
if (navigation) { if (navigation != nullptr) {
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
} else {
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
} }
Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed");
} }
if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) {
@ -382,6 +390,35 @@ bool NavigationPolygonInstance::is_enabled() const {
return enabled; return enabled;
} }
void NavigationPolygonInstance::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
Navigation2DServer::get_singleton()->region_set_navigation_layers(region, navigation_layers);
}
uint32_t NavigationPolygonInstance::get_navigation_layers() const {
return navigation_layers;
}
void NavigationPolygonInstance::set_enter_cost(real_t p_enter_cost) {
ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive.");
enter_cost = MAX(p_enter_cost, 0.0);
Navigation2DServer::get_singleton()->region_set_enter_cost(region, p_enter_cost);
}
real_t NavigationPolygonInstance::get_enter_cost() const {
return enter_cost;
}
void NavigationPolygonInstance::set_travel_cost(real_t p_travel_cost) {
ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive.");
travel_cost = MAX(p_travel_cost, 0.0);
Navigation2DServer::get_singleton()->region_set_enter_cost(region, travel_cost);
}
real_t NavigationPolygonInstance::get_travel_cost() const {
return travel_cost;
}
RID NavigationPolygonInstance::get_region_rid() const { RID NavigationPolygonInstance::get_region_rid() const {
return region; return region;
} }
@ -409,26 +446,35 @@ void NavigationPolygonInstance::_notification(int p_what) {
} }
break; break;
} }
c = Object::cast_to<Node2D>(c->get_parent()); c = Object::cast_to<Node2D>(c->get_parent());
} }
if (enabled && navigation == nullptr) {
// did not find a valid navigation node parent, fallback to default navigation map on world resource
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
}
if (enabled) {
Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed");
}
} break; } break;
case NOTIFICATION_TRANSFORM_CHANGED: { case NOTIFICATION_TRANSFORM_CHANGED: {
Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform()); Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform());
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
if (navigation) { if (navigation) {
Navigation2DServer::get_singleton()->region_set_map(region, RID()); Navigation2DServer::get_singleton()->region_set_map(region, RID());
} }
navigation = nullptr; navigation = nullptr;
if (enabled) {
Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed");
}
} break; } break;
case NOTIFICATION_DRAW: { case NOTIFICATION_DRAW: {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) {
PoolVector<Vector2> verts = navpoly->get_vertices(); PoolVector<Vector2> verts = navpoly->get_vertices();
int vsize = verts.size(); if (verts.size() < 3) {
if (vsize < 3) {
return; return;
} }
@ -438,33 +484,54 @@ void NavigationPolygonInstance::_notification(int p_what) {
} else { } else {
color = get_tree()->get_debug_navigation_disabled_color(); color = get_tree()->get_debug_navigation_disabled_color();
} }
Vector<Color> colors; Color doors_color = color.lightened(0.2);
Vector<Vector2> vertices;
vertices.resize(vsize);
colors.resize(vsize);
{
PoolVector<Vector2>::Read vr = verts.read();
for (int i = 0; i < vsize; i++) {
vertices.write[i] = vr[i];
colors.write[i] = color;
}
}
Vector<int> indices; RandomPCG rand;
for (int i = 0; i < navpoly->get_polygon_count(); i++) { for (int i = 0; i < navpoly->get_polygon_count(); i++) {
// An array of vertices for this polygon.
Vector<int> polygon = navpoly->get_polygon(i); Vector<int> polygon = navpoly->get_polygon(i);
for (int j = 2; j < polygon.size(); j++) { Vector<Vector2> vertices;
int kofs[3] = { 0, j - 1, j }; vertices.resize(polygon.size());
for (int k = 0; k < 3; k++) { for (int j = 0; j < polygon.size(); j++) {
int idx = polygon[kofs[k]]; ERR_FAIL_INDEX(polygon[j], verts.size());
ERR_FAIL_INDEX(idx, vsize); vertices.write[j] = verts[polygon[j]];
indices.push_back(idx);
}
} }
// Generate the polygon color, slightly randomly modified from the settings one.
Color random_variation_color;
random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1);
random_variation_color.a = color.a;
Vector<Color> colors;
colors.push_back(random_variation_color);
VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), vertices, colors);
}
// Draw the region
Transform2D xform = get_global_transform();
const Navigation2DServer *ns = Navigation2DServer::get_singleton();
real_t radius = 1.0;
if (navigation != nullptr) {
radius = Navigation2DServer::get_singleton()->map_get_edge_connection_margin(navigation->get_rid());
} else {
radius = Navigation2DServer::get_singleton()->map_get_edge_connection_margin(get_world_2d()->get_navigation_map());
}
radius = radius * 0.5;
for (int i = 0; i < ns->region_get_connections_count(region); i++) {
// Two main points
Vector2 a = ns->region_get_connection_pathway_start(region, i);
a = xform.affine_inverse().xform(a);
Vector2 b = ns->region_get_connection_pathway_end(region, i);
b = xform.affine_inverse().xform(b);
draw_line(a, b, doors_color);
// Draw a circle to illustrate the margins.
real_t angle = a.angle_to_point(b);
draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color);
draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color);
} }
VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, vertices, colors);
} }
} break; } break;
} }
@ -504,6 +571,14 @@ void NavigationPolygonInstance::_navpoly_changed() {
} }
} }
void NavigationPolygonInstance::_map_changed(RID p_map) {
if (navigation != nullptr && enabled && (navigation->get_rid() == p_map)) {
update();
} else if (is_inside_tree() && enabled && (get_world_2d()->get_navigation_map() == p_map)) {
update();
}
}
String NavigationPolygonInstance::get_configuration_warning() const { String NavigationPolygonInstance::get_configuration_warning() const {
if (!is_visible_in_tree() || !is_inside_tree()) { if (!is_visible_in_tree() || !is_inside_tree()) {
return String(); return String();
@ -516,19 +591,6 @@ String NavigationPolygonInstance::get_configuration_warning() const {
} }
warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon.");
} }
const Node2D *c = this;
while (c) {
if (Object::cast_to<Navigation2D>(c)) {
return warning;
}
c = Object::cast_to<Node2D>(c->get_parent());
}
if (warning != String()) {
warning += "\n\n";
}
warning += TTR("NavigationPolygonInstance must be a child or grandchild to a Navigation2D node. It only provides navigation data.");
return warning; return warning;
} }
@ -540,20 +602,33 @@ void NavigationPolygonInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationPolygonInstance::set_enabled); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationPolygonInstance::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationPolygonInstance::is_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationPolygonInstance::is_enabled);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPolygonInstance::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPolygonInstance::get_navigation_layers);
ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationPolygonInstance::get_region_rid); ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationPolygonInstance::get_region_rid);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationPolygonInstance::set_enter_cost);
ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationPolygonInstance::get_enter_cost);
ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationPolygonInstance::set_travel_cost);
ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationPolygonInstance::get_travel_cost);
ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed); ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "travel_cost"), "set_travel_cost", "get_travel_cost");
ClassDB::bind_method(D_METHOD("_map_changed"), &NavigationPolygonInstance::_map_changed);
} }
NavigationPolygonInstance::NavigationPolygonInstance() { NavigationPolygonInstance::NavigationPolygonInstance() {
enabled = true;
set_notify_transform(true); set_notify_transform(true);
region = Navigation2DServer::get_singleton()->region_create(); region = Navigation2DServer::get_singleton()->region_create();
Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
navigation = nullptr; Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
} }
NavigationPolygonInstance::~NavigationPolygonInstance() { NavigationPolygonInstance::~NavigationPolygonInstance() {

View file

@ -45,7 +45,7 @@ class NavigationPolygon : public Resource {
Vector<PoolVector<Vector2>> outlines; Vector<PoolVector<Vector2>> outlines;
mutable Rect2 item_rect; mutable Rect2 item_rect;
mutable bool rect_cache_dirty; mutable bool rect_cache_dirty = true;
Mutex navmesh_generation; Mutex navmesh_generation;
// Navigation mesh // Navigation mesh
@ -96,12 +96,18 @@ class Navigation2D;
class NavigationPolygonInstance : public Node2D { class NavigationPolygonInstance : public Node2D {
GDCLASS(NavigationPolygonInstance, Node2D); GDCLASS(NavigationPolygonInstance, Node2D);
bool enabled; bool enabled = true;
RID region; RID region;
Navigation2D *navigation; Navigation2D *navigation = nullptr;
Ref<NavigationPolygon> navpoly; Ref<NavigationPolygon> navpoly;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
uint32_t navigation_layers = 1;
void _navpoly_changed(); void _navpoly_changed();
void _map_changed(RID p_map);
protected: protected:
void _notification(int p_what); void _notification(int p_what);
@ -116,8 +122,17 @@ public:
void set_enabled(bool p_enabled); void set_enabled(bool p_enabled);
bool is_enabled() const; bool is_enabled() const;
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
RID get_region_rid() const; RID get_region_rid() const;
void set_enter_cost(real_t p_enter_cost);
real_t get_enter_cost() const;
void set_travel_cost(real_t p_travel_cost);
real_t get_travel_cost() const;
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly); void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly);
Ref<NavigationPolygon> get_navigation_polygon() const; Ref<NavigationPolygon> get_navigation_polygon() const;

View file

@ -62,6 +62,8 @@ void TileMap::_notification(int p_what) {
while (c) { while (c) {
navigation = Object::cast_to<Navigation2D>(c); navigation = Object::cast_to<Navigation2D>(c);
if (navigation) { if (navigation) {
// only for <3.5 backward compatibility
bake_navigation = true;
break; break;
} }
@ -87,7 +89,7 @@ void TileMap::_notification(int p_what) {
_update_quadrant_space(RID()); _update_quadrant_space(RID());
for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
Quadrant &q = E->get(); Quadrant &q = E->get();
if (navigation) { if (bake_navigation) {
q.clear_navpoly(); q.clear_navpoly();
} }
@ -154,7 +156,7 @@ void TileMap::_update_quadrant_transform() {
} }
Transform2D nav_rel; Transform2D nav_rel;
if (navigation) { if (bake_navigation) {
nav_rel = get_relative_transform_to_parent(navigation); nav_rel = get_relative_transform_to_parent(navigation);
} }
@ -168,7 +170,7 @@ void TileMap::_update_quadrant_transform() {
Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform); Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform);
} }
if (navigation) { if (bake_navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) { for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
Navigation2DServer::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform); Navigation2DServer::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform);
} }
@ -336,7 +338,7 @@ void TileMap::update_dirty_quadrants() {
Physics2DServer *ps = Physics2DServer::get_singleton(); Physics2DServer *ps = Physics2DServer::get_singleton();
Vector2 tofs = get_cell_draw_offset(); Vector2 tofs = get_cell_draw_offset();
Transform2D nav_rel; Transform2D nav_rel;
if (navigation) { if (bake_navigation) {
nav_rel = get_relative_transform_to_parent(navigation); nav_rel = get_relative_transform_to_parent(navigation);
} }
@ -381,7 +383,7 @@ void TileMap::update_dirty_quadrants() {
} }
int shape_idx = 0; int shape_idx = 0;
if (navigation) { if (bake_navigation) {
q.clear_navpoly(); q.clear_navpoly();
} }
@ -604,7 +606,7 @@ void TileMap::update_dirty_quadrants() {
vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D()); vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D());
} }
if (navigation) { if (bake_navigation) {
Ref<NavigationPolygon> navpoly; Ref<NavigationPolygon> navpoly;
Vector2 npoly_ofs; Vector2 npoly_ofs;
if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) {
@ -621,7 +623,12 @@ void TileMap::update_dirty_quadrants() {
_fix_cell_transform(xform, c, npoly_ofs, s); _fix_cell_transform(xform, c, npoly_ofs, s);
RID region = Navigation2DServer::get_singleton()->region_create(); RID region = Navigation2DServer::get_singleton()->region_create();
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); if (navigation) {
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
} else {
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
}
Navigation2DServer::get_singleton()->region_set_navigation_layers(region, navigation_layers);
Navigation2DServer::get_singleton()->region_set_transform(region, nav_rel * xform); Navigation2DServer::get_singleton()->region_set_transform(region, nav_rel * xform);
Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly); Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly);
@ -818,7 +825,7 @@ void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
dirty_quadrant_list.remove(&q.dirty_list); dirty_quadrant_list.remove(&q.dirty_list);
} }
if (navigation) { if (bake_navigation) {
q.clear_navpoly(); q.clear_navpoly();
} }
@ -1394,6 +1401,28 @@ float TileMap::get_collision_bounce() const {
return bounce; return bounce;
} }
void TileMap::set_bake_navigation(bool p_bake_navigation) {
bake_navigation = p_bake_navigation;
for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
_make_quadrant_dirty(E);
}
}
bool TileMap::is_baking_navigation() {
return bake_navigation;
}
void TileMap::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
_make_quadrant_dirty(E);
}
}
uint32_t TileMap::get_navigation_layers() {
return navigation_layers;
}
uint32_t TileMap::get_collision_layer() const { uint32_t TileMap::get_collision_layer() const {
return collision_layer; return collision_layer;
} }
@ -1811,6 +1840,12 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_bounce", "value"), &TileMap::set_collision_bounce); ClassDB::bind_method(D_METHOD("set_collision_bounce", "value"), &TileMap::set_collision_bounce);
ClassDB::bind_method(D_METHOD("get_collision_bounce"), &TileMap::get_collision_bounce); ClassDB::bind_method(D_METHOD("get_collision_bounce"), &TileMap::get_collision_bounce);
ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &TileMap::set_bake_navigation);
ClassDB::bind_method(D_METHOD("is_baking_navigation"), &TileMap::is_baking_navigation);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &TileMap::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &TileMap::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &TileMap::set_occluder_light_mask); ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &TileMap::set_occluder_light_mask);
ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &TileMap::get_occluder_light_mask); ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &TileMap::get_occluder_light_mask);
@ -1868,6 +1903,10 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Navigation", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_GROUP("Occluder", "occluder_"); ADD_GROUP("Occluder", "occluder_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");

View file

@ -79,6 +79,8 @@ private:
CollisionObject2D *collision_parent; CollisionObject2D *collision_parent;
bool use_kinematic; bool use_kinematic;
Navigation2D *navigation; Navigation2D *navigation;
bool bake_navigation = false;
uint32_t navigation_layers = 1;
bool show_collision = false; bool show_collision = false;
union PosKey { union PosKey {
@ -303,6 +305,12 @@ public:
void set_collision_bounce(float p_bounce); void set_collision_bounce(float p_bounce);
float get_collision_bounce() const; float get_collision_bounce() const;
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers();
void set_mode(Mode p_mode); void set_mode(Mode p_mode);
Mode get_mode() const; Mode get_mode() const;

View file

@ -33,7 +33,7 @@
#include "servers/navigation_server.h" #include "servers/navigation_server.h"
Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const { Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const {
return NavigationServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize); return NavigationServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize, navigation_layers);
} }
Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const {
@ -76,6 +76,14 @@ void Navigation::set_edge_connection_margin(float p_edge_connection_margin) {
NavigationServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin); NavigationServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin);
} }
void Navigation::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
}
uint32_t Navigation::get_navigation_layers() const {
return navigation_layers;
}
void Navigation::_bind_methods() { void Navigation::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rid"), &Navigation::get_rid); ClassDB::bind_method(D_METHOD("get_rid"), &Navigation::get_rid);
@ -97,10 +105,16 @@ void Navigation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation::set_edge_connection_margin); ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation::set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation::get_edge_connection_margin); ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation::get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &Navigation::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &Navigation::get_navigation_layers);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_height"), "set_cell_height", "get_cell_height"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_height"), "set_cell_height", "get_cell_height");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::_RID, "map")));
} }
void Navigation::_notification(int p_what) { void Navigation::_notification(int p_what) {
@ -109,19 +123,21 @@ void Navigation::_notification(int p_what) {
NavigationServer::get_singleton()->map_set_active(map, true); NavigationServer::get_singleton()->map_set_active(map, true);
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
NavigationServer::get_singleton()->map_set_active(map, false); // FIXME 3.5 with this old navigation node only
// if the node gets deleted this exit causes annoying error prints in debug
// It tries to deactive a map that itself has send a free command to the server.
//NavigationServer::get_singleton()->map_set_active(map, false);
} break; } break;
} }
} }
Navigation::Navigation() { Navigation::Navigation() {
map = NavigationServer::get_singleton()->map_create(); map = NavigationServer::get_singleton()->map_create();
NavigationServer::get_singleton()->map_set_active(map, true);
set_cell_size(0.3); NavigationServer::get_singleton()->map_set_up(map, get_up_vector());
set_cell_height(0.2); NavigationServer::get_singleton()->map_set_cell_size(map, get_cell_size());
set_edge_connection_margin(5.0); // Five meters, depends a lot on the agents radius NavigationServer::get_singleton()->map_set_cell_height(map, get_cell_height());
NavigationServer::get_singleton()->map_set_edge_connection_margin(map, get_edge_connection_margin());
up = Vector3(0, 1, 0);
} }
Navigation::~Navigation() { Navigation::~Navigation() {

View file

@ -39,10 +39,12 @@ class Navigation : public Spatial {
RID map; RID map;
Vector3 up; Vector3 up = Vector3(0, 1, 0);
real_t cell_size; real_t cell_size = 0.25;
real_t cell_height; real_t cell_height = 0.25;
real_t edge_connection_margin; real_t edge_connection_margin = 0.25;
uint32_t navigation_layers = 1;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -66,6 +68,9 @@ public:
return cell_height; return cell_height;
} }
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_edge_connection_margin(float p_edge_connection_margin); void set_edge_connection_margin(float p_edge_connection_margin);
float get_edge_connection_margin() const { float get_edge_connection_margin() const {
return edge_connection_margin; return edge_connection_margin;

View file

@ -70,6 +70,9 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent::set_path_max_distance); ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent::get_path_max_distance); ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent::get_path_max_distance);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent::set_target_location); ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent::get_target_location); ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent::get_next_location); ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent::get_next_location);
@ -94,6 +97,7 @@ void NavigationAgent::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached")); ADD_SIGNAL(MethodInfo("target_reached"));
@ -103,11 +107,7 @@ void NavigationAgent::_bind_methods() {
void NavigationAgent::_notification(int p_what) { void NavigationAgent::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_POST_ENTER_TREE: {
agent_parent = Object::cast_to<Spatial>(get_parent());
set_avoidance_enabled(avoidance_enabled);
// Search the navigation node and set it // Search the navigation node and set it
{ {
Navigation *nav = nullptr; Navigation *nav = nullptr;
@ -120,20 +120,40 @@ void NavigationAgent::_notification(int p_what) {
p = p->get_parent(); p = p->get_parent();
} }
} }
set_navigation(nav); set_navigation(nav);
} }
// need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready.
// cannot use READY as ready does not get called if Node is readded to SceneTree
set_agent_parent(get_parent());
set_physics_process_internal(true); set_physics_process_internal(true);
} break; } break;
case NOTIFICATION_PARENTED: {
if (is_inside_tree() && (get_parent() != agent_parent)) {
// only react to PARENTED notifications when already inside_tree and parent changed, e.g. users switch nodes around
// PARENTED notification fires also when Node is added in scripts to a parent
// this would spam transforms fails and world fails while Node is outside SceneTree
// when node gets reparented when joining the tree POST_ENTER_TREE takes care of this
set_agent_parent(get_parent());
set_physics_process_internal(true);
}
} break;
case NOTIFICATION_UNPARENTED: {
// if agent has no parent no point in processing it until reparented
set_agent_parent(nullptr);
set_physics_process_internal(false);
} break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
agent_parent = nullptr; set_agent_parent(nullptr);
set_navigation(nullptr); set_navigation(nullptr);
set_physics_process_internal(false); set_physics_process_internal(false);
// Want to call ready again when the node enters the tree again. We're not using enter_tree notification because // Want to call ready again when the node enters the tree again. We're not using enter_tree notification because
// the navigation map may not be ready at that time. This fixes issues with taking the agent out of the scene tree. // the navigation map may not be ready at that time. This fixes issues with taking the agent out of the scene tree.
request_ready(); request_ready();
} break; } break;
case NOTIFICATION_PAUSED: { case NOTIFICATION_PAUSED: {
if (agent_parent && !agent_parent->can_process()) { if (agent_parent && !agent_parent->can_process()) {
map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid()); map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid());
@ -143,6 +163,7 @@ void NavigationAgent::_notification(int p_what) {
map_before_pause = RID(); map_before_pause = RID();
} }
} break; } break;
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (agent_parent && !agent_parent->can_process()) { if (agent_parent && !agent_parent->can_process()) {
map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid()); map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid());
@ -152,26 +173,21 @@ void NavigationAgent::_notification(int p_what) {
map_before_pause = RID(); map_before_pause = RID();
} }
} break; } break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) { if (agent_parent) {
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
}
_check_distance_to_target(); _check_distance_to_target();
} }
} break; } break;
} }
} }
NavigationAgent::NavigationAgent() : NavigationAgent::NavigationAgent() {
agent_parent(nullptr),
navigation(nullptr),
agent(RID()),
avoidance_enabled(false),
target_desired_distance(1.0),
navigation_height_offset(0.0),
path_max_distance(3.0),
velocity_submitted(false),
target_reached(false),
navigation_finished(true) {
agent = NavigationServer::get_singleton()->agent_create(); agent = NavigationServer::get_singleton()->agent_create();
set_neighbor_dist(50.0); set_neighbor_dist(50.0);
set_max_neighbors(10); set_max_neighbors(10);
@ -218,6 +234,38 @@ Node *NavigationAgent::get_navigation_node() const {
return Object::cast_to<Node>(navigation); return Object::cast_to<Node>(navigation);
} }
void NavigationAgent::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
NavigationServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done");
if (Object::cast_to<Spatial>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Spatial>(p_agent_parent);
if (navigation == nullptr) {
// no navigation node found in parent nodes, use default navigation map from world resource
NavigationServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world()->get_navigation_map());
} else {
NavigationServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid());
}
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
agent_parent = nullptr;
NavigationServer::get_singleton()->agent_set_map(get_rid(), RID());
}
}
void NavigationAgent::set_navigation_layers(uint32_t p_layers) {
bool _navigation_layers_changed = navigation_layers != p_layers;
navigation_layers = p_layers;
if (_navigation_layers_changed) {
_request_repath();
}
}
uint32_t NavigationAgent::get_navigation_layers() const {
return navigation_layers;
}
void NavigationAgent::set_target_desired_distance(real_t p_dd) { void NavigationAgent::set_target_desired_distance(real_t p_dd) {
target_desired_distance = p_dd; target_desired_distance = p_dd;
} }
@ -266,10 +314,7 @@ real_t NavigationAgent::get_path_max_distance() {
void NavigationAgent::set_target_location(Vector3 p_location) { void NavigationAgent::set_target_location(Vector3 p_location) {
target_location = p_location; target_location = p_location;
navigation_path.clear(); _request_repath();
target_reached = false;
navigation_finished = false;
update_frame_id = 0;
} }
Vector3 NavigationAgent::get_target_location() const { Vector3 NavigationAgent::get_target_location() const {
@ -279,7 +324,7 @@ Vector3 NavigationAgent::get_target_location() const {
Vector3 NavigationAgent::get_next_location() { Vector3 NavigationAgent::get_next_location() {
update_navigation(); update_navigation();
if (navigation_path.size() == 0) { if (navigation_path.size() == 0) {
ERR_FAIL_COND_V(agent_parent == nullptr, Vector3()); ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
return agent_parent->get_global_transform().origin; return agent_parent->get_global_transform().origin;
} else { } else {
return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0); return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
@ -287,7 +332,7 @@ Vector3 NavigationAgent::get_next_location() {
} }
real_t NavigationAgent::distance_to_target() const { real_t NavigationAgent::distance_to_target() const {
ERR_FAIL_COND_V(agent_parent == nullptr, 0.0); ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent.");
return agent_parent->get_global_transform().origin.distance_to(target_location); return agent_parent->get_global_transform().origin.distance_to(target_location);
} }
@ -343,7 +388,7 @@ void NavigationAgent::update_navigation() {
if (agent_parent == nullptr) { if (agent_parent == nullptr) {
return; return;
} }
if (navigation == nullptr) { if (!agent_parent->is_inside_tree()) {
return; return;
} }
if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
@ -377,7 +422,11 @@ void NavigationAgent::update_navigation() {
} }
if (reload_path) { if (reload_path) {
navigation_path = NavigationServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true); if (navigation == nullptr) {
navigation_path = NavigationServer::get_singleton()->map_get_path(agent_parent->get_world()->get_navigation_map(), o, target_location, true, navigation_layers);
} else {
navigation_path = NavigationServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true, navigation_layers);
}
navigation_finished = false; navigation_finished = false;
nav_path_index = 0; nav_path_index = 0;
emit_signal("path_changed"); emit_signal("path_changed");
@ -403,6 +452,13 @@ void NavigationAgent::update_navigation() {
} }
} }
void NavigationAgent::_request_repath() {
navigation_path.clear();
target_reached = false;
navigation_finished = false;
update_frame_id = 0;
}
void NavigationAgent::_check_distance_to_target() { void NavigationAgent::_check_distance_to_target() {
if (!target_reached) { if (!target_reached) {
if (distance_to_target() < target_desired_distance) { if (distance_to_target() < target_desired_distance) {

View file

@ -40,36 +40,37 @@ class Navigation;
class NavigationAgent : public Node { class NavigationAgent : public Node {
GDCLASS(NavigationAgent, Node); GDCLASS(NavigationAgent, Node);
Spatial *agent_parent; Spatial *agent_parent = nullptr;
Navigation *navigation; Navigation *navigation = nullptr;
RID agent; RID agent;
RID map_before_pause; RID map_before_pause;
bool avoidance_enabled; bool avoidance_enabled = false;
uint32_t navigation_layers = 1;
real_t target_desired_distance; real_t target_desired_distance = 1.0;
real_t radius; real_t radius = 0.0;
real_t navigation_height_offset; real_t navigation_height_offset = 0.0;
bool ignore_y; bool ignore_y = false;
real_t neighbor_dist; real_t neighbor_dist = 0.0;
int max_neighbors; int max_neighbors = 0;
real_t time_horizon; real_t time_horizon = 0.0;
real_t max_speed; real_t max_speed = 0.0;
real_t path_max_distance; real_t path_max_distance = 3.0;
Vector3 target_location; Vector3 target_location;
Vector<Vector3> navigation_path; Vector<Vector3> navigation_path;
int nav_path_index; int nav_path_index = 0;
bool velocity_submitted; bool velocity_submitted = false;
Vector3 prev_safe_velocity; Vector3 prev_safe_velocity;
/// The submitted target velocity /// The submitted target velocity
Vector3 target_velocity; Vector3 target_velocity;
bool target_reached; bool target_reached = false;
bool navigation_finished; bool navigation_finished = true;
// No initialized on purpose // No initialized on purpose
uint32_t update_frame_id; uint32_t update_frame_id = 0;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -94,6 +95,11 @@ public:
void set_avoidance_enabled(bool p_enabled); void set_avoidance_enabled(bool p_enabled);
bool get_avoidance_enabled() const; bool get_avoidance_enabled() const;
void set_agent_parent(Node *p_agent_parent);
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
void set_target_desired_distance(real_t p_dd); void set_target_desired_distance(real_t p_dd);
real_t get_target_desired_distance() const { real_t get_target_desired_distance() const {
return target_desired_distance; return target_desired_distance;
@ -163,6 +169,7 @@ public:
private: private:
void update_navigation(); void update_navigation();
void _request_repath();
void _check_distance_to_target(); void _check_distance_to_target();
}; };

View file

@ -51,6 +51,8 @@ void NavigationMeshInstance::set_enabled(bool p_enabled) {
} else { } else {
if (navigation) { if (navigation) {
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
} else {
NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map());
} }
} }
@ -70,6 +72,35 @@ bool NavigationMeshInstance::is_enabled() const {
return enabled; return enabled;
} }
void NavigationMeshInstance::set_navigation_layers(uint32_t p_navigation_layers) {
navigation_layers = p_navigation_layers;
NavigationServer::get_singleton()->region_set_navigation_layers(region, navigation_layers);
}
uint32_t NavigationMeshInstance::get_navigation_layers() const {
return navigation_layers;
}
void NavigationMeshInstance::set_enter_cost(real_t p_enter_cost) {
ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive.");
enter_cost = MAX(p_enter_cost, 0.0);
NavigationServer::get_singleton()->region_set_enter_cost(region, p_enter_cost);
}
real_t NavigationMeshInstance::get_enter_cost() const {
return enter_cost;
}
void NavigationMeshInstance::set_travel_cost(real_t p_travel_cost) {
ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive.");
travel_cost = MAX(p_travel_cost, 0.0);
NavigationServer::get_singleton()->region_set_enter_cost(region, travel_cost);
}
real_t NavigationMeshInstance::get_travel_cost() const {
return travel_cost;
}
RID NavigationMeshInstance::get_region_rid() const { RID NavigationMeshInstance::get_region_rid() const {
return region; return region;
} }
@ -92,6 +123,11 @@ void NavigationMeshInstance::_notification(int p_what) {
c = c->get_parent_spatial(); c = c->get_parent_spatial();
} }
if (enabled && navigation == nullptr) {
// did not find a valid navigation node parent, fallback to default navigation map on world resource
NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map());
}
if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
MeshInstance *dm = memnew(MeshInstance); MeshInstance *dm = memnew(MeshInstance);
dm->set_mesh(navmesh->get_debug_mesh()); dm->set_mesh(navmesh->get_debug_mesh());
@ -165,7 +201,7 @@ Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const {
} }
struct BakeThreadsArgs { struct BakeThreadsArgs {
NavigationMeshInstance *nav_region; NavigationMeshInstance *nav_region = nullptr;
}; };
void _bake_navigation_mesh(void *p_user_data) { void _bake_navigation_mesh(void *p_user_data) {
@ -216,16 +252,8 @@ String NavigationMeshInstance::get_configuration_warning() const {
if (!navmesh.is_valid()) { if (!navmesh.is_valid()) {
return TTR("A NavigationMesh resource must be set or created for this node to work."); return TTR("A NavigationMesh resource must be set or created for this node to work.");
} }
const Spatial *c = this;
while (c) {
if (Object::cast_to<Navigation>(c)) {
return String();
}
c = Object::cast_to<Spatial>(c->get_parent()); return String();
}
return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
} }
void NavigationMeshInstance::_bind_methods() { void NavigationMeshInstance::_bind_methods() {
@ -235,13 +263,25 @@ void NavigationMeshInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled);
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationMeshInstance::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationMeshInstance::get_navigation_layers);
ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationMeshInstance::get_region_rid); ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationMeshInstance::get_region_rid);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationMeshInstance::set_enter_cost);
ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationMeshInstance::get_enter_cost);
ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationMeshInstance::set_travel_cost);
ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationMeshInstance::get_travel_cost);
ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationMeshInstance::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationMeshInstance::bake_navigation_mesh, DEFVAL(true));
ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "travel_cost"), "set_travel_cost", "get_travel_cost");
ADD_SIGNAL(MethodInfo("navigation_mesh_changed")); ADD_SIGNAL(MethodInfo("navigation_mesh_changed"));
ADD_SIGNAL(MethodInfo("bake_finished")); ADD_SIGNAL(MethodInfo("bake_finished"));
@ -255,6 +295,8 @@ void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_
NavigationMeshInstance::NavigationMeshInstance() { NavigationMeshInstance::NavigationMeshInstance() {
set_notify_transform(true); set_notify_transform(true);
region = NavigationServer::get_singleton()->region_create(); region = NavigationServer::get_singleton()->region_create();
NavigationServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
NavigationServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
enabled = true; enabled = true;
} }

View file

@ -40,10 +40,14 @@ class Navigation;
class NavigationMeshInstance : public Spatial { class NavigationMeshInstance : public Spatial {
GDCLASS(NavigationMeshInstance, Spatial); GDCLASS(NavigationMeshInstance, Spatial);
bool enabled; bool enabled = true;
RID region; RID region;
Ref<NavigationMesh> navmesh; Ref<NavigationMesh> navmesh;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
uint32_t navigation_layers = 1;
Navigation *navigation = nullptr; Navigation *navigation = nullptr;
Node *debug_view = nullptr; Node *debug_view = nullptr;
Thread bake_thread; Thread bake_thread;
@ -57,8 +61,17 @@ public:
void set_enabled(bool p_enabled); void set_enabled(bool p_enabled);
bool is_enabled() const; bool is_enabled() const;
void set_navigation_layers(uint32_t p_navigation_layers);
uint32_t get_navigation_layers() const;
RID get_region_rid() const; RID get_region_rid() const;
void set_enter_cost(real_t p_enter_cost);
real_t get_enter_cost() const;
void set_travel_cost(real_t p_travel_cost);
real_t get_travel_cost() const;
void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
Ref<NavigationMesh> get_navigation_mesh() const; Ref<NavigationMesh> get_navigation_mesh() const;

View file

@ -796,6 +796,11 @@ void register_scene_types() {
GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/2d_physics"), i + 1), ""); GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/2d_physics"), i + 1), "");
GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/3d_physics"), i + 1), ""); GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/3d_physics"), i + 1), "");
} }
for (int i = 0; i < 32; i++) {
GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/2d_navigation"), i + 1), "");
GLOBAL_DEF(vformat("%s/layer_%d", PNAME("layer_names/3d_navigation"), i + 1), "");
};
} }
void initialize_theme() { void initialize_theme() {

View file

@ -31,6 +31,7 @@
#include "navigation_mesh.h" #include "navigation_mesh.h"
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
ERR_FAIL_COND(p_mesh.is_null());
vertices = PoolVector<Vector3>(); vertices = PoolVector<Vector3>();
clear_polygons(); clear_polygons();
@ -40,6 +41,7 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
continue; continue;
} }
Array arr = p_mesh->surface_get_arrays(i); Array arr = p_mesh->surface_get_arrays(i);
ERR_CONTINUE(arr.size() != Mesh::ARRAY_MAX);
PoolVector<Vector3> varr = arr[Mesh::ARRAY_VERTEX]; PoolVector<Vector3> varr = arr[Mesh::ARRAY_VERTEX];
PoolVector<int> iarr = arr[Mesh::ARRAY_INDEX]; PoolVector<int> iarr = arr[Mesh::ARRAY_INDEX];
if (varr.size() == 0 || iarr.size() == 0) { if (varr.size() == 0 || iarr.size() == 0) {
@ -299,13 +301,16 @@ void NavigationMesh::add_polygon(const Vector<int> &p_polygon) {
polygons.push_back(polygon); polygons.push_back(polygon);
_change_notify(); _change_notify();
} }
int NavigationMesh::get_polygon_count() const { int NavigationMesh::get_polygon_count() const {
return polygons.size(); return polygons.size();
} }
Vector<int> NavigationMesh::get_polygon(int p_idx) { Vector<int> NavigationMesh::get_polygon(int p_idx) {
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
return polygons[p_idx].indices; return polygons[p_idx].indices;
} }
void NavigationMesh::clear_polygons() { void NavigationMesh::clear_polygons() {
polygons.clear(); polygons.clear();
} }
@ -579,27 +584,4 @@ bool NavigationMesh::_get(const StringName &p_name, Variant &r_ret) const {
} }
#endif // DISABLE_DEPRECATED #endif // DISABLE_DEPRECATED
NavigationMesh::NavigationMesh() { NavigationMesh::NavigationMesh() {}
cell_size = 0.25f;
cell_height = 0.25f;
agent_height = 1.5f;
agent_radius = 0.5f;
agent_max_climb = 0.25f;
agent_max_slope = 45.0f;
region_min_size = 2.0f;
region_merge_size = 20.0f;
edge_max_length = 12.0f;
edge_max_error = 1.3f;
verts_per_poly = 6.0f;
detail_sample_distance = 6.0f;
detail_sample_max_error = 5.0f;
partition_type = SAMPLE_PARTITION_WATERSHED;
parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
collision_mask = 0xFFFFFFFF;
source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN;
source_group_name = "navmesh";
filter_low_hanging_obstacles = false;
filter_ledge_spans = false;
filter_walkable_low_height_spans = false;
}

View file

@ -90,30 +90,30 @@ public:
}; };
protected: protected:
float cell_size; float cell_size = 0.25f;
float cell_height; float cell_height = 0.25f;
float agent_height; float agent_height = 1.5f;
float agent_radius; float agent_radius = 0.5f;
float agent_max_climb; float agent_max_climb = 0.25f;
float agent_max_slope; float agent_max_slope = 45.0f;
float region_min_size; float region_min_size = 2.0f;
float region_merge_size; float region_merge_size = 20.0f;
float edge_max_length; float edge_max_length = 12.0f;
float edge_max_error; float edge_max_error = 1.3f;
float verts_per_poly; float verts_per_poly = 6.0f;
float detail_sample_distance; float detail_sample_distance = 6.0f;
float detail_sample_max_error; float detail_sample_max_error = 1.0f;
SamplePartitionType partition_type; SamplePartitionType partition_type = SAMPLE_PARTITION_WATERSHED;
ParsedGeometryType parsed_geometry_type; ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
uint32_t collision_mask; uint32_t collision_mask = 0xFFFFFFFF;
SourceGeometryMode source_geometry_mode; SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN;
StringName source_group_name; StringName source_group_name = "navmesh";
bool filter_low_hanging_obstacles; bool filter_low_hanging_obstacles = false;
bool filter_ledge_spans; bool filter_ledge_spans = false;
bool filter_walkable_low_height_spans; bool filter_walkable_low_height_spans = false;
public: public:
// Recast settings // Recast settings

View file

@ -257,6 +257,7 @@ void World::_update(uint64_t p_frame) {
RID World::get_space() const { RID World::get_space() const {
return space; return space;
} }
RID World::get_scenario() const { RID World::get_scenario() const {
return scenario; return scenario;
} }
@ -342,9 +343,12 @@ World::World() {
PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1));
ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
// Create default navigation map
navigation_map = NavigationServer::get_singleton()->map_create(); navigation_map = NavigationServer::get_singleton()->map_create();
NavigationServer::get_singleton()->map_set_active(navigation_map, true); NavigationServer::get_singleton()->map_set_active(navigation_map, true);
NavigationServer::get_singleton()->map_set_up(navigation_map, GLOBAL_DEF("navigation/3d/default_map_up", Vector3(0, 1, 0)));
NavigationServer::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25)); NavigationServer::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25));
NavigationServer::get_singleton()->map_set_cell_height(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_height", 0.25));
NavigationServer::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25)); NavigationServer::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25));
#ifdef _3D_DISABLED #ifdef _3D_DISABLED

View file

@ -327,7 +327,7 @@ RID World2D::get_space() {
return space; return space;
} }
RID World2D::get_navigation_map() { RID World2D::get_navigation_map() const {
return navigation_map; return navigation_map;
} }
@ -367,11 +367,12 @@ World2D::World2D() {
Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0)); Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0));
ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
// Create and configure the navigation_map to be more friendly with pixels than meters. // Create default navigation map
navigation_map = Navigation2DServer::get_singleton()->map_create(); navigation_map = Navigation2DServer::get_singleton()->map_create();
Navigation2DServer::get_singleton()->map_set_active(navigation_map, true); Navigation2DServer::get_singleton()->map_set_active(navigation_map, true);
Navigation2DServer::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1)); Navigation2DServer::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1.0));
Navigation2DServer::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1)); Navigation2DServer::get_singleton()->map_set_cell_height(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_height", 1.0));
Navigation2DServer::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1.0));
indexer = memnew(SpatialIndexer2D); indexer = memnew(SpatialIndexer2D);
} }

View file

@ -66,7 +66,7 @@ protected:
public: public:
RID get_canvas(); RID get_canvas();
RID get_space(); RID get_space();
RID get_navigation_map(); RID get_navigation_map() const;
Physics2DDirectSpaceState *get_direct_space_state(); Physics2DDirectSpaceState *get_direct_space_state();

View file

@ -34,10 +34,6 @@
#include "core/math/transform_2d.h" #include "core/math/transform_2d.h"
#include "servers/navigation_server.h" #include "servers/navigation_server.h"
/**
@author AndreaCatania
*/
Navigation2DServer *Navigation2DServer::singleton = nullptr; Navigation2DServer *Navigation2DServer::singleton = nullptr;
#define FORWARD_0_C(FUNC_NAME) \ #define FORWARD_0_C(FUNC_NAME) \
@ -69,6 +65,18 @@ Navigation2DServer *Navigation2DServer::singleton = nullptr;
return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \ return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \
} }
#define FORWARD_3_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, CONV_0, CONV_1, CONV_2) \
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2) \
const { \
return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2))); \
}
#define FORWARD_3_C(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, CONV_0, CONV_1, CONV_2) \
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2) \
const { \
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2)); \
}
#define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \ #define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \ Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
const { \ const { \
@ -81,24 +89,46 @@ Navigation2DServer *Navigation2DServer::singleton = nullptr;
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \ return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \
} }
#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) \
Navigation2DServer::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(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4))); \
}
#define FORWARD_5_C(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) \
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \
const { \
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4)); \
}
RID rid_to_rid(const RID d) { RID rid_to_rid(const RID d) {
return d; return d;
} }
bool bool_to_bool(const bool d) { bool bool_to_bool(const bool d) {
return d; return d;
} }
int int_to_int(const int d) { int int_to_int(const int d) {
return d; return d;
} }
uint32_t uint32_to_uint32(const uint32_t d) {
return d;
}
real_t real_to_real(const real_t d) { real_t real_to_real(const real_t d) {
return d; return d;
} }
Vector3 v2_to_v3(const Vector2 d) { Vector3 v2_to_v3(const Vector2 d) {
return Vector3(d.x, 0.0, d.y); return Vector3(d.x, 0.0, d.y);
} }
Vector2 v3_to_v2(const Vector3 &d) { Vector2 v3_to_v2(const Vector3 &d) {
return Vector2(d.x, d.z); return Vector2(d.x, d.z);
} }
Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) { Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
Vector<Vector2> nd; Vector<Vector2> nd;
nd.resize(d.size()); nd.resize(d.size());
@ -107,21 +137,27 @@ Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
} }
return nd; return nd;
} }
Transform trf2_to_trf3(const Transform2D &d) { Transform trf2_to_trf3(const Transform2D &d) {
Vector3 o(v2_to_v3(d.get_origin())); Vector3 o(v2_to_v3(d.get_origin()));
Basis b; Basis b;
b.rotate(Vector3(0, 1, 0), d.get_rotation()); b.rotate(Vector3(0, -1, 0), d.get_rotation());
b.scale(v2_to_v3(d.get_scale()));
return Transform(b, o); return Transform(b, o);
} }
Object *obj_to_obj(Object *d) { Object *obj_to_obj(Object *d) {
return d; return d;
} }
StringName sn_to_sn(StringName &d) { StringName sn_to_sn(StringName &d) {
return d; return d;
} }
Variant var_to_var(Variant &d) { Variant var_to_var(Variant &d) {
return d; return d;
} }
Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) { Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
if (d.is_valid()) { if (d.is_valid()) {
return d->get_mesh(); return d->get_mesh();
@ -130,15 +166,21 @@ Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
} }
} }
void Navigation2DServer::_emit_map_changed(RID p_map) {
emit_signal("map_changed", p_map);
}
void Navigation2DServer::_bind_methods() { void Navigation2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create); ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create);
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active); ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active);
ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &Navigation2DServer::map_is_active); ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &Navigation2DServer::map_is_active);
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size); ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size); ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_cell_height", "map", "cell_height"), &Navigation2DServer::map_set_cell_size);
ClassDB::bind_method(D_METHOD("map_get_cell_height", "map"), &Navigation2DServer::map_get_cell_size);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &Navigation2DServer::map_set_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &Navigation2DServer::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &Navigation2DServer::map_get_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &Navigation2DServer::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &Navigation2DServer::map_get_path); ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &Navigation2DServer::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &Navigation2DServer::map_get_closest_point); ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &Navigation2DServer::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &Navigation2DServer::map_get_closest_point_owner); ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &Navigation2DServer::map_get_closest_point_owner);
@ -146,10 +188,19 @@ void Navigation2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &Navigation2DServer::map_get_agents); ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &Navigation2DServer::map_get_agents);
ClassDB::bind_method(D_METHOD("region_create"), &Navigation2DServer::region_create); ClassDB::bind_method(D_METHOD("region_create"), &Navigation2DServer::region_create);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &Navigation2DServer::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &Navigation2DServer::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &Navigation2DServer::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &Navigation2DServer::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &Navigation2DServer::region_set_map); ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &Navigation2DServer::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &Navigation2DServer::region_get_map); ClassDB::bind_method(D_METHOD("region_get_map", "region"), &Navigation2DServer::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &Navigation2DServer::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &Navigation2DServer::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &Navigation2DServer::region_set_transform); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &Navigation2DServer::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &Navigation2DServer::region_set_navpoly); ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &Navigation2DServer::region_set_navpoly);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &Navigation2DServer::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &Navigation2DServer::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &Navigation2DServer::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("agent_create"), &Navigation2DServer::agent_create); ClassDB::bind_method(D_METHOD("agent_create"), &Navigation2DServer::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &Navigation2DServer::agent_set_map); ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &Navigation2DServer::agent_set_map);
@ -166,10 +217,15 @@ void Navigation2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &Navigation2DServer::free); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &Navigation2DServer::free);
ClassDB::bind_method(D_METHOD("_emit_map_changed"), &Navigation2DServer::_emit_map_changed);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::_RID, "map")));
} }
Navigation2DServer::Navigation2DServer() { Navigation2DServer::Navigation2DServer() {
singleton = this; singleton = this;
ERR_FAIL_COND_MSG(!NavigationServer::get_singleton(), "The NavigationServer singleton should be initialized before the Navigation2DServer one.");
NavigationServer::get_singleton_mut()->connect("map_changed", this, "_emit_map_changed");
} }
Navigation2DServer::~Navigation2DServer() { Navigation2DServer::~Navigation2DServer() {
@ -193,23 +249,37 @@ bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
void FORWARD_2_C(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real); void FORWARD_2_C(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid); real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
void FORWARD_2_C(map_set_cell_height, RID, p_map, real_t, p_cell_height, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_cell_height, RID, p_map, rid_to_rid);
void FORWARD_2_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real); void FORWARD_2_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid); real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
Vector<Vector2> FORWARD_4_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool); Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_navigation_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32);
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); Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
RID FORWARD_0_C(region_create); RID FORWARD_0_C(region_create);
void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2_C(region_set_enter_cost, RID, p_region, real_t, p_enter_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_enter_cost, RID, p_region, rid_to_rid);
void FORWARD_2_C(region_set_travel_cost, RID, p_region, real_t, p_travel_cost, rid_to_rid, real_to_real);
real_t FORWARD_1_C(region_get_travel_cost, RID, p_region, rid_to_rid);
void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2_C(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid);
void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3); void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
void Navigation2DServer::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const { void Navigation2DServer::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const {
NavigationServer::get_singleton()->region_set_navmesh(p_region, poly_to_mesh(p_nav_mesh)); NavigationServer::get_singleton()->region_set_navmesh(p_region, poly_to_mesh(p_nav_mesh));
} }
int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int);
RID Navigation2DServer::agent_create() const { RID Navigation2DServer::agent_create() const {
RID agent = NavigationServer::get_singleton()->agent_create(); RID agent = NavigationServer::get_singleton()->agent_create();
NavigationServer::get_singleton()->agent_set_ignore_y(agent, true); NavigationServer::get_singleton()->agent_set_ignore_y(agent, true);

View file

@ -28,10 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/ /*************************************************************************/
/**
@author AndreaCatania
*/
#ifndef NAVIGATION_2D_SERVER_H #ifndef NAVIGATION_2D_SERVER_H
#define NAVIGATION_2D_SERVER_H #define NAVIGATION_2D_SERVER_H
@ -45,6 +41,8 @@ class Navigation2DServer : public Object {
static Navigation2DServer *singleton; static Navigation2DServer *singleton;
void _emit_map_changed(RID p_map);
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -70,6 +68,10 @@ public:
/// Returns the map cell size. /// Returns the map cell size.
virtual real_t map_get_cell_size(RID p_map) const; virtual real_t map_get_cell_size(RID p_map) const;
/// Set the map cell height used to weld the navigation mesh polygons.
virtual void map_set_cell_height(RID p_map, real_t p_cell_height) const;
virtual real_t map_get_cell_height(RID p_map) const;
/// Set the map edge connection margin used to weld the compatible region edges. /// Set the map edge connection margin used to weld the compatible region edges.
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const; virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const;
@ -77,7 +79,7 @@ public:
virtual real_t map_get_edge_connection_margin(RID p_map) const; virtual real_t map_get_edge_connection_margin(RID p_map) const;
/// Returns the navigation path to reach the destination from the origin. /// Returns the navigation path to reach the destination from the origin.
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize) const; virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const; virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const;
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const; virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const;
@ -88,16 +90,33 @@ public:
/// Creates a new region. /// Creates a new region.
virtual RID region_create() const; virtual RID region_create() const;
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) const;
virtual real_t region_get_enter_cost(RID p_region) const;
/// Set the travel_cost of a region
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) const;
virtual real_t region_get_travel_cost(RID p_region) const;
/// Set the map of this region. /// Set the map of this region.
virtual void region_set_map(RID p_region, RID p_map) const; virtual void region_set_map(RID p_region, RID p_map) const;
virtual RID region_get_map(RID p_region) const; virtual RID region_get_map(RID p_region) const;
/// Set the region's layers
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const;
virtual uint32_t region_get_navigation_layers(RID p_region) const;
/// Set the global transformation of this region. /// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform2D p_transform) const; virtual void region_set_transform(RID p_region, Transform2D p_transform) const;
/// Set the navigation poly of this region. /// Set the navigation poly of this region.
virtual void region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const; virtual void region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const;
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const;
virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const;
virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const;
/// Creates the agent. /// Creates the agent.
virtual RID agent_create() const; virtual RID agent_create() const;
@ -165,4 +184,4 @@ public:
virtual ~Navigation2DServer(); virtual ~Navigation2DServer();
}; };
#endif #endif // NAVIGATION_2D_SERVER_H

View file

@ -28,10 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/ /*************************************************************************/
/**
@author AndreaCatania
*/
#include "navigation_server.h" #include "navigation_server.h"
NavigationServer *NavigationServer::singleton = nullptr; NavigationServer *NavigationServer::singleton = nullptr;
@ -48,7 +44,7 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_cell_height", "map"), &NavigationServer::map_get_cell_height); ClassDB::bind_method(D_METHOD("map_get_cell_height", "map"), &NavigationServer::map_get_cell_height);
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer::map_set_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer::map_set_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer::map_get_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer::map_get_edge_connection_margin);
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &NavigationServer::map_get_path); ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer::map_get_path, DEFVAL(1));
ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer::map_get_closest_point_to_segment, DEFVAL(false)); ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer::map_get_closest_point_to_segment, DEFVAL(false));
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer::map_get_closest_point); ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer::map_get_closest_point);
ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer::map_get_closest_point_normal); ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer::map_get_closest_point_normal);
@ -58,11 +54,22 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer::map_get_agents); ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer::map_get_agents);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer::region_create); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer::region_create);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer::region_set_enter_cost);
ClassDB::bind_method(D_METHOD("region_get_enter_cost", "region"), &NavigationServer::region_get_enter_cost);
ClassDB::bind_method(D_METHOD("region_set_travel_cost", "region", "travel_cost"), &NavigationServer::region_set_travel_cost);
ClassDB::bind_method(D_METHOD("region_get_travel_cost", "region"), &NavigationServer::region_get_travel_cost);
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer::region_set_map); ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer::region_set_map);
ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer::region_get_map); ClassDB::bind_method(D_METHOD("region_get_map", "region"), &NavigationServer::region_get_map);
ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer::region_set_navigation_layers);
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer::region_set_transform); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer::region_set_navmesh); ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer::region_set_navmesh);
ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer::region_bake_navmesh); ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer::region_bake_navmesh);
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer::region_get_connection_pathway_end);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer::agent_create); ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer::agent_create);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer::agent_set_map); ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer::agent_set_map);
@ -82,6 +89,8 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer::set_active); ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer::set_active);
ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer::process); ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer::process);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::_RID, "map")));
} }
const NavigationServer *NavigationServer::get_singleton() { const NavigationServer *NavigationServer::get_singleton() {

View file

@ -28,10 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/ /*************************************************************************/
/**
@author AndreaCatania
*/
#ifndef NAVIGATION_SERVER_H #ifndef NAVIGATION_SERVER_H
#define NAVIGATION_SERVER_H #define NAVIGATION_SERVER_H
@ -94,7 +90,7 @@ public:
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0; virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
/// Returns the navigation path to reach the destination from the origin. /// Returns the navigation path to reach the destination from the origin.
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const = 0; virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 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_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; virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const = 0;
@ -107,19 +103,36 @@ public:
/// Creates a new region. /// Creates a new region.
virtual RID region_create() const = 0; virtual RID region_create() const = 0;
/// Set the enter_cost of a region
virtual void region_set_enter_cost(RID p_region, real_t p_enter_cost) const = 0;
virtual real_t region_get_enter_cost(RID p_region) const = 0;
/// Set the travel_cost of a region
virtual void region_set_travel_cost(RID p_region, real_t p_travel_cost) const = 0;
virtual real_t region_get_travel_cost(RID p_region) const = 0;
/// Set the map of this region. /// Set the map of this region.
virtual void region_set_map(RID p_region, RID p_map) const = 0; virtual void region_set_map(RID p_region, RID p_map) const = 0;
virtual RID region_get_map(RID p_region) const = 0; virtual RID region_get_map(RID p_region) const = 0;
/// Set the region's layers
virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) const = 0;
virtual uint32_t region_get_navigation_layers(RID p_region) const = 0;
/// Set the global transformation of this region. /// Set the global transformation of this region.
virtual void region_set_transform(RID p_region, Transform p_transform) const = 0; virtual void region_set_transform(RID p_region, Transform p_transform) const = 0;
/// Set the navigation mesh of this region. /// Set the navigation mesh of this region.
virtual void region_set_navmesh(RID p_region, Ref<NavigationMesh> p_nav_mesh) const = 0; virtual void region_set_navmesh(RID p_region, Ref<NavigationMesh> p_nav_mesh) const = 0;
/// Bake the navigation mesh /// Bake the navigation mesh.
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const = 0; virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const = 0;
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const = 0;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0;
/// Creates the agent. /// Creates the agent.
virtual RID agent_create() const = 0; virtual RID agent_create() const = 0;
@ -207,4 +220,4 @@ public:
static NavigationServer *new_default_server(); static NavigationServer *new_default_server();
}; };
#endif #endif // NAVIGATION_SERVER_H