diff --git a/SConstruct b/SConstruct
index 606697a2e..1dcc4b6b6 100644
--- a/SConstruct
+++ b/SConstruct
@@ -184,7 +184,8 @@ opts.Add(BoolVariable("builtin_opus", "Use the built-in Opus library", True))
opts.Add(BoolVariable("builtin_pcre2", "Use the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_recast", "Use the built-in Recast library", True))
-opts.Add(BoolVariable("builtin_rvo2", "Use the built-in RVO2 library", True))
+opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
+opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
opts.Add(BoolVariable("builtin_zstd", "Use the built-in Zstd library", True))
diff --git a/core/global_constants.cpp b/core/global_constants.cpp
index e84ba4844..16ca13589 100644
--- a/core/global_constants.cpp
+++ b/core/global_constants.cpp
@@ -602,6 +602,7 @@ void register_global_constants() {
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_NAVIGATION);
+ BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_AVOIDANCE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FILE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_DIR);
diff --git a/core/object/object.h b/core/object/object.h
index 2b7092ca6..24ccfb766 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -70,6 +70,7 @@ enum PropertyHint {
PROPERTY_HINT_LAYERS_3D_RENDER,
PROPERTY_HINT_LAYERS_3D_PHYSICS,
PROPERTY_HINT_LAYERS_3D_NAVIGATION,
+ PROPERTY_HINT_LAYERS_AVOIDANCE,
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_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 5427b157e..76706f78f 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1449,10 +1449,6 @@
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]. Whitespaces are [b]not[/b] removed from either end of a name. 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].
-
- 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.
-
Hints that a float property should be edited via an exponential easing function. The hint string can include [code]"attenuation"[/code] to flip the curve horizontally and/or [code]"inout"[/code] to also include in/out easing.
@@ -1486,39 +1482,42 @@
Hints that an integer property is a bitmask using the optionally named 2D navigation layers.
-
+
+ Hints that an integer property is a bitmask using the optionally named avoidance layers.
+
+
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 directory. Editing it will show a file dialog for picking the path.
-
+
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 directory outside the project folder. Editing it will show a file dialog for picking the path.
-
+
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 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 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 color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited.
-
+
Hints that an image is compressed using lossy compression.
-
+
Hints that an image is compressed using lossless compression.
-
+
-
+
Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance:
[codeblock]
hint_string = "%s:" % [TYPE_INT] # Array of inteters.
@@ -1528,34 +1527,38 @@
[/codeblock]
[b]Note:[/b] The final colon is required to specify for properly detecting built-in types.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+ 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.
+
+
Hints that a vector property should allow linking values (e.g. to edit both [code]x[/code] and [code]y[/code] together).
-
+
The property is serialized and saved in the scene file (default).
diff --git a/doc/classes/Navigation2DServer.xml b/doc/classes/Navigation2DServer.xml
index 837fcf24d..d66818197 100644
--- a/doc/classes/Navigation2DServer.xml
+++ b/doc/classes/Navigation2DServer.xml
@@ -23,6 +23,13 @@
Creates the agent.
+
+
+
+
+ Return [code]true[/code] if the specified [param agent] uses avoidance.
+
+
@@ -37,15 +44,48 @@
Returns [code]true[/code] if the map got changed the previous frame.
-
+
- Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
- [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code].
+ Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
+ [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
+
+
+
+
+
+
+
+ If [param enabled] is [code]true[/code] the specified [param agent] uses avoidance.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_layers[/code] bitmask.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_mask[/code] bitmask.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
+ The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
@@ -96,20 +136,20 @@
Sets the radius of the agent.
-
+
-
+
- Sets the new target velocity.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
-
+
-
+
- The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
@@ -117,7 +157,15 @@
- Sets the current velocity of the agent.
+ Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position far away use [method agent_set_velocity_forced] instead to reset the internal velocity state.
+
+
+
+
+
+
+
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position far away this function should be used in the same frame. If called frequently this function can get agents stuck.
@@ -339,6 +387,13 @@
Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
+
+
+
+
+ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
+
+
@@ -403,6 +458,50 @@
Set the map edge connection margin used to weld the compatible region edges.
+
+
+
+ Creates a new navigation obstacle.
+
+
+
+
+
+
+ Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sets the navigation map [RID] for the obstacle.
+
+
+
+
+
+
+
+ Sets the position of the obstacle in world space.
+
+
+
+
+
+
+
+ Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
+
+
diff --git a/doc/classes/NavigationAgent.xml b/doc/classes/NavigationAgent.xml
index 651fd92b3..b7ff8c122 100644
--- a/doc/classes/NavigationAgent.xml
+++ b/doc/classes/NavigationAgent.xml
@@ -5,8 +5,7 @@
3D agent that is used in navigation to reach a position 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 [World3D] 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 setting [member target_position] it is required to use the [method get_next_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
- [b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues.
+ [b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
@@ -17,6 +16,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
+
+
+
+
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+
+ Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
+
+
@@ -26,16 +39,16 @@
- Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
+ Returns the reachable final position of the current navigation path in global coordinates. This position can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
-
+
Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic.
-
+
Returns which index the agent is currently on in the navigation path's [PoolVector3Array].
@@ -81,7 +94,7 @@
- Returns [code]true[/code] if [member target_position] is reachable.
+ Returns true if [member target_position] is reachable. The target position is set using [member target_position].
@@ -97,6 +110,22 @@
Sets the [Navigation] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation] node.
+
+
+
+
+
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+
+
+ Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
+
+
@@ -112,20 +141,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
-
+
- Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
-
- The NavigationAgent height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
-
- If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [member velocity] is set and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+
+
+ A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
+
+
+ A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
+
+
+ The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
If [code]true[/code] shows debug visuals for this agent.
@@ -139,8 +174,8 @@
If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color.
-
- Ignores collisions on the Y axis. Must be [code]true[/code] to move on a horizontal plane.
+
+ The height of the avoidance agent. Agent's will ignore other agents or obstacles that are above or below their current position + height in 2D avoidance. Does nothing in 3D avoidance which uses radius spheres alone.
The maximum number of neighbors for the agent to consider.
@@ -157,6 +192,9 @@
The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update.
+
+ The height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
+
The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
@@ -171,10 +209,20 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
- The user-defined target position. Setting this property will clear the current navigation path.
+ If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
-
- The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive.
+
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+
+
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+
+
+ If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
+ If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including [member height] in 2D avoidance.
+
+
+ Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
@@ -209,7 +257,7 @@
- Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index e93e8dbe3..05454d374 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -5,8 +5,7 @@
2D agent that is used in navigation to reach a position 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 setting [member target_position] it is required to use the [method get_next_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
- [b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues.
+ [b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
@@ -17,6 +16,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
+
+
+
+
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+
+ Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
+
+
@@ -26,16 +39,16 @@
- Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
+ Returns the reachable final position of the current navigation path in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
-
+
Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic.
-
+
Returns which index the agent is currently on in the navigation path's [PoolVector2Array].
@@ -81,7 +94,7 @@
- Returns [code]true[/code] if [member target_position] is reachable.
+ Returns true if [member target_position] is reachable. The target position is set using [member target_position].
@@ -97,6 +110,22 @@
Sets the [Navigation2D] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation2D] node.
+
+
+
+
+
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+
+
+ Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
+
+
@@ -112,17 +141,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
-
+
- Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
- If [code]true[/code] the agent is registered for an RVO avoidance callback on the [Navigation2DServer]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+ If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [member velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
+
+
+ A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
+
+
+ A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
+
+
+ The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
If [code]true[/code] shows debug visuals for this agent.
@@ -168,10 +206,16 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
- The user-defined target position. Setting this property will clear the current navigation path.
+ If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
-
- The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive.
+
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+
+
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+
+
+ Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
@@ -206,7 +250,7 @@
- Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
+ Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
diff --git a/doc/classes/NavigationObstacle.xml b/doc/classes/NavigationObstacle.xml
index a2f673284..a15325bac 100644
--- a/doc/classes/NavigationObstacle.xml
+++ b/doc/classes/NavigationObstacle.xml
@@ -1,25 +1,54 @@
-
+
- 3D obstacle used in navigation for collision avoidance.
+ 3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
- 3D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation] node, or using [method set_navigation]. [NavigationObstacle] is physics safe.
- Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
+ 3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
+ If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
+ Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
+ Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
+
+
+
+ Returns the [RID] of this agent on the [NavigationServer3D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
+
+
+
+
+
+
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
+
+
Returns the [Navigation] node that the obstacle is using for its navigation system.
-
+
- Returns the [RID] of this obstacle on the [NavigationServer].
+ Returns the [RID] of this obstacle on the [NavigationServer3D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
+
+
+
+
+
+
+
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
@@ -29,13 +58,33 @@
Sets the [Navigation] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation] node.
+
+
+
+
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
+
+
-
- Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
+
+ A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
-
- The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
+
+ Sets the obstacle height used in 2D avoidance. 2D avoidance using agent's ignore obstacles that are below or above them.
+
+
+ Sets the avoidance radius for the obstacle.
+
+
+ If [code]true[/code] the obstacle affects 3D avoidance using agent's with obstacle [member radius].
+ If [code]false[/code] the obstacle affects 2D avoidance using agent's with both obstacle [member vertices] as well as obstacle [member radius].
+
+
+ Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
+
+
+ The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml
index e0ec76b61..21651444d 100644
--- a/doc/classes/NavigationObstacle2D.xml
+++ b/doc/classes/NavigationObstacle2D.xml
@@ -1,25 +1,54 @@
-
+
- 2D obstacle used in navigation for collision avoidance.
+ 2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
- 2D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationObstacle2D] is physics safe.
- Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
+ 2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
+ If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
+ Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
+ Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
+
+
+
+ Returns the [RID] of this agent on the [NavigationServer2D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
+
+
+
+
+
+
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+
+
+
+
+
+ Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
+
+
Returns the [Navigation2D] node that the obstacle is using for its navigation system.
-
+
- Returns the [RID] of this obstacle on the [Navigation2DServer].
+ Returns the [RID] of this obstacle on the [NavigationServer2D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
+
+
+
+
+
+
+
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
@@ -29,13 +58,26 @@
Sets the [Navigation2D] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation2D] node.
+
+
+
+
+ Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
+
+
-
- Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
+
+ A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
-
- The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
+
+ Sets the avoidance radius for the obstacle.
+
+
+ Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
+
+
+ The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
diff --git a/doc/classes/NavigationPolygonInstance.xml b/doc/classes/NavigationPolygonInstance.xml
index 9332c4bf7..241b3d87c 100644
--- a/doc/classes/NavigationPolygonInstance.xml
+++ b/doc/classes/NavigationPolygonInstance.xml
@@ -15,6 +15,13 @@
+
+
+
+
+ Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
+
+
@@ -41,6 +48,14 @@
Returns the current navigation map [RID] use by this region.
+
+
+
+
+
+ Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
+
+
@@ -58,6 +73,13 @@
+
+ A bitfield determining all avoidance layers for the avoidance constrain.
+
+
+ If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
+ [b]Experimental:[/b] This is an experimental feature and should not be used in production as agent's can get stuck on the navigation polygon corners and edges especially at high frame rate.
+
Determines if the [NavigationPolygonInstance] is enabled or disabled.
diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml
index f3270a379..56b0c60bc 100644
--- a/doc/classes/NavigationServer.xml
+++ b/doc/classes/NavigationServer.xml
@@ -23,6 +23,13 @@
Creates the agent.
+
+
+
+
+ Returns [code]true[/code] if the provided [param agent] has avoidance enabled.
+
+
@@ -30,6 +37,13 @@
Returns the navigation map [RID] the requested [code]agent[/code] is currently assigned to.
+
+
+
+
+ Returns [code]true[/code] if the provided [param agent] uses avoidance in 3D space Vector3(x,y,z) instead of horizontal 2D Vector2(x,y) / Vector3(x,0.0,z).
+
+
@@ -37,15 +51,56 @@
Returns [code]true[/code] if the map got changed the previous frame.
-
+
- Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
- [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code].
+ Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
+ [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
+
+
+
+
+
+
+
+ If [param enabled] the provided [param agent] calculates avoidance.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_layers[/code] bitmask.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_mask[/code] bitmask.
+
+
+
+
+
+
+
+ Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
+ The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
+
+
+
+
+
+
+
+ Updates the provided [param agent] [param height].
@@ -96,20 +151,30 @@
Sets the radius of the agent.
-
+
-
-
+
+
- Sets the new target velocity.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
-
+
-
+
- The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
+
+
+
+
+
+
+
+ Sets if the agent uses the 2D avoidance or the 3D avoidance while avoidance is enabled.
+ If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
+ If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including the agents height in 2D avoidance.
@@ -117,7 +182,15 @@
- Sets the current velocity of the agent.
+ Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method agent_set_velocity_forced] as well to reset the internal simulation velocity.
+
+
+
+
+
+
+
+ Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
@@ -365,6 +438,13 @@
Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
+
+
+
+
+ Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
+
+
@@ -452,6 +532,59 @@
Sets the map up direction.
+
+
+
+ Creates a new obstacle.
+
+
+
+
+
+
+ Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
+
+
+
+
+
+
+
+ Set the obstacles's [code]avoidance_layers[/code] bitmask.
+
+
+
+
+
+
+
+ Sets the [param height] for the [param obstacle]. In 3D agents will ignore obstacles that are above or below them while using 2D avoidance.
+
+
+
+
+
+
+
+ Assigns the [param obstacle] to a navigation map.
+
+
+
+
+
+
+
+ Updates the [param position] in world space for the [param obstacle].
+
+
+
+
+
+
+
+ Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
+
+
@@ -631,6 +764,11 @@
+
+
+ Emitted when avoidance debug settings are changed. Only available in debug builds.
+
+
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 7fbd821e9..36d112d2e 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -517,6 +517,33 @@
Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc.
+
+ Color of the avoidance agents radius, visible when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ If enabled, displays avoidance agents radius when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ If enabled, displays avoidance obstacles radius when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ If enabled, displays static avoidance obstacles when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ Color of the avoidance obstacles radius, visible when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ Color of the static avoidance obstacles edges when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ Color of the static avoidance obstacles edges when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ Color of the static avoidance obstacles faces when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
+
+
+ Color of the static avoidance obstacles faces when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
+
Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
@@ -1351,6 +1378,102 @@
Optional name for the 3D render layer 9.
+
+ Optional name for the navigation avoidance layer 1. If left empty, the layer will display as "Layer 1".
+
+
+ Optional name for the navigation avoidance layer 2. If left empty, the layer will display as "Layer 2".
+
+
+ Optional name for the navigation avoidance layer 3. If left empty, the layer will display as "Layer 3".
+
+
+ Optional name for the navigation avoidance layer 4. If left empty, the layer will display as "Layer 4".
+
+
+ Optional name for the navigation avoidance layer 5. If left empty, the layer will display as "Layer 5".
+
+
+ Optional name for the navigation avoidance layer 6. If left empty, the layer will display as "Layer 6".
+
+
+ Optional name for the navigation avoidance layer 7. If left empty, the layer will display as "Layer 7".
+
+
+ Optional name for the navigation avoidance layer 8. If left empty, the layer will display as "Layer 8".
+
+
+ Optional name for the navigation avoidance layer 9. If left empty, the layer will display as "Layer 9".
+
+
+ Optional name for the navigation avoidance layer 10. If left empty, the layer will display as "Layer 10".
+
+
+ Optional name for the navigation avoidance layer 11. If left empty, the layer will display as "Layer 11".
+
+
+ Optional name for the navigation avoidance layer 12. If left empty, the layer will display as "Layer 12".
+
+
+ Optional name for the navigation avoidance layer 13. If left empty, the layer will display as "Layer 13".
+
+
+ Optional name for the navigation avoidance layer 14. If left empty, the layer will display as "Layer 14".
+
+
+ Optional name for the navigation avoidance layer 15. If left empty, the layer will display as "Layer 15".
+
+
+ Optional name for the navigation avoidance layer 16. If left empty, the layer will display as "Layer 16".
+
+
+ Optional name for the navigation avoidance layer 17. If left empty, the layer will display as "Layer 17".
+
+
+ Optional name for the navigation avoidance layer 18. If left empty, the layer will display as "Layer 18".
+
+
+ Optional name for the navigation avoidance layer 19. If left empty, the layer will display as "Layer 19".
+
+
+ Optional name for the navigation avoidance layer 20. If left empty, the layer will display as "Layer 20".
+
+
+ Optional name for the navigation avoidance layer 21. If left empty, the layer will display as "Layer 21".
+
+
+ Optional name for the navigation avoidance layer 22. If left empty, the layer will display as "Layer 22".
+
+
+ Optional name for the navigation avoidance layer 23. If left empty, the layer will display as "Layer 23".
+
+
+ Optional name for the navigation avoidance layer 24. If left empty, the layer will display as "Layer 24".
+
+
+ Optional name for the navigation avoidance layer 25. If left empty, the layer will display as "Layer 25".
+
+
+ Optional name for the navigation avoidance layer 26. If left empty, the layer will display as "Layer 26".
+
+
+ Optional name for the navigation avoidance layer 27. If left empty, the layer will display as "Layer 27".
+
+
+ Optional name for the navigation avoidance layer 28. If left empty, the layer will display as "Layer 28".
+
+
+ Optional name for the navigation avoidance layer 29. If left empty, the layer will display as "Layer 29".
+
+
+ Optional name for the navigation avoidance layer 30. If left empty, the layer will display as "Layer 30".
+
+
+ Optional name for the navigation avoidance layer 31. If left empty, the layer will display as "Layer 31".
+
+
+ Optional name for the navigation avoidance layer 32. If left empty, the layer will display as "Layer 32".
+
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
@@ -1408,6 +1531,12 @@
Default link connection radius for 3D navigation maps. See [method NavigationServer3D.map_set_link_connection_radius].
+
+ If enabled and avoidance calculations use multiple threads the threads run with high priority.
+
+
+ If enabled the avoidance calculations use multiple threads.
+
If enabled 3D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World3D default navigation maps.
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 61c9d4601..b9f703d61 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -367,6 +367,10 @@ void EditorExportPlatform::gen_debug_flags(Vector &r_flags, int p_flags)
r_flags.push_back("--debug-navigation");
}
+ if (p_flags & DEBUG_FLAG_VIEW_AVOIDANCE) {
+ r_flags.push_back("--debug-avoidance");
+ }
+
if (p_flags & DEBUG_FLAG_SHADER_FALLBACKS) {
r_flags.push_back("--debug-shader-fallbacks");
}
@@ -1293,6 +1297,10 @@ void EditorExportPlatform::gen_export_flags(Vector &r_flags, int p_flags
if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
r_flags.push_back("--debug-navigation");
}
+
+ if (p_flags & DEBUG_FLAG_VIEW_AVOIDANCE) {
+ r_flags.push_back("--debug-avoidance");
+ }
}
EditorExportPlatform::EditorExportPlatform() {
}
diff --git a/editor/editor_export.h b/editor/editor_export.h
index 3a95b922c..5db12cd42 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -321,7 +321,8 @@ public:
DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
DEBUG_FLAG_VIEW_COLLISONS = 8,
DEBUG_FLAG_VIEW_NAVIGATION = 16,
- DEBUG_FLAG_SHADER_FALLBACKS = 32,
+ DEBUG_FLAG_VIEW_AVOIDANCE = 32,
+ DEBUG_FLAG_SHADER_FALLBACKS = 64,
};
virtual Error run(const Ref &p_preset, int p_device, int p_debug_flags) { return OK; }
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d8b6439be..46812f475 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -127,6 +127,8 @@
#include "editor/plugins/mesh_instance_editor_plugin.h"
#include "editor/plugins/multimesh_editor_plugin.h"
#include "editor/plugins/navigation_link_2d_editor_plugin.h"
+#include "editor/plugins/navigation_obstacle_2d_editor_plugin.h"
+#include "editor/plugins/navigation_obstacle_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_editor_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
#include "editor/plugins/path_editor_plugin.h"
@@ -2767,6 +2769,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
editor_run.set_debug_navigation(!ischecked);
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
+ } break;
+ case RUN_DEBUG_AVOIDANCE: {
+ bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_AVOIDANCE));
+ debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked);
+ run_native->set_debug_avoidance(!ischecked);
+ editor_run.set_debug_avoidance(!ischecked);
+ EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked);
+
} break;
case RUN_DEBUG_SHADER_FALLBACKS: {
bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_SHADER_FALLBACKS));
@@ -3059,6 +3069,7 @@ void EditorNode::_update_debug_options() {
bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
bool check_debug_collisons = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false);
bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
+ bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
bool check_debug_shader_fallbacks = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_shader_fallbacks", false);
bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
@@ -3075,6 +3086,9 @@ void EditorNode::_update_debug_options() {
if (check_debug_navigation) {
_menu_option_confirm(RUN_DEBUG_NAVIGATION, true);
}
+ if (check_debug_avoidance) {
+ _menu_option_confirm(RUN_DEBUG_AVOIDANCE, true);
+ }
if (check_debug_shader_fallbacks) {
_menu_option_confirm(RUN_DEBUG_SHADER_FALLBACKS, true);
}
@@ -6509,6 +6523,11 @@ EditorNode::EditorNode() {
p->get_item_count() - 1,
TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project."));
+ p->add_check_shortcut(ED_SHORTCUT("editor/visible_avoidance", TTR("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
+ p->set_item_tooltip(
+ p->get_item_count() - 1,
+ TTR("When this option is enabled, avoidance debug information will be visible in the running project."));
+
if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3") {
p->add_separator();
@@ -7011,12 +7030,14 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(RoomManagerEditorPlugin(this)));
add_editor_plugin(memnew(RoomEditorPlugin(this)));
add_editor_plugin(memnew(OccluderEditorPlugin(this)));
+ add_editor_plugin(memnew(NavigationObstacleEditorPlugin(this)));
add_editor_plugin(memnew(PortalEditorPlugin(this)));
add_editor_plugin(memnew(PackedSceneEditorPlugin(this)));
add_editor_plugin(memnew(Path2DEditorPlugin(this)));
add_editor_plugin(memnew(PathEditorPlugin(this)));
add_editor_plugin(memnew(Line2DEditorPlugin(this)));
add_editor_plugin(memnew(NavigationLink2DEditorPlugin));
+ add_editor_plugin(memnew(NavigationObstacle2DEditorPlugin(this)));
add_editor_plugin(memnew(Polygon2DEditorPlugin(this)));
add_editor_plugin(memnew(LightOccluder2DEditorPlugin(this)));
add_editor_plugin(memnew(GradientEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 2d70f9054..0b5593093 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -447,6 +447,7 @@ private:
RUN_LIVE_DEBUG,
RUN_DEBUG_COLLISONS,
RUN_DEBUG_NAVIGATION,
+ RUN_DEBUG_AVOIDANCE,
RUN_DEBUG_SHADER_FALLBACKS,
RUN_DEPLOY_REMOTE_DEBUG,
RUN_RELOAD_SCRIPTS,
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index fb7cef798..4c2c8788a 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1117,6 +1117,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
layer_group_size = 4;
layer_count = 32;
} break;
+
+ case LAYER_AVOIDANCE: {
+ basename = "layer_names/avoidance";
+ layer_group_size = 4;
+ layer_count = 32;
+ } break;
}
Vector names;
@@ -3691,7 +3697,13 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
editor->setup(options);
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_2D_NAVIGATION || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
+ } 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 ||
+ p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;
switch (p_hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER:
@@ -3712,6 +3724,9 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;
break;
+ case PROPERTY_HINT_LAYERS_AVOIDANCE:
+ lt = EditorPropertyLayers::LAYER_AVOIDANCE;
+ break;
default: {
} //compiler could be smarter here and realize this can't happen
}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 003733383..aac8b17f6 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -313,6 +313,7 @@ public:
LAYER_PHYSICS_3D,
LAYER_RENDER_3D,
LAYER_NAVIGATION_3D,
+ LAYER_AVOIDANCE,
};
private:
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index e1c7e558a..e09dffb01 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -98,6 +98,10 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
args.push_back("--debug-navigation");
}
+ if (debug_avoidance) {
+ args.push_back("--debug-avoidance");
+ }
+
if (debug_shader_fallbacks) {
args.push_back("--debug-shader-fallbacks");
}
@@ -305,6 +309,14 @@ bool EditorRun::get_debug_navigation() const {
return debug_navigation;
}
+void EditorRun::set_debug_avoidance(bool p_debug) {
+ debug_avoidance = p_debug;
+}
+
+bool EditorRun::get_debug_avoidance() const {
+ return debug_avoidance;
+}
+
void EditorRun::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
@@ -318,5 +330,6 @@ EditorRun::EditorRun() {
running_scene = "";
debug_collisions = false;
debug_navigation = false;
+ debug_avoidance = false;
debug_shader_fallbacks = false;
}
diff --git a/editor/editor_run.h b/editor/editor_run.h
index b44841d10..bc2db5ce1 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -50,6 +50,7 @@ public:
private:
bool debug_collisions;
bool debug_navigation;
+ bool debug_avoidance;
bool debug_shader_fallbacks;
Status status;
String running_scene;
@@ -69,6 +70,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
+ void set_debug_avoidance(bool p_debug);
+ bool get_debug_avoidance() const;
+
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index db7cf7042..410ccc31a 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -30,23 +30,23 @@
#include "editor_run_native.h"
-#include "core/object/class_db.h"
#include "core/error/error_macros.h"
#include "core/io/image.h"
-#include "core/os/memory.h"
+#include "core/object/class_db.h"
#include "core/object/reference.h"
-#include "core/typedefs.h"
+#include "core/os/memory.h"
#include "core/string/ustring.h"
+#include "core/typedefs.h"
#include "core/variant/variant.h"
#include "editor_export.h"
#include "editor_node.h"
#include "editor_scale.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/popup_menu.h"
+#include "scene/gui/rich_text_label.h"
#include "scene/main/node.h"
#include "scene/resources/texture.h"
-#include "scene/gui/dialogs.h"
-#include "scene/gui/rich_text_label.h"
void EditorRunNative::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -156,6 +156,9 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) {
if (debug_navigation) {
flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION;
}
+ if (debug_avoidance) {
+ flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_AVOIDANCE;
+ }
if (debug_shader_fallbacks) {
flags |= EditorExportPlatform::DEBUG_FLAG_SHADER_FALLBACKS;
}
@@ -210,6 +213,14 @@ bool EditorRunNative::get_debug_navigation() const {
return debug_navigation;
}
+void EditorRunNative::set_debug_avoidance(bool p_debug) {
+ debug_avoidance = p_debug;
+}
+
+bool EditorRunNative::get_debug_avoidance() const {
+ return debug_avoidance;
+}
+
void EditorRunNative::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
@@ -234,6 +245,7 @@ EditorRunNative::EditorRunNative() {
deploy_debug_remote = false;
debug_collisions = false;
debug_navigation = false;
+ debug_avoidance = false;
debug_shader_fallbacks = false;
resume_idx = 0;
resume_platform = 0;
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index 8c0e53c0b..b1e3742d1 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -51,6 +51,7 @@ class EditorRunNative : public HBoxContainer {
bool deploy_debug_remote;
bool debug_collisions;
bool debug_navigation;
+ bool debug_avoidance;
bool debug_shader_fallbacks;
int resume_idx;
@@ -75,6 +76,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
+ void set_debug_avoidance(bool p_debug);
+ bool get_debug_avoidance() const;
+
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;
diff --git a/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp
new file mode 100644
index 000000000..caebad348
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_2d_editor_plugin.cpp
@@ -0,0 +1,75 @@
+/**************************************************************************/
+/* navigation_obstacle_2d_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "navigation_obstacle_2d_editor_plugin.h"
+
+#include "core/object/undo_redo.h"
+#include "editor/editor_node.h"
+
+Node2D *NavigationObstacle2DEditor::_get_node() const {
+ return node;
+}
+
+void NavigationObstacle2DEditor::_set_node(Node *p_polygon) {
+ node = Object::cast_to(p_polygon);
+}
+
+void NavigationObstacle2DEditor::_action_add_polygon(const Variant &p_polygon) {
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("NavigationObstacle2D Add Polygon");
+ undo_redo->add_do_method(node, "set_vertices", p_polygon);
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+ undo_redo->commit_action();
+}
+
+void NavigationObstacle2DEditor::_action_remove_polygon(int p_idx) {
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("NavigationObstacle2D Remove Polygon");
+ undo_redo->add_do_method(node, "set_vertices", Variant(Vector()));
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+ undo_redo->commit_action();
+}
+
+void NavigationObstacle2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action("NavigationObstacle2D Set Polygon");
+ undo_redo->add_do_method(node, "set_vertices", p_polygon);
+ undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
+ undo_redo->commit_action();
+}
+
+NavigationObstacle2DEditor::NavigationObstacle2DEditor(EditorNode *p_editor, bool p_wip_destructive) :
+ AbstractPolygon2DEditor(p_editor, p_wip_destructive) {
+ node = nullptr;
+}
+
+NavigationObstacle2DEditorPlugin::NavigationObstacle2DEditorPlugin(EditorNode *p_node) :
+ AbstractPolygon2DEditorPlugin(p_node, memnew(NavigationObstacle2DEditor(p_node)), "NavigationObstacle2D") {
+}
diff --git a/editor/plugins/navigation_obstacle_2d_editor_plugin.h b/editor/plugins/navigation_obstacle_2d_editor_plugin.h
new file mode 100644
index 000000000..25d212559
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_2d_editor_plugin.h
@@ -0,0 +1,61 @@
+#ifndef NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
+#define NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
+
+/**************************************************************************/
+/* navigation_obstacle_2d_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "editor/plugins/abstract_polygon_2d_editor.h"
+#include "scene/2d/navigation_obstacle_2d.h"
+
+class NavigationObstacle2DEditor : public AbstractPolygon2DEditor {
+ GDCLASS(NavigationObstacle2DEditor, AbstractPolygon2DEditor);
+
+ NavigationObstacle2D *node;
+
+protected:
+ virtual Node2D *_get_node() const ;
+ virtual void _set_node(Node *p_polygon) ;
+
+ virtual void _action_add_polygon(const Variant &p_polygon) ;
+ virtual void _action_remove_polygon(int p_idx) ;
+ virtual void _action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) ;
+
+public:
+ NavigationObstacle2DEditor(EditorNode *p_editor, bool p_wip_destructive = true);
+};
+
+class NavigationObstacle2DEditorPlugin : public AbstractPolygon2DEditorPlugin {
+ GDCLASS(NavigationObstacle2DEditorPlugin, AbstractPolygon2DEditorPlugin);
+
+public:
+ NavigationObstacle2DEditorPlugin(EditorNode *p_node);
+};
+
+#endif // NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
new file mode 100644
index 000000000..fbc350b54
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -0,0 +1,624 @@
+/**************************************************************************/
+/* navigation_obstacle_3d_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "navigation_obstacle_3d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "core/core_string_names.h"
+#include "core/input/input.h"
+#include "core/math/geometry.h"
+#include "core/object/undo_redo.h"
+#include "core/os/file_access.h"
+#include "core/os/keyboard.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "scene/3d/camera.h"
+#include "scene/gui/separator.h"
+#include "spatial_editor_plugin.h"
+
+void NavigationObstacleEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ button_create->set_icon(get_theme_icon("Edit", "EditorIcons"));
+ button_edit->set_icon(get_theme_icon("MovePoint", "EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed", this, "_node_removed");
+
+ } break;
+ }
+}
+
+void NavigationObstacleEditor::_node_removed(Node *p_node) {
+ if (p_node == obstacle_node) {
+ obstacle_node = nullptr;
+ if (point_lines_meshinstance->get_parent() == p_node) {
+ p_node->remove_child(point_lines_meshinstance);
+ }
+ hide();
+ }
+}
+
+void NavigationObstacleEditor::_menu_option(int p_option) {
+ switch (p_option) {
+ case MODE_CREATE: {
+ mode = MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+ mode = MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+ }
+}
+
+void NavigationObstacleEditor::_wip_close() {
+ ERR_FAIL_COND_MSG(!obstacle_node, "Edited NavigationObstacle is not valid.");
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Set NavigationObstacle Vertices"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+
+ Vector wipt;
+ wipt.resize(wip.size());
+ for (int i = 0; i < wip.size(); ++i) {
+ wipt.write[i] = wip[i];
+ }
+
+ PoolVector3Array polygon_3d_vertices;
+ Vector triangulated_polygon_2d_indices = Geometry::triangulate_polygon(wipt);
+
+ if (!triangulated_polygon_2d_indices.empty()) {
+ polygon_3d_vertices.resize(wip.size());
+ PoolVector3Array::Write p3dvw = polygon_3d_vertices.write();
+ Vector3 *polygon_3d_vertices_ptr = p3dvw.ptr();
+ for (int i = 0; i < wip.size(); i++) {
+ const Vector2 &vert = wip[i];
+ polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
+ }
+ }
+
+ undo_redo->add_do_method(obstacle_node, "set_vertices", polygon_3d_vertices);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ wip.clear();
+ wip_active = false;
+ mode = MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point = -1;
+ undo_redo->commit_action();
+}
+
+EditorPlugin::AfterGUIInput NavigationObstacleEditor::forward_3d_gui_input(Camera *p_camera, const Ref &p_event) {
+ if (!obstacle_node) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ Transform gt = obstacle_node->get_global_transform();
+ Transform gi = gt.affine_inverse();
+ Plane p(Vector3(0.0, 1.0, 0.0), gt.origin);
+
+ Ref mb = p_event;
+
+ if (mb.is_valid()) {
+ Vector2 gpoint = mb->get_position();
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x, spoint.z);
+
+ //DO NOT snap here, it's confusing in 3D for adding points.
+ //Let the snap happen when the point is being moved, instead.
+ //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+
+ PoolVector2Array poly = _get_polygon();
+
+ //first check if a point is to be added (segment split)
+ real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
+
+ switch (mode) {
+ case MODE_CREATE: {
+ if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ if (!wip_active) {
+ wip.clear();
+ wip.push_back(cpoint);
+ wip_active = true;
+ edited_point_pos = cpoint;
+ snap_ignore = false;
+ _polygon_draw();
+ edited_point = 1;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else {
+ if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) {
+ //wip closed
+ _wip_close();
+
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else {
+ wip.push_back(cpoint);
+ edited_point = wip.size();
+ snap_ignore = false;
+ _polygon_draw();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) {
+ _wip_close();
+ }
+
+ } break;
+
+ case MODE_EDIT: {
+ if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ if (mb->get_control()) {
+ if (poly.size() < 3) {
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Edit Vertices"));
+ undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
+ poly.push_back(cpoint);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+
+ //search edges
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 points[2] = {
+ p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))),
+ p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y)))
+ };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points);
+ if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) {
+ continue; //not valid to reuse point
+ }
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ pre_move_edit = poly;
+ poly.insert(closest_idx + 1, cpoint);
+ edited_point = closest_idx + 1;
+ edited_point_pos = cpoint;
+ _set_polygon(poly);
+ _polygon_draw();
+ snap_ignore = true;
+
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ } else {
+ //look for points to move
+
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ pre_move_edit = poly;
+ edited_point = closest_idx;
+ edited_point_pos = poly[closest_idx];
+ _polygon_draw();
+ snap_ignore = false;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ } else {
+ snap_ignore = false;
+
+ if (edited_point != -1) {
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
+ poly.set(edited_point, edited_point_pos);
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Edit Poly"));
+ //undo_redo->add_do_method(obj, "set_polygon", poly);
+ //undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+
+ edited_point = -1;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+ if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) {
+ int closest_idx = -1;
+ Vector2 closest_pos;
+ real_t closest_dist = 1e10;
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
+
+ real_t d = cp.distance_to(gpoint);
+ if (d < closest_dist && d < grab_threshold) {
+ closest_dist = d;
+ closest_pos = cp;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
+ //undo_redo->add_undo_method(obj, "set_polygon", poly);
+ poly.remove(closest_idx);
+ //undo_redo->add_do_method(obj, "set_polygon", poly);
+ undo_redo->add_do_method(this, "_polygon_draw");
+ undo_redo->add_undo_method(this, "_polygon_draw");
+ undo_redo->commit_action();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+
+ } break;
+ }
+ }
+
+ Ref mm = p_event;
+
+ if (mm.is_valid()) {
+ if (edited_point != -1 && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT) != 0)) {
+ Vector2 gpoint = mm->get_position();
+
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x, spoint.z);
+
+ if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ snap_ignore = false;
+ }
+
+ if (!snap_ignore && SpatialEditor::get_singleton()->is_snap_enabled()) {
+ cpoint = cpoint.snapped(Vector2(
+ SpatialEditor::get_singleton()->get_translate_snap(),
+ SpatialEditor::get_singleton()->get_translate_snap()));
+ }
+ edited_point_pos = cpoint;
+
+ _polygon_draw();
+ }
+ }
+
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+}
+
+PoolVector2Array NavigationObstacleEditor::_get_polygon() {
+ ERR_FAIL_COND_V_MSG(!obstacle_node, PoolVector2Array(), "Edited object is not valid.");
+ return PoolVector2Array(obstacle_node->call("get_polygon"));
+}
+
+void NavigationObstacleEditor::_set_polygon(PoolVector2Array p_poly) {
+ ERR_FAIL_COND_MSG(!obstacle_node, "Edited object is not valid.");
+ obstacle_node->call("set_polygon", p_poly);
+}
+
+void NavigationObstacleEditor::_polygon_draw() {
+ if (!obstacle_node) {
+ return;
+ }
+
+ PoolVector2Array poly;
+ PoolVector3Array polygon_3d_vertices;
+
+ if (wip_active) {
+ poly = wip;
+ } else {
+ poly = _get_polygon();
+ }
+ polygon_3d_vertices.resize(poly.size());
+ PoolVector3Array::Write p3dvw = polygon_3d_vertices.write();
+ Vector3 *polygon_3d_vertices_ptr = p3dvw.ptr();
+
+ for (int i = 0; i < poly.size(); i++) {
+ const Vector2 &vert = poly[i];
+ polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
+ }
+
+ point_handle_mesh->clear_surfaces();
+ point_lines_mesh->clear_surfaces();
+ point_lines_meshinstance->set_material_override(line_material);
+ point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES);
+
+ Rect2 rect;
+
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 p, p2;
+ if (i == edited_point) {
+ p = edited_point_pos;
+ } else {
+ p = poly[i];
+ }
+
+ if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
+ p2 = edited_point_pos;
+ } else {
+ p2 = poly[(i + 1) % poly.size()];
+ }
+
+ if (i == 0) {
+ rect.position = p;
+ } else {
+ rect.expand_to(p);
+ }
+
+ Vector3 point = Vector3(p.x, 0.0, p.y);
+ Vector3 next_point = Vector3(p2.x, 0.0, p2.y);
+
+ point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
+ point_lines_mesh->surface_add_vertex(point);
+ point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
+ point_lines_mesh->surface_add_vertex(next_point);
+
+ //Color col=Color(1,0.3,0.1,0.8);
+ //vpc->draw_line(point,next_point,col,2);
+ //vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+
+ rect = rect.grow(1);
+
+ AABB r;
+ r.position.x = rect.position.x;
+ r.position.y = 0.0;
+ r.position.z = rect.position.y;
+ r.size.x = rect.size.x;
+ r.size.y = 0;
+ r.size.z = rect.size.y;
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));
+
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size);
+ point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
+ point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));
+
+ point_lines_mesh->surface_end();
+
+ if (poly.size() == 0) {
+ return;
+ }
+
+ Array point_handle_mesh_array;
+ point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
+ Vector point_handle_mesh_vertices;
+
+ point_handle_mesh_vertices.resize(poly.size());
+ Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw();
+
+ for (int i = 0; i < poly.size(); i++) {
+ Vector2 point_2d;
+ Vector2 p2;
+
+ if (i == edited_point) {
+ point_2d = edited_point_pos;
+ } else {
+ point_2d = poly[i];
+ }
+
+ Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y);
+ point_handle_mesh_vertices_ptr[i] = point_handle_3d;
+ }
+
+ point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices;
+ point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array);
+ point_handle_mesh->surface_set_material(0, handle_material);
+}
+
+void NavigationObstacleEditor::edit(Node *p_node) {
+ obstacle_node = Object::cast_to(p_node);
+
+ if (obstacle_node) {
+ //Enable the pencil tool if the polygon is empty
+ if (_get_polygon().empty()) {
+ _menu_option(MODE_CREATE);
+ }
+ wip.clear();
+ wip_active = false;
+ edited_point = -1;
+ p_node->add_child(point_lines_meshinstance);
+ _polygon_draw();
+
+ } else {
+ obstacle_node = nullptr;
+
+ if (point_lines_meshinstance->get_parent()) {
+ point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance);
+ }
+ }
+}
+
+void NavigationObstacleEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_polygon_draw"), &NavigationObstacleEditor::_polygon_draw);
+ ClassDB::bind_method(D_METHOD("_node_removed"), &NavigationObstacleEditor::_node_removed);
+ ClassDB::bind_method(D_METHOD("_menu_option"), &NavigationObstacleEditor::_menu_option);
+}
+
+NavigationObstacleEditor::NavigationObstacleEditor() {
+ obstacle_node = nullptr;
+
+ button_create = nullptr;
+ button_edit = nullptr;
+
+ panel = nullptr;
+ obstacle_node = nullptr;
+ point_lines_meshinstance = nullptr;
+ point_handles_meshinstance = nullptr;
+ options = nullptr;
+
+ edited_point = 0;
+ wip_active = false;
+ snap_ignore = false;
+
+ prev_depth = 0.0f;
+
+ add_child(memnew(VSeparator));
+ button_create = memnew(Button);
+ button_create->set_flat(true);
+ add_child(button_create);
+ button_create->connect("pressed", this, "_menu_option", varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+
+ button_edit = memnew(Button);
+ button_edit->set_flat(true);
+ add_child(button_edit);
+ button_edit->connect("pressed", this, "_menu_option", varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+
+ mode = MODE_EDIT;
+ wip_active = false;
+ point_lines_meshinstance = memnew(MeshInstance);
+ point_lines_mesh.instance();
+ point_lines_meshinstance->set_mesh(point_lines_mesh);
+ point_lines_meshinstance->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
+
+ line_material = Ref(memnew(SpatialMaterial));
+ line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ line_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ line_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ line_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ line_material->set_albedo(Color(1, 1, 1));
+
+ handle_material = Ref(memnew(SpatialMaterial));
+ handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ handle_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true);
+ handle_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ handle_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ handle_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ Ref handle = EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Editor3DHandle", "EditorIcons");
+ handle_material->set_point_size(handle->get_width());
+ handle_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle);
+
+ point_handles_meshinstance = memnew(MeshInstance);
+ point_lines_meshinstance->add_child(point_handles_meshinstance);
+ point_handle_mesh.instance();
+ point_handles_meshinstance->set_mesh(point_handle_mesh);
+ point_handles_meshinstance->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
+
+ snap_ignore = false;
+}
+
+NavigationObstacleEditor::~NavigationObstacleEditor() {
+ memdelete(point_lines_meshinstance);
+}
+
+void NavigationObstacleEditorPlugin::edit(Object *p_object) {
+ obstacle_editor->edit(Object::cast_to(p_object));
+}
+
+bool NavigationObstacleEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to(p_object);
+}
+
+void NavigationObstacleEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ obstacle_editor->show();
+ } else {
+ obstacle_editor->hide();
+ obstacle_editor->edit(nullptr);
+ }
+}
+
+NavigationObstacleEditorPlugin::NavigationObstacleEditorPlugin(EditorNode *p_node) {
+ obstacle_editor = memnew(NavigationObstacleEditor);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor);
+
+ obstacle_editor->hide();
+}
+
+NavigationObstacleEditorPlugin::~NavigationObstacleEditorPlugin() {
+}
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
new file mode 100644
index 000000000..4d026f010
--- /dev/null
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
@@ -0,0 +1,117 @@
+#ifndef NAVIGATION_OBSTACLE_EDITOR_PLUGIN_H
+#define NAVIGATION_OBSTACLE_EDITOR_PLUGIN_H
+
+/**************************************************************************/
+/* navigation_obstacle_3d_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "editor/editor_plugin.h"
+#include "scene/3d/collision_polygon.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/gui/box_container.h"
+#include "scene/resources/immediate_mesh.h"
+
+#include "scene/3d/navigation_obstacle.h"
+
+class CanvasItemEditor;
+class MenuButton;
+
+class NavigationObstacleEditor : public HBoxContainer {
+ GDCLASS(NavigationObstacleEditor, HBoxContainer);
+
+ enum Mode {
+ MODE_CREATE,
+ MODE_EDIT,
+ };
+
+ Mode mode;
+
+ Button *button_create;
+ Button *button_edit;
+
+ Ref line_material;
+ Ref handle_material;
+
+ Panel *panel;
+ NavigationObstacle *obstacle_node;
+ Ref point_lines_mesh;
+ MeshInstance *point_lines_meshinstance;
+ MeshInstance *point_handles_meshinstance;
+ Ref point_handle_mesh;
+
+ MenuButton *options;
+
+ int edited_point;
+ Vector2 edited_point_pos;
+ PoolVector2Array pre_move_edit;
+ PoolVector2Array wip;
+ bool wip_active;
+ bool snap_ignore;
+
+ float prev_depth;
+
+ void _wip_close();
+ void _polygon_draw();
+ void _menu_option(int p_option);
+
+ PoolVector2Array _get_polygon();
+ void _set_polygon(PoolVector2Array p_poly);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+public:
+ virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera *p_camera, const Ref &p_event);
+ void edit(Node *p_node);
+
+ NavigationObstacleEditor();
+ ~NavigationObstacleEditor();
+};
+
+class NavigationObstacleEditorPlugin : public EditorPlugin {
+ GDCLASS(NavigationObstacleEditorPlugin, EditorPlugin);
+
+ NavigationObstacleEditor *obstacle_editor = nullptr;
+
+public:
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera *p_camera, const Ref &p_event) { return obstacle_editor->forward_3d_gui_input(p_camera, p_event); }
+
+ virtual String get_name() const { return "NavigationObstacleEditor"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ NavigationObstacleEditorPlugin(EditorNode *p_node);
+ ~NavigationObstacleEditorPlugin();
+};
+
+#endif // NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 4171678ae..88e341b1b 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -30,15 +30,19 @@
#include "property_editor.h"
-#include "core/variant/array.h"
-#include "core/object/class_db.h"
-#include "core/math/color.h"
+#include "core/config/project_settings.h"
+#include "core/containers/pair.h"
+#include "core/containers/rb_map.h"
+#include "core/containers/rb_set.h"
+#include "core/containers/rid.h"
#include "core/error/error_list.h"
#include "core/error/error_macros.h"
+#include "core/input/input.h"
+#include "core/input/input_event.h"
#include "core/io/resource_loader.h"
-#include "core/containers/rb_map.h"
#include "core/math/aabb.h"
#include "core/math/basis.h"
+#include "core/math/color.h"
#include "core/math/expression.h"
#include "core/math/math_funcs.h"
#include "core/math/plane.h"
@@ -48,20 +52,16 @@
#include "core/math/transform_2d.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
-#include "core/input/input.h"
-#include "core/input/input_event.h"
+#include "core/object/class_db.h"
+#include "core/object/ref_ptr.h"
+#include "core/object/resource.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/main_loop.h"
#include "core/os/memory.h"
-#include "core/containers/pair.h"
-#include "core/config/project_settings.h"
-#include "core/object/ref_ptr.h"
-#include "core/object/resource.h"
-#include "core/containers/rid.h"
-#include "core/object/script_language.h"
-#include "core/containers/rb_set.h"
#include "core/string/string_name.h"
#include "core/typedefs.h"
+#include "core/variant/array.h"
#include "editor/array_property_edit.h"
#include "editor/create_dialog.h"
#include "editor/dictionary_property_edit.h"
@@ -460,7 +460,13 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
updating = false;
return false;
- } 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) {
+ } 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 ||
+ hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
String basename;
switch (hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER:
@@ -481,6 +487,9 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
basename = "layer_names/3d_navigation";
break;
+ case PROPERTY_HINT_LAYERS_AVOIDANCE:
+ basename = "layer_names/avoidance";
+ break;
}
checks20gc->show();
diff --git a/main/main.cpp b/main/main.cpp
index 67d1060ac..78329f778 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -159,6 +159,7 @@ static bool use_debug_profiler = false;
#ifdef DEBUG_ENABLED
static bool debug_collisions = false;
static bool debug_navigation = false;
+static bool debug_avoidance = false;
static bool debug_shader_fallbacks = false;
#endif
static int frame_delay = 0;
@@ -375,6 +376,7 @@ void Main::print_help(const char *p_binary) {
#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
+ OS::get_singleton()->print(" --debug-avoidance Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-shader-fallbacks Use the fallbacks of the shaders which have one when running the scene (GL ES 3 only).\n");
#endif
OS::get_singleton()->print(" --frame-delay Simulate high CPU load (delay each frame by milliseconds).\n");
@@ -912,6 +914,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
debug_collisions = true;
} else if (I->get() == "--debug-navigation") {
debug_navigation = true;
+ } else if (I->get() == "--debug-avoidance") {
+ debug_avoidance = true;
} else if (I->get() == "--debug-shader-fallbacks") {
debug_shader_fallbacks = true;
#endif
@@ -1928,8 +1932,19 @@ bool Main::start() {
}
if (debug_navigation) {
sml->set_debug_navigation_hint(true);
+ }
+ if (debug_avoidance) {
+ sml->set_debug_avoidance_hint(true);
+ }
+ if (debug_navigation || debug_avoidance) {
NavigationServer::get_singleton()->set_active(true);
NavigationServer::get_singleton()->set_debug_enabled(true);
+ if (debug_navigation) {
+ NavigationServer::get_singleton()->set_debug_navigation_enabled(true);
+ }
+ if (debug_avoidance) {
+ NavigationServer::get_singleton()->set_debug_avoidance_enabled(true);
+ }
}
#endif
diff --git a/modules/cscript/cscript_parser.cpp b/modules/cscript/cscript_parser.cpp
index 2b5046cfb..7ac8ab22c 100644
--- a/modules/cscript/cscript_parser.cpp
+++ b/modules/cscript/cscript_parser.cpp
@@ -4266,6 +4266,16 @@ void CScriptParser::_parse_class(ClassNode *p_class) {
break;
}
+ if (tokenizer->get_token() == CScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "PROPERTY_HINT_LAYERS_AVOIDANCE") {
+ _ADVANCE_AND_CONSUME_NEWLINES;
+ if (tokenizer->get_token() != CScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected \")\" in the avoidance 3D navigation hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_AVOIDANCE;
+ break;
+ }
+
if (tokenizer->get_token() == CScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration
current_export.hint = PROPERTY_HINT_ENUM;
diff --git a/modules/entity_spell_system/data/items/item_template.cpp b/modules/entity_spell_system/data/items/item_template.cpp
index 9a4beda0d..627a25d14 100644
--- a/modules/entity_spell_system/data/items/item_template.cpp
+++ b/modules/entity_spell_system/data/items/item_template.cpp
@@ -550,7 +550,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_teaches_spells"), &ItemTemplate::get_teaches_spells);
ClassDB::bind_method(D_METHOD("set_teaches_spells", "spells"), &ItemTemplate::set_teaches_spells);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "teaches_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_teaches_spells", "get_teaches_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "teaches_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_teaches_spells", "get_teaches_spells");
//// Grants Spells ////
ClassDB::bind_method(D_METHOD("get_num_grants_spells"), &ItemTemplate::get_num_grants_spells);
@@ -561,7 +561,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_grants_spells"), &ItemTemplate::get_grants_spells);
ClassDB::bind_method(D_METHOD("set_grants_spells", "spells"), &ItemTemplate::set_grants_spells);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "grants_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_grants_spells", "get_grants_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "grants_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_grants_spells", "get_grants_spells");
//// Auras ////
ClassDB::bind_method(D_METHOD("get_num_auras"), &ItemTemplate::get_num_auras);
@@ -572,7 +572,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &ItemTemplate::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &ItemTemplate::set_auras);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
//// Required Skills ////
ClassDB::bind_method(D_METHOD("get_num_required_skills"), &ItemTemplate::get_num_required_skills);
@@ -582,7 +582,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_required_skills"), &ItemTemplate::get_required_skills);
ClassDB::bind_method(D_METHOD("set_required_skills", "auras"), &ItemTemplate::set_required_skills);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "required_skills", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_required_skills", "get_required_skills");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "required_skills", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_required_skills", "get_required_skills");
//Use spell
ClassDB::bind_method(D_METHOD("get_use_spell"), &ItemTemplate::get_use_spell);
diff --git a/modules/entity_spell_system/data/items/model_visual.cpp b/modules/entity_spell_system/data/items/model_visual.cpp
index 11eb6335d..9f5c884b9 100644
--- a/modules/entity_spell_system/data/items/model_visual.cpp
+++ b/modules/entity_spell_system/data/items/model_visual.cpp
@@ -96,5 +96,5 @@ void ModelVisual::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visual_entries"), &ModelVisual::get_visual_entries);
ClassDB::bind_method(D_METHOD("set_visual_entries", "visual_entrys"), &ModelVisual::set_visual_entries);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "visual_entries", PROPERTY_HINT_NONE, "23/19:ModelVisualEntry", PROPERTY_USAGE_DEFAULT, "ModelVisualEntry"), "set_visual_entries", "get_visual_entries");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "visual_entries", PROPERTY_HINT_NONE, "23/20:ModelVisualEntry", PROPERTY_USAGE_DEFAULT, "ModelVisualEntry"), "set_visual_entries", "get_visual_entries");
}
diff --git a/modules/entity_spell_system/data/species/entity_species_data.cpp b/modules/entity_spell_system/data/species/entity_species_data.cpp
index b5c252fc1..f0eeb0f74 100644
--- a/modules/entity_spell_system/data/species/entity_species_data.cpp
+++ b/modules/entity_spell_system/data/species/entity_species_data.cpp
@@ -202,7 +202,7 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_model_datas"), &EntitySpeciesData::get_model_datas);
ClassDB::bind_method(D_METHOD("set_model_datas", "model_datas"), &EntitySpeciesData::set_model_datas);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "model_datas", PROPERTY_HINT_NONE, "23/19:SpeciesModelData", PROPERTY_USAGE_DEFAULT, "SpeciesModelData"), "set_model_datas", "get_model_datas");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "model_datas", PROPERTY_HINT_NONE, "23/20:SpeciesModelData", PROPERTY_USAGE_DEFAULT, "SpeciesModelData"), "set_model_datas", "get_model_datas");
//Spells
ClassDB::bind_method(D_METHOD("get_spell", "index"), &EntitySpeciesData::get_spell);
@@ -214,7 +214,7 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_spells"), &EntitySpeciesData::get_spells);
ClassDB::bind_method(D_METHOD("set_spells", "spells"), &EntitySpeciesData::set_spells);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
//Auras
ClassDB::bind_method(D_METHOD("get_aura", "index"), &EntitySpeciesData::get_aura);
@@ -226,5 +226,5 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &EntitySpeciesData::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &EntitySpeciesData::set_auras);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
}
diff --git a/modules/entity_spell_system/data/species/species_model_data.cpp b/modules/entity_spell_system/data/species/species_model_data.cpp
index e46fbcdcc..fc83aaa2b 100644
--- a/modules/entity_spell_system/data/species/species_model_data.cpp
+++ b/modules/entity_spell_system/data/species/species_model_data.cpp
@@ -331,7 +331,7 @@ void SpeciesModelData::_get_property_list(List *p_list) const {
int count = _customizable_slots_string.get_slice_count(",");
for (int i = 0; i < count; ++i) {
- p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_slots/" + itos(i) + "_" + _customizable_slots_string.get_slicec(',', i), PROPERTY_HINT_NONE, "23/19:ModelVisualEntry", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "ModelVisualEntry"));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_slots/" + itos(i) + "_" + _customizable_slots_string.get_slicec(',', i), PROPERTY_HINT_NONE, "23/20:ModelVisualEntry", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "ModelVisualEntry"));
}
count = _customizable_color_slots_string.get_slice_count(",");
@@ -339,7 +339,7 @@ void SpeciesModelData::_get_property_list(List *p_list) const {
for (int i = 0; i < count; ++i) {
p_list->push_back(PropertyInfo(Variant::INT, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/texture_layer", PROPERTY_HINT_ENUM, ESS::get_singleton()->texture_layers_get(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED));
p_list->push_back(PropertyInfo(Variant::INT, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/bone_slot_mask", PROPERTY_HINT_FLAGS, ESS::get_singleton()->skeletons_bones_index_get(_bone_structure), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/colors", PROPERTY_HINT_NONE, "23/19:Color", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "Color"));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/colors", PROPERTY_HINT_NONE, "23/20:Color", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "Color"));
}
}
void SpeciesModelData::_validate_property(PropertyInfo &property) const {
diff --git a/modules/entity_spell_system/data/spells/spell.cpp b/modules/entity_spell_system/data/spells/spell.cpp
index e73258b4e..decc400a8 100644
--- a/modules/entity_spell_system/data/spells/spell.cpp
+++ b/modules/entity_spell_system/data/spells/spell.cpp
@@ -2728,7 +2728,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_caster_get"), &Spell::spells_cast_on_caster_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_caster_set", "caster_aura_applys"), &Spell::spells_cast_on_caster_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_caster", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_caster_set", "spells_cast_on_caster_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_caster", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_caster_set", "spells_cast_on_caster_get");
//ADD_GROUP("Target Aura Apply", "target_aura_applys");
ClassDB::bind_method(D_METHOD("spells_cast_on_target_num_get"), &Spell::spells_cast_on_target_num_get);
@@ -2739,7 +2739,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_target_get"), &Spell::spells_cast_on_target_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_target_set", "target_aura_applys"), &Spell::spells_cast_on_target_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_target", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_target_set", "spells_cast_on_target_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_target", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_target_set", "spells_cast_on_target_get");
//ADD_GROUP("Apply Auras On Learn", "on_learn_auras");
ClassDB::bind_method(D_METHOD("on_learn_cast_spells_num_get"), &Spell::on_learn_cast_spells_num_get);
@@ -2750,7 +2750,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_learn_get"), &Spell::spells_cast_on_learn_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_learn_set", "spells"), &Spell::spells_cast_on_learn_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_learn", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_learn_set", "spells_cast_on_learn_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_learn", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_learn_set", "spells_cast_on_learn_get");
ADD_GROUP("Texts", "text");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::STRING, "desc"), "_get_description", PropertyInfo(Variant::INT, "class_level"), PropertyInfo(Variant::INT, "character_level")));
diff --git a/modules/entity_spell_system/database/ess_resource_db_static.cpp b/modules/entity_spell_system/database/ess_resource_db_static.cpp
index 2d4d9629b..821df37d0 100644
--- a/modules/entity_spell_system/database/ess_resource_db_static.cpp
+++ b/modules/entity_spell_system/database/ess_resource_db_static.cpp
@@ -312,11 +312,11 @@ void ESSResourceDBStatic::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_remap_ids", "value"), &ESSResourceDBStatic::set_remap_ids);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "remap_ids"), "set_remap_ids", "get_remap_ids");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_skills", PROPERTY_HINT_NONE, "23/19:EntitySkillData", PROPERTY_USAGE_DEFAULT, "EntitySkillData"), "set_entity_skills", "get_entity_skills");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_datas", PROPERTY_HINT_NONE, "23/19:EntityData", PROPERTY_USAGE_DEFAULT, "EntityData"), "set_entity_datas", "get_entity_datas");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "item_templates", PROPERTY_HINT_NONE, "23/19:ItemTemplate", PROPERTY_USAGE_DEFAULT, "ItemTemplate"), "set_item_templates", "get_item_templates");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_species_datas", PROPERTY_HINT_NONE, "23/19:EntitySpeciesData", PROPERTY_USAGE_DEFAULT, "EntitySpeciesData"), "set_entity_species_datas", "get_entity_species_datas");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_skills", PROPERTY_HINT_NONE, "23/20:EntitySkillData", PROPERTY_USAGE_DEFAULT, "EntitySkillData"), "set_entity_skills", "get_entity_skills");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_datas", PROPERTY_HINT_NONE, "23/20:EntityData", PROPERTY_USAGE_DEFAULT, "EntityData"), "set_entity_datas", "get_entity_datas");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "item_templates", PROPERTY_HINT_NONE, "23/20:ItemTemplate", PROPERTY_USAGE_DEFAULT, "ItemTemplate"), "set_item_templates", "get_item_templates");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_species_datas", PROPERTY_HINT_NONE, "23/20:EntitySpeciesData", PROPERTY_USAGE_DEFAULT, "EntitySpeciesData"), "set_entity_species_datas", "get_entity_species_datas");
}
diff --git a/modules/entity_spell_system/entities/data/entity_class_data.cpp b/modules/entity_spell_system/entities/data/entity_class_data.cpp
index e7ffd1267..3b32bc2f7 100644
--- a/modules/entity_spell_system/entities/data/entity_class_data.cpp
+++ b/modules/entity_spell_system/entities/data/entity_class_data.cpp
@@ -415,7 +415,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_entity_resources"), &EntityClassData::get_entity_resources);
ClassDB::bind_method(D_METHOD("set_entity_resources", "entity_resources"), &EntityClassData::set_entity_resources);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
ClassDB::bind_method(D_METHOD("_setup_resources", "entity"), &EntityClassData::_setup_resources);
@@ -428,7 +428,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_specs"), &EntityClassData::get_specs);
ClassDB::bind_method(D_METHOD("set_specs", "specs"), &EntityClassData::set_specs);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "specs", PROPERTY_HINT_NONE, "23/19:CharacterSpec", PROPERTY_USAGE_DEFAULT, "CharacterSpec"), "set_specs", "get_specs");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "specs", PROPERTY_HINT_NONE, "23/20:CharacterSpec", PROPERTY_USAGE_DEFAULT, "CharacterSpec"), "set_specs", "get_specs");
//// Spell ////
ClassDB::bind_method(D_METHOD("get_num_spells"), &EntityClassData::get_num_spells);
@@ -439,7 +439,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_spells"), &EntityClassData::get_spells);
ClassDB::bind_method(D_METHOD("set_spells", "spells"), &EntityClassData::set_spells);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
//// Start Spells ////
ClassDB::bind_method(D_METHOD("get_num_start_spells"), &EntityClassData::get_num_start_spells);
@@ -450,7 +450,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_start_spells"), &EntityClassData::get_start_spells);
ClassDB::bind_method(D_METHOD("set_start_spells", "spells"), &EntityClassData::set_start_spells);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "start_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_start_spells", "get_start_spells");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "start_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_start_spells", "get_start_spells");
//// AURAS ////
ClassDB::bind_method(D_METHOD("get_num_auras"), &EntityClassData::get_num_auras);
@@ -461,7 +461,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &EntityClassData::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &EntityClassData::set_auras);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
//Vendor
ClassDB::bind_method(D_METHOD("get_vendor_item_data"), &EntityClassData::get_vendor_item_data);
@@ -485,7 +485,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_craft_recipes"), &EntityClassData::get_craft_recipes);
ClassDB::bind_method(D_METHOD("set_craft_recipes", "recipe"), &EntityClassData::set_craft_recipes);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
//// AI ACTIONS ////
ClassDB::bind_method(D_METHOD("get_num_ais"), &EntityClassData::get_num_ais);
@@ -496,7 +496,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ais"), &EntityClassData::get_ais);
ClassDB::bind_method(D_METHOD("set_ais", "auras"), &EntityClassData::set_ais);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "ais", PROPERTY_HINT_NONE, "23/19:EntityAI", PROPERTY_USAGE_DEFAULT, "EntityAI"), "set_ais", "get_ais");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "ais", PROPERTY_HINT_NONE, "23/20:EntityAI", PROPERTY_USAGE_DEFAULT, "EntityAI"), "set_ais", "get_ais");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "EntityAI"), "_get_ai_instance"));
diff --git a/modules/entity_spell_system/entities/data/item_container_data.cpp b/modules/entity_spell_system/entities/data/item_container_data.cpp
index aa5b7ab63..17477f72e 100644
--- a/modules/entity_spell_system/entities/data/item_container_data.cpp
+++ b/modules/entity_spell_system/entities/data/item_container_data.cpp
@@ -70,5 +70,5 @@ void ItemContainerData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_container_datas"), &ItemContainerData::get_container_datas);
ClassDB::bind_method(D_METHOD("set_container_datas", "container_datas"), &ItemContainerData::set_container_datas);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "container_datas", PROPERTY_HINT_NONE, "23/19:ItemContainerDataEntry", PROPERTY_USAGE_DEFAULT, "ItemContainerDataEntry"), "set_container_datas", "get_container_datas");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "container_datas", PROPERTY_HINT_NONE, "23/20:ItemContainerDataEntry", PROPERTY_USAGE_DEFAULT, "ItemContainerDataEntry"), "set_container_datas", "get_container_datas");
}
diff --git a/modules/entity_spell_system/entities/data/vendor_item_data.cpp b/modules/entity_spell_system/entities/data/vendor_item_data.cpp
index 40455c6a0..ba8e5dc72 100644
--- a/modules/entity_spell_system/entities/data/vendor_item_data.cpp
+++ b/modules/entity_spell_system/entities/data/vendor_item_data.cpp
@@ -64,5 +64,5 @@ void VendorItemData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_vendor_datas"), &VendorItemData::get_vendor_datas);
ClassDB::bind_method(D_METHOD("set_vendor_datas", "vendor_datas"), &VendorItemData::set_vendor_datas);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "vendor_datas", PROPERTY_HINT_NONE, "23/19:VendorItemDataEntry", PROPERTY_USAGE_DEFAULT, "VendorItemDataEntry"), "set_vendor_datas", "get_vendor_datas");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "vendor_datas", PROPERTY_HINT_NONE, "23/20:VendorItemDataEntry", PROPERTY_USAGE_DEFAULT, "VendorItemDataEntry"), "set_vendor_datas", "get_vendor_datas");
}
diff --git a/modules/entity_spell_system/entities/entity.cpp b/modules/entity_spell_system/entities/entity.cpp
index 9cd58182b..370ee6598 100644
--- a/modules/entity_spell_system/entities/entity.cpp
+++ b/modules/entity_spell_system/entities/entity.cpp
@@ -7082,7 +7082,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sauras_get"), &Entity::sauras_get);
ClassDB::bind_method(D_METHOD("sauras_set", "data"), &Entity::sauras_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sauras", PROPERTY_HINT_NONE, "23/19:AuraData", PROPERTY_USAGE_ENTITY_HIDDEN, "AuraData"), "sauras_set", "sauras_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sauras", PROPERTY_HINT_NONE, "23/20:AuraData", PROPERTY_USAGE_ENTITY_HIDDEN, "AuraData"), "sauras_set", "sauras_get");
//Hooks
BIND_VMETHOD(MethodInfo("_moved"));
@@ -7332,7 +7332,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sresources_get"), &Entity::sresources_get);
ClassDB::bind_method(D_METHOD("sresources_set", "caster_aura_applys"), &Entity::sresources_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sresources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityResource"), "sresources_set", "sresources_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sresources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityResource"), "sresources_set", "sresources_get");
//GCD
ADD_SIGNAL(MethodInfo("sgcd_started", PropertyInfo(Variant::OBJECT, "entity", PROPERTY_HINT_RESOURCE_TYPE, "Entity"), PropertyInfo(Variant::REAL, "value")));
@@ -7367,7 +7367,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sdatas_get"), &Entity::sdatas_get);
ClassDB::bind_method(D_METHOD("sdatas_set", "data"), &Entity::sdatas_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sdatas", PROPERTY_HINT_NONE, "23/19:EntityDataContainer", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityDataContainer"), "sdatas_set", "sdatas_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sdatas", PROPERTY_HINT_NONE, "23/20:EntityDataContainer", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityDataContainer"), "sdatas_set", "sdatas_get");
//States
ADD_SIGNAL(MethodInfo("sstate_changed", PropertyInfo(Variant::INT, "value")));
@@ -7503,7 +7503,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sspells_get"), &Entity::sspells_get);
ClassDB::bind_method(D_METHOD("sspells_set", "data"), &Entity::sspells_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sspells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_ENTITY_HIDDEN, "Spell"), "sspells_set", "sspells_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sspells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_ENTITY_HIDDEN, "Spell"), "sspells_set", "sspells_get");
//Crafting
BIND_VMETHOD(MethodInfo("_crafts", PropertyInfo(Variant::INT, "id")));
@@ -7538,7 +7538,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("scraft_recipes_get"), &Entity::scraft_recipes_get);
ClassDB::bind_method(D_METHOD("scraft_recipes_set", "caster_aura_applys"), &Entity::scraft_recipes_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "scraft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_ENTITY_HIDDEN, "CraftRecipe"), "scraft_recipes_set", "scraft_recipes_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "scraft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_ENTITY_HIDDEN, "CraftRecipe"), "scraft_recipes_set", "scraft_recipes_get");
//Skills
ClassDB::bind_method(D_METHOD("skill_hass_id", "id"), &Entity::skill_hass_id);
@@ -7562,7 +7562,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sskills_get"), &Entity::sskills_get);
ClassDB::bind_method(D_METHOD("sskills_set", "data"), &Entity::sskills_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sskills", PROPERTY_HINT_NONE, "23/19:EntitySkill", PROPERTY_USAGE_ENTITY_HIDDEN, "EntitySkill"), "sskills_set", "sskills_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sskills", PROPERTY_HINT_NONE, "23/20:EntitySkill", PROPERTY_USAGE_ENTITY_HIDDEN, "EntitySkill"), "sskills_set", "sskills_get");
//skeleton
ClassDB::bind_method(D_METHOD("body_get"), &Entity::body_get);
diff --git a/modules/entity_spell_system/material_cache/ess_material_cache.cpp b/modules/entity_spell_system/material_cache/ess_material_cache.cpp
index febd8b47b..1f3ca7499 100644
--- a/modules/entity_spell_system/material_cache/ess_material_cache.cpp
+++ b/modules/entity_spell_system/material_cache/ess_material_cache.cpp
@@ -337,7 +337,7 @@ void ESSMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &ESSMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &ESSMaterialCache::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("texture_add", "texture"), &ESSMaterialCache::texture_add);
ClassDB::bind_method(D_METHOD("texture_remove", "texture"), &ESSMaterialCache::texture_remove);
diff --git a/modules/entity_spell_system/singletons/ess.cpp b/modules/entity_spell_system/singletons/ess.cpp
index 0d55603dc..377c4254b 100644
--- a/modules/entity_spell_system/singletons/ess.cpp
+++ b/modules/entity_spell_system/singletons/ess.cpp
@@ -857,7 +857,7 @@ void ESS::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &ESS::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &ESS::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("material_cache_get", "key"), &ESS::material_cache_get);
ClassDB::bind_method(D_METHOD("material_cache_unref", "key"), &ESS::material_cache_unref);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index e6cc88401..ee6ea2937 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,14 +30,14 @@
#include "gdscript_parser.h"
-#include "core/core_string_names.h"
#include "core/config/engine.h"
-#include "core/io/resource_loader.h"
-#include "core/os/file_access.h"
-#include "core/string/print_string.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
+#include "core/io/resource_loader.h"
#include "core/object/reference.h"
#include "core/object/script_language.h"
+#include "core/os/file_access.h"
+#include "core/string/print_string.h"
#include "gdscript.h"
template
@@ -4342,6 +4342,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
break;
}
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "PROPERTY_HINT_LAYERS_AVOIDANCE") {
+ _ADVANCE_AND_CONSUME_NEWLINES;
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected \")\" in the avoidance 3D navigation hint.");
+ return;
+ }
+ current_export.hint = PROPERTY_HINT_LAYERS_AVOIDANCE;
+ break;
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration
current_export.hint = PROPERTY_HINT_ENUM;
diff --git a/modules/material_maker/nodes/mm_material.cpp b/modules/material_maker/nodes/mm_material.cpp
index 114daa6c1..553f481e5 100644
--- a/modules/material_maker/nodes/mm_material.cpp
+++ b/modules/material_maker/nodes/mm_material.cpp
@@ -221,7 +221,7 @@ void MMMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_nodes"), &MMMaterial::get_nodes);
ClassDB::bind_method(D_METHOD("set_nodes", "value"), &MMMaterial::set_nodes);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "23/19:MMNode", PROPERTY_USAGE_DEFAULT, "MMNode"), "set_nodes", "get_nodes");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "23/20:MMNode", PROPERTY_USAGE_DEFAULT, "MMNode"), "set_nodes", "get_nodes");
ClassDB::bind_method(D_METHOD("get_initialized"), &MMMaterial::get_initialized);
ClassDB::bind_method(D_METHOD("set_initialized", "value"), &MMMaterial::set_initialized);
diff --git a/modules/material_maker/nodes/mm_node.cpp b/modules/material_maker/nodes/mm_node.cpp
index 50c11599b..bfd7eced2 100644
--- a/modules/material_maker/nodes/mm_node.cpp
+++ b/modules/material_maker/nodes/mm_node.cpp
@@ -236,11 +236,11 @@ void MMNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_input_properties"), &MMNode::get_input_properties);
ClassDB::bind_method(D_METHOD("set_input_properties", "value"), &MMNode::set_input_properties);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "input_properties", PROPERTY_HINT_NONE, "23/19:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_input_properties", "get_input_properties");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "input_properties", PROPERTY_HINT_NONE, "23/20:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_input_properties", "get_input_properties");
ClassDB::bind_method(D_METHOD("get_output_properties"), &MMNode::get_output_properties);
ClassDB::bind_method(D_METHOD("set_output_properties", "value"), &MMNode::set_output_properties);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "output_properties", PROPERTY_HINT_NONE, "23/19:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_output_properties", "get_output_properties");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "output_properties", PROPERTY_HINT_NONE, "23/20:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_output_properties", "get_output_properties");
ClassDB::bind_method(D_METHOD("get_properties_initialized"), &MMNode::get_properties_initialized);
ClassDB::bind_method(D_METHOD("set_properties_initialized", "value"), &MMNode::set_properties_initialized);
diff --git a/modules/navigation/SCsub b/modules/navigation/SCsub
index 85ea73248..5148486e0 100644
--- a/modules/navigation/SCsub
+++ b/modules/navigation/SCsub
@@ -34,12 +34,14 @@ if env["builtin_recast"]:
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
-# RVO2
-if env["builtin_rvo2"]:
- thirdparty_dir = "#thirdparty/rvo2/"
+# RVO 2D Thirdparty source files
+if env["builtin_rvo2_2d"]:
+ thirdparty_dir = "#thirdparty/rvo2/rvo2_2d/"
thirdparty_sources = [
- "Agent.cpp",
- "KdTree.cpp",
+ "Agent2d.cpp",
+ "Obstacle2d.cpp",
+ "KdTree2d.cpp",
+ "RVOSimulator2d.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
@@ -48,8 +50,25 @@ if env["builtin_rvo2"]:
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- env.modules_sources += thirdparty_obj
+ #env.modules_sources += thirdparty_obj
+# RVO 3D Thirdparty source files
+if env["builtin_rvo2_3d"]:
+ thirdparty_dir = "#thirdparty/rvo2/rvo2_3d/"
+ thirdparty_sources = [
+ "Agent3d.cpp",
+ "KdTree3d.cpp",
+ "RVOSimulator3d.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir])
+
+ env_thirdparty = env_navigation.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+env.modules_sources += thirdparty_obj
# Pandemonium source files
diff --git a/modules/navigation/nav_agent.cpp b/modules/navigation/nav_agent.cpp
new file mode 100644
index 000000000..3447a0398
--- /dev/null
+++ b/modules/navigation/nav_agent.cpp
@@ -0,0 +1,373 @@
+/**************************************************************************/
+/* nav_agent.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "nav_agent.h"
+
+#include "nav_map.h"
+
+NavAgent::NavAgent() {
+ height = 1.0;
+ radius = 1.0;
+ max_speed = 1.0;
+ time_horizon_agents = 1.0;
+ time_horizon_obstacles = 0.0;
+ max_neighbors = 5;
+ neighbor_distance = 5.0;
+ clamp_speed = true; // Experimental, clamps velocity to max_speed.
+
+ map = nullptr;
+
+ use_3d_avoidance = false;
+ avoidance_enabled = false;
+
+ avoidance_layers = 1;
+ avoidance_mask = 1;
+ avoidance_priority = 1.0;
+
+ avoidance_callback.id = ObjectID(0);
+ agent_dirty = true;
+
+ map_update_id = 0;
+}
+
+void NavAgent::set_avoidance_enabled(bool p_enabled) {
+ avoidance_enabled = p_enabled;
+ _update_rvo_agent_properties();
+}
+
+void NavAgent::set_use_3d_avoidance(bool p_enabled) {
+ use_3d_avoidance = p_enabled;
+ _update_rvo_agent_properties();
+}
+
+void NavAgent::_update_rvo_agent_properties() {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.neighborDist_ = neighbor_distance;
+ rvo_agent_3d.maxNeighbors_ = max_neighbors;
+ rvo_agent_3d.timeHorizon_ = time_horizon_agents;
+ rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
+ rvo_agent_3d.radius_ = radius;
+ rvo_agent_3d.maxSpeed_ = max_speed;
+ rvo_agent_3d.position_ = RVO3D::Vector3(position.x, position.y, position.z);
+ // Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
+ //rvo_agent_3d.velocity_ = RVO3D::Vector3(velocity.x, velocity.y ,velocity.z);
+ rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
+ rvo_agent_3d.height_ = height;
+ rvo_agent_3d.avoidance_layers_ = avoidance_layers;
+ rvo_agent_3d.avoidance_mask_ = avoidance_mask;
+ rvo_agent_3d.avoidance_priority_ = avoidance_priority;
+ } else {
+ rvo_agent_2d.neighborDist_ = neighbor_distance;
+ rvo_agent_2d.maxNeighbors_ = max_neighbors;
+ rvo_agent_2d.timeHorizon_ = time_horizon_agents;
+ rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
+ rvo_agent_2d.radius_ = radius;
+ rvo_agent_2d.maxSpeed_ = max_speed;
+ rvo_agent_2d.position_ = RVO2D::Vector2(position.x, position.z);
+ rvo_agent_2d.elevation_ = position.y;
+ // Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
+ //rvo_agent_2d.velocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ rvo_agent_2d.height_ = height;
+ rvo_agent_2d.avoidance_layers_ = avoidance_layers;
+ rvo_agent_2d.avoidance_mask_ = avoidance_mask;
+ rvo_agent_2d.avoidance_priority_ = avoidance_priority;
+ }
+
+ if (map != nullptr) {
+ if (avoidance_enabled) {
+ map->set_agent_as_controlled(this);
+ } else {
+ map->remove_agent_as_controlled(this);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_map(NavMap *p_map) {
+ map = p_map;
+ agent_dirty = true;
+}
+
+bool NavAgent::is_map_changed() {
+ if (map) {
+ bool is_changed = map->get_map_update_id() != map_update_id;
+ map_update_id = map->get_map_update_id();
+ return is_changed;
+ } else {
+ return false;
+ }
+}
+
+void NavAgent::set_neighbor_distance(real_t p_neighbor_distance) {
+ neighbor_distance = p_neighbor_distance;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.neighborDist_ = neighbor_distance;
+ } else {
+ rvo_agent_2d.neighborDist_ = neighbor_distance;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_max_neighbors(int p_max_neighbors) {
+ max_neighbors = p_max_neighbors;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.maxNeighbors_ = max_neighbors;
+ } else {
+ rvo_agent_2d.maxNeighbors_ = max_neighbors;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_time_horizon_agents(real_t p_time_horizon) {
+ time_horizon_agents = p_time_horizon;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.timeHorizon_ = time_horizon_agents;
+ } else {
+ rvo_agent_2d.timeHorizon_ = time_horizon_agents;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_time_horizon_obstacles(real_t p_time_horizon) {
+ time_horizon_obstacles = p_time_horizon;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
+ } else {
+ rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_radius(real_t p_radius) {
+ radius = p_radius;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.radius_ = radius;
+ } else {
+ rvo_agent_2d.radius_ = radius;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_height(real_t p_height) {
+ height = p_height;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.height_ = height;
+ } else {
+ rvo_agent_2d.height_ = height;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_max_speed(real_t p_max_speed) {
+ max_speed = p_max_speed;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.maxSpeed_ = max_speed;
+ } else {
+ rvo_agent_2d.maxSpeed_ = max_speed;
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_position(const Vector3 p_position) {
+ position = p_position;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.position_ = RVO3D::Vector3(p_position.x, p_position.y, p_position.z);
+ } else {
+ rvo_agent_2d.elevation_ = p_position.y;
+ rvo_agent_2d.position_ = RVO2D::Vector2(p_position.x, p_position.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_target_position(const Vector3 p_target_position) {
+ target_position = p_target_position;
+}
+
+void NavAgent::set_velocity(const Vector3 p_velocity) {
+ // Sets the "wanted" velocity for an agent as a suggestion
+ // This velocity is not guaranteed, RVO simulation will only try to fulfill it
+ velocity = p_velocity;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
+ } else {
+ rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_velocity_forced(const Vector3 p_velocity) {
+ // This function replaces the internal rvo simulation velocity
+ // should only be used after the agent was teleported
+ // as it destroys consistency in movement in cramped situations
+ // use velocity instead to update with a safer "suggestion"
+ velocity_forced = p_velocity;
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ rvo_agent_3d.velocity_ = RVO3D::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ } else {
+ rvo_agent_2d.velocity_ = RVO2D::Vector2(p_velocity.x, p_velocity.z);
+ }
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::update() {
+ // Updates this agent with the calculated results from the rvo simulation
+ if (avoidance_enabled) {
+ if (use_3d_avoidance) {
+ velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ } else {
+ velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ }
+ }
+}
+
+void NavAgent::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_mask_ = avoidance_mask;
+ } else {
+ rvo_agent_2d.avoidance_mask_ = avoidance_mask;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_layers_ = avoidance_layers;
+ } else {
+ rvo_agent_2d.avoidance_layers_ = avoidance_layers;
+ }
+ agent_dirty = true;
+}
+
+void NavAgent::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ if (use_3d_avoidance) {
+ rvo_agent_3d.avoidance_priority_ = avoidance_priority;
+ } else {
+ rvo_agent_2d.avoidance_priority_ = avoidance_priority;
+ }
+ agent_dirty = true;
+};
+
+bool NavAgent::check_dirty() {
+ const bool was_dirty = agent_dirty;
+ agent_dirty = false;
+ return was_dirty;
+}
+
+const Dictionary NavAgent::get_avoidance_data() const {
+ // Returns debug data from RVO simulation internals of this agent.
+ Dictionary _avoidance_data;
+ if (use_3d_avoidance) {
+ _avoidance_data["max_neighbors"] = int(rvo_agent_3d.maxNeighbors_);
+ _avoidance_data["max_speed"] = float(rvo_agent_3d.maxSpeed_);
+ _avoidance_data["neighbor_distance"] = float(rvo_agent_3d.neighborDist_);
+ _avoidance_data["new_velocity"] = Vector3(rvo_agent_3d.newVelocity_.x(), rvo_agent_3d.newVelocity_.y(), rvo_agent_3d.newVelocity_.z());
+ _avoidance_data["velocity"] = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ _avoidance_data["position"] = Vector3(rvo_agent_3d.position_.x(), rvo_agent_3d.position_.y(), rvo_agent_3d.position_.z());
+ _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_3d.prefVelocity_.x(), rvo_agent_3d.prefVelocity_.y(), rvo_agent_3d.prefVelocity_.z());
+ _avoidance_data["radius"] = float(rvo_agent_3d.radius_);
+ _avoidance_data["time_horizon_agents"] = float(rvo_agent_3d.timeHorizon_);
+ _avoidance_data["time_horizon_obstacles"] = 0.0;
+ _avoidance_data["height"] = float(rvo_agent_3d.height_);
+ _avoidance_data["avoidance_layers"] = int(rvo_agent_3d.avoidance_layers_);
+ _avoidance_data["avoidance_mask"] = int(rvo_agent_3d.avoidance_mask_);
+ _avoidance_data["avoidance_priority"] = float(rvo_agent_3d.avoidance_priority_);
+ } else {
+ _avoidance_data["max_neighbors"] = int(rvo_agent_2d.maxNeighbors_);
+ _avoidance_data["max_speed"] = float(rvo_agent_2d.maxSpeed_);
+ _avoidance_data["neighbor_distance"] = float(rvo_agent_2d.neighborDist_);
+ _avoidance_data["new_velocity"] = Vector3(rvo_agent_2d.newVelocity_.x(), 0.0, rvo_agent_2d.newVelocity_.y());
+ _avoidance_data["velocity"] = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ _avoidance_data["position"] = Vector3(rvo_agent_2d.position_.x(), 0.0, rvo_agent_2d.position_.y());
+ _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_2d.prefVelocity_.x(), 0.0, rvo_agent_2d.prefVelocity_.y());
+ _avoidance_data["radius"] = float(rvo_agent_2d.radius_);
+ _avoidance_data["time_horizon_agents"] = float(rvo_agent_2d.timeHorizon_);
+ _avoidance_data["time_horizon_obstacles"] = float(rvo_agent_2d.timeHorizonObst_);
+ _avoidance_data["height"] = float(rvo_agent_2d.height_);
+ _avoidance_data["avoidance_layers"] = int(rvo_agent_2d.avoidance_layers_);
+ _avoidance_data["avoidance_mask"] = int(rvo_agent_2d.avoidance_mask_);
+ _avoidance_data["avoidance_priority"] = float(rvo_agent_2d.avoidance_priority_);
+ }
+ return _avoidance_data;
+}
+
+void NavAgent::set_avoidance_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) {
+ avoidance_callback.id = p_id;
+ avoidance_callback.method = p_method;
+ avoidance_callback.udata = p_udata;
+}
+
+bool NavAgent::has_avoidance_callback() const {
+ return avoidance_callback.id != 0;
+}
+
+void NavAgent::dispatch_avoidance_callback() {
+ if (avoidance_callback.id == 0) {
+ return;
+ }
+ Object *obj = ObjectDB::get_instance(avoidance_callback.id);
+ if (!obj) {
+ avoidance_callback.id = ObjectID(0);
+ return;
+ }
+
+ Variant::CallError responseCallError;
+
+ Vector3 new_velocity;
+
+ if (use_3d_avoidance) {
+ new_velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
+ } else {
+ new_velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
+ }
+
+ if (clamp_speed) {
+ new_velocity = new_velocity.limit_length(max_speed);
+ }
+
+ avoidance_callback.new_velocity = new_velocity;
+
+ const Variant *vp[2] = { &avoidance_callback.new_velocity, &avoidance_callback.udata };
+ int argc = (avoidance_callback.udata.get_type() == Variant::NIL) ? 1 : 2;
+ obj->call(avoidance_callback.method, vp, argc, responseCallError);
+}
diff --git a/modules/navigation/nav_agent.h b/modules/navigation/nav_agent.h
new file mode 100644
index 000000000..0027de626
--- /dev/null
+++ b/modules/navigation/nav_agent.h
@@ -0,0 +1,160 @@
+
+#ifndef NAV_AGENT_H
+#define NAV_AGENT_H
+
+/**************************************************************************/
+/* nav_agent.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "core/containers/local_vector.h"
+#include "core/object/class_db.h"
+#include "nav_agent.h"
+#include "nav_rid.h"
+
+#include
+#include
+
+class NavMap;
+
+class NavAgent : public NavRid {
+ struct AvoidanceComputedCallback {
+ ObjectID id;
+ StringName method;
+ Variant udata;
+ Variant new_velocity;
+ };
+
+ Vector3 position;
+ Vector3 target_position;
+ Vector3 velocity;
+ Vector3 velocity_forced;
+ real_t height;
+ real_t radius;
+ real_t max_speed;
+ real_t time_horizon_agents;
+ real_t time_horizon_obstacles;
+ int max_neighbors;
+ real_t neighbor_distance;
+ Vector3 safe_velocity;
+ bool clamp_speed; // Experimental, clamps velocity to max_speed.
+
+ NavMap *map = nullptr;
+
+ RVO2D::Agent2D rvo_agent_2d;
+ RVO3D::Agent3D rvo_agent_3d;
+ bool use_3d_avoidance;
+ bool avoidance_enabled;
+
+ uint32_t avoidance_layers;
+ uint32_t avoidance_mask;
+ real_t avoidance_priority;
+
+ AvoidanceComputedCallback avoidance_callback;
+
+ bool agent_dirty;
+
+ uint32_t map_update_id;
+
+public:
+ NavAgent();
+
+ void set_avoidance_enabled(bool p_enabled);
+ bool is_avoidance_enabled() { return avoidance_enabled; }
+
+ void set_use_3d_avoidance(bool p_enabled);
+ bool get_use_3d_avoidance() { return use_3d_avoidance; }
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() { return map; }
+
+ bool is_map_changed();
+
+ RVO2D::Agent2D *get_rvo_agent_2d() { return &rvo_agent_2d; }
+ RVO3D::Agent3D *get_rvo_agent_3d() { return &rvo_agent_3d; }
+
+ void set_neighbor_distance(real_t p_neighbor_distance);
+ real_t get_neighbor_distance() const { return neighbor_distance; }
+
+ void set_max_neighbors(int p_max_neighbors);
+ int get_max_neighbors() const { return max_neighbors; }
+
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
+
+ void set_radius(real_t p_radius);
+ real_t get_radius() const { return radius; }
+
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_max_speed(real_t p_max_speed);
+ real_t get_max_speed() const { return max_speed; }
+
+ void set_position(const Vector3 p_position);
+ const Vector3 &get_position() const { return position; }
+
+ void set_target_position(const Vector3 p_target_position);
+ const Vector3 &get_target_position() const { return target_position; }
+
+ void set_velocity(const Vector3 p_velocity);
+ const Vector3 &get_velocity() const { return velocity; }
+
+ void set_velocity_forced(const Vector3 p_velocity);
+ const Vector3 &get_velocity_forced() const { return velocity_forced; }
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const { return avoidance_layers; };
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const { return avoidance_mask; };
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const { return avoidance_priority; };
+
+ bool check_dirty();
+
+ // Updates this agent with rvo data after the rvo simulation avoidance step.
+ void update();
+
+ // RVO debug data from the last frame update.
+ const Dictionary get_avoidance_data() const;
+
+ void set_avoidance_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
+ bool has_avoidance_callback() const;
+
+ void dispatch_avoidance_callback();
+
+private:
+ void _update_rvo_agent_properties();
+};
+
+#endif // NAV_AGENT_H
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 50a4c555d..4af88054c 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -30,13 +30,16 @@
#include "nav_map.h"
+#include "core/config/project_settings.h"
#include "core/os/threaded_array_processor.h"
+#include "nav_agent.h"
#include "nav_link.h"
+#include "nav_obstacle.h"
#include "nav_region.h"
-#include "rvo_agent.h"
#include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h"
-#include
+
+#include
#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
@@ -593,7 +596,7 @@ void NavMap::add_region(NavRegion *p_region) {
void NavMap::remove_region(NavRegion *p_region) {
int64_t region_index = regions.find(p_region);
- if (region_index != -1) {
+ if (region_index >= 0) {
regions.remove_unordered(region_index);
regenerate_links = true;
}
@@ -606,47 +609,79 @@ void NavMap::add_link(NavLink *p_link) {
void NavMap::remove_link(NavLink *p_link) {
int64_t link_index = links.find(p_link);
- if (link_index != -1) {
+ if (link_index >= 0) {
links.remove_unordered(link_index);
regenerate_links = true;
}
}
-bool NavMap::has_agent(RvoAgent *agent) const {
- return (agents.find(agent) != -1);
+bool NavMap::has_agent(NavAgent *agent) const {
+ return (agents.find(agent) >= 0);
}
-void NavMap::add_agent(RvoAgent *agent) {
+void NavMap::add_agent(NavAgent *agent) {
if (!has_agent(agent)) {
agents.push_back(agent);
agents_dirty = true;
}
}
-void NavMap::remove_agent(RvoAgent *agent) {
+void NavMap::remove_agent(NavAgent *agent) {
remove_agent_as_controlled(agent);
int64_t agent_index = agents.find(agent);
- if (agent_index != -1) {
+ if (agent_index >= 0) {
agents.remove_unordered(agent_index);
agents_dirty = true;
}
}
-void NavMap::set_agent_as_controlled(RvoAgent *agent) {
- const bool exist = (controlled_agents.find(agent) != -1);
+bool NavMap::has_obstacle(NavObstacle *obstacle) const {
+ return (obstacles.find(obstacle) >= 0);
+}
- if (!exist) {
- ERR_FAIL_COND(!has_agent(agent));
- controlled_agents.push_back(agent);
+void NavMap::add_obstacle(NavObstacle *obstacle) {
+ if (!has_obstacle(obstacle)) {
+ obstacles.push_back(obstacle);
+ obstacles_dirty = true;
}
}
-void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
- int64_t active_avoidance_agent_index = controlled_agents.find(agent);
- if (active_avoidance_agent_index != -1) {
- controlled_agents.remove_unordered(active_avoidance_agent_index);
+void NavMap::remove_obstacle(NavObstacle *obstacle) {
+ int64_t obstacle_index = obstacles.find(obstacle);
+ if (obstacle_index >= 0) {
+ obstacles.remove_unordered(obstacle_index);
+ obstacles_dirty = true;
+ }
+}
+
+void NavMap::set_agent_as_controlled(NavAgent *agent) {
+ remove_agent_as_controlled(agent);
+ if (agent->get_use_3d_avoidance()) {
+ int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
+ if (agent_3d_index < 0) {
+ active_3d_avoidance_agents.push_back(agent);
+ agents_dirty = true;
+ }
+ } else {
+ int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
+ if (agent_2d_index < 0) {
+ active_2d_avoidance_agents.push_back(agent);
+ agents_dirty = true;
+ }
+ }
+}
+
+void NavMap::remove_agent_as_controlled(NavAgent *agent) {
+ int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
+ if (agent_3d_index >= 0) {
+ active_3d_avoidance_agents.remove_unordered(agent_3d_index);
+ agents_dirty = true;
+ }
+ int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
+ if (agent_2d_index >= 0) {
+ active_2d_avoidance_agents.remove_unordered(agent_2d_index);
agents_dirty = true;
}
}
@@ -951,20 +986,30 @@ void NavMap::sync() {
map_update_id = (map_update_id + 1) % 9999999;
}
- // Update agents tree.
- if (agents_dirty) {
- // cannot use LocalVector here as RVO library expects std::vector to build KdTree
- std::vector raw_agents;
- raw_agents.reserve(agents.size());
- for (size_t i(0); i < agents.size(); i++) {
- raw_agents.push_back(agents[i]->get_agent());
- }
+ // Do we have modified obstacle positions?
+ for (uint32_t i = 0; i < obstacles.size(); ++i) {
+ NavObstacle *obstacle = obstacles[i];
- rvo.buildAgentTree(raw_agents);
+ if (obstacle->check_dirty()) {
+ obstacles_dirty = true;
+ }
+ }
+ // Do we have modified agent arrays?
+ for (uint32_t i = 0; i < agents.size(); ++i) {
+ NavAgent *agent = agents[i];
+ if (agent->check_dirty()) {
+ agents_dirty = true;
+ }
+ }
+
+ // Update avoidance worlds.
+ if (obstacles_dirty || agents_dirty) {
+ _update_rvo_simulation();
}
regenerate_polygons = false;
regenerate_links = false;
+ obstacles_dirty = false;
agents_dirty = false;
// Performance Monitor
@@ -978,35 +1023,197 @@ void NavMap::sync() {
pm_edge_free_count = _new_pm_edge_free_count;
}
-void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
- (*(agent + index))->get_agent()->computeNeighbors(&rvo);
- (*(agent + index))->get_agent()->computeNewVelocity(deltatime);
+void NavMap::_update_rvo_obstacles_tree_2d() {
+ int obstacle_vertex_count = 0;
+ for (uint32_t i = 0; i < obstacles.size(); ++i) {
+ NavObstacle *obstacle = obstacles[i];
+
+ obstacle_vertex_count += obstacle->get_vertices().size();
+ }
+
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree
+ std::vector raw_obstacles;
+ raw_obstacles.reserve(obstacle_vertex_count);
+
+ // The following block is modified copy from RVO2D::AddObstacle()
+ // Obstacles are linked and depend on all other obstacles.
+ for (uint32_t k = 0; k < obstacles.size(); ++k) {
+ NavObstacle *obstacle = obstacles[k];
+
+ const Vector3 &_obstacle_position = obstacle->get_position();
+ const Vector &_obstacle_vertices = obstacle->get_vertices();
+
+ if (_obstacle_vertices.size() < 2) {
+ continue;
+ }
+
+ std::vector rvo_2d_vertices;
+ rvo_2d_vertices.reserve(_obstacle_vertices.size());
+
+ uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();
+
+ for (int i = 0; i < _obstacle_vertices.size(); ++i) {
+ const Vector3 &_obstacle_vertex = _obstacle_vertices[i];
+
+ rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z));
+ }
+
+ const size_t obstacleNo = raw_obstacles.size();
+
+ for (size_t i = 0; i < rvo_2d_vertices.size(); i++) {
+ RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D();
+ rvo_2d_obstacle->point_ = rvo_2d_vertices[i];
+ rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;
+
+ if (i != 0) {
+ rvo_2d_obstacle->prevObstacle_ = raw_obstacles.back();
+ rvo_2d_obstacle->prevObstacle_->nextObstacle_ = rvo_2d_obstacle;
+ }
+
+ if (i == rvo_2d_vertices.size() - 1) {
+ rvo_2d_obstacle->nextObstacle_ = raw_obstacles[obstacleNo];
+ rvo_2d_obstacle->nextObstacle_->prevObstacle_ = rvo_2d_obstacle;
+ }
+
+ rvo_2d_obstacle->unitDir_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]);
+
+ if (rvo_2d_vertices.size() == 2) {
+ rvo_2d_obstacle->isConvex_ = true;
+ } else {
+ rvo_2d_obstacle->isConvex_ = (leftOf(rvo_2d_vertices[(i == 0 ? rvo_2d_vertices.size() - 1 : i - 1)], rvo_2d_vertices[i], rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);
+ }
+
+ rvo_2d_obstacle->id_ = raw_obstacles.size();
+
+ raw_obstacles.push_back(rvo_2d_obstacle);
+ }
+ }
+
+ rvo_simulation_2d.kdTree_->buildObstacleTree(raw_obstacles);
+}
+
+void NavMap::_update_rvo_agents_tree_2d() {
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
+ std::vector raw_agents;
+ raw_agents.reserve(active_2d_avoidance_agents.size());
+ for (uint32_t k = 0; k < active_2d_avoidance_agents.size(); ++k) {
+ NavAgent *agent = active_2d_avoidance_agents[k];
+ raw_agents.push_back(agent->get_rvo_agent_2d());
+ }
+ rvo_simulation_2d.kdTree_->buildAgentTree(raw_agents);
+}
+
+void NavMap::_update_rvo_agents_tree_3d() {
+ // Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
+ std::vector raw_agents;
+ raw_agents.reserve(active_3d_avoidance_agents.size());
+ for (uint32_t k = 0; k < active_3d_avoidance_agents.size(); ++k) {
+ NavAgent *agent = active_3d_avoidance_agents[k];
+ raw_agents.push_back(agent->get_rvo_agent_3d());
+ }
+ rvo_simulation_3d.kdTree_->buildAgentTree(raw_agents);
+}
+
+void NavMap::_update_rvo_simulation() {
+ if (obstacles_dirty) {
+ _update_rvo_obstacles_tree_2d();
+ }
+ if (agents_dirty) {
+ _update_rvo_agents_tree_2d();
+ _update_rvo_agents_tree_3d();
+ }
+}
+
+void NavMap::compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent) {
+ (*(agent + index))->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
+ (*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
+ (*(agent + index))->get_rvo_agent_2d()->update(&rvo_simulation_2d);
+ (*(agent + index))->update();
+}
+
+void NavMap::compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent) {
+ (*(agent + index))->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
+ (*(agent + index))->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
+ (*(agent + index))->get_rvo_agent_3d()->update(&rvo_simulation_3d);
+ (*(agent + index))->update();
}
void NavMap::step(real_t p_deltatime) {
deltatime = p_deltatime;
- if (controlled_agents.size() > 0) {
+ if (active_2d_avoidance_agents.size() > 0) {
#ifndef NO_THREADS
- if (step_work_pool.get_thread_count() == 0) {
- step_work_pool.init();
+ if (use_threads && avoidance_use_multiple_threads) {
+ if (step_work_pool.get_thread_count() == 0) {
+ step_work_pool.init();
+ }
+ step_work_pool.do_work(
+ active_2d_avoidance_agents.size(),
+ this,
+ &NavMap::compute_single_avoidance_step_2d,
+ active_2d_avoidance_agents.ptr());
+ } else {
+ for (int i(0); i < static_cast(active_2d_avoidance_agents.size()); i++) {
+ NavAgent *agent = active_2d_avoidance_agents[i];
+
+ agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);
+ agent->update();
+ }
}
- step_work_pool.do_work(
- controlled_agents.size(),
- this,
- &NavMap::compute_single_step,
- controlled_agents.ptr());
#else
- for (int i(0); i < static_cast(controlled_agents.size()); i++) {
- controlled_agents[i]->get_agent()->computeNeighbors(&rvo);
- controlled_agents[i]->get_agent()->computeNewVelocity(deltatime);
+ for (int i(0); i < static_cast(active_2d_avoidance_agents.size()); i++) {
+ NavAgent *agent = active_2d_avoidance_agents[i];
+
+ agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
+ agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);
+ agent->update();
+ }
+#endif // NO_THREADS
+ }
+
+ if (active_3d_avoidance_agents.size() > 0) {
+#ifndef NO_THREADS
+ if (use_threads && avoidance_use_multiple_threads) {
+ if (step_work_pool.get_thread_count() == 0) {
+ step_work_pool.init();
+ }
+ step_work_pool.do_work(
+ active_3d_avoidance_agents.size(),
+ this,
+ &NavMap::compute_single_avoidance_step_3d,
+ active_3d_avoidance_agents.ptr());
+ } else {
+ for (int i(0); i < static_cast(active_3d_avoidance_agents.size()); i++) {
+ NavAgent *agent = active_3d_avoidance_agents[i];
+
+ agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);
+ agent->update();
+ }
+ }
+#else
+ for (int i(0); i < static_cast(active_3d_avoidance_agents.size()); i++) {
+ NavAgent *agent = active_3d_avoidance_agents[i];
+
+ agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
+ agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);
+ agent->update();
}
#endif // NO_THREADS
}
}
void NavMap::dispatch_callbacks() {
- for (int i(0); i < static_cast(controlled_agents.size()); i++) {
- controlled_agents[i]->dispatch_callback();
+ for (int i(0); i < static_cast(active_2d_avoidance_agents.size()); i++) {
+ active_2d_avoidance_agents[i]->dispatch_avoidance_callback();
+ }
+
+ for (int i(0); i < static_cast(active_3d_avoidance_agents.size()); i++) {
+ active_3d_avoidance_agents[i]->dispatch_avoidance_callback();
}
}
@@ -1054,6 +1261,11 @@ NavMap::NavMap() {
regenerate_polygons = true;
regenerate_links = true;
agents_dirty = false;
+ agents_dirty = true;
+ obstacles_dirty = true;
+ use_threads = true;
+ avoidance_use_multiple_threads = true;
+ avoidance_use_high_priority_threads = true;
deltatime = 0.0;
map_update_id = 0;
link_connection_radius = 1.0;
@@ -1068,6 +1280,9 @@ NavMap::NavMap() {
pm_edge_merge_count = 0;
pm_edge_connection_count = 0;
pm_edge_free_count = 0;
+
+ avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");
+ avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");
}
NavMap::~NavMap() {
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index ae7bc3bdc..3ecbc2d10 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -38,12 +38,17 @@
#include "core/os/thread_work_pool.h"
#include "nav_utils.h"
-#include
+#include
+#include
+
+#include
+#include
class NavLink;
class NavRegion;
-class RvoAgent;
+class NavAgent;
class NavRegion;
+class NavObstacle;
class NavMap : public NavRid {
/// Map Up
@@ -75,17 +80,25 @@ class NavMap : public NavRid {
/// Map polygons
LocalVector polygons;
- /// Rvo world
- RVO::KdTree rvo;
+ /// RVO avoidance worlds
+ RVO2D::RVOSimulator2D rvo_simulation_2d;
+ RVO3D::RVOSimulator3D rvo_simulation_3d;
- /// Is agent array modified?
- bool agents_dirty;
+ /// avoidance controlled agents
+ LocalVector active_2d_avoidance_agents;
+ LocalVector active_3d_avoidance_agents;
+
+ /// dirty flag when one of the agent's arrays are modified
+ bool agents_dirty = true;
/// All the Agents (even the controlled one)
- LocalVector agents;
+ LocalVector agents;
- /// Controlled agents
- LocalVector controlled_agents;
+ /// All the avoidance obstacles (both static and dynamic)
+ LocalVector obstacles;
+
+ /// Are rvo obstacles modified?
+ bool obstacles_dirty;
/// Physics delta time
real_t deltatime;
@@ -93,6 +106,10 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id;
+ bool use_threads;
+ bool avoidance_use_multiple_threads;
+ bool avoidance_use_high_priority_threads;
+
// Performance Monitor
int pm_region_count;
int pm_agent_count;
@@ -163,15 +180,22 @@ public:
return links;
}
- bool has_agent(RvoAgent *agent) const;
- void add_agent(RvoAgent *agent);
- void remove_agent(RvoAgent *agent);
- const LocalVector &get_agents() const {
+ bool has_agent(NavAgent *agent) const;
+ void add_agent(NavAgent *agent);
+ void remove_agent(NavAgent *agent);
+ const LocalVector &get_agents() const {
return agents;
}
- void set_agent_as_controlled(RvoAgent *agent);
- void remove_agent_as_controlled(RvoAgent *agent);
+ void set_agent_as_controlled(NavAgent *agent);
+ void remove_agent_as_controlled(NavAgent *agent);
+
+ bool has_obstacle(NavObstacle *obstacle) const;
+ void add_obstacle(NavObstacle *obstacle);
+ void remove_obstacle(NavObstacle *obstacle);
+ const LocalVector &get_obstacles() const {
+ return obstacles;
+ }
uint32_t get_map_update_id() const {
return map_update_id;
@@ -192,8 +216,15 @@ public:
int get_pm_edge_free_count() const { return pm_edge_free_count; }
private:
- void compute_single_step(uint32_t index, RvoAgent **agent);
+ void compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent);
+ void compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent);
+
void clip_path(const LocalVector &p_navigation_polys, Vector &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector *r_path_types, Array *r_path_rids, Vector *r_path_owners) const;
+
+ void _update_rvo_simulation();
+ void _update_rvo_obstacles_tree_2d();
+ void _update_rvo_agents_tree_2d();
+ void _update_rvo_agents_tree_3d();
};
#endif // NAV_MAP_H
diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/nav_obstacle.cpp
similarity index 50%
rename from modules/navigation/rvo_agent.cpp
rename to modules/navigation/nav_obstacle.cpp
index 35fead284..558933075 100644
--- a/modules/navigation/rvo_agent.cpp
+++ b/modules/navigation/nav_obstacle.cpp
@@ -1,48 +1,74 @@
-/*************************************************************************/
-/* rvo_agent.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_obstacle.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
-#include "rvo_agent.h"
+#include "nav_obstacle.h"
#include "nav_map.h"
-RvoAgent::RvoAgent() {
+NavObstacle::NavObstacle() {
map = nullptr;
- callback.id = ObjectID(0);
+ height = 0.0;
+ avoidance_layers = 1;
+ obstacle_dirty = true;
map_update_id = 0;
}
-void RvoAgent::set_map(NavMap *p_map) {
- map = p_map;
+NavObstacle::~NavObstacle() {}
+
+void NavObstacle::set_map(NavMap *p_map) {
+ if (map != p_map) {
+ map = p_map;
+ obstacle_dirty = true;
+ }
}
-bool RvoAgent::is_map_changed() {
+void NavObstacle::set_position(const Vector3 p_position) {
+ if (position != p_position) {
+ position = p_position;
+ obstacle_dirty = true;
+ }
+}
+
+void NavObstacle::set_height(const real_t p_height) {
+ if (height != p_height) {
+ height = p_height;
+ obstacle_dirty = true;
+ }
+}
+
+void NavObstacle::set_vertices(const Vector &p_vertices) {
+ vertices = p_vertices;
+ obstacle_dirty = true;
+}
+
+bool NavObstacle::is_map_changed() {
if (map) {
bool is_changed = map->get_map_update_id() != map_update_id;
map_update_id = map->get_map_update_id();
@@ -52,31 +78,13 @@ bool RvoAgent::is_map_changed() {
}
}
-void RvoAgent::set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) {
- callback.id = p_id;
- callback.method = p_method;
- callback.udata = p_udata;
+void NavObstacle::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ obstacle_dirty = true;
}
-bool RvoAgent::has_callback() const {
- return callback.id != 0;
-}
-
-void RvoAgent::dispatch_callback() {
- if (callback.id == 0) {
- return;
- }
- Object *obj = ObjectDB::get_instance(callback.id);
- if (!obj) {
- callback.id = ObjectID(0);
- return;
- }
-
- Variant::CallError responseCallError;
-
- callback.new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z());
-
- const Variant *vp[2] = { &callback.new_velocity, &callback.udata };
- int argc = (callback.udata.get_type() == Variant::NIL) ? 1 : 2;
- obj->call(callback.method, vp, argc, responseCallError);
+bool NavObstacle::check_dirty() {
+ const bool was_dirty = obstacle_dirty;
+ obstacle_dirty = false;
+ return was_dirty;
}
diff --git a/modules/navigation/rvo_agent.h b/modules/navigation/nav_obstacle.h
similarity index 50%
rename from modules/navigation/rvo_agent.h
rename to modules/navigation/nav_obstacle.h
index c16c326ed..1e01ab2f9 100644
--- a/modules/navigation/rvo_agent.h
+++ b/modules/navigation/nav_obstacle.h
@@ -1,73 +1,79 @@
-#ifndef RVO_AGENT_H
-#define RVO_AGENT_H
-/*************************************************************************/
-/* rvo_agent.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#include "core/object/object.h"
+#ifndef NAV_OBSTACLE_H
+#define NAV_OBSTACLE_H
+
+/**************************************************************************/
+/* nav_obstacle.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "core/object/class_db.h"
+#include "core/containers/local_vector.h"
+#include "nav_agent.h"
#include "nav_rid.h"
-#include
-
class NavMap;
-class RvoAgent : public NavRid {
- struct AvoidanceComputedCallback {
- ObjectID id;
- StringName method;
- Variant udata;
- Variant new_velocity;
- };
-
+class NavObstacle : public NavRid {
NavMap *map;
- RVO::Agent agent;
- AvoidanceComputedCallback callback;
+ Vector3 position;
+ Vector vertices;
+
+ real_t height;
+
+ uint32_t avoidance_layers;
+
+ bool obstacle_dirty;
+
uint32_t map_update_id;
public:
- RvoAgent();
+ NavObstacle();
+ ~NavObstacle();
void set_map(NavMap *p_map);
- NavMap *get_map() {
- return map;
- }
+ NavMap *get_map() { return map; }
- RVO::Agent *get_agent() {
- return &agent;
- }
+ void set_position(const Vector3 p_position);
+ const Vector3 &get_position() const { return position; }
+
+ void set_height(const real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_vertices(const Vector &p_vertices);
+ const Vector &get_vertices() const { return vertices; }
bool is_map_changed();
- void set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
- bool has_callback() const;
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const { return avoidance_layers; };
- void dispatch_callback();
+ bool check_dirty();
};
-#endif // RVO_AGENT_H
+#endif // NAV_OBSTACLE_H
diff --git a/modules/navigation/pandemonium_navigation_2d_server.cpp b/modules/navigation/pandemonium_navigation_2d_server.cpp
index 1d629c520..4ca17f1e9 100644
--- a/modules/navigation/pandemonium_navigation_2d_server.cpp
+++ b/modules/navigation/pandemonium_navigation_2d_server.cpp
@@ -158,6 +158,15 @@ Vector vector_v3_to_v2(const Vector &d) {
return nd;
}
+static Vector vector_v2_to_v3(const Vector &d) {
+ Vector nd;
+ nd.resize(d.size());
+ for (int i(0); i < nd.size(); i++) {
+ nd.write[i] = v2_to_v3(d[i]);
+ }
+ return nd;
+}
+
PoolVector poolvector_v3_to_v2(const PoolVector &d) {
PoolVector nd;
nd.resize(d.size());
@@ -221,6 +230,8 @@ Array FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid);
Array FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid);
+Array FORWARD_1_C(map_get_obstacles, RID, p_map, rid_to_rid);
+
RID FORWARD_1_C(region_get_map, RID, p_region, rid_to_rid);
RID FORWARD_1_C(agent_get_map, RID, p_agent, rid_to_rid);
@@ -302,36 +313,56 @@ ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
RID PandemoniumNavigation2DServer::agent_create() {
RID agent = NavigationServer::get_singleton()->agent_create();
- NavigationServer::get_singleton()->agent_set_ignore_y(agent, true);
return agent;
}
+void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_to_rid, bool_to_bool);
+
+bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid);
+
void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
-void FORWARD_2(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
+void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
+
+void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
-void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
+void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
-void FORWARD_2(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
+void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
-void FORWARD_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
-
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
-void FORWARD_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
-
void FORWARD_1(free, RID, p_object, rid_to_rid);
+void FORWARD_4(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
+
+void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32);
+void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real);
+
+RID PandemoniumNavigation2DServer::obstacle_create() {
+ RID obstacle = NavigationServer::get_singleton()->obstacle_create();
+ return obstacle;
+}
+void FORWARD_2(obstacle_set_map, RID, p_obstacle, RID, p_map, rid_to_rid, rid_to_rid);
+RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid);
+void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3);
+void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
+
+void PandemoniumNavigation2DServer::obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) {
+ NavigationServer::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices));
+}
+
NavigationUtilities::PathQueryResult2D PandemoniumNavigation2DServer::_query_path(const NavigationUtilities::PathQueryParameters2D &p_parameters) const {
NavigationUtilities::PathQueryParameters params;
params.pathfinding_algorithm = p_parameters.pathfinding_algorithm;
diff --git a/modules/navigation/pandemonium_navigation_2d_server.h b/modules/navigation/pandemonium_navigation_2d_server.h
index f14e0708c..9c65cad74 100644
--- a/modules/navigation/pandemonium_navigation_2d_server.h
+++ b/modules/navigation/pandemonium_navigation_2d_server.h
@@ -91,6 +91,7 @@ public:
virtual Array map_get_links(RID p_map) const;
virtual Array map_get_regions(RID p_map) const;
virtual Array map_get_agents(RID p_map) const;
+ virtual Array map_get_obstacles(RID p_map) const;
virtual void map_force_update(RID p_map);
@@ -175,6 +176,9 @@ public:
virtual void agent_set_map(RID p_agent, RID p_map);
virtual RID agent_get_map(RID p_agent) const;
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled);
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const;
+
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
@@ -200,7 +204,8 @@ public:
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time);
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon);
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon);
/// The radius of this agent.
/// Must be non-negative.
@@ -210,23 +215,33 @@ public:
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed);
- /// Current velocity of the agent
- virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
+ /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
+ virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity);
- /// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity);
+ /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
+ /// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
+ virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position);
- /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore);
-
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const;
/// Callback called at the end of the RVO process
- virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant());
+ virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant());
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers);
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask);
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority);
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create();
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map);
+ virtual RID obstacle_get_map(RID p_obstacle) const;
+ virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position);
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices);
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers);
/// Destroy the `RID`
virtual void free(RID p_object);
diff --git a/modules/navigation/pandemonium_navigation_server.cpp b/modules/navigation/pandemonium_navigation_server.cpp
index 8990d3fb0..68cbbbe36 100644
--- a/modules/navigation/pandemonium_navigation_server.cpp
+++ b/modules/navigation/pandemonium_navigation_server.cpp
@@ -115,6 +115,15 @@ using namespace NavigationUtilities;
PandemoniumNavigationServer::PandemoniumNavigationServer() {
active = true;
+
+ pm_region_count = 0;
+ pm_agent_count = 0;
+ pm_link_count = 0;
+ pm_polygon_count = 0;
+ pm_edge_count = 0;
+ pm_edge_merge_count = 0;
+ pm_edge_connection_count = 0;
+ pm_edge_free_count = 0;
}
PandemoniumNavigationServer::~PandemoniumNavigationServer() {
@@ -326,7 +335,7 @@ Array PandemoniumNavigationServer::map_get_agents(RID p_map) const {
const NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND_V(map == nullptr, agents_rids);
- const LocalVector agents = map->get_agents();
+ const LocalVector agents = map->get_agents();
agents_rids.resize(agents.size());
for (uint32_t i = 0; i < agents.size(); i++) {
@@ -336,6 +345,18 @@ Array PandemoniumNavigationServer::map_get_agents(RID p_map) const {
return agents_rids;
}
+Array PandemoniumNavigationServer::map_get_obstacles(RID p_map) const {
+ Array obstacles_rids;
+ const NavMap *map = map_owner.getornull(p_map);
+ ERR_FAIL_COND_V(map == nullptr, obstacles_rids);
+ const LocalVector obstacles = map->get_obstacles();
+ obstacles_rids.resize(obstacles.size());
+ for (uint32_t i = 0; i < obstacles.size(); i++) {
+ obstacles_rids[i] = obstacles[i]->get_self();
+ }
+ return obstacles_rids;
+}
+
RID PandemoniumNavigationServer::region_get_map(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(region == nullptr, RID());
@@ -348,7 +369,7 @@ RID PandemoniumNavigationServer::region_get_map(RID p_region) const {
}
RID PandemoniumNavigationServer::agent_get_map(RID p_agent) const {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, RID());
if (agent->get_map()) {
@@ -648,14 +669,42 @@ ObjectID PandemoniumNavigationServer::link_get_owner_id(RID p_link) const {
RID PandemoniumNavigationServer::agent_create() {
PandemoniumNavigationServer *mut_this = const_cast(this);
MutexLock lock(mut_this->operations_mutex);
- RvoAgent *agent = memnew(RvoAgent());
+ NavAgent *agent = memnew(NavAgent());
RID rid = agent_owner.make_rid(agent);
agent->set_self(rid);
return rid;
}
+COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_avoidance_enabled(p_enabled);
+}
+
+bool PandemoniumNavigationServer::agent_get_avoidance_enabled(RID p_agent) const {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, false);
+
+ return agent->is_avoidance_enabled();
+}
+
+COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_use_3d_avoidance(p_enabled);
+}
+
+bool PandemoniumNavigationServer::agent_get_use_3d_avoidance(RID p_agent) const {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, false);
+
+ return agent->get_use_3d_avoidance();
+}
+
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
if (agent->get_map()) {
@@ -675,87 +724,99 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
agent->set_map(map);
map->add_agent(agent);
- if (agent->has_callback()) {
+ if (agent->has_avoidance_callback()) {
map->set_agent_as_controlled(agent);
}
}
}
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->neighborDist_ = p_dist;
+ agent->set_neighbor_distance(p_dist);
}
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->maxNeighbors_ = p_count;
+ agent->set_max_neighbors(p_count);
}
-COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->timeHorizon_ = p_time;
+ agent->set_time_horizon_agents(p_time_horizon);
+}
+
+COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_time_horizon_obstacles(p_time_horizon);
}
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->radius_ = p_radius;
+ agent->set_radius(p_radius);
+}
+
+COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+
+ agent->set_height(p_height);
}
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->maxSpeed_ = p_max_speed;
+ agent->set_max_speed(p_max_speed);
}
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ agent->set_velocity(p_velocity);
}
-COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+ agent->set_velocity_forced(p_velocity);
}
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- 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) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
- ERR_FAIL_COND(agent == nullptr);
-
- agent->get_agent()->ignore_y_ = p_ignore;
+ agent->set_position(p_position);
}
bool PandemoniumNavigationServer::agent_is_map_changed(RID p_agent) const {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false);
return agent->is_map_changed();
}
-COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+COMMAND_4(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->set_callback(p_object_id, p_method, p_udata);
+ agent->set_avoidance_callback(p_object_id, p_method, p_udata);
if (agent->get_map()) {
if (p_object_id == ObjectID()) {
@@ -766,6 +827,91 @@ COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p
}
}
+COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_layers(p_layers);
+}
+
+COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask) {
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_mask(p_mask);
+}
+
+COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ NavAgent *agent = agent_owner.getornull(p_agent);
+ ERR_FAIL_COND(agent == nullptr);
+ agent->set_avoidance_priority(p_priority);
+}
+
+RID PandemoniumNavigationServer::obstacle_create() {
+ PandemoniumNavigationServer *mut_this = const_cast(this);
+ MutexLock lock(mut_this->operations_mutex);
+ NavObstacle *obstacle = memnew(NavObstacle());
+ RID rid = obstacle_owner.make_rid(obstacle);
+ obstacle->set_self(rid);
+ return rid;
+}
+
+COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+
+ if (obstacle->get_map()) {
+ if (obstacle->get_map()->get_self() == p_map) {
+ return; // Pointless
+ }
+
+ obstacle->get_map()->remove_obstacle(obstacle);
+ }
+
+ obstacle->set_map(nullptr);
+
+ if (p_map.is_valid()) {
+ NavMap *map = map_owner.getornull(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ obstacle->set_map(map);
+ map->add_obstacle(obstacle);
+ }
+}
+
+RID PandemoniumNavigationServer::obstacle_get_map(RID p_obstacle) const {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND_V(obstacle == nullptr, RID());
+ if (obstacle->get_map()) {
+ return obstacle->get_map()->get_self();
+ }
+ return RID();
+}
+
+COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_height(p_height);
+}
+
+COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_position(p_position);
+}
+
+void PandemoniumNavigationServer::obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_vertices(p_vertices);
+}
+
+COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
+ ERR_FAIL_COND(obstacle == nullptr);
+ obstacle->set_avoidance_layers(p_layers);
+}
+
COMMAND_1(free, RID, p_object) {
if (!p_object.is_valid()) {
ERR_FAIL_MSG("Invalid RID.");
@@ -790,12 +936,19 @@ COMMAND_1(free, RID, p_object) {
}
// Remove any assigned agent
- LocalVector agents = map->get_agents();
+ LocalVector agents = map->get_agents();
for (uint32_t i = 0; i < agents.size(); i++) {
map->remove_agent(agents[i]);
agents[i]->set_map(nullptr);
}
+ // Remove any assigned obstacles
+ LocalVector obstacles = map->get_obstacles();
+ for (uint32_t i = 0; i < obstacles.size(); i++) {
+ map->remove_obstacle(obstacles[i]);
+ obstacles[i]->set_map(nullptr);
+ }
+
int map_index = active_maps.find(map);
active_maps.remove(map_index);
active_maps_update_id.remove(map_index);
@@ -826,7 +979,7 @@ COMMAND_1(free, RID, p_object) {
link_owner.free(p_object);
} else if (agent_owner.owns(p_object)) {
- RvoAgent *agent = agent_owner.getornull(p_object);
+ NavAgent *agent = agent_owner.getornull(p_object);
// Removes this agent from the map if assigned
if (agent->get_map() != nullptr) {
@@ -837,6 +990,16 @@ COMMAND_1(free, RID, p_object) {
agent_owner.free(p_object);
memdelete(agent);
+ } else if (obstacle_owner.owns(p_object)) {
+ NavObstacle *obstacle = obstacle_owner.getornull(p_object);
+
+ // Removes this agent from the map if assigned
+ if (obstacle->get_map() != nullptr) {
+ obstacle->get_map()->remove_obstacle(obstacle);
+ obstacle->set_map(nullptr);
+ }
+
+ obstacle_owner.free(p_object);
} else {
ERR_FAIL_COND("Invalid RID.");
}
diff --git a/modules/navigation/pandemonium_navigation_server.h b/modules/navigation/pandemonium_navigation_server.h
index 087625436..7f9a19bfc 100644
--- a/modules/navigation/pandemonium_navigation_server.h
+++ b/modules/navigation/pandemonium_navigation_server.h
@@ -34,10 +34,11 @@
#include "core/containers/rid.h"
+#include "nav_agent.h"
#include "nav_link.h"
#include "nav_map.h"
#include "nav_region.h"
-#include "rvo_agent.h"
+#include "nav_obstacle.h"
/// The commands are functions executed during the `sync` phase.
@@ -73,21 +74,22 @@ class PandemoniumNavigationServer : public NavigationServer {
mutable RID_Owner link_owner;
mutable RID_Owner map_owner;
mutable RID_Owner region_owner;
- mutable RID_Owner agent_owner;
+ mutable RID_Owner agent_owner;
+ mutable RID_Owner obstacle_owner;
bool active;
LocalVector active_maps;
LocalVector active_maps_update_id;
// Performance Monitor
- int pm_region_count = 0;
- int pm_agent_count = 0;
- int pm_link_count = 0;
- int pm_polygon_count = 0;
- int pm_edge_count = 0;
- int pm_edge_merge_count = 0;
- int pm_edge_connection_count = 0;
- int pm_edge_free_count = 0;
+ int pm_region_count;
+ int pm_agent_count;
+ int pm_link_count;
+ int pm_polygon_count;
+ int pm_edge_count;
+ int pm_edge_merge_count;
+ int pm_edge_connection_count;
+ int pm_edge_free_count;
public:
PandemoniumNavigationServer();
@@ -129,6 +131,7 @@ public:
virtual Array map_get_links(RID p_map) const;
virtual Array map_get_regions(RID p_map) const;
virtual Array map_get_agents(RID p_map) const;
+ virtual Array map_get_obstacles(RID p_map) const;
virtual void map_force_update(RID p_map);
@@ -176,19 +179,36 @@ public:
virtual ObjectID link_get_owner_id(RID p_link) const;
virtual RID agent_create();
+ COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled);
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const;
+ COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled);
+ virtual bool agent_get_use_3d_avoidance(RID p_agent) const;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
virtual RID agent_get_map(RID p_agent) const;
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
- COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
+ COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon);
+ COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon);
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius);
+ COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height);
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed);
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity);
- COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity);
+ COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity);
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position);
- COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore);
virtual bool agent_is_map_changed(RID p_agent) const;
- COMMAND_4_DEF(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant());
+ COMMAND_4_DEF(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant());
+
+ COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers);
+ COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask);
+ COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority);
+
+ virtual RID obstacle_create();
+ COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map);
+ virtual RID obstacle_get_map(RID p_obstacle) const;
+ COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position);
+ COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height);
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices);
+ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
COMMAND_1(free, RID, p_object);
diff --git a/modules/navigation_dummy/dummy_navigation_2d_server.h b/modules/navigation_dummy/dummy_navigation_2d_server.h
index ea8b822b5..e5a42dcd4 100644
--- a/modules/navigation_dummy/dummy_navigation_2d_server.h
+++ b/modules/navigation_dummy/dummy_navigation_2d_server.h
@@ -33,6 +33,7 @@ public:
virtual Array map_get_links(RID p_map) const { return Array(); }
virtual Array map_get_regions(RID p_map) const { return Array(); }
virtual Array map_get_agents(RID p_map) const { return Array(); }
+ virtual Array map_get_obstacles(RID p_map) const { return Array(); }
virtual void map_force_update(RID p_map) {}
virtual RID region_create() { return RID(); }
@@ -76,17 +77,31 @@ public:
virtual RID agent_create() { return RID(); }
virtual void agent_set_map(RID p_agent, RID p_map) {}
virtual RID agent_get_map(RID p_agent) const { return RID(); }
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) {}
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const { return false; }
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) {}
virtual void agent_set_max_neighbors(RID p_agent, int p_count) {}
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time) {}
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) {}
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_radius(RID p_agent, real_t p_radius) {}
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) {}
+ virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) {}
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) {}
- virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) {}
virtual void agent_set_position(RID p_agent, Vector2 p_position) {}
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) {}
virtual bool agent_is_map_changed(RID p_agent) const { return false; }
- virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
+ virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) {}
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) {}
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) {}
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create() { return RID(); }
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map) {}
+ virtual RID obstacle_get_map(RID p_obstacle) const { return RID(); }
+ virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) {}
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) {}
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) {}
virtual void free(RID p_object) {}
diff --git a/modules/navigation_dummy/dummy_navigation_server.h b/modules/navigation_dummy/dummy_navigation_server.h
index 20cf29315..b0edcc462 100644
--- a/modules/navigation_dummy/dummy_navigation_server.h
+++ b/modules/navigation_dummy/dummy_navigation_server.h
@@ -35,6 +35,7 @@ public:
virtual Array map_get_links(RID p_map) const { return Array(); }
virtual Array map_get_regions(RID p_map) const { return Array(); }
virtual Array map_get_agents(RID p_map) const { return Array(); }
+ virtual Array map_get_obstacles(RID p_map) const { return Array(); }
virtual void map_force_update(RID p_map) {}
virtual RID region_create() { return RID(); }
@@ -78,17 +79,34 @@ public:
virtual RID agent_create() { return RID(); }
virtual void agent_set_map(RID p_agent, RID p_map) {}
virtual RID agent_get_map(RID p_agent) const { return RID(); }
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) {}
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const { return false; }
+ virtual void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) {}
+ virtual bool agent_get_use_3d_avoidance(RID p_agent) const { return false; }
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) {}
virtual void agent_set_max_neighbors(RID p_agent, int p_count) {}
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time) {}
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) {}
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_radius(RID p_agent, real_t p_radius) {}
+ virtual void agent_set_height(RID p_agent, real_t p_height) {}
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) {}
+ virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) {}
virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) {}
- virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) {}
virtual void agent_set_position(RID p_agent, Vector3 p_position) {}
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) {}
virtual bool agent_is_map_changed(RID p_agent) const { return false; }
- virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
+ virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) {}
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) {}
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) {}
+
+ virtual RID obstacle_create() { return RID(); }
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map) {}
+ virtual RID obstacle_get_map(RID p_obstacle) const { return RID(); }
+ virtual void obstacle_set_height(RID p_obstacle, real_t p_height) {}
+ virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) {}
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) {}
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) {}
virtual void free(RID p_object){};
diff --git a/modules/props/material_cache/prop_material_cache.cpp b/modules/props/material_cache/prop_material_cache.cpp
index abe2dac12..a6d230931 100644
--- a/modules/props/material_cache/prop_material_cache.cpp
+++ b/modules/props/material_cache/prop_material_cache.cpp
@@ -329,7 +329,7 @@ void PropMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropMaterialCache::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("texture_add", "texture"), &PropMaterialCache::texture_add);
ClassDB::bind_method(D_METHOD("texture_remove", "texture"), &PropMaterialCache::texture_remove);
diff --git a/modules/props/prop_instance_merger.cpp b/modules/props/prop_instance_merger.cpp
index 61307b451..d22f34b33 100644
--- a/modules/props/prop_instance_merger.cpp
+++ b/modules/props/prop_instance_merger.cpp
@@ -944,7 +944,7 @@ void PropInstanceMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropInstanceMerger::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropInstanceMerger::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
//Meshes
ClassDB::bind_method(D_METHOD("mesh_get", "index"), &PropInstanceMerger::mesh_get);
diff --git a/modules/props/props/prop_data.cpp b/modules/props/props/prop_data.cpp
index af6ea3e86..f9e52a5eb 100644
--- a/modules/props/props/prop_data.cpp
+++ b/modules/props/props/prop_data.cpp
@@ -163,7 +163,7 @@ void PropData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_props"), &PropData::get_props);
ClassDB::bind_method(D_METHOD("set_props", "props"), &PropData::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropDataEntry", PROPERTY_USAGE_DEFAULT, "PropDataEntry"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropDataEntry", PROPERTY_USAGE_DEFAULT, "PropDataEntry"), "set_props", "get_props");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &PropData::add_textures_into);
diff --git a/modules/props/singleton/prop_cache.cpp b/modules/props/singleton/prop_cache.cpp
index 48cc372c0..0ed3e4d74 100644
--- a/modules/props/singleton/prop_cache.cpp
+++ b/modules/props/singleton/prop_cache.cpp
@@ -408,7 +408,7 @@ void PropCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropCache::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("material_cache_get", "prop"), &PropCache::material_cache_get);
ClassDB::bind_method(D_METHOD("material_cache_unref", "prop"), &PropCache::material_cache_unref);
diff --git a/modules/props/tiled_wall/tiled_wall_data.cpp b/modules/props/tiled_wall/tiled_wall_data.cpp
index 5ebc683a8..8d6e482ee 100644
--- a/modules/props/tiled_wall/tiled_wall_data.cpp
+++ b/modules/props/tiled_wall/tiled_wall_data.cpp
@@ -673,7 +673,7 @@ void TiledWallData::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TiledWallData::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TiledWallData::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &TiledWallData::add_textures_into);
diff --git a/modules/props_2d/props/prop_2d_data.cpp b/modules/props_2d/props/prop_2d_data.cpp
index a47a714fb..410800967 100644
--- a/modules/props_2d/props/prop_2d_data.cpp
+++ b/modules/props_2d/props/prop_2d_data.cpp
@@ -141,7 +141,7 @@ void Prop2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_props"), &Prop2DData::get_props);
ClassDB::bind_method(D_METHOD("set_props", "props"), &Prop2DData::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DDataEntry", PROPERTY_USAGE_DEFAULT, "Prop2DDataEntry"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DDataEntry", PROPERTY_USAGE_DEFAULT, "Prop2DDataEntry"), "set_props", "get_props");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &Prop2DData::add_textures_into);
diff --git a/modules/props_2d/tiled_wall/tiled_wall_2d_data.cpp b/modules/props_2d/tiled_wall/tiled_wall_2d_data.cpp
index b64d91456..de2609742 100644
--- a/modules/props_2d/tiled_wall/tiled_wall_2d_data.cpp
+++ b/modules/props_2d/tiled_wall/tiled_wall_2d_data.cpp
@@ -257,7 +257,7 @@ void TiledWall2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_textures"), &TiledWall2DData::get_textures);
ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TiledWall2DData::set_textures);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
//flavour_textures
ClassDB::bind_method(D_METHOD("get_flavour_texture", "index"), &TiledWall2DData::get_flavour_texture);
@@ -269,7 +269,7 @@ void TiledWall2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_flavour_textures"), &TiledWall2DData::get_flavour_textures);
ClassDB::bind_method(D_METHOD("set_flavour_textures", "textures"), &TiledWall2DData::set_flavour_textures);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "flavour_textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_flavour_textures", "get_flavour_textures");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "flavour_textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_flavour_textures", "get_flavour_textures");
ClassDB::bind_method(D_METHOD("get_flavour_chance"), &TiledWall2DData::get_flavour_chance);
ClassDB::bind_method(D_METHOD("set_flavour_chance", "texture"), &TiledWall2DData::set_flavour_chance);
diff --git a/modules/steering_ai/proximities/gsai_proximity.cpp b/modules/steering_ai/proximities/gsai_proximity.cpp
index 8f06016df..68e3ae407 100644
--- a/modules/steering_ai/proximities/gsai_proximity.cpp
+++ b/modules/steering_ai/proximities/gsai_proximity.cpp
@@ -56,7 +56,7 @@ void GSAIProximity::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_agents"), &GSAIProximity::get_agents_arr);
ClassDB::bind_method(D_METHOD("set_agents", "arr"), &GSAIProximity::set_agents_arr);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "agents", PROPERTY_HINT_NONE, "23/19:GSAISteeringAgent", PROPERTY_USAGE_DEFAULT, "GSAISteeringAgent"), "set_agents", "get_agents");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "agents", PROPERTY_HINT_NONE, "23/20:GSAISteeringAgent", PROPERTY_USAGE_DEFAULT, "GSAISteeringAgent"), "set_agents", "get_agents");
BIND_VMETHOD(MethodInfo(Variant::INT, "_find_neighbors", PropertyInfo(Variant::OBJECT, "callback", PROPERTY_HINT_RESOURCE_TYPE, "FuncRef")));
diff --git a/modules/terraman/library/terrain_library.cpp b/modules/terraman/library/terrain_library.cpp
index 651967656..d8b4ec050 100644
--- a/modules/terraman/library/terrain_library.cpp
+++ b/modules/terraman/library/terrain_library.cpp
@@ -392,7 +392,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TerrainLibrary::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TerrainLibrary::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
BIND_VMETHOD(MethodInfo("_liquid_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_liquid_material_cache_get", PropertyInfo(Variant::INT, "key")));
@@ -416,7 +416,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("liquid_materials_get"), &TerrainLibrary::liquid_materials_get);
ClassDB::bind_method(D_METHOD("liquid_materials_set"), &TerrainLibrary::liquid_materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
BIND_VMETHOD(MethodInfo("_prop_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_prop_material_cache_get", PropertyInfo(Variant::INT, "key")));
@@ -440,7 +440,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("prop_materials_get"), &TerrainLibrary::prop_materials_get);
ClassDB::bind_method(D_METHOD("prop_materials_set"), &TerrainLibrary::prop_materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ClassDB::bind_method(D_METHOD("terra_surface_get", "index"), &TerrainLibrary::terra_surface_get);
ClassDB::bind_method(D_METHOD("terra_surface_add", "value"), &TerrainLibrary::terra_surface_add);
diff --git a/modules/terraman/library/terrain_library_merger.cpp b/modules/terraman/library/terrain_library_merger.cpp
index 2258e5623..4e0f5204e 100644
--- a/modules/terraman/library/terrain_library_merger.cpp
+++ b/modules/terraman/library/terrain_library_merger.cpp
@@ -462,12 +462,12 @@ void TerrainLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibraryMerger::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibraryMerger::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &TerrainLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &TerrainLibraryMerger::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &TerrainLibraryMerger::get_prop_uv_rect);
diff --git a/modules/terraman/library/terrain_library_merger_pcm.cpp b/modules/terraman/library/terrain_library_merger_pcm.cpp
index dd9f74f0e..f337f2991 100644
--- a/modules/terraman/library/terrain_library_merger_pcm.cpp
+++ b/modules/terraman/library/terrain_library_merger_pcm.cpp
@@ -1002,12 +1002,12 @@ void TerrainLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibraryMergerPCM::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibraryMergerPCM::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &TerrainLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &TerrainLibraryMergerPCM::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &TerrainLibraryMergerPCM::get_prop_uv_rect);
diff --git a/modules/terraman/library/terrain_library_simple.cpp b/modules/terraman/library/terrain_library_simple.cpp
index edbc9258c..fea2103e3 100644
--- a/modules/terraman/library/terrain_library_simple.cpp
+++ b/modules/terraman/library/terrain_library_simple.cpp
@@ -147,5 +147,5 @@ void TerrainLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibrarySimple::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibrarySimple::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceSimple", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceSimple", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
}
diff --git a/modules/terraman/library/terrain_material_cache.cpp b/modules/terraman/library/terrain_material_cache.cpp
index cfb20604f..8b72437e8 100644
--- a/modules/terraman/library/terrain_material_cache.cpp
+++ b/modules/terraman/library/terrain_material_cache.cpp
@@ -296,7 +296,7 @@ void TerrainMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TerrainMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TerrainMaterialCache::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("surface_get", "index"), &TerrainMaterialCache::surface_get);
ClassDB::bind_method(D_METHOD("surface_id_get", "index"), &TerrainMaterialCache::surface_id_get);
diff --git a/modules/terraman/world/terrain_chunk.cpp b/modules/terraman/world/terrain_chunk.cpp
index 26d345e95..d7ac0b38d 100644
--- a/modules/terraman/world/terrain_chunk.cpp
+++ b/modules/terraman/world/terrain_chunk.cpp
@@ -1462,7 +1462,7 @@ void TerrainChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &TerrainChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &TerrainChunk::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes
diff --git a/modules/terraman/world/terrain_world.cpp b/modules/terraman/world/terrain_world.cpp
index e2426fded..1f5e49849 100644
--- a/modules/terraman/world/terrain_world.cpp
+++ b/modules/terraman/world/terrain_world.cpp
@@ -1151,7 +1151,7 @@ void TerrainWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &TerrainWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &TerrainWorld::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
@@ -1168,7 +1168,7 @@ void TerrainWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &TerrainWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &TerrainWorld::chunks_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:TerrainChunk", PROPERTY_USAGE_DEFAULT, "TerrainChunk"), "chunks_set", "chunks_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:TerrainChunk", PROPERTY_USAGE_DEFAULT, "TerrainChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &TerrainWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &TerrainWorld::generation_queue_get_index);
diff --git a/modules/terraman_2d/library/terrain_2d_library_merger.cpp b/modules/terraman_2d/library/terrain_2d_library_merger.cpp
index 6a565e14b..bdb43996f 100644
--- a/modules/terraman_2d/library/terrain_2d_library_merger.cpp
+++ b/modules/terraman_2d/library/terrain_2d_library_merger.cpp
@@ -416,12 +416,12 @@ void Terrain2DLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibraryMerger::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibraryMerger::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_2D_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &Terrain2DLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &Terrain2DLibraryMerger::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &Terrain2DLibraryMerger::get_prop_uv_rect);
diff --git a/modules/terraman_2d/library/terrain_2d_library_merger_pcm.cpp b/modules/terraman_2d/library/terrain_2d_library_merger_pcm.cpp
index 7982e1da0..1d1b693c9 100644
--- a/modules/terraman_2d/library/terrain_2d_library_merger_pcm.cpp
+++ b/modules/terraman_2d/library/terrain_2d_library_merger_pcm.cpp
@@ -792,12 +792,12 @@ void Terrain2DLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibraryMergerPCM::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibraryMergerPCM::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_2D_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &Terrain2DLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &Terrain2DLibraryMergerPCM::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &Terrain2DLibraryMergerPCM::get_prop_uv_rect);
diff --git a/modules/terraman_2d/library/terrain_2d_library_simple.cpp b/modules/terraman_2d/library/terrain_2d_library_simple.cpp
index 9cc9f5bd2..148cd8688 100644
--- a/modules/terraman_2d/library/terrain_2d_library_simple.cpp
+++ b/modules/terraman_2d/library/terrain_2d_library_simple.cpp
@@ -147,5 +147,5 @@ void Terrain2DLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibrarySimple::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibrarySimple::set_terra_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceSimple", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceSimple", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
}
diff --git a/modules/terraman_2d/world/terrain_2d_chunk.cpp b/modules/terraman_2d/world/terrain_2d_chunk.cpp
index db16f7ecf..62b9429da 100644
--- a/modules/terraman_2d/world/terrain_2d_chunk.cpp
+++ b/modules/terraman_2d/world/terrain_2d_chunk.cpp
@@ -1584,7 +1584,7 @@ void Terrain2DChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &Terrain2DChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &Terrain2DChunk::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes
diff --git a/modules/terraman_2d/world/terrain_2d_world.cpp b/modules/terraman_2d/world/terrain_2d_world.cpp
index d87b9d108..ba0e4993c 100644
--- a/modules/terraman_2d/world/terrain_2d_world.cpp
+++ b/modules/terraman_2d/world/terrain_2d_world.cpp
@@ -1238,7 +1238,7 @@ void Terrain2DWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &Terrain2DWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &Terrain2DWorld::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "Terrain2DChunk")));
@@ -1255,7 +1255,7 @@ void Terrain2DWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &Terrain2DWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &Terrain2DWorld::chunks_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:Terrain2DChunk", PROPERTY_USAGE_DEFAULT, "Terrain2DChunk"), "chunks_set", "chunks_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:Terrain2DChunk", PROPERTY_USAGE_DEFAULT, "Terrain2DChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &Terrain2DWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &Terrain2DWorld::generation_queue_get_index);
diff --git a/modules/texture_packer/texture_merger.cpp b/modules/texture_packer/texture_merger.cpp
index 4a5ad321d..557cea0ca 100644
--- a/modules/texture_packer/texture_merger.cpp
+++ b/modules/texture_packer/texture_merger.cpp
@@ -314,7 +314,7 @@ void TextureMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_textures"), &TextureMerger::get_textures);
ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TextureMerger::set_textures);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
ClassDB::bind_method(D_METHOD("get_packer"), &TextureMerger::get_packer);
ClassDB::bind_method(D_METHOD("set_packer", "packer"), &TextureMerger::set_packer);
diff --git a/modules/users/managers/user_manager_static.cpp b/modules/users/managers/user_manager_static.cpp
index 64febd186..0333db808 100644
--- a/modules/users/managers/user_manager_static.cpp
+++ b/modules/users/managers/user_manager_static.cpp
@@ -128,7 +128,7 @@ UserManagerStatic::~UserManagerStatic() {
void UserManagerStatic::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_users"), &UserManagerStatic::get_users);
ClassDB::bind_method(D_METHOD("set_users", "users"), &UserManagerStatic::set_users);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "users", PROPERTY_HINT_NONE, "23/19:User", PROPERTY_USAGE_DEFAULT, "User"), "set_users", "get_users");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "users", PROPERTY_HINT_NONE, "23/20:User", PROPERTY_USAGE_DEFAULT, "User"), "set_users", "get_users");
ClassDB::bind_method(D_METHOD("get_create_user_name"), &UserManagerStatic::get_create_user_name_bind);
ClassDB::bind_method(D_METHOD("set_create_user_name", "val"), &UserManagerStatic::set_create_user_name_bind);
diff --git a/modules/users/users/user.cpp b/modules/users/users/user.cpp
index 64caacf84..73d14c3e6 100644
--- a/modules/users/users/user.cpp
+++ b/modules/users/users/user.cpp
@@ -327,7 +327,7 @@ void User::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_modules"), &User::get_modules);
ClassDB::bind_method(D_METHOD("set_modules", "modules"), &User::set_modules);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "modules", PROPERTY_HINT_NONE, "23/19:UserModule", PROPERTY_USAGE_DEFAULT, "UserModule"), "set_modules", "get_modules");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "modules", PROPERTY_HINT_NONE, "23/20:UserModule", PROPERTY_USAGE_DEFAULT, "UserModule"), "set_modules", "get_modules");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "ret"), "_check_password", PropertyInfo(Variant::STRING, "password")));
BIND_VMETHOD(MethodInfo("_create_password", PropertyInfo(Variant::STRING, "password")));
diff --git a/modules/voxelman/library/voxel_library.cpp b/modules/voxelman/library/voxel_library.cpp
index e1ed0d594..23e6ba9e5 100644
--- a/modules/voxelman/library/voxel_library.cpp
+++ b/modules/voxelman/library/voxel_library.cpp
@@ -393,7 +393,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &VoxelLibrary::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &VoxelLibrary::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
BIND_VMETHOD(MethodInfo("_liquid_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_liquid_material_cache_get", PropertyInfo(Variant::INT, "key")));
@@ -417,7 +417,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("liquid_materials_get"), &VoxelLibrary::liquid_materials_get);
ClassDB::bind_method(D_METHOD("liquid_materials_set"), &VoxelLibrary::liquid_materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
BIND_VMETHOD(MethodInfo("_prop_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_prop_material_cache_get", PropertyInfo(Variant::INT, "key")));
@@ -441,7 +441,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("prop_materials_get"), &VoxelLibrary::prop_materials_get);
ClassDB::bind_method(D_METHOD("prop_materials_set"), &VoxelLibrary::prop_materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ClassDB::bind_method(D_METHOD("voxel_surface_get", "index"), &VoxelLibrary::voxel_surface_get);
ClassDB::bind_method(D_METHOD("voxel_surface_add", "value"), &VoxelLibrary::voxel_surface_add);
diff --git a/modules/voxelman/library/voxel_library_merger.cpp b/modules/voxelman/library/voxel_library_merger.cpp
index 68b16116a..9172b15dc 100644
--- a/modules/voxelman/library/voxel_library_merger.cpp
+++ b/modules/voxelman/library/voxel_library_merger.cpp
@@ -462,12 +462,12 @@ void VoxelLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibraryMerger::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibraryMerger::set_voxel_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &VoxelLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &VoxelLibraryMerger::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &VoxelLibraryMerger::get_prop_uv_rect);
diff --git a/modules/voxelman/library/voxel_library_merger_pcm.cpp b/modules/voxelman/library/voxel_library_merger_pcm.cpp
index 99ca7a3c9..c2a972425 100644
--- a/modules/voxelman/library/voxel_library_merger_pcm.cpp
+++ b/modules/voxelman/library/voxel_library_merger_pcm.cpp
@@ -805,12 +805,12 @@ void VoxelLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibraryMergerPCM::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibraryMergerPCM::set_voxel_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &VoxelLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &VoxelLibraryMergerPCM::set_props);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &VoxelLibraryMergerPCM::get_prop_uv_rect);
diff --git a/modules/voxelman/library/voxel_library_simple.cpp b/modules/voxelman/library/voxel_library_simple.cpp
index 22ecbbe87..55119aefd 100644
--- a/modules/voxelman/library/voxel_library_simple.cpp
+++ b/modules/voxelman/library/voxel_library_simple.cpp
@@ -147,5 +147,5 @@ void VoxelLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibrarySimple::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibrarySimple::set_voxel_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceSimple", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceSimple"), "set_voxel_surfaces", "get_voxel_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceSimple", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceSimple"), "set_voxel_surfaces", "get_voxel_surfaces");
}
diff --git a/modules/voxelman/library/voxel_material_cache.cpp b/modules/voxelman/library/voxel_material_cache.cpp
index 88062abaf..1c7c0d417 100644
--- a/modules/voxelman/library/voxel_material_cache.cpp
+++ b/modules/voxelman/library/voxel_material_cache.cpp
@@ -296,7 +296,7 @@ void VoxelMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &VoxelMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &VoxelMaterialCache::materials_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("surface_get", "index"), &VoxelMaterialCache::surface_get);
ClassDB::bind_method(D_METHOD("surface_id_get", "index"), &VoxelMaterialCache::surface_id_get);
diff --git a/modules/voxelman/world/voxel_chunk.cpp b/modules/voxelman/world/voxel_chunk.cpp
index 1baaa8894..6d4377150 100644
--- a/modules/voxelman/world/voxel_chunk.cpp
+++ b/modules/voxelman/world/voxel_chunk.cpp
@@ -1450,7 +1450,7 @@ void VoxelChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &VoxelChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &VoxelChunk::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes
diff --git a/modules/voxelman/world/voxel_world.cpp b/modules/voxelman/world/voxel_world.cpp
index 7f04daf08..06a271215 100644
--- a/modules/voxelman/world/voxel_world.cpp
+++ b/modules/voxelman/world/voxel_world.cpp
@@ -1111,7 +1111,7 @@ void VoxelWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &VoxelWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &VoxelWorld::voxel_structures_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
@@ -1128,7 +1128,7 @@ void VoxelWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &VoxelWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &VoxelWorld::chunks_set);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:VoxelChunk", PROPERTY_USAGE_DEFAULT, "VoxelChunk"), "chunks_set", "chunks_get");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:VoxelChunk", PROPERTY_USAGE_DEFAULT, "VoxelChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &VoxelWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &VoxelWorld::generation_queue_get_index);
diff --git a/modules/web/html/bbcode_parser.cpp b/modules/web/html/bbcode_parser.cpp
index 04e6fba25..9c03590ab 100644
--- a/modules/web/html/bbcode_parser.cpp
+++ b/modules/web/html/bbcode_parser.cpp
@@ -527,7 +527,7 @@ void BBCodeParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_child_tags"), &BBCodeParserTag::get_child_tags);
ClassDB::bind_method(D_METHOD("set_child_tags", "val"), &BBCodeParserTag::set_child_tags);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/19:BBCodeParserTag", PROPERTY_USAGE_DEFAULT, "BBCodeParserTag"), "set_child_tags", "get_child_tags");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/20:BBCodeParserTag", PROPERTY_USAGE_DEFAULT, "BBCodeParserTag"), "set_child_tags", "get_child_tags");
ClassDB::bind_method(D_METHOD("add_child_attribute", "tag"), &BBCodeParserTag::add_child_attribute);
ClassDB::bind_method(D_METHOD("remote_child_attribute", "index"), &BBCodeParserTag::remote_child_attribute);
@@ -537,7 +537,7 @@ void BBCodeParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_attributes"), &BBCodeParserTag::get_attributes);
ClassDB::bind_method(D_METHOD("set_attributes", "val"), &BBCodeParserTag::set_attributes);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/19:BBCodeParserAttribute", PROPERTY_USAGE_DEFAULT, "BBCodeParserAttribute"), "set_attributes", "get_attributes");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/20:BBCodeParserAttribute", PROPERTY_USAGE_DEFAULT, "BBCodeParserAttribute"), "set_attributes", "get_attributes");
ClassDB::bind_method(D_METHOD("get_first", "t"), &BBCodeParserTag::get_first);
ClassDB::bind_method(D_METHOD("get_firstc", "t", "attrib", "val"), &BBCodeParserTag::get_firstc);
diff --git a/modules/web/html/form_validator.cpp b/modules/web/html/form_validator.cpp
index 0d9899ae3..0026002d2 100644
--- a/modules/web/html/form_validator.cpp
+++ b/modules/web/html/form_validator.cpp
@@ -796,7 +796,7 @@ void FormField::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_entries"), &FormField::get_entries);
ClassDB::bind_method(D_METHOD("set_entries", "array"), &FormField::set_entries);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entries", PROPERTY_HINT_NONE, "23/19:FormFieldEntry", PROPERTY_USAGE_DEFAULT, "FormFieldEntry"), "set_entries", "get_entries");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entries", PROPERTY_HINT_NONE, "23/20:FormFieldEntry", PROPERTY_USAGE_DEFAULT, "FormFieldEntry"), "set_entries", "get_entries");
ClassDB::bind_method(D_METHOD("need_to_exist"), &FormField::need_to_exist);
ClassDB::bind_method(D_METHOD("need_to_be_int"), &FormField::need_to_be_int);
@@ -905,7 +905,7 @@ void FormValidator::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fields"), &FormValidator::get_fields);
ClassDB::bind_method(D_METHOD("set_fields", "array"), &FormValidator::set_fields);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fields", PROPERTY_HINT_NONE, "23/19:FormField", PROPERTY_USAGE_DEFAULT, "FormField"), "set_fields", "get_fields");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fields", PROPERTY_HINT_NONE, "23/20:FormField", PROPERTY_USAGE_DEFAULT, "FormField"), "set_fields", "get_fields");
BIND_VMETHOD(MethodInfo("_validate", PropertyInfo(Variant::OBJECT, "request", PROPERTY_HINT_RESOURCE_TYPE, "WebServerRequest")));
ClassDB::bind_method(D_METHOD("validate", "request"), &FormValidator::validate);
diff --git a/modules/web/html/html_parser.cpp b/modules/web/html/html_parser.cpp
index 65ab2b8f3..dcc09f962 100644
--- a/modules/web/html/html_parser.cpp
+++ b/modules/web/html/html_parser.cpp
@@ -570,7 +570,7 @@ void HTMLParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_child_tags"), &HTMLParserTag::get_child_tags);
ClassDB::bind_method(D_METHOD("set_child_tags", "val"), &HTMLParserTag::set_child_tags);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/19:HTMLParserTag", PROPERTY_USAGE_DEFAULT, "HTMLParserTag"), "set_child_tags", "get_child_tags");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/20:HTMLParserTag", PROPERTY_USAGE_DEFAULT, "HTMLParserTag"), "set_child_tags", "get_child_tags");
ClassDB::bind_method(D_METHOD("add_child_attribute", "tag"), &HTMLParserTag::add_child_attribute);
ClassDB::bind_method(D_METHOD("remote_child_attribute", "index"), &HTMLParserTag::remote_child_attribute);
@@ -580,7 +580,7 @@ void HTMLParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_attributes"), &HTMLParserTag::get_attributes);
ClassDB::bind_method(D_METHOD("set_attributes", "val"), &HTMLParserTag::set_attributes);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/19:HTMLParserAttribute", PROPERTY_USAGE_DEFAULT, "HTMLParserAttribute"), "set_attributes", "get_attributes");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/20:HTMLParserAttribute", PROPERTY_USAGE_DEFAULT, "HTMLParserAttribute"), "set_attributes", "get_attributes");
ClassDB::bind_method(D_METHOD("get_first", "t"), &HTMLParserTag::get_first);
ClassDB::bind_method(D_METHOD("get_firstc", "t", "attrib", "val"), &HTMLParserTag::get_firstc);
diff --git a/modules/web/http/web_root.cpp b/modules/web/http/web_root.cpp
index 693ea40b8..1ed8b5820 100644
--- a/modules/web/http/web_root.cpp
+++ b/modules/web/http/web_root.cpp
@@ -209,7 +209,7 @@ void WebRoot::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_middlewares"), &WebRoot::get_middlewares);
ClassDB::bind_method(D_METHOD("set_middlewares", "data"), &WebRoot::set_middlewares);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "middlewares", PROPERTY_HINT_NONE, "23/19:WebServerMiddleware", PROPERTY_USAGE_DEFAULT, "WebServerMiddleware"), "set_middlewares", "get_middlewares");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "middlewares", PROPERTY_HINT_NONE, "23/20:WebServerMiddleware", PROPERTY_USAGE_DEFAULT, "WebServerMiddleware"), "set_middlewares", "get_middlewares");
ClassDB::bind_method(D_METHOD("process_middlewares", "request"), &WebRoot::process_middlewares);
ClassDB::bind_method(D_METHOD("try_send_wwwroot_file", "request"), &WebRoot::try_send_wwwroot_file);
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index d34e298c5..7301a9ea8 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -63,8 +63,11 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent2D::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent2D::get_max_neighbors);
- ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent2D::set_time_horizon);
- ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent2D::get_time_horizon);
+ ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent2D::set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent2D::get_time_horizon_agents);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent2D::set_time_horizon_obstacles);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent2D::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent2D::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent2D::get_max_speed);
@@ -88,11 +91,15 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_position"), &NavigationAgent2D::get_next_position);
- ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
+ ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent2D::set_velocity_forced);
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent2D::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result);
- ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent2D::get_nav_path);
- ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent2D::get_nav_path_index);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent2D::get_current_navigation_path);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent2D::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached);
ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable);
ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished);
@@ -100,6 +107,17 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent2D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent2D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent2D::set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent2D::get_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent2D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent2D::get_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent2D::set_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent2D::get_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent2D::set_avoidance_priority);
+ ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent2D::get_avoidance_priority);
+
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_path_desired_distance", "get_path_desired_distance");
@@ -110,13 +128,17 @@ void NavigationAgent2D::_bind_methods() {
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_time_horizon", "get_time_horizon");
- 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::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1,or_greater"), "set_max_neighbors", "get_max_neighbors");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.01,100000,0.01,or_greater,suffix:px/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
-#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent2D::get_debug_enabled);
ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent2D::set_debug_use_custom);
@@ -136,7 +158,6 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "debug_path_custom_line_width", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_line_width", "get_debug_path_custom_line_width");
-#endif // DEBUG_ENABLED
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -170,6 +191,10 @@ void NavigationAgent2D::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
+ if (agent_parent && avoidance_enabled) {
+ Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ }
+
#ifdef DEBUG_ENABLED
if (Navigation2DServer::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
@@ -222,10 +247,18 @@ void NavigationAgent2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
- 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());
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ if (avoidance_enabled) {
+ Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
+ Navigation2DServer::get_singleton()->agent_set_velocity(agent, velocity);
+ }
+ }
+ if (velocity_forced_submitted) {
+ velocity_forced_submitted = false;
+ if (avoidance_enabled) {
+ Navigation2DServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
+ }
}
_check_distance_to_target();
}
@@ -244,6 +277,9 @@ NavigationAgent2D::NavigationAgent2D() {
navigation = nullptr;
avoidance_enabled = false;
+ avoidance_layers = 1;
+ avoidance_mask = 1;
+ avoidance_priority = 1.0;
navigation_layers = 1;
path_metadata_flags = NavigationPathQueryParameters2D::PATH_METADATA_INCLUDE_ALL;
@@ -257,11 +293,13 @@ NavigationAgent2D::NavigationAgent2D() {
set_neighbor_dist(500.0);
set_max_neighbors(10);
- set_time_horizon(20.0);
+ set_time_horizon_agents(1.0);
+ set_time_horizon_obstacles(0.0);
set_radius(10.0);
set_max_speed(200.0);
- time_horizon = 0.0;
+ velocity_forced_submitted = false;
+ target_position_submitted = false;
nav_path_index = 0;
update_frame_id = 0;
@@ -269,7 +307,6 @@ NavigationAgent2D::NavigationAgent2D() {
navigation_query.instance();
navigation_result.instance();
-#ifdef DEBUG_ENABLED
debug_enabled = false;
debug_path_dirty = true;
debug_path_custom_point_size = 4.0;
@@ -277,8 +314,14 @@ NavigationAgent2D::NavigationAgent2D() {
debug_use_custom = false;
debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0);
+#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton()->connect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
+
+ set_avoidance_layers(avoidance_layers);
+ set_avoidance_mask(avoidance_mask);
+ set_avoidance_priority(avoidance_priority);
+ set_avoidance_enabled(avoidance_enabled);
}
NavigationAgent2D::~NavigationAgent2D() {
@@ -298,9 +341,11 @@ NavigationAgent2D::~NavigationAgent2D() {
void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- Navigation2DServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
+ Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(agent, true);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
- Navigation2DServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(agent, false);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
}
}
@@ -310,7 +355,7 @@ bool NavigationAgent2D::get_avoidance_enabled() const {
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, ObjectID(), "_avoidance_done");
+ Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
if (Object::cast_to(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(p_agent_parent);
@@ -323,7 +368,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map());
}
// create new avoidance callback if enabled
- set_avoidance_enabled(avoidance_enabled);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
agent_parent = nullptr;
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
@@ -379,6 +424,71 @@ bool NavigationAgent2D::get_navigation_layer_value(int p_layer_number) const {
return get_navigation_layers() & (1 << (p_layer_number - 1));
}
+void NavigationAgent2D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ Navigation2DServer::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
+}
+
+uint32_t NavigationAgent2D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationAgent2D::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ Navigation2DServer::get_singleton()->agent_set_avoidance_mask(get_rid(), p_mask);
+}
+
+uint32_t NavigationAgent2D::get_avoidance_mask() const {
+ return avoidance_mask;
+}
+
+void NavigationAgent2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationAgent2D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationAgent2D::set_avoidance_mask_value(int p_mask_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_avoidance_mask();
+ if (p_value) {
+ mask |= 1 << (p_mask_number - 1);
+ } else {
+ mask &= ~(1 << (p_mask_number - 1));
+ }
+ set_avoidance_mask(mask);
+}
+
+bool NavigationAgent2D::get_avoidance_mask_value(int p_mask_number) const {
+ ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ return get_avoidance_mask() & (1 << (p_mask_number - 1));
+}
+
+void NavigationAgent2D::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ Navigation2DServer::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
+}
+
+real_t NavigationAgent2D::get_avoidance_priority() const {
+ return avoidance_priority;
+}
+
void NavigationAgent2D::set_path_metadata_flags(const int p_flags) {
path_metadata_flags = p_flags;
}
@@ -412,6 +522,7 @@ void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
}
void NavigationAgent2D::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
radius = p_radius;
Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
}
@@ -426,12 +537,26 @@ void NavigationAgent2D::set_max_neighbors(int p_count) {
Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
-void NavigationAgent2D::set_time_horizon(real_t p_time) {
- time_horizon = p_time;
- Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+void NavigationAgent2D::set_time_horizon_agents(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
+ return;
+ }
+ time_horizon_agents = p_time_horizon;
+ Navigation2DServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+}
+
+void NavigationAgent2D::set_time_horizon_obstacles(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
+ return;
+ }
+ time_horizon_obstacles = p_time_horizon;
+ Navigation2DServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent2D::set_max_speed(real_t p_max_speed) {
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
max_speed = p_max_speed;
Navigation2DServer::get_singleton()->agent_set_max_speed(agent, max_speed);
}
@@ -469,7 +594,7 @@ Ref NavigationAgent2D::get_current_navigation_resul
return navigation_result;
}
-const Vector &NavigationAgent2D::get_nav_path() const {
+const Vector &NavigationAgent2D::get_current_navigation_path() const {
return navigation_result->get_path();
}
@@ -501,24 +626,23 @@ Vector2 NavigationAgent2D::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
-void NavigationAgent2D::set_velocity(Vector2 p_velocity) {
- target_velocity = p_velocity;
- Navigation2DServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
- Navigation2DServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+void NavigationAgent2D::set_velocity_forced(Vector2 p_velocity) {
+ velocity_forced = p_velocity;
+ velocity_forced_submitted = true;
+
+ Navigation2DServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
+}
+
+void NavigationAgent2D::set_velocity(const Vector2 p_velocity) {
+ velocity = p_velocity;
velocity_submitted = true;
+ Navigation2DServer::get_singleton()->agent_set_velocity(agent, velocity);
}
void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
- const Vector2 velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
- prev_safe_velocity = velocity;
-
- if (!velocity_submitted) {
- target_velocity = Vector2();
- return;
- }
- velocity_submitted = false;
-
- emit_signal("velocity_computed", velocity);
+ const Vector2 new_safe_velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
+ safe_velocity = new_safe_velocity;
+ emit_signal("velocity_computed", safe_velocity);
}
String NavigationAgent2D::get_configuration_warning() const {
@@ -536,9 +660,6 @@ void NavigationAgent2D::update_navigation() {
if (!agent_parent->is_inside_tree()) {
return;
}
- if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
- return;
- }
update_frame_id = Engine::get_singleton()->get_physics_frames();
@@ -690,7 +811,6 @@ void NavigationAgent2D::_check_distance_to_target() {
////////DEBUG////////////////////////////////////////////////////////////
-#ifdef DEBUG_ENABLED
void NavigationAgent2D::set_debug_enabled(bool p_enabled) {
debug_enabled = p_enabled;
debug_path_dirty = true;
@@ -736,6 +856,7 @@ float NavigationAgent2D::get_debug_path_custom_line_width() const {
return debug_path_custom_line_width;
}
+#ifdef DEBUG_ENABLED
void NavigationAgent2D::_navigation_debug_changed() {
debug_path_dirty = true;
}
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index c90e48d7a..0f81b6d61 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -48,6 +48,9 @@ class NavigationAgent2D : public Node {
RID map_override;
bool avoidance_enabled;
+ uint32_t avoidance_layers;
+ uint32_t avoidance_mask;
+ real_t avoidance_priority;
uint32_t navigation_layers;
int path_metadata_flags;
@@ -56,7 +59,8 @@ class NavigationAgent2D : public Node {
real_t radius;
real_t neighbor_dist;
int max_neighbors;
- real_t time_horizon;
+ real_t time_horizon_agents;
+ real_t time_horizon_obstacles;
real_t max_speed;
real_t path_max_distance;
@@ -65,16 +69,28 @@ class NavigationAgent2D : public Node {
Ref navigation_query;
Ref navigation_result;
int nav_path_index;
+
+ // the velocity result of the avoidance simulation step
+ Vector2 safe_velocity;
+
+ /// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
+ // this velocity is not guaranteed, the simulation will try to fulfil it if possible
+ // if other agents or obstacles interfere it will be changed accordingly
+ Vector2 velocity;
bool velocity_submitted;
- Vector2 prev_safe_velocity;
- /// The submitted target velocity
- Vector2 target_velocity;
+
+ /// The submitted forced velocity, overrides the rvo agent velocity on the next update
+ // should only be used very intentionally and not every frame as it interferes with the simulation stability
+ Vector2 velocity_forced;
+ bool velocity_forced_submitted;
+
+ bool target_position_submitted;
+
bool target_reached;
bool navigation_finished;
// No initialized on purpose
uint32_t update_frame_id;
-#ifdef DEBUG_ENABLED
bool debug_enabled;
bool debug_path_dirty;
RID debug_path_instance;
@@ -83,11 +99,6 @@ class NavigationAgent2D : public Node {
bool debug_use_custom;
Color debug_path_custom_color;
-private:
- void _navigation_debug_changed();
- void _update_debug_path();
-#endif // DEBUG_ENABLED
-
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -119,6 +130,21 @@ public:
void set_navigation_layer_value(int p_layer_number, bool p_value);
bool get_navigation_layer_value(int p_layer_number) const;
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_avoidance_mask_value(int p_mask_number, bool p_value);
+ bool get_avoidance_mask_value(int p_mask_number) const;
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const;
+
void set_path_metadata_flags(const int p_flags);
int get_path_metadata_flags() const;
@@ -150,10 +176,11 @@ public:
return max_neighbors;
}
- void set_time_horizon(real_t p_time);
- real_t get_time_horizon() const {
- return time_horizon;
- }
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
real_t get_max_speed() const {
@@ -170,9 +197,9 @@ public:
Ref get_current_navigation_result() const;
- const Vector &get_nav_path() const;
+ const Vector &get_current_navigation_path() const;
- int get_nav_path_index() const {
+ int get_current_navigation_path_index() const {
return nav_path_index;
}
@@ -182,12 +209,15 @@ public:
bool is_navigation_finished();
Vector2 get_final_position();
- void set_velocity(Vector2 p_velocity);
+ void set_velocity(const Vector2 p_velocity);
+ Vector2 get_velocity() { return velocity; }
+
+ void set_velocity_forced(const Vector2 p_velocity);
+
void _avoidance_done(Vector3 p_new_velocity);
virtual String get_configuration_warning() const;
-#ifdef DEBUG_ENABLED
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
@@ -202,12 +232,16 @@ public:
void set_debug_path_custom_line_width(float p_line_width);
float get_debug_path_custom_line_width() const;
-#endif // DEBUG_ENABLED
private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
+
+#ifdef DEBUG_ENABLED
+ void _navigation_debug_changed();
+ void _update_debug_path();
+#endif // DEBUG_ENABLED
};
#endif
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index e0c607408..33cc65dcc 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -109,7 +109,7 @@ void NavigationLink2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled())) {
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled())) {
Color color;
if (enabled) {
color = Navigation2DServer::get_singleton()->get_debug_navigation_link_connection_color();
@@ -168,7 +168,7 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
}
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
+ if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@@ -232,7 +232,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
update_configuration_warning();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
+ if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@@ -254,7 +254,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
update_configuration_warning();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
+ if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index f20564c20..8e029e6e2 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -30,41 +30,45 @@
#include "navigation_obstacle_2d.h"
+#include "core/config/engine.h"
#include "scene/2d/collision_shape_2d.h"
#include "scene/2d/navigation_2d.h"
#include "scene/2d/node_2d.h"
#include "scene/2d/physics_body_2d.h"
+#include "scene/resources/world_2d.h"
#include "servers/navigation_2d_server.h"
void NavigationObstacle2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
+ ClassDB::bind_method(D_METHOD("get_obstacle_rid"), &NavigationObstacle2D::get_obstacle_rid);
+ ClassDB::bind_method(D_METHOD("get_agent_rid"), &NavigationObstacle2D::get_agent_rid);
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle2D::set_navigation_node);
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle2D::get_navigation_node);
- ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
- ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius");
-}
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle2D::get_velocity);
-void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "radius") {
- if (estimate_radius) {
- p_property.usage = PROPERTY_USAGE_NOEDITOR;
- }
- }
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle2D::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle2D::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle2D::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle2D::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle2D::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle2D::get_avoidance_layer_value);
+
+ ADD_GROUP("Avoidance", "avoidance_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
}
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- parent_node2d = Object::cast_to(get_parent());
- reevaluate_agent_radius();
-
// Search the navigation node and set it
{
Navigation2D *nav = nullptr;
@@ -78,69 +82,131 @@ void NavigationObstacle2D::_notification(int p_what) {
}
}
- set_navigation(nav);
+ navigation = nav;
}
+ if (navigation) {
+ _update_map(navigation->get_rid());
+ } else if (map_override.is_valid()) {
+ _update_map(map_override);
+ } else if (is_inside_tree()) {
+ _update_map(get_world_2d()->get_navigation_map());
+ } else {
+ _update_map(RID());
+ }
+ previous_transform = get_global_transform();
+ // need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
+ Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+ _update_position(get_global_transform().get_origin());
+
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
set_navigation(nullptr);
set_physics_process_internal(false);
+ _update_map(RID());
request_ready(); // required to solve an issue with losing the navigation
- } break;
- case NOTIFICATION_PARENTED: {
- parent_node2d = Object::cast_to(get_parent());
- reevaluate_agent_radius();
- } break;
- case NOTIFICATION_UNPARENTED: {
- parent_node2d = nullptr;
+
} break;
case NOTIFICATION_PAUSED: {
- if (parent_node2d && !parent_node2d->can_process()) {
- map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid());
- Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
- Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_UNPAUSED: {
- if (parent_node2d && !parent_node2d->can_process()) {
- map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid());
- Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
- Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node2d && parent_node2d->is_inside_tree()) {
- Navigation2DServer::get_singleton()->agent_set_position(agent, parent_node2d->get_global_transform().get_origin());
- }
+ if (is_inside_tree()) {
+ _update_position(get_global_transform().get_origin());
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ // only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
+ if (!previous_velocity.is_equal_approx(velocity)) {
+ Navigation2DServer::get_singleton()->agent_set_velocity(fake_agent, velocity);
+ }
+ previous_velocity = velocity;
+ }
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+#ifdef DEBUG_ENABLED
+ if (is_inside_tree()) {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (Navigation2DServer::get_singleton()->get_debug_enabled() && Navigation2DServer::get_singleton()->get_debug_avoidance_enabled()) {
+ is_debug_enabled = true;
+ }
+
+ if (is_debug_enabled) {
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+ }
+ }
+#endif // DEBUG_ENABLED
} break;
}
}
-NavigationObstacle2D::NavigationObstacle2D() :
- navigation(nullptr),
- agent(RID()) {
- agent = Navigation2DServer::get_singleton()->agent_create();
- initialize_agent();
+NavigationObstacle2D::NavigationObstacle2D() {
+ navigation = nullptr;
+
+ obstacle = Navigation2DServer::get_singleton()->obstacle_create();
+ fake_agent = Navigation2DServer::get_singleton()->agent_create();
+
+ // change properties of the fake agent so it can act as fake obstacle with a radius
+ Navigation2DServer::get_singleton()->agent_set_neighbor_dist(fake_agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_max_neighbors(fake_agent, 0);
+ Navigation2DServer::get_singleton()->agent_set_time_horizon_agents(fake_agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_time_horizon_obstacles(fake_agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_max_speed(fake_agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_mask(fake_agent, 0);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_priority(fake_agent, 1.0);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+
+ set_radius(radius);
+ set_vertices(vertices);
+ set_avoidance_layers(avoidance_layers);
}
NavigationObstacle2D::~NavigationObstacle2D() {
- Navigation2DServer::get_singleton()->free(agent);
- agent = RID(); // Pointless
+ ERR_FAIL_NULL(Navigation2DServer::get_singleton());
+ Navigation2DServer::get_singleton()->free(obstacle);
+ obstacle = RID();
+
+ Navigation2DServer::get_singleton()->free(fake_agent);
+ fake_agent = RID();
}
void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) {
- if (navigation == p_nav && navigation != nullptr) {
+ if (navigation == p_nav) {
return; // Pointless
}
navigation = p_nav;
- Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
+
+ if (navigation) {
+ _update_map(navigation->get_rid());
+ } else if (map_override.is_valid()) {
+ _update_map(map_override);
+ } else if (is_inside_tree()) {
+ _update_map(get_world_2d()->get_navigation_map());
+ } else {
+ _update_map(RID());
+ }
}
void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
@@ -153,75 +219,145 @@ Node *NavigationObstacle2D::get_navigation_node() const {
return Object::cast_to(navigation);
}
-String NavigationObstacle2D::get_configuration_warning() const {
- if (!Object::cast_to(get_parent())) {
- return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
- }
-
- if (Object::cast_to(get_parent())) {
- return TTR("The NavigationObstacle2D is intended for constantly moving bodies like KinematicBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
- "\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail");
- }
-
- return String();
-}
-
-void NavigationObstacle2D::initialize_agent() {
- Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
- Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, 0);
- Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, 0.0);
- Navigation2DServer::get_singleton()->agent_set_max_speed(agent, 0.0);
-}
-
-void NavigationObstacle2D::reevaluate_agent_radius() {
- if (!estimate_radius) {
- Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node2d && parent_node2d->is_inside_tree()) {
- Navigation2DServer::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+void NavigationObstacle2D::set_vertices(const Vector &p_vertices) {
+ vertices = p_vertices;
+ Navigation2DServer::get_singleton()->obstacle_set_vertices(obstacle, vertices);
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
+ update();
}
}
-real_t NavigationObstacle2D::estimate_agent_radius() const {
- if (parent_node2d && parent_node2d->is_inside_tree()) {
- // Estimate the radius of this physics body
- real_t radius = 0.0;
- for (int i(0); i < parent_node2d->get_child_count(); i++) {
- // For each collision shape
- CollisionShape2D *cs = Object::cast_to(parent_node2d->get_child(i));
- if (cs && cs->is_inside_tree()) {
- // Take the distance between the Body center to the shape center
- real_t r = cs->get_transform().get_origin().length();
- if (cs->get_shape().is_valid()) {
- // and add the enclosing shape radius
- r += cs->get_shape()->get_enclosing_radius();
- }
- Size2 s = cs->get_global_transform().get_scale();
- r *= MAX(s.x, s.y);
- // Takes the biggest radius
- radius = MAX(radius, r);
- } else if (cs && !cs->is_inside_tree()) {
- WARN_PRINT("A CollisionShape2D of the NavigationObstacle2D parent node was not inside the SceneTree when estimating the obstacle radius."
- "\nMove the NavigationObstacle2D to a child position below any CollisionShape2D node of the parent node so the CollisionShape2D is already inside the SceneTree.");
- }
- }
- Vector2 s = parent_node2d->get_global_transform().get_scale();
- radius *= MAX(s.x, s.y);
-
- if (radius > 0.0) {
- return radius;
- }
+void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
}
- return 1.0; // Never a 0 radius
+ map_override = p_navigation_map;
+ _update_map(map_override);
}
-void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
- estimate_radius = p_estimate_radius;
- _change_notify();
- reevaluate_agent_radius();
+RID NavigationObstacle2D::get_navigation_map() const {
+ if (navigation) {
+ return navigation->get_rid();
+ } else if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_2d()->get_navigation_map();
+ }
+
+ return RID();
}
void NavigationObstacle2D::set_radius(real_t p_radius) {
- ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
- reevaluate_agent_radius();
+
+ Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0.0);
+ Navigation2DServer::get_singleton()->agent_set_radius(fake_agent, radius);
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
+ update();
+ }
}
+
+void NavigationObstacle2D::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
+ Navigation2DServer::get_singleton()->agent_set_avoidance_layers(fake_agent, avoidance_layers);
+}
+
+uint32_t NavigationObstacle2D::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationObstacle2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationObstacle2D::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationObstacle2D::set_velocity(const Vector2 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
+ Navigation2DServer::get_singleton()->agent_set_velocity(fake_agent, velocity);
+}
+
+void NavigationObstacle2D::_update_map(RID p_map) {
+ Navigation2DServer::get_singleton()->obstacle_set_map(obstacle, p_map);
+ Navigation2DServer::get_singleton()->agent_set_map(fake_agent, p_map);
+ map_current = p_map;
+}
+
+void NavigationObstacle2D::_update_position(const Vector2 p_position) {
+ if (vertices.size() > 0) {
+ Navigation2DServer::get_singleton()->obstacle_set_position(obstacle, p_position);
+ }
+ if (radius > 0.0) {
+ Navigation2DServer::get_singleton()->agent_set_position(fake_agent, p_position);
+ }
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationObstacle2D::_update_fake_agent_radius_debug() {
+ if (radius > 0.0 && Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
+ Color debug_radius_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
+ draw_circle(get_global_transform().get_origin(), radius, debug_radius_color);
+ }
+}
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationObstacle2D::_update_static_obstacle_debug() {
+ if (get_vertices().size() > 2 && Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
+ bool obstacle_pushes_inward = Geometry::is_polygon_clockwise(get_vertices());
+
+ Color debug_static_obstacle_face_color;
+
+ if (obstacle_pushes_inward) {
+ debug_static_obstacle_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
+ } else {
+ debug_static_obstacle_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
+ }
+
+ Vector debug_obstacle_polygon_vertices = get_vertices();
+
+ Vector debug_obstacle_polygon_colors;
+ debug_obstacle_polygon_colors.resize(debug_obstacle_polygon_vertices.size());
+ debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
+
+ RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_obstacle_polygon_vertices, debug_obstacle_polygon_colors);
+
+ Color debug_static_obstacle_edge_color;
+
+ if (obstacle_pushes_inward) {
+ debug_static_obstacle_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
+ } else {
+ debug_static_obstacle_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
+ }
+
+ Vector debug_obstacle_line_vertices = get_vertices();
+ debug_obstacle_line_vertices.push_back(debug_obstacle_line_vertices[0]);
+ debug_obstacle_line_vertices.resize(debug_obstacle_line_vertices.size());
+
+ Vector debug_obstacle_line_colors;
+ debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
+ debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
+
+ RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
+ }
+}
+#endif // DEBUG_ENABLED
\ No newline at end of file
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
index 76d86bfef..1097373dc 100644
--- a/scene/2d/navigation_obstacle_2d.h
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -30,26 +30,42 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "scene/main/node.h"
+#include "scene/2d/node_2d.h"
class Navigation2D;
class Node2D;
-class NavigationObstacle2D : public Node {
- GDCLASS(NavigationObstacle2D, Node);
+class NavigationObstacle2D : public Node2D {
+ GDCLASS(NavigationObstacle2D, Node2D);
Navigation2D *navigation;
- Node2D *parent_node2d = nullptr;
- RID agent;
+ RID obstacle;
RID map_before_pause;
+ RID map_override; //!!!
+ RID map_current;
- bool estimate_radius = true;
- real_t radius = 1.0;
+ real_t radius = 0.0;
+
+ Vector vertices;
+
+ RID fake_agent;
+ uint32_t avoidance_layers = 1;
+
+ Transform2D previous_transform;
+
+ Vector2 velocity;
+ Vector2 previous_velocity;
+ bool velocity_submitted = false;
+
+#ifdef DEBUG_ENABLED
+private:
+ void _update_fake_agent_radius_debug();
+ void _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
- void _validate_property(PropertyInfo &property) const;
void _notification(int p_what);
public:
@@ -64,24 +80,37 @@ public:
void set_navigation_node(Node *p_nav);
Node *get_navigation_node() const;
- RID get_rid() const {
- return agent;
- }
+ RID get_obstacle_rid() const { return obstacle; }
+ RID get_agent_rid() const { return fake_agent; }
+
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
- void set_estimate_radius(bool p_estimate_radius);
- bool is_radius_estimated() const {
- return estimate_radius;
- }
void set_radius(real_t p_radius);
real_t get_radius() const {
return radius;
}
- virtual String get_configuration_warning() const;
+
+ void set_vertices(const Vector &p_vertices);
+ const Vector &get_vertices() const { return vertices; };
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_velocity(const Vector2 p_velocity);
+ Vector2 get_velocity() const { return velocity; };
+
+ void _avoidance_done(Vector3 p_new_velocity); // Dummy
private:
- void initialize_agent();
- void reevaluate_agent_radius();
- real_t estimate_agent_radius() const;
+ void _update_map(RID p_map);
+ void _update_position(const Vector2 p_position);
};
#endif
diff --git a/scene/2d/navigation_polygon_instance.cpp b/scene/2d/navigation_polygon_instance.cpp
index bc55d7263..5ecf7915f 100644
--- a/scene/2d/navigation_polygon_instance.cpp
+++ b/scene/2d/navigation_polygon_instance.cpp
@@ -32,10 +32,12 @@
#include "core/config/engine.h"
#include "core/core_string_names.h"
+#include "core/math/geometry.h"
#include "core/object/func_ref.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
#include "navigation_2d.h"
+#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_polygon.h"
@@ -58,6 +60,7 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
if (!enabled) {
Navigation2DServer::get_singleton()->region_set_map(region, RID());
+ Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
} else {
if (navigation != nullptr) {
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
@@ -68,10 +71,12 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
}
}
+
+ Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
}
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
+ if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@@ -184,12 +189,17 @@ void NavigationPolygonInstance::_notification(int p_what) {
c = Object::cast_to(c->get_parent());
}
- if (enabled && navigation == nullptr) {
- // did not find a valid navigation node parent, fallback to default navigation map on world resource
- if (map_override.is_valid()) {
- Navigation2DServer::get_singleton()->region_set_map(region, map_override);
- } else {
- Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
+ if (enabled) {
+ RID map = get_navigation_map();
+
+ Navigation2DServer::get_singleton()->region_set_map(region, map);
+ Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
+
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ Navigation2DServer::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map);
+ Navigation2DServer::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
+ }
}
}
@@ -198,6 +208,12 @@ void NavigationPolygonInstance::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
set_physics_process_internal(true);
+
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ Navigation2DServer::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
+ }
+ }
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
@@ -218,14 +234,23 @@ void NavigationPolygonInstance::_notification(int p_what) {
}
} break;
case NOTIFICATION_EXIT_TREE: {
- if (navigation) {
- Navigation2DServer::get_singleton()->region_set_map(region, RID());
- }
+ Navigation2DServer::get_singleton()->region_set_map(region, RID());
+
navigation = nullptr;
+
+ if (enabled) {
+ Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
+ }
+
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ Navigation2DServer::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
+ }
+ }
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) {
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) && navpoly.is_valid()) {
_update_debug_mesh();
_update_debug_edge_connections_mesh();
}
@@ -240,17 +265,17 @@ void NavigationPolygonInstance::set_navigation_polygon(const Refdisconnect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
+ navpoly->disconnect(CoreStringNames::get_singleton()->changed, this, "_navigation_polygon_changed");
}
navpoly = p_navpoly;
Navigation2DServer::get_singleton()->region_set_navpoly(region, p_navpoly);
if (navpoly.is_valid()) {
- navpoly->connect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
+ navpoly->connect(CoreStringNames::get_singleton()->changed, this, "_navigation_polygon_changed");
}
- _navpoly_changed();
+ _navigation_polygon_changed();
}
Ref NavigationPolygonInstance::get_navigation_polygon() const {
@@ -268,7 +293,9 @@ void NavigationPolygonInstance::set_navigation_map(RID p_navigation_map) {
}
RID NavigationPolygonInstance::get_navigation_map() const {
- if (map_override.is_valid()) {
+ if (navigation) {
+ return navigation->get_rid();
+ } else if (map_override.is_valid()) {
return map_override;
} else if (is_inside_tree()) {
return get_world_2d()->get_navigation_map();
@@ -305,7 +332,7 @@ void NavigationPolygonInstance::_bake_finished() {
emit_signal("bake_finished");
}
-void NavigationPolygonInstance::_navpoly_changed() {
+void NavigationPolygonInstance::_navigation_polygon_changed() {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
update();
}
@@ -313,6 +340,8 @@ void NavigationPolygonInstance::_navpoly_changed() {
Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly);
}
+ _update_avoidance_constrain();
+
emit_signal("navigation_polygon_changed");
_change_notify("navpoly");
@@ -365,6 +394,13 @@ void NavigationPolygonInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationPolygonInstance::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationPolygonInstance::get_navigation_layer_value);
+ ClassDB::bind_method(D_METHOD("set_constrain_avoidance", "enabled"), &NavigationPolygonInstance::set_constrain_avoidance);
+ ClassDB::bind_method(D_METHOD("get_constrain_avoidance"), &NavigationPolygonInstance::get_constrain_avoidance);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationPolygonInstance::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationPolygonInstance::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationPolygonInstance::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationPolygonInstance::get_avoidance_layer_value);
+
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);
@@ -388,12 +424,14 @@ void NavigationPolygonInstance::_bind_methods() {
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");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
#ifdef DEBUG_ENABLED
- ClassDB::bind_method(D_METHOD("_navigation_map_changed"), &NavigationPolygonInstance::_navigation_map_changed);
ClassDB::bind_method(D_METHOD("_navigation_debug_changed"), &NavigationPolygonInstance::_navigation_debug_changed);
#endif
- ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed);
+ ClassDB::bind_method(D_METHOD("_navigation_map_changed"), &NavigationPolygonInstance::_navigation_map_changed);
+ ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationPolygonInstance::_navigation_polygon_changed);
ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
ADD_SIGNAL(MethodInfo("bake_finished"));
@@ -412,13 +450,15 @@ NavigationPolygonInstance::NavigationPolygonInstance() {
navigation_layers = 1;
+ constrain_avoidance = false;
+ avoidance_layers = 1;
+
baking_started = false;
Navigation2DServer::get_singleton()->region_set_owner_id(region, get_instance_id());
Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
#ifdef DEBUG_ENABLED
- Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton()->connect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
}
@@ -428,68 +468,135 @@ NavigationPolygonInstance::~NavigationPolygonInstance() {
Navigation2DServer::get_singleton()->free(region);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ Navigation2DServer::get_singleton()->free(constrain_avoidance_obstacles[i]);
+ }
+ }
+ constrain_avoidance_obstacles.clear();
+
#ifdef DEBUG_ENABLED
- Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton()->disconnect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
}
-#ifdef DEBUG_ENABLED
-void NavigationPolygonInstance::_update_debug_mesh() {
- /*
- Vector navigation_polygon_vertices = navigation_polygon->get_vertices();
- if (navigation_polygon_vertices.size() < 3) {
+void NavigationPolygonInstance::_update_avoidance_constrain() {
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ Navigation2DServer::get_singleton()->free(constrain_avoidance_obstacles[i]);
+ constrain_avoidance_obstacles[i] = RID();
+ }
+ }
+ constrain_avoidance_obstacles.clear();
+
+ if (!constrain_avoidance) {
return;
}
- const Navigation2DServer2D *ns2d = Navigation2DServer2D::get_singleton();
-
- bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
- bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
-
- Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color();
- Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color();
-
- if (!enabled) {
- debug_face_color = ns2d->get_debug_navigation_geometry_face_disabled_color();
- debug_edge_color = ns2d->get_debug_navigation_geometry_edge_disabled_color();
+ if (get_navigation_polygon() == nullptr) {
+ return;
}
- RandomPCG rand;
+ Ref _navpoly = get_navigation_polygon();
+ int _outline_count = _navpoly->get_outline_count();
+ if (_outline_count == 0) {
+ return;
+ }
- for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) {
- // An array of vertices for this polygon.
- Vector polygon = navigation_polygon->get_polygon(i);
- Vector debug_polygon_vertices;
- debug_polygon_vertices.resize(polygon.size());
- for (int j = 0; j < polygon.size(); j++) {
- ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
- debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]];
+ for (int outline_index(0); outline_index < _outline_count; outline_index++) {
+ //TODO navpoly should probably use normal vectors internally
+ const PoolVector &_outline = _navpoly->get_outline(outline_index);
+
+ Vector outline;
+ outline.resize(_outline.size());
+ for (int i = 0; i < _outline.size(); ++i) {
+ outline.write[i] = outline[i];
}
- // Generate the polygon color, slightly randomly modified from the settings one.
- Color random_variation_color = debug_face_color;
- if (enabled_geometry_face_random_color) {
- random_variation_color.set_hsv(
- debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1,
- debug_face_color.get_s(),
- debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
+ const int outline_size = _outline.size();
+ if (outline_size < 3) {
+ ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's");
+ continue;
}
- random_variation_color.a = debug_face_color.a;
- Vector debug_face_colors;
- debug_face_colors.push_back(random_variation_color);
- RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_polygon_vertices, debug_face_colors);
+ RID obstacle_rid = Navigation2DServer::get_singleton()->obstacle_create();
+ constrain_avoidance_obstacles.push_back(obstacle_rid);
- if (enabled_edge_lines) {
- Vector debug_edge_colors;
- debug_edge_colors.push_back(debug_edge_color);
- debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline.
- RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors);
+ Vector new_obstacle_outline;
+
+ if (outline_index == 0) {
+ for (int i(0); i < outline_size; i++) {
+ new_obstacle_outline.push_back(_outline[outline_size - i - 1]);
+ }
+ ERR_FAIL_COND_MSG(Geometry::is_polygon_clockwise(outline), "Outermost outline needs to be clockwise to push avoidance agent inside");
+ } else {
+ for (int i(0); i < outline_size; i++) {
+ new_obstacle_outline.push_back(_outline[i]);
+ }
+ }
+ new_obstacle_outline.resize(outline_size);
+
+ Navigation2DServer::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline);
+ Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers);
+ if (is_inside_tree()) {
+ Navigation2DServer::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
+ Navigation2DServer::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
}
}
- */
+ constrain_avoidance_obstacles.resize(_outline_count);
+}
+void NavigationPolygonInstance::set_constrain_avoidance(bool p_enabled) {
+ constrain_avoidance = p_enabled;
+ _update_avoidance_constrain();
+ property_list_changed_notify();
+}
+
+bool NavigationPolygonInstance::get_constrain_avoidance() const {
+ return constrain_avoidance;
+}
+
+void NavigationPolygonInstance::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "avoidance_layers") {
+ if (!constrain_avoidance) {
+ p_property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+ }
+}
+
+void NavigationPolygonInstance::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ if (constrain_avoidance_obstacles.size() > 0) {
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers);
+ }
+ }
+}
+
+uint32_t NavigationPolygonInstance::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationPolygonInstance::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationPolygonInstance::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationPolygonInstance::_update_debug_mesh() {
PoolVector navigation_polygon_vertices = navpoly->get_vertices();
if (navigation_polygon_vertices.size() < 3) {
return;
@@ -499,11 +606,11 @@ void NavigationPolygonInstance::_update_debug_mesh() {
bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
- bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map());
+ //bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map());
Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color();
Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color();
- Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
+ //Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
if (!enabled) {
debug_face_color = ns2d->get_debug_navigation_geometry_face_disabled_color();
@@ -540,17 +647,24 @@ void NavigationPolygonInstance::_update_debug_mesh() {
RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors);
}
}
+}
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationPolygonInstance::_update_debug_edge_connections_mesh() {
+ const Navigation2DServer *ns2d = Navigation2DServer::get_singleton();
+ bool enable_edge_connections = ns2d->get_debug_navigation_enable_edge_connections();
if (enable_edge_connections) {
+ Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
+
// Draw the region edge connections.
Transform2D xform = get_global_transform();
+ RID map = get_navigation_map();
+
real_t radius = 1.0;
- if (navigation != nullptr) {
- radius = ns2d->map_get_edge_connection_margin(navigation->get_rid()) / 2.0;
- } else {
- radius = ns2d->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0;
- }
+ radius = ns2d->map_get_edge_connection_margin(map) / 2.0;
for (int i = 0; i < ns2d->region_get_connections_count(region); i++) {
// Two main points
@@ -568,31 +682,3 @@ void NavigationPolygonInstance::_update_debug_mesh() {
}
}
#endif // DEBUG_ENABLED
-
-#ifdef DEBUG_ENABLED
-void NavigationPolygonInstance::_update_debug_edge_connections_mesh() {
- const Navigation2DServer *ns2d = Navigation2DServer::get_singleton();
- const Navigation2DServer *ns = Navigation2DServer::get_singleton();
- bool enable_edge_connections = ns->get_debug_navigation_enable_edge_connections();
- Color debug_edge_connection_color = ns->get_debug_navigation_edge_connection_color();
-
- if (enable_edge_connections) {
- // Draw the region edge connections.
- Transform2D xform = get_global_transform();
- real_t radius = ns2d->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0;
- for (int i = 0; i < ns2d->region_get_connections_count(region); i++) {
- // Two main points
- Vector2 a = ns2d->region_get_connection_pathway_start(region, i);
- a = xform.affine_inverse().xform(a);
- Vector2 b = ns2d->region_get_connection_pathway_end(region, i);
- b = xform.affine_inverse().xform(b);
- draw_line(a, b, debug_edge_connection_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, debug_edge_connection_color);
- draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, debug_edge_connection_color);
- }
- }
-}
-#endif // DEBUG_ENABLED
\ No newline at end of file
diff --git a/scene/2d/navigation_polygon_instance.h b/scene/2d/navigation_polygon_instance.h
index 6d1b97ccd..e0f9b4fdb 100644
--- a/scene/2d/navigation_polygon_instance.h
+++ b/scene/2d/navigation_polygon_instance.h
@@ -57,7 +57,11 @@ class NavigationPolygonInstance : public Node2D {
Transform2D current_global_transform;
- void _navpoly_changed();
+ bool constrain_avoidance;
+ LocalVector constrain_avoidance_obstacles;
+ uint32_t avoidance_layers;
+
+ void _navigation_polygon_changed();
#ifdef DEBUG_ENABLED
void _update_debug_mesh();
@@ -68,6 +72,7 @@ class NavigationPolygonInstance : public Node2D {
protected:
void _notification(int p_what);
+ void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
public:
@@ -99,6 +104,15 @@ public:
void set_navigation_polygon(const Ref &p_navpoly);
Ref get_navigation_polygon() const;
+ void set_constrain_avoidance(bool p_enabled);
+ bool get_constrain_avoidance() const;
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
void set_navigation_map(RID p_navigation_map);
RID get_navigation_map() const;
@@ -109,6 +123,9 @@ public:
NavigationPolygonInstance();
~NavigationPolygonInstance();
+
+private:
+ void _update_avoidance_constrain();
};
#endif // NAVIGATIONPOLYGON_H
diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp
index 2d2d7fc9d..4a45e6c40 100644
--- a/scene/3d/navigation_agent.cpp
+++ b/scene/3d/navigation_agent.cpp
@@ -56,11 +56,14 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent::get_radius);
- ClassDB::bind_method(D_METHOD("set_agent_height_offset", "agent_height_offset"), &NavigationAgent::set_agent_height_offset);
- ClassDB::bind_method(D_METHOD("get_agent_height_offset"), &NavigationAgent::get_agent_height_offset);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NavigationAgent::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &NavigationAgent::get_height);
- ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent::set_ignore_y);
- ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent::get_ignore_y);
+ ClassDB::bind_method(D_METHOD("set_path_height_offset", "path_height_offset"), &NavigationAgent::set_path_height_offset);
+ ClassDB::bind_method(D_METHOD("get_path_height_offset"), &NavigationAgent::get_path_height_offset);
+
+ ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationAgent::set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationAgent::get_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent::set_navigation_node);
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent::get_navigation_node);
@@ -74,8 +77,11 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent::get_max_neighbors);
- ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent::set_time_horizon);
- ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent::get_time_horizon);
+ ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent::set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent::get_time_horizon_agents);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent::set_time_horizon_obstacles);
+ ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent::get_max_speed);
@@ -96,35 +102,58 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_position"), &NavigationAgent::get_next_position);
- ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
+
+ ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent::set_velocity_forced);
+
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
+
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent::get_current_navigation_result);
- ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent::get_nav_path);
- ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent::get_nav_path_index);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent::get_current_navigation_path);
+ ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent::is_target_reached);
ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent::is_target_reachable);
ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent::is_navigation_finished);
ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent::get_final_position);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent::get_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent::set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent::get_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent::get_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent::set_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent::get_avoidance_mask_value);
+ ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent::set_avoidance_priority);
+ ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent::get_avoidance_priority);
+
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent::_avoidance_done);
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_path_desired_distance", "get_path_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_target_desired_distance", "get_target_desired_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01"), "set_agent_height_offset", "get_agent_height_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,or_greater,suffix:m"), "set_path_height_offset", "get_path_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_time_horizon", "get_time_horizon");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.01,10000,0.01,or_greater,suffix:m/s"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent::set_debug_enabled);
@@ -176,6 +205,10 @@ void NavigationAgent::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
+ if (avoidance_enabled) {
+ NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ }
+
#ifdef DEBUG_ENABLED
if (NavigationServer::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
@@ -227,11 +260,23 @@ void NavigationAgent::_notification(int p_what) {
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (agent_parent) {
- 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);
+ if (agent_parent && target_position_submitted) {
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ if (!use_3d_avoidance) {
+ stored_y_velocity = velocity.y;
+ velocity.y = 0.0;
+ }
+ NavigationServer::get_singleton()->agent_set_velocity(agent, velocity);
+ }
+ }
+ if (velocity_forced_submitted) {
+ velocity_forced_submitted = false;
+ if (avoidance_enabled) {
+ NavigationServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
+ }
}
_check_distance_to_target();
}
@@ -250,24 +295,37 @@ NavigationAgent::NavigationAgent() {
navigation = nullptr;
avoidance_enabled = false;
+ use_3d_avoidance = false;
+ avoidance_layers = 1;
+ avoidance_mask = 1;
+ avoidance_priority = 1.0;
navigation_layers = 1;
path_metadata_flags = NavigationPathQueryParameters3D::PATH_METADATA_INCLUDE_ALL;
path_desired_distance = 1.0;
target_desired_distance = 1.0;
- navigation_height_offset = 0.0;
+ path_height_offset = 0.0;
path_max_distance = 3.0;
velocity_submitted = false;
target_reached = false;
+ time_horizon_agents = 1.0;
+ time_horizon_obstacles = 0.0;
navigation_finished = true;
+ velocity_forced_submitted = false;
+ stored_y_velocity = 0.0;
+
+ target_position_submitted = false;
+ height = 1.0;
+
agent = NavigationServer::get_singleton()->agent_create();
- set_neighbor_dist(0.0);
- set_max_neighbors(0);
- set_time_horizon(0.0);
- set_radius(1.0);
- set_max_speed(0.0);
- set_ignore_y(false);
+ NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
+ NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+ NavigationServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+ NavigationServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
+ NavigationServer::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer::get_singleton()->agent_set_height(agent, height);
+ NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
nav_path_index = 0;
update_frame_id = 0;
@@ -275,6 +333,12 @@ NavigationAgent::NavigationAgent() {
navigation_query.instance();
navigation_result.instance();
+ set_avoidance_layers(avoidance_layers);
+ set_avoidance_mask(avoidance_mask);
+ set_avoidance_priority(avoidance_priority);
+ set_use_3d_avoidance(use_3d_avoidance);
+ set_avoidance_enabled(avoidance_enabled);
+
#ifdef DEBUG_ENABLED
debug_enabled = false;
debug_path_dirty = true;
@@ -307,9 +371,11 @@ NavigationAgent::~NavigationAgent() {
void NavigationAgent::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
- NavigationServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
+ NavigationServer::get_singleton()->agent_set_avoidance_enabled(agent, true);
+ NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
- NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
+ NavigationServer::get_singleton()->agent_set_avoidance_enabled(agent, false);
+ NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
}
}
@@ -338,24 +404,17 @@ Node *NavigationAgent::get_navigation_node() const {
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, ObjectID(), "_avoidance_done");
+ NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
+
if (Object::cast_to(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(p_agent_parent);
- if (map_override.is_valid()) {
- NavigationServer::get_singleton()->agent_set_map(get_rid(), map_override);
- } else if (navigation != nullptr) {
- NavigationServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid());
- } else {
- // 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_3d()->get_navigation_map());
- }
-
+ NavigationServer::get_singleton()->agent_set_map(get_rid(), get_navigation_map());
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
agent_parent = nullptr;
- NavigationServer::get_singleton()->agent_set_map(get_rid(), RID());
+ NavigationServer::get_singleton()->agent_set_map(get_rid(), get_navigation_map());
}
}
@@ -403,7 +462,9 @@ void NavigationAgent::set_navigation_map(RID p_navigation_map) {
}
RID NavigationAgent::get_navigation_map() const {
- if (map_override.is_valid()) {
+ if (navigation) {
+ return navigation->get_rid();
+ } else if (map_override.is_valid()) {
return map_override;
} else if (agent_parent != nullptr) {
return agent_parent->get_world_3d()->get_navigation_map();
@@ -420,17 +481,29 @@ void NavigationAgent::set_target_desired_distance(real_t p_dd) {
}
void NavigationAgent::set_radius(real_t p_radius) {
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
radius = p_radius;
NavigationServer::get_singleton()->agent_set_radius(agent, radius);
}
-void NavigationAgent::set_agent_height_offset(real_t p_hh) {
- navigation_height_offset = p_hh;
+void NavigationAgent::set_height(real_t p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
+ if (Math::is_equal_approx(height, p_height)) {
+ return;
+ }
+
+ height = p_height;
+ NavigationServer::get_singleton()->agent_set_height(agent, height);
}
-void NavigationAgent::set_ignore_y(bool p_ignore_y) {
- ignore_y = p_ignore_y;
- NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y);
+void NavigationAgent::set_path_height_offset(real_t p_path_height_offset) {
+ path_height_offset = p_path_height_offset;
+}
+
+void NavigationAgent::set_use_3d_avoidance(bool p_use_3d_avoidance) {
+ use_3d_avoidance = p_use_3d_avoidance;
+ NavigationServer::get_singleton()->agent_set_use_3d_avoidance(agent, use_3d_avoidance);
+ property_list_changed_notify();
}
void NavigationAgent::set_neighbor_dist(real_t p_dist) {
@@ -443,12 +516,26 @@ void NavigationAgent::set_max_neighbors(int p_count) {
NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
-void NavigationAgent::set_time_horizon(real_t p_time) {
- time_horizon = p_time;
- NavigationServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+void NavigationAgent::set_time_horizon_agents(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
+ return;
+ }
+ time_horizon_agents = p_time_horizon;
+ NavigationServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
+}
+
+void NavigationAgent::set_time_horizon_obstacles(real_t p_time_horizon) {
+ ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
+ if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
+ return;
+ }
+ time_horizon_obstacles = p_time_horizon;
+ NavigationServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent::set_max_speed(real_t p_max_speed) {
+ ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
max_speed = p_max_speed;
NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
}
@@ -478,7 +565,7 @@ Vector3 NavigationAgent::get_next_position() {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
return agent_parent->get_global_transform().origin;
} else {
- return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
+ return navigation_path[nav_path_index] - Vector3(0, path_height_offset, 0);
}
}
@@ -486,7 +573,7 @@ Ref NavigationAgent::get_current_navigation_result(
return navigation_result;
}
-const Vector &NavigationAgent::get_nav_path() const {
+const Vector &NavigationAgent::get_current_navigation_path() const {
return navigation_result->get_path();
}
@@ -520,23 +607,28 @@ Vector3 NavigationAgent::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
-void NavigationAgent::set_velocity(Vector3 p_velocity) {
- target_velocity = p_velocity;
- NavigationServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
- NavigationServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+void NavigationAgent::set_velocity_forced(Vector3 p_velocity) {
+ // Intentionally not checking for equality of the parameter.
+ // We need to always submit the velocity to the navigation server, even when it is the same, in order to run avoidance every frame.
+ // Revisit later when the navigation server can update avoidance without users resubmitting the velocity.
+
+ velocity_forced = p_velocity;
+ velocity_forced_submitted = true;
+}
+
+void NavigationAgent::set_velocity(const Vector3 p_velocity) {
+ velocity = p_velocity;
velocity_submitted = true;
}
void NavigationAgent::_avoidance_done(Vector3 p_new_velocity) {
- prev_safe_velocity = p_new_velocity;
+ safe_velocity = p_new_velocity;
- if (!velocity_submitted) {
- target_velocity = Vector3();
- return;
+ if (!use_3d_avoidance) {
+ safe_velocity.y = stored_y_velocity;
}
- velocity_submitted = false;
- emit_signal("velocity_computed", p_new_velocity);
+ emit_signal("velocity_computed", safe_velocity);
}
String NavigationAgent::get_configuration_warning() const {
@@ -578,8 +670,8 @@ void NavigationAgent::update_navigation() {
Vector3 segment[2];
segment[0] = navigation_path[nav_path_index - 1];
segment[1] = navigation_path[nav_path_index];
- segment[0].y -= navigation_height_offset;
- segment[1].y -= navigation_height_offset;
+ segment[0].y -= path_height_offset;
+ segment[1].y -= path_height_offset;
Vector3 p = Geometry::get_closest_point_to_segment(origin, segment);
if (origin.distance_to(p) >= path_max_distance) {
@@ -594,14 +686,7 @@ void NavigationAgent::update_navigation() {
navigation_query->set_target_position(target_position);
navigation_query->set_navigation_layers(navigation_layers);
navigation_query->set_metadata_flags(path_metadata_flags);
-
- if (map_override.is_valid()) {
- navigation_query->set_map(map_override);
- } else if (navigation != nullptr) {
- navigation_query->set_map(navigation->get_rid());
- } else {
- navigation_query->set_map(agent_parent->get_world_3d()->get_navigation_map());
- }
+ navigation_query->set_map(get_navigation_map());
NavigationServer::get_singleton()->query_path(navigation_query, navigation_result);
@@ -626,7 +711,7 @@ void NavigationAgent::update_navigation() {
const Array &navigation_path_rids = navigation_result->get_path_rids();
const Vector &navigation_path_owners = navigation_result->get_path_owner_ids();
- while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) {
+ while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, path_height_offset, 0)) < path_desired_distance) {
Dictionary details;
const Vector3 waypoint = navigation_path[nav_path_index];
@@ -695,6 +780,71 @@ void NavigationAgent::update_navigation() {
}
}
+void NavigationAgent::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
+}
+
+uint32_t NavigationAgent::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationAgent::set_avoidance_mask(uint32_t p_mask) {
+ avoidance_mask = p_mask;
+ NavigationServer::get_singleton()->agent_set_avoidance_mask(get_rid(), avoidance_mask);
+}
+
+uint32_t NavigationAgent::get_avoidance_mask() const {
+ return avoidance_mask;
+}
+
+void NavigationAgent::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationAgent::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationAgent::set_avoidance_mask_value(int p_mask_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_avoidance_mask();
+ if (p_value) {
+ mask |= 1 << (p_mask_number - 1);
+ } else {
+ mask &= ~(1 << (p_mask_number - 1));
+ }
+ set_avoidance_mask(mask);
+}
+
+bool NavigationAgent::get_avoidance_mask_value(int p_mask_number) const {
+ ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
+ return get_avoidance_mask() & (1 << (p_mask_number - 1));
+}
+
+void NavigationAgent::set_avoidance_priority(real_t p_priority) {
+ ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
+ avoidance_priority = p_priority;
+ NavigationServer::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
+}
+
+real_t NavigationAgent::get_avoidance_priority() const {
+ return avoidance_priority;
+}
+
void NavigationAgent::_request_repath() {
navigation_result->reset();
target_reached = false;
diff --git a/scene/3d/navigation_agent.h b/scene/3d/navigation_agent.h
index c9bdc05bd..6471f1ce4 100644
--- a/scene/3d/navigation_agent.h
+++ b/scene/3d/navigation_agent.h
@@ -1,5 +1,6 @@
#ifndef NAVIGATION_AGENT_H
#define NAVIGATION_AGENT_H
+
/*************************************************************************/
/* navigation_agent.h */
/*************************************************************************/
@@ -50,29 +51,52 @@ class NavigationAgent : public Node {
RID map_override;
bool avoidance_enabled;
+ bool use_3d_avoidance;
+ uint32_t avoidance_layers;
+ uint32_t avoidance_mask;
+ real_t avoidance_priority;
uint32_t navigation_layers;
int path_metadata_flags;
real_t path_desired_distance;
real_t target_desired_distance;
+ real_t height;
real_t radius;
- real_t navigation_height_offset;
- bool ignore_y;
+ real_t path_height_offset;
real_t neighbor_dist;
int max_neighbors;
- real_t time_horizon;
+ real_t time_horizon_agents;
+ real_t time_horizon_obstacles;
real_t max_speed;
-
real_t path_max_distance;
Vector3 target_position;
+
Ref navigation_query;
Ref navigation_result;
int nav_path_index;
+
+ // the velocity result of the avoidance simulation step
+ Vector3 safe_velocity;
+
+ /// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
+ // this velocity is not guaranteed, the simulation will try to fulfil it if possible
+ // if other agents or obstacles interfere it will be changed accordingly
+ Vector3 velocity;
+
bool velocity_submitted;
- Vector3 prev_safe_velocity;
- /// The submitted target velocity
- Vector3 target_velocity;
+
+ /// The submitted forced velocity, overrides the rvo agent velocity on the next update
+ // should only be used very intentionally and not every frame as it interferes with the simulation stability
+ Vector3 velocity_forced;
+ bool velocity_forced_submitted;
+
+ // 2D avoidance has no y-axis. This stores and reapplies the y-axis velocity to the agent before and after the avoidance step.
+ // While not perfect it at least looks way better than agent's that clip through everything that is not a flat surface
+ float stored_y_velocity;
+
+ bool target_position_submitted;
+
bool target_reached;
bool navigation_finished;
// No initialized on purpose
@@ -88,10 +112,6 @@ class NavigationAgent : public Node {
Color debug_path_custom_color;
Ref debug_agent_path_line_custom_material;
Ref debug_agent_path_point_custom_material;
-
-private:
- void _navigation_debug_changed();
- void _update_debug_path();
#endif // DEBUG_ENABLED
protected:
@@ -125,6 +145,21 @@ public:
void set_navigation_layer_value(int p_layer_number, bool p_value);
bool get_navigation_layer_value(int p_layer_number) const;
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_mask(uint32_t p_mask);
+ uint32_t get_avoidance_mask() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_avoidance_mask_value(int p_mask_number, bool p_value);
+ bool get_avoidance_mask_value(int p_mask_number) const;
+
+ void set_avoidance_priority(real_t p_priority);
+ real_t get_avoidance_priority() const;
+
void set_path_metadata_flags(const int p_flags);
int get_path_metadata_flags() const;
@@ -146,15 +181,14 @@ public:
return radius;
}
- void set_agent_height_offset(real_t p_hh);
- real_t get_agent_height_offset() const {
- return navigation_height_offset;
- }
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
- void set_ignore_y(bool p_ignore_y);
- bool get_ignore_y() const {
- return ignore_y;
- }
+ void set_path_height_offset(real_t p_path_height_offset);
+ real_t get_path_height_offset() const { return path_height_offset; }
+
+ void set_use_3d_avoidance(bool p_use_3d_avoidance);
+ bool get_use_3d_avoidance() const { return use_3d_avoidance; }
void set_neighbor_dist(real_t p_dist);
real_t get_neighbor_dist() const {
@@ -166,10 +200,11 @@ public:
return max_neighbors;
}
- void set_time_horizon(real_t p_time);
- real_t get_time_horizon() const {
- return time_horizon;
- }
+ void set_time_horizon_agents(real_t p_time_horizon);
+ real_t get_time_horizon_agents() const { return time_horizon_agents; }
+
+ void set_time_horizon_obstacles(real_t p_time_horizon);
+ real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
real_t get_max_speed() const {
@@ -186,9 +221,9 @@ public:
Ref get_current_navigation_result() const;
- const Vector &get_nav_path() const;
+ const Vector &get_current_navigation_path() const;
- int get_nav_path_index() const {
+ int get_current_navigation_path_index() const {
return nav_path_index;
}
@@ -198,7 +233,11 @@ public:
bool is_navigation_finished();
Vector3 get_final_position();
- void set_velocity(Vector3 p_velocity);
+ void set_velocity(const Vector3 p_velocity);
+ Vector3 get_velocity() { return velocity; }
+
+ void set_velocity_forced(const Vector3 p_velocity);
+
void _avoidance_done(Vector3 p_new_velocity);
virtual String get_configuration_warning() const;
@@ -221,6 +260,11 @@ private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
+
+#ifdef DEBUG_ENABLED
+ void _navigation_debug_changed();
+ void _update_debug_path();
+#endif // DEBUG_ENABLED
};
#endif
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index ebd41a85a..e5b78a1da 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -48,7 +48,7 @@ void NavigationLink3D::_update_debug_mesh() {
return;
}
- if (!NavigationServer::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer::get_singleton()->get_debug_navigation_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
diff --git a/scene/3d/navigation_mesh_instance.cpp b/scene/3d/navigation_mesh_instance.cpp
index ba8891c72..cd7d57dc4 100644
--- a/scene/3d/navigation_mesh_instance.cpp
+++ b/scene/3d/navigation_mesh_instance.cpp
@@ -201,7 +201,7 @@ void NavigationMeshInstance::_notification(int p_what) {
NavigationServer::get_singleton()->region_set_transform(region, current_global_transform);
#ifdef DEBUG_ENABLED
- if (NavigationServer::get_singleton()->get_debug_enabled()) {
+ if (NavigationServer::get_singleton()->get_debug_navigation_enabled()) {
_update_debug_mesh();
}
#endif // DEBUG_ENABLED
@@ -320,7 +320,7 @@ void NavigationMeshInstance::_navigation_mesh_changed() {
emit_signal("navigation_mesh_changed");
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && NavigationServer::get_singleton()->get_debug_enabled()) {
+ if (is_inside_tree() && NavigationServer::get_singleton()->get_debug_navigation_enabled()) {
if (navmesh.is_valid()) {
_update_debug_mesh();
_update_debug_edge_connections_mesh();
@@ -467,7 +467,7 @@ NavigationMeshInstance::~NavigationMeshInstance() {
#ifdef DEBUG_ENABLED
void NavigationMeshInstance::_update_debug_mesh() {
- if (!NavigationServer::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer::get_singleton()->get_debug_navigation_enabled()) {
if (debug_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_instance, false);
}
@@ -633,7 +633,7 @@ void NavigationMeshInstance::_update_debug_mesh() {
#ifdef DEBUG_ENABLED
void NavigationMeshInstance::_update_debug_edge_connections_mesh() {
- if (!NavigationServer::get_singleton()->get_debug_enabled()) {
+ if (!NavigationServer::get_singleton()->get_debug_navigation_enabled()) {
if (debug_edge_connections_instance.is_valid()) {
RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
}
diff --git a/scene/3d/navigation_obstacle.cpp b/scene/3d/navigation_obstacle.cpp
index 6508f1bb4..e8bc0096a 100644
--- a/scene/3d/navigation_obstacle.cpp
+++ b/scene/3d/navigation_obstacle.cpp
@@ -30,41 +30,66 @@
#include "navigation_obstacle.h"
+#include "core/config/engine.h"
#include "scene/3d/collision_shape.h"
#include "scene/3d/navigation.h"
#include "scene/3d/physics_body.h"
#include "scene/3d/spatial.h"
+#include "scene/resources/material.h"
+#include "scene/resources/mesh.h"
#include "scene/resources/shape.h"
+#include "scene/resources/world_3d.h"
#include "servers/navigation_server.h"
+#include "servers/rendering_server.h"
void NavigationObstacle::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle::get_rid);
+ ClassDB::bind_method(D_METHOD("get_obstacle_rid"), &NavigationObstacle::get_obstacle_rid);
+ ClassDB::bind_method(D_METHOD("get_agent_rid"), &NavigationObstacle::get_agent_rid);
+
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle::get_navigation_map);
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle::set_navigation_node);
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle::get_navigation_node);
- ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle::is_radius_estimated);
- ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle::set_estimate_radius);
+
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_radius", "get_radius");
-}
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NavigationObstacle::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &NavigationObstacle::get_height);
-void NavigationObstacle::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "radius") {
- if (estimate_radius) {
- p_property.usage = PROPERTY_USAGE_NOEDITOR;
- }
- }
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle::get_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle::set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle::get_avoidance_layers);
+
+ ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle::set_avoidance_layer_value);
+ ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle::get_avoidance_layer_value);
+
+ ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationObstacle::set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationObstacle::get_use_3d_avoidance);
+
+#ifdef DEBUG_ENABLED
+ ClassDB::bind_method(D_METHOD("_update_fake_agent_radius_debug"), &NavigationObstacle::_update_fake_agent_radius_debug);
+ ClassDB::bind_method(D_METHOD("_update_static_obstacle_debug"), &NavigationObstacle::_update_static_obstacle_debug);
+#endif // DEBUG_ENABLED
+
+ ADD_GROUP("Avoidance", "avoidance_");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.0,100,0.01,suffix:m"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices"), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
}
void NavigationObstacle::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- parent_spatial = Object::cast_to(get_parent());
- reevaluate_agent_radius();
-
+ case NOTIFICATION_POST_ENTER_TREE: {
// Search the navigation node and set it
{
Navigation *nav = nullptr;
@@ -81,23 +106,38 @@ void NavigationObstacle::_notification(int p_what) {
set_navigation(nav);
}
+ _update_map(get_navigation_map());
+
+ previous_transform = get_global_transform();
+ // need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
+ NavigationServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+ _update_position(get_global_transform().origin);
+
set_physics_process_internal(true);
+
+#ifdef DEBUG_ENABLED
+ if ((NavigationServer::get_singleton()->get_debug_avoidance_enabled()) &&
+ (NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius())) {
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_PAUSED: {
- if (parent_spatial && !parent_spatial->can_process()) {
- map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid());
- NavigationServer::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_spatial && parent_spatial->can_process() && !(map_before_pause == RID())) {
- NavigationServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_UNPAUSED: {
- if (parent_spatial && !parent_spatial->can_process()) {
- map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid());
- NavigationServer::get_singleton()->agent_set_map(get_rid(), RID());
- } else if (parent_spatial && parent_spatial->can_process() && !(map_before_pause == RID())) {
- NavigationServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ if (!can_process()) {
+ map_before_pause = map_current;
+ _update_map(RID());
+ } else if (can_process() && !(map_before_pause == RID())) {
+ _update_map(map_before_pause);
map_before_pause = RID();
}
} break;
@@ -105,40 +145,109 @@ void NavigationObstacle::_notification(int p_what) {
set_navigation(nullptr);
set_physics_process_internal(false);
request_ready(); // required to solve an issue with losing the navigation
- } break;
- case NOTIFICATION_PARENTED: {
- parent_spatial = Object::cast_to(get_parent());
- reevaluate_agent_radius();
- } break;
- case NOTIFICATION_UNPARENTED: {
- parent_spatial = nullptr;
+
+ _update_map(RID());
+#ifdef DEBUG_ENABLED
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
+ }
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_spatial && parent_spatial->is_inside_tree()) {
- NavigationServer::get_singleton()->agent_set_position(agent, parent_spatial->get_global_transform().origin);
- }
+ if (is_inside_tree()) {
+ _update_position(get_global_transform().origin);
- PhysicsBody *rigid = Object::cast_to(get_parent());
- if (rigid) {
- Vector3 v = rigid->get_linear_velocity();
- NavigationServer::get_singleton()->agent_set_velocity(agent, v);
- NavigationServer::get_singleton()->agent_set_target_velocity(agent, v);
+ if (velocity_submitted) {
+ velocity_submitted = false;
+ // only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
+ if (!previous_velocity.is_equal_approx(velocity)) {
+ NavigationServer::get_singleton()->agent_set_velocity(fake_agent, velocity);
+ }
+ previous_velocity = velocity;
+ }
+#ifdef DEBUG_ENABLED
+ if (fake_agent_radius_debug_instance.is_valid() && radius > 0.0) {
+ RS::get_singleton()->instance_set_transform(fake_agent_radius_debug_instance, get_global_transform());
+ }
+ if (static_obstacle_debug_instance.is_valid() && get_vertices().size() > 0) {
+ RS::get_singleton()->instance_set_transform(static_obstacle_debug_instance, get_global_transform());
+ }
+#endif // DEBUG_ENABLED
}
} break;
}
}
-NavigationObstacle::NavigationObstacle() :
- navigation(nullptr),
- agent(RID()) {
- agent = NavigationServer::get_singleton()->agent_create();
- initialize_agent();
+NavigationObstacle::NavigationObstacle() {
+ navigation = nullptr;
+
+ height = 1.0;
+ radius = 0.0;
+
+ avoidance_layers = 1;
+
+ use_3d_avoidance = false;
+
+ velocity_submitted = false;
+
+ obstacle = NavigationServer::get_singleton()->obstacle_create();
+ fake_agent = NavigationServer::get_singleton()->agent_create();
+
+ // change properties of the fake agent so it can act as fake obstacle with a radius
+ NavigationServer::get_singleton()->agent_set_neighbor_dist(fake_agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_max_neighbors(fake_agent, 0);
+ NavigationServer::get_singleton()->agent_set_time_horizon_agents(fake_agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_time_horizon_obstacles(fake_agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_max_speed(fake_agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_avoidance_mask(fake_agent, 1);
+ NavigationServer::get_singleton()->agent_set_avoidance_priority(fake_agent, 1.0);
+ NavigationServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
+
+ set_radius(radius);
+ set_height(height);
+ set_vertices(vertices);
+ set_avoidance_layers(avoidance_layers);
+ set_use_3d_avoidance(use_3d_avoidance);
+
+#ifdef DEBUG_ENABLED
+ NavigationServer::get_singleton()->connect("avoidance_debug_changed", this, "_update_fake_agent_radius_debug");
+ NavigationServer::get_singleton()->connect("avoidance_debug_changed", this, "_update_static_obstacle_debug");
+ _update_fake_agent_radius_debug();
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
}
NavigationObstacle::~NavigationObstacle() {
- NavigationServer::get_singleton()->free(agent);
- agent = RID(); // Pointless
+ ERR_FAIL_NULL(NavigationServer::get_singleton());
+
+ NavigationServer::get_singleton()->free(obstacle);
+ obstacle = RID();
+
+ NavigationServer::get_singleton()->free(fake_agent);
+ fake_agent = RID();
+
+#ifdef DEBUG_ENABLED
+ NavigationServer::get_singleton()->disconnect("avoidance_debug_changed", this, "_update_fake_agent_radius_debug");
+ NavigationServer::get_singleton()->disconnect("avoidance_debug_changed", this, "_update_static_obstacle_debug");
+
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(fake_agent_radius_debug_instance);
+ }
+ if (fake_agent_radius_debug_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(fake_agent_radius_debug_mesh->get_rid());
+ }
+
+ if (static_obstacle_debug_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(static_obstacle_debug_instance);
+ }
+ if (static_obstacle_debug_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(static_obstacle_debug_mesh->get_rid());
+ }
+#endif // DEBUG_ENABLED
}
void NavigationObstacle::set_navigation(Navigation *p_nav) {
@@ -147,7 +256,7 @@ void NavigationObstacle::set_navigation(Navigation *p_nav) {
}
navigation = p_nav;
- NavigationServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
+ NavigationServer::get_singleton()->agent_set_map(fake_agent, get_navigation_map());
}
void NavigationObstacle::set_navigation_node(Node *p_nav) {
@@ -160,75 +269,347 @@ Node *NavigationObstacle::get_navigation_node() const {
return Object::cast_to(navigation);
}
-String NavigationObstacle::get_configuration_warning() const {
- if (!Object::cast_to(get_parent())) {
- return TTR("The NavigationObstacle only serves to provide collision avoidance to a Spatial inheriting parent object.");
+void NavigationObstacle::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
}
-
- if (Object::cast_to(get_parent())) {
- return TTR("The NavigationObstacle is intended for constantly moving bodies like KinematicBody3D or RigidBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
- "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail");
- }
-
- return String();
+ map_override = p_navigation_map;
+ _update_map(get_navigation_map());
}
-void NavigationObstacle::initialize_agent() {
- NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
- NavigationServer::get_singleton()->agent_set_max_neighbors(agent, 0);
- NavigationServer::get_singleton()->agent_set_time_horizon(agent, 0.0);
- NavigationServer::get_singleton()->agent_set_max_speed(agent, 0.0);
-}
-
-void NavigationObstacle::reevaluate_agent_radius() {
- if (!estimate_radius) {
- NavigationServer::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_spatial && parent_spatial->is_inside_tree()) {
- NavigationServer::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
+RID NavigationObstacle::get_navigation_map() const {
+ if (navigation) {
+ navigation->get_rid();
+ } else if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_3d()->get_navigation_map();
}
+ return RID();
}
-real_t NavigationObstacle::estimate_agent_radius() const {
- if (parent_spatial && parent_spatial->is_inside_tree()) {
- // Estimate the radius of this physics body
- real_t radius = 0.0;
- for (int i(0); i < parent_spatial->get_child_count(); i++) {
- // For each collision shape
- CollisionShape *cs = Object::cast_to(parent_spatial->get_child(i));
- if (cs && cs->is_inside_tree()) {
- // Take the distance between the Body center to the shape center
- real_t r = cs->get_transform().origin.length();
- if (cs->get_shape().is_valid()) {
- // and add the enclosing shape radius
- r += cs->get_shape()->get_enclosing_radius();
- }
- Vector3 s = cs->get_global_transform().basis.get_scale();
- r *= MAX(s.x, MAX(s.y, s.z));
- // Takes the biggest radius
- radius = MAX(radius, r);
- } else if (cs && !cs->is_inside_tree()) {
- WARN_PRINT("A CollisionShape of the NavigationObstacle parent node was not inside the SceneTree when estimating the obstacle radius."
- "\nMove the NavigationObstacle to a child position below any CollisionShape node of the parent node so the CollisionShape is already inside the SceneTree.");
- }
- }
- Vector3 s = parent_spatial->get_global_transform().basis.get_scale();
- radius *= MAX(s.x, MAX(s.y, s.z));
-
- if (radius > 0.0) {
- return radius;
- }
- }
- return 1.0; // Never a 0 radius
-}
-
-void NavigationObstacle::set_estimate_radius(bool p_estimate_radius) {
- estimate_radius = p_estimate_radius;
- _change_notify();
- reevaluate_agent_radius();
+void NavigationObstacle::set_vertices(const Vector &p_vertices) {
+ vertices = p_vertices;
+ NavigationServer::get_singleton()->obstacle_set_vertices(obstacle, vertices);
+#ifdef DEBUG_ENABLED
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
}
void NavigationObstacle::set_radius(real_t p_radius) {
- ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
+ ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
+ if (Math::is_equal_approx(radius, p_radius)) {
+ return;
+ }
+
radius = p_radius;
- reevaluate_agent_radius();
+ NavigationServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0.0);
+ NavigationServer::get_singleton()->agent_set_radius(fake_agent, radius);
+
+#ifdef DEBUG_ENABLED
+ _update_fake_agent_radius_debug();
+#endif // DEBUG_ENABLED
}
+
+void NavigationObstacle::set_height(real_t p_height) {
+ ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
+ if (Math::is_equal_approx(height, p_height)) {
+ return;
+ }
+
+ height = p_height;
+ NavigationServer::get_singleton()->obstacle_set_height(obstacle, height);
+ NavigationServer::get_singleton()->agent_set_height(fake_agent, height);
+#ifdef DEBUG_ENABLED
+ _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
+}
+
+void NavigationObstacle::set_avoidance_layers(uint32_t p_layers) {
+ avoidance_layers = p_layers;
+ NavigationServer::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
+ NavigationServer::get_singleton()->agent_set_avoidance_layers(fake_agent, avoidance_layers);
+}
+
+uint32_t NavigationObstacle::get_avoidance_layers() const {
+ return avoidance_layers;
+}
+
+void NavigationObstacle::set_avoidance_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
+ uint32_t avoidance_layers_new = get_avoidance_layers();
+ if (p_value) {
+ avoidance_layers_new |= 1 << (p_layer_number - 1);
+ } else {
+ avoidance_layers_new &= ~(1 << (p_layer_number - 1));
+ }
+ set_avoidance_layers(avoidance_layers_new);
+}
+
+bool NavigationObstacle::get_avoidance_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
+ return get_avoidance_layers() & (1 << (p_layer_number - 1));
+}
+
+void NavigationObstacle::set_use_3d_avoidance(bool p_use_3d_avoidance) {
+ use_3d_avoidance = p_use_3d_avoidance;
+ _update_use_3d_avoidance(use_3d_avoidance);
+ property_list_changed_notify();
+}
+
+void NavigationObstacle::set_velocity(const Vector3 p_velocity) {
+ velocity = p_velocity;
+ velocity_submitted = true;
+}
+
+void NavigationObstacle::_update_map(RID p_map) {
+ // avoidance obstacles are 2D only, no point keeping them on the map while 3D is used
+ if (use_3d_avoidance) {
+ NavigationServer::get_singleton()->obstacle_set_map(obstacle, RID());
+ } else {
+ NavigationServer::get_singleton()->obstacle_set_map(obstacle, p_map);
+ }
+ NavigationServer::get_singleton()->agent_set_map(fake_agent, p_map);
+ map_current = p_map;
+}
+
+void NavigationObstacle::_update_position(const Vector3 p_position) {
+ if (vertices.size() > 0) {
+ NavigationServer::get_singleton()->obstacle_set_position(obstacle, p_position);
+ }
+
+ if (radius > 0.0) {
+ NavigationServer::get_singleton()->agent_set_position(fake_agent, p_position);
+ }
+}
+
+void NavigationObstacle::_update_use_3d_avoidance(bool p_use_3d_avoidance) {
+ NavigationServer::get_singleton()->agent_set_use_3d_avoidance(fake_agent, use_3d_avoidance);
+ _update_map(map_current);
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationObstacle::_update_fake_agent_radius_debug() {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (NavigationServer::get_singleton()->get_debug_enabled() &&
+ NavigationServer::get_singleton()->get_debug_avoidance_enabled() &&
+ NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
+ is_debug_enabled = true;
+ }
+
+ if (is_debug_enabled == false) {
+ if (fake_agent_radius_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, false);
+ }
+ return;
+ }
+
+ if (!fake_agent_radius_debug_instance.is_valid()) {
+ fake_agent_radius_debug_instance = RenderingServer::get_singleton()->instance_create();
+ }
+ if (!fake_agent_radius_debug_mesh.is_valid()) {
+ fake_agent_radius_debug_mesh = Ref(memnew(ArrayMesh));
+ }
+ fake_agent_radius_debug_mesh->clear_surfaces();
+
+ Vector face_vertex_array;
+ Vector face_indices_array;
+
+ int i, j, prevrow, thisrow, point;
+ float x, y, z;
+
+ int rings = 16;
+ int radial_segments = 32;
+
+ point = 0;
+
+ thisrow = 0;
+ prevrow = 0;
+ for (j = 0; j <= (rings + 1); j++) {
+ float v = j;
+ float w;
+
+ v /= (rings + 1);
+ w = sin(Math_PI * v);
+ y = (radius)*cos(Math_PI * v);
+
+ for (i = 0; i <= radial_segments; i++) {
+ float u = i;
+ u /= radial_segments;
+
+ x = sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
+
+ Vector3 p = Vector3(x * radius * w, y, z * radius * w);
+ face_vertex_array.push_back(p);
+
+ point++;
+
+ if (i > 0 && j > 0) {
+ face_indices_array.push_back(prevrow + i - 1);
+ face_indices_array.push_back(prevrow + i);
+ face_indices_array.push_back(thisrow + i - 1);
+
+ face_indices_array.push_back(prevrow + i);
+ face_indices_array.push_back(thisrow + i);
+ face_indices_array.push_back(thisrow + i - 1);
+ };
+ };
+
+ prevrow = thisrow;
+ thisrow = point;
+ };
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
+
+ fake_agent_radius_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+ Ref face_material = NavigationServer::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_material();
+ fake_agent_radius_debug_mesh->surface_set_material(0, face_material);
+
+ RS::get_singleton()->instance_set_base(fake_agent_radius_debug_instance, fake_agent_radius_debug_mesh->get_rid());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(fake_agent_radius_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(fake_agent_radius_debug_instance, is_visible_in_tree());
+ }
+}
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+void NavigationObstacle::_update_static_obstacle_debug() {
+ bool is_debug_enabled = false;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ is_debug_enabled = true;
+ } else if (NavigationServer::get_singleton()->get_debug_enabled() &&
+ NavigationServer::get_singleton()->get_debug_avoidance_enabled() &&
+ NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
+ is_debug_enabled = true;
+ }
+
+ if (is_debug_enabled == false) {
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
+ return;
+ }
+
+ if (vertices.size() < 3) {
+ if (static_obstacle_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, false);
+ }
+ return;
+ }
+
+ if (!static_obstacle_debug_instance.is_valid()) {
+ static_obstacle_debug_instance = RenderingServer::get_singleton()->instance_create();
+ }
+ if (!static_obstacle_debug_mesh.is_valid()) {
+ static_obstacle_debug_mesh = Ref(memnew(ArrayMesh));
+ }
+ static_obstacle_debug_mesh->clear_surfaces();
+
+ Vector polygon_2d_vertices;
+ polygon_2d_vertices.resize(vertices.size());
+ Vector2 *polygon_2d_vertices_ptr = polygon_2d_vertices.ptrw();
+
+ for (int i = 0; i < vertices.size(); ++i) {
+ Vector3 obstacle_vertex = vertices[i];
+ Vector2 obstacle_vertex_2d = Vector2(obstacle_vertex.x, obstacle_vertex.z);
+ polygon_2d_vertices_ptr[i] = obstacle_vertex_2d;
+ }
+
+ Vector triangulated_polygon_2d_indices = Geometry::triangulate_polygon(polygon_2d_vertices);
+
+ if (triangulated_polygon_2d_indices.empty()) {
+ // failed triangulation
+ return;
+ }
+
+ bool obstacle_pushes_inward = Geometry::is_polygon_clockwise(polygon_2d_vertices);
+
+ Vector face_vertex_array;
+ Vector face_indices_array;
+
+ face_vertex_array.resize(polygon_2d_vertices.size());
+ face_indices_array.resize(triangulated_polygon_2d_indices.size());
+
+ Vector3 *face_vertex_array_ptr = face_vertex_array.ptrw();
+ int *face_indices_array_ptr = face_indices_array.ptrw();
+
+ for (int i = 0; i < triangulated_polygon_2d_indices.size(); ++i) {
+ int vertex_index = triangulated_polygon_2d_indices[i];
+ const Vector2 &vertex_2d = polygon_2d_vertices[vertex_index];
+ Vector3 vertex_3d = Vector3(vertex_2d.x, 0.0, vertex_2d.y);
+ face_vertex_array_ptr[vertex_index] = vertex_3d;
+ face_indices_array_ptr[i] = vertex_index;
+ }
+
+ Array face_mesh_array;
+ face_mesh_array.resize(Mesh::ARRAY_MAX);
+ face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
+ face_mesh_array[Mesh::ARRAY_INDEX] = face_indices_array;
+
+ static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
+
+ Vector edge_vertex_array;
+
+ for (int i = 0; i < polygon_2d_vertices.size(); ++i) {
+ int from_index = i - 1;
+ int to_index = i;
+
+ if (i == 0) {
+ from_index = polygon_2d_vertices.size() - 1;
+ }
+
+ const Vector2 &vertex_2d_from = polygon_2d_vertices[from_index];
+ const Vector2 &vertex_2d_to = polygon_2d_vertices[to_index];
+
+ Vector3 vertex_3d_ground_from = Vector3(vertex_2d_from.x, 0.0, vertex_2d_from.y);
+ Vector3 vertex_3d_ground_to = Vector3(vertex_2d_to.x, 0.0, vertex_2d_to.y);
+
+ edge_vertex_array.push_back(vertex_3d_ground_from);
+ edge_vertex_array.push_back(vertex_3d_ground_to);
+
+ Vector3 vertex_3d_height_from = Vector3(vertex_2d_from.x, height, vertex_2d_from.y);
+ Vector3 vertex_3d_height_to = Vector3(vertex_2d_to.x, height, vertex_2d_to.y);
+
+ edge_vertex_array.push_back(vertex_3d_height_from);
+ edge_vertex_array.push_back(vertex_3d_height_to);
+
+ edge_vertex_array.push_back(vertex_3d_ground_from);
+ edge_vertex_array.push_back(vertex_3d_height_from);
+ }
+
+ Array edge_mesh_array;
+ edge_mesh_array.resize(Mesh::ARRAY_MAX);
+ edge_mesh_array[Mesh::ARRAY_VERTEX] = edge_vertex_array;
+
+ static_obstacle_debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, edge_mesh_array);
+
+ Ref face_material;
+ Ref edge_material;
+
+ if (obstacle_pushes_inward) {
+ face_material = NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
+ edge_material = NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
+ } else {
+ face_material = NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
+ edge_material = NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
+ }
+
+ static_obstacle_debug_mesh->surface_set_material(0, face_material);
+ static_obstacle_debug_mesh->surface_set_material(1, edge_material);
+
+ RS::get_singleton()->instance_set_base(static_obstacle_debug_instance, static_obstacle_debug_mesh->get_rid());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(static_obstacle_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(static_obstacle_debug_instance, is_visible_in_tree());
+ }
+}
+#endif // DEBUG_ENABLED
diff --git a/scene/3d/navigation_obstacle.h b/scene/3d/navigation_obstacle.h
index f199794aa..2e16dcf87 100644
--- a/scene/3d/navigation_obstacle.h
+++ b/scene/3d/navigation_obstacle.h
@@ -1,5 +1,6 @@
#ifndef NAVIGATION_OBSTACLE_H
#define NAVIGATION_OBSTACLE_H
+
/*************************************************************************/
/* navigation_obstacle.h */
/*************************************************************************/
@@ -30,26 +31,51 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "scene/main/node.h"
+#include "scene/3d/spatial.h"
class Navigation;
class Spatial;
-class NavigationObstacle : public Node {
- GDCLASS(NavigationObstacle, Node);
+class NavigationObstacle : public Spatial {
+ GDCLASS(NavigationObstacle, Spatial);
- Spatial *parent_spatial = nullptr;
Navigation *navigation;
- RID agent;
+ RID obstacle;
RID map_before_pause;
+ RID map_override;
+ RID map_current;
- bool estimate_radius = true;
- real_t radius = 1.0;
+ real_t height;
+ real_t radius;
+
+ Vector vertices;
+
+ RID fake_agent;
+ uint32_t avoidance_layers;
+
+ bool use_3d_avoidance;
+
+ Transform previous_transform;
+
+ Vector3 velocity;
+ Vector3 previous_velocity;
+ bool velocity_submitted;
+
+#ifdef DEBUG_ENABLED
+ RID fake_agent_radius_debug_instance;
+ Ref fake_agent_radius_debug_mesh;
+
+ RID static_obstacle_debug_instance;
+ Ref static_obstacle_debug_mesh;
+
+private:
+ void _update_fake_agent_radius_debug();
+ void _update_static_obstacle_debug();
+#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
- void _validate_property(PropertyInfo &property) const;
void _notification(int p_what);
public:
@@ -64,25 +90,41 @@ public:
void set_navigation_node(Node *p_nav);
Node *get_navigation_node() const;
- RID get_rid() const {
- return agent;
- }
+ RID get_obstacle_rid() const { return obstacle; }
+ RID get_agent_rid() const { return fake_agent; }
+
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
- void set_estimate_radius(bool p_estimate_radius);
- bool is_radius_estimated() const {
- return estimate_radius;
- }
void set_radius(real_t p_radius);
real_t get_radius() const {
return radius;
}
- virtual String get_configuration_warning() const;
+ void set_height(real_t p_height);
+ real_t get_height() const { return height; }
+
+ void set_vertices(const Vector &p_vertices);
+ const Vector &get_vertices() const { return vertices; };
+
+ void set_avoidance_layers(uint32_t p_layers);
+ uint32_t get_avoidance_layers() const;
+
+ void set_avoidance_layer_value(int p_layer_number, bool p_value);
+ bool get_avoidance_layer_value(int p_layer_number) const;
+
+ void set_use_3d_avoidance(bool p_use_3d_avoidance);
+ bool get_use_3d_avoidance() const { return use_3d_avoidance; }
+
+ void set_velocity(const Vector3 p_velocity);
+ Vector3 get_velocity() const { return velocity; };
+
+ void _avoidance_done(Vector3 p_new_velocity); // Dummy
private:
- void initialize_agent();
- void reevaluate_agent_radius();
- real_t estimate_agent_radius() const;
+ void _update_map(RID p_map);
+ void _update_position(const Vector3 p_position);
+ void _update_use_3d_avoidance(bool p_use_3d_avoidance);
};
#endif
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 34e97bd62..a6d862774 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2936,7 +2936,7 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_NONE, "23/19:RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "RichTextEffect"), "set_effects", "get_effects");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_NONE, "23/20:RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "RichTextEffect"), "set_effects", "get_effects");
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 259e40e70..da05414c1 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -929,6 +929,14 @@ void SceneTree::set_debug_navigation_hint(bool p_enabled) {
bool SceneTree::is_debugging_navigation_hint() const {
return debug_navigation_hint;
}
+
+void SceneTree::set_debug_avoidance_hint(bool p_enabled) {
+ debug_avoidance_hint = p_enabled;
+}
+
+bool SceneTree::is_debugging_avoidance_hint() const {
+ return debug_avoidance_hint;
+}
#endif
void SceneTree::set_debug_collisions_color(const Color &p_color) {
@@ -2031,6 +2039,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_debugging_collisions_hint"), &SceneTree::is_debugging_collisions_hint);
ClassDB::bind_method(D_METHOD("set_debug_navigation_hint", "enable"), &SceneTree::set_debug_navigation_hint);
ClassDB::bind_method(D_METHOD("is_debugging_navigation_hint"), &SceneTree::is_debugging_navigation_hint);
+ ClassDB::bind_method(D_METHOD("set_debug_avoidance_hint", "enable"), &SceneTree::set_debug_avoidance_hint);
+ ClassDB::bind_method(D_METHOD("is_debugging_avoidance_hint"), &SceneTree::is_debugging_avoidance_hint);
ClassDB::bind_method(D_METHOD("set_edited_scene_root", "scene"), &SceneTree::set_edited_scene_root);
ClassDB::bind_method(D_METHOD("get_edited_scene_root"), &SceneTree::get_edited_scene_root);
@@ -2232,6 +2242,7 @@ SceneTree::SceneTree() {
#ifdef DEBUG_ENABLED
debug_collisions_hint = false;
debug_navigation_hint = false;
+ debug_avoidance_hint = false;
#endif
debug_collisions_color = GLOBAL_DEF("debug/shapes/collision/shape_color", Color(0.0, 0.6, 0.7, 0.42));
debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8));
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 59b6d7e78..8e04b3e22 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -30,10 +30,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#include "core/containers/self_list.h"
#include "core/io/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
-#include "core/containers/self_list.h"
class PackedScene;
class Node;
@@ -118,6 +118,7 @@ private:
#ifdef DEBUG_ENABLED
bool debug_collisions_hint;
bool debug_navigation_hint;
+ bool debug_avoidance_hint;
#endif
bool pause;
int root_lock;
@@ -351,6 +352,9 @@ public:
void set_debug_navigation_hint(bool p_enabled);
bool is_debugging_navigation_hint() const;
+
+ void set_debug_avoidance_hint(bool p_enabled);
+ bool is_debugging_avoidance_hint() const;
#else
void set_debug_collisions_hint(bool p_enabled) {}
bool is_debugging_collisions_hint() const {
@@ -361,6 +365,11 @@ public:
bool is_debugging_navigation_hint() const {
return false;
}
+
+ void set_debug_avoidance_hint(bool p_enabled) {}
+ bool is_debugging_avoidance_hint() const {
+ return false;
+ }
#endif
void set_debug_collisions_color(const Color &p_color);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index aaeb589a9..ace3d023d 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -733,6 +733,10 @@ void register_scene_types() {
GLOBAL_DEF("layer_names/2d_navigation/layer_" + itos(i + 1), "");
GLOBAL_DEF("layer_names/3d_navigation/layer_" + itos(i + 1), "");
}
+
+ for (int i = 0; i < 32; i++) {
+ GLOBAL_DEF("layer_names/avoidance/layer_" + itos(i + 1), "");
+ }
}
void initialize_theme() {
diff --git a/servers/navigation_2d_server.cpp b/servers/navigation_2d_server.cpp
index 3905396bc..dcec06fae 100644
--- a/servers/navigation_2d_server.cpp
+++ b/servers/navigation_2d_server.cpp
@@ -60,6 +60,7 @@ void Navigation2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &Navigation2DServer::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &Navigation2DServer::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &Navigation2DServer::map_get_agents);
+ ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &Navigation2DServer::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &Navigation2DServer::map_force_update);
ClassDB::bind_method(D_METHOD("region_create"), &Navigation2DServer::region_create);
@@ -101,18 +102,31 @@ void Navigation2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &Navigation2DServer::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &Navigation2DServer::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &Navigation2DServer::agent_set_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &Navigation2DServer::agent_get_avoidance_enabled);
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &Navigation2DServer::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &Navigation2DServer::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_dist", "agent", "dist"), &Navigation2DServer::agent_set_neighbor_dist);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &Navigation2DServer::agent_set_max_neighbors);
- ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &Navigation2DServer::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &Navigation2DServer::agent_set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &Navigation2DServer::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &Navigation2DServer::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &Navigation2DServer::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &Navigation2DServer::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &Navigation2DServer::agent_set_velocity);
- ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &Navigation2DServer::agent_set_target_velocity);
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &Navigation2DServer::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &Navigation2DServer::agent_is_map_changed);
- ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "object_id", "method", "userdata"), &Navigation2DServer::agent_set_avoidance_callback, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &Navigation2DServer::agent_set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &Navigation2DServer::agent_set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &Navigation2DServer::agent_set_avoidance_priority);
+
+ ClassDB::bind_method(D_METHOD("obstacle_create"), &Navigation2DServer::obstacle_create);
+ ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &Navigation2DServer::obstacle_set_map);
+ ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &Navigation2DServer::obstacle_get_map);
+ ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &Navigation2DServer::obstacle_set_position);
+ ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &Navigation2DServer::obstacle_set_vertices);
+ ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &Navigation2DServer::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &Navigation2DServer::free);
@@ -164,6 +178,22 @@ bool Navigation2DServer::get_debug_enabled() const {
}
#ifdef DEBUG_ENABLED
+void Navigation2DServer::set_debug_navigation_enabled(bool p_enabled) {
+ NavigationServer::get_singleton()->set_debug_navigation_enabled(p_enabled);
+}
+
+bool Navigation2DServer::get_debug_navigation_enabled() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_enabled();
+}
+
+void Navigation2DServer::set_debug_avoidance_enabled(bool p_enabled) {
+ NavigationServer::get_singleton()->set_debug_avoidance_enabled(p_enabled);
+}
+
+bool Navigation2DServer::get_debug_avoidance_enabled() const {
+ return NavigationServer::get_singleton()->get_debug_avoidance_enabled();
+}
+
void Navigation2DServer::set_debug_navigation_edge_connection_color(const Color &p_color) {
NavigationServer::get_singleton()->set_debug_navigation_edge_connection_color(p_color);
}
@@ -266,6 +296,78 @@ float Navigation2DServer::get_debug_navigation_agent_path_point_size() const {
return NavigationServer::get_singleton()->get_debug_navigation_agent_path_point_size();
}
+void Navigation2DServer::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_enable_agents_radius(p_value);
+}
+
+bool Navigation2DServer::get_debug_navigation_avoidance_enable_agents_radius() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_agents_radius();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_radius(p_value);
+}
+
+bool Navigation2DServer::get_debug_navigation_avoidance_enable_obstacles_radius() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_agents_radius_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_agents_radius_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_agents_radius_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_obstacles_radius_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_obstacles_radius_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_face_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_face_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(p_color);
+}
+
+Color Navigation2DServer::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
+}
+
+void Navigation2DServer::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
+ NavigationServer::get_singleton()->set_debug_navigation_avoidance_enable_obstacles_static(p_value);
+}
+
+bool Navigation2DServer::get_debug_navigation_avoidance_enable_obstacles_static() const {
+ return NavigationServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static();
+}
+
#endif
Vector Navigation2DServerManager::navigation_servers;
diff --git a/servers/navigation_2d_server.h b/servers/navigation_2d_server.h
index aa33651df..abf8562c1 100644
--- a/servers/navigation_2d_server.h
+++ b/servers/navigation_2d_server.h
@@ -98,6 +98,7 @@ public:
virtual Array map_get_links(RID p_map) const = 0;
virtual Array map_get_regions(RID p_map) const = 0;
virtual Array map_get_agents(RID p_map) const = 0;
+ virtual Array map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map) = 0;
@@ -182,6 +183,9 @@ public:
virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
+
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
@@ -207,7 +211,8 @@ public:
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time) = 0;
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
/// The radius of this agent.
/// Must be non-negative.
@@ -217,23 +222,33 @@ public:
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
- /// Current velocity of the agent
- virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0;
+ /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
+ virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0;
- /// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) = 0;
+ /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
+ /// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
+ virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0;
- /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) = 0;
-
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process
- virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0;
+ virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0;
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create() = 0;
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
+ virtual RID obstacle_get_map(RID p_obstacle) const = 0;
+ virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) = 0;
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) = 0;
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
/// Destroy the `RID`
virtual void free(RID p_object) = 0;
@@ -252,6 +267,12 @@ public:
bool get_debug_enabled() const;
#ifdef DEBUG_ENABLED
+ void set_debug_navigation_enabled(bool p_enabled);
+ bool get_debug_navigation_enabled() const;
+
+ void set_debug_avoidance_enabled(bool p_enabled);
+ bool get_debug_avoidance_enabled() const;
+
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
@@ -291,6 +312,33 @@ public:
void set_debug_navigation_agent_path_point_size(float p_point_size);
float get_debug_navigation_agent_path_point_size() const;
+ void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_agents_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
+
+ void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_agents_radius_color() const;
+
+ void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_obstacles_radius_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_static() const;
+
private:
void _emit_navigation_debug_changed_signal();
#endif
diff --git a/servers/navigation_server.cpp b/servers/navigation_server.cpp
index abdbda0ff..dfc0112b0 100644
--- a/servers/navigation_server.cpp
+++ b/servers/navigation_server.cpp
@@ -74,6 +74,7 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer::map_get_links);
ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer::map_get_regions);
ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer::map_get_agents);
+ ClassDB::bind_method(D_METHOD("map_get_obstacles", "map"), &NavigationServer::map_get_obstacles);
ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer::map_force_update);
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer::query_path);
@@ -120,18 +121,37 @@ void NavigationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_get_owner_id", "link"), &NavigationServer::link_get_owner_id);
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_enabled", "agent", "enabled"), &NavigationServer::agent_set_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_get_avoidance_enabled", "agent"), &NavigationServer::agent_get_avoidance_enabled);
+ ClassDB::bind_method(D_METHOD("agent_set_use_3d_avoidance", "agent", "enabled"), &NavigationServer::agent_set_use_3d_avoidance);
+ ClassDB::bind_method(D_METHOD("agent_get_use_3d_avoidance", "agent"), &NavigationServer::agent_get_use_3d_avoidance);
+
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer::agent_set_map);
ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer::agent_get_map);
ClassDB::bind_method(D_METHOD("agent_set_neighbor_dist", "agent", "dist"), &NavigationServer::agent_set_neighbor_dist);
ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer::agent_set_max_neighbors);
- ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &NavigationServer::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer::agent_set_time_horizon_agents);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer::agent_set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer::agent_set_radius);
+ ClassDB::bind_method(D_METHOD("agent_set_height", "agent", "height"), &NavigationServer::agent_set_height);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer::agent_set_velocity_forced);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer::agent_set_velocity);
- ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer::agent_set_target_velocity);
+
ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer::agent_set_position);
ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer::agent_is_map_changed);
- ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &NavigationServer::agent_set_callback, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "object_id", "method", "userdata"), &NavigationServer::agent_set_avoidance_callback, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer::agent_set_avoidance_layers);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer::agent_set_avoidance_mask);
+ ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer::agent_set_avoidance_priority);
+
+ ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer::obstacle_create);
+ ClassDB::bind_method(D_METHOD("obstacle_set_map", "obstacle", "map"), &NavigationServer::obstacle_set_map);
+ ClassDB::bind_method(D_METHOD("obstacle_get_map", "obstacle"), &NavigationServer::obstacle_get_map);
+ ClassDB::bind_method(D_METHOD("obstacle_set_height", "obstacle", "height"), &NavigationServer::obstacle_set_height);
+ ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer::obstacle_set_position);
+ ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer::obstacle_set_vertices);
+ ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer::free);
@@ -145,6 +165,7 @@ void NavigationServer::_bind_methods() {
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
ADD_SIGNAL(MethodInfo("navigation_debug_changed"));
+ ADD_SIGNAL(MethodInfo("avoidance_debug_changed"));
BIND_ENUM_CONSTANT(INFO_ACTIVE_MAPS);
BIND_ENUM_CONSTANT(INFO_REGION_COUNT);
@@ -156,8 +177,12 @@ void NavigationServer::_bind_methods() {
BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT);
+ GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
+ GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
+
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("_emit_navigation_debug_changed_signal"), &NavigationServer::_emit_navigation_debug_changed_signal);
+ ClassDB::bind_method(D_METHOD("_emit_avoidance_debug_changed_signal"), &NavigationServer::_emit_avoidance_debug_changed_signal);
#endif // DEBUG_ENABLED
}
@@ -175,6 +200,12 @@ NavigationServer::NavigationServer() {
_debug_enabled = true;
_debug_dirty = true;
+ _debug_navigation_enabled = false;
+ _navigation_debug_dirty = true;
+
+ _debug_avoidance_enabled = false;
+ _avoidance_debug_dirty = true;
+
_debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
_debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
_debug_navigation_geometry_face_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_color", Color(0.5, 1.0, 1.0, 0.4));
@@ -197,10 +228,22 @@ NavigationServer::NavigationServer() {
_debug_navigation_agent_path_point_size = GLOBAL_DEF("debug/shapes/navigation/agent_path_point_size", 4.0);
+ _debug_navigation_avoidance_agents_radius_color = GLOBAL_DEF("debug/shapes/avoidance/agents_radius_color", Color(1.0, 1.0, 0.0, 0.25));
+ _debug_navigation_avoidance_obstacles_radius_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_radius_color", Color(1.0, 0.5, 0.0, 0.25));
+ _debug_navigation_avoidance_static_obstacle_pushin_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushin_color", Color(1.0, 0.0, 0.0, 0.0));
+ _debug_navigation_avoidance_static_obstacle_pushin_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushin_color", Color(1.0, 0.0, 0.0, 1.0));
+ _debug_navigation_avoidance_static_obstacle_pushout_face_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_face_pushout_color", Color(1.0, 1.0, 0.0, 0.5));
+ _debug_navigation_avoidance_static_obstacle_pushout_edge_color = GLOBAL_DEF("debug/shapes/avoidance/obstacles_static_edge_pushout_color", Color(1.0, 1.0, 0.0, 1.0));
+ _debug_navigation_avoidance_enable_agents_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_agents_radius", true);
+ _debug_navigation_avoidance_enable_obstacles_radius = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_radius", true);
+ _debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/enable_obstacles_static", true);
+
if (Engine::get_singleton()->is_editor_hint()) {
// enable NavigationServer when in Editor or else navmesh edge connections are invisible
// on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this
set_debug_enabled(true);
+ set_debug_navigation_enabled(true);
+ set_debug_avoidance_enabled(true);
}
#endif // DEBUG_ENABLED
}
@@ -233,11 +276,18 @@ bool NavigationServer::get_debug_enabled() const {
#ifdef DEBUG_ENABLED
void NavigationServer::_emit_navigation_debug_changed_signal() {
- if (_debug_dirty) {
- _debug_dirty = false;
+ if (_navigation_debug_dirty) {
+ _navigation_debug_dirty = false;
emit_signal("navigation_debug_changed");
}
}
+
+void NavigationServer::_emit_avoidance_debug_changed_signal() {
+ if (_avoidance_debug_dirty) {
+ _avoidance_debug_dirty = false;
+ emit_signal("avoidance_debug_changed");
+ }
+}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
@@ -405,7 +455,7 @@ Ref NavigationServer::get_debug_navigation_agent_path_point_mat
}
Ref material = Ref(memnew(SpatialMaterial));
- material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ //material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
material->set_albedo(_debug_navigation_agent_path_color);
material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true);
material->set_point_size(_debug_navigation_agent_path_point_size);
@@ -418,6 +468,103 @@ Ref NavigationServer::get_debug_navigation_agent_path_point_mat
return _debug_navigation_agent_path_point_material;
}
+Ref NavigationServer::get_debug_navigation_avoidance_agents_radius_material() {
+ if (_debug_navigation_avoidance_agents_radius_material.is_valid()) {
+ return _debug_navigation_avoidance_agents_radius_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_agents_radius_color);
+ material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+
+ _debug_navigation_avoidance_agents_radius_material = material;
+ return _debug_navigation_avoidance_agents_radius_material;
+}
+
+Ref NavigationServer::get_debug_navigation_avoidance_obstacles_radius_material() {
+ if (_debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
+ return _debug_navigation_avoidance_obstacles_radius_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_obstacles_radius_color);
+ material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+
+ _debug_navigation_avoidance_obstacles_radius_material = material;
+ return _debug_navigation_avoidance_obstacles_radius_material;
+}
+
+Ref NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushin_face_material() {
+ if (_debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
+ return _debug_navigation_avoidance_static_obstacle_pushin_face_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushin_face_color);
+ material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+
+ _debug_navigation_avoidance_static_obstacle_pushin_face_material = material;
+ return _debug_navigation_avoidance_static_obstacle_pushin_face_material;
+}
+
+Ref NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushout_face_material() {
+ if (_debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
+ return _debug_navigation_avoidance_static_obstacle_pushout_face_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushout_face_color);
+ material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+
+ _debug_navigation_avoidance_static_obstacle_pushout_face_material = material;
+ return _debug_navigation_avoidance_static_obstacle_pushout_face_material;
+}
+
+Ref NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushin_edge_material() {
+ if (_debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
+ return _debug_navigation_avoidance_static_obstacle_pushin_edge_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ //material->set_transparency(SpatialMaterial::TRANSPARENCY_ALPHA);
+ //material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushin_edge_color);
+ //material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+ material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
+
+ _debug_navigation_avoidance_static_obstacle_pushin_edge_material = material;
+ return _debug_navigation_avoidance_static_obstacle_pushin_edge_material;
+}
+
+Ref NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushout_edge_material() {
+ if (_debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
+ return _debug_navigation_avoidance_static_obstacle_pushout_edge_material;
+ }
+
+ Ref material = Ref(memnew(SpatialMaterial));
+ material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ ///material->set_transparency(SpatialMaterial::TRANSPARENCY_ALPHA);
+ //material->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushout_edge_color);
+ //material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 2);
+ material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
+
+ _debug_navigation_avoidance_static_obstacle_pushout_edge_material = material;
+ return _debug_navigation_avoidance_static_obstacle_pushout_edge_material;
+}
+
void NavigationServer::set_debug_navigation_edge_connection_color(const Color &p_color) {
_debug_navigation_edge_connection_color = p_color;
if (_debug_navigation_edge_connections_material.is_valid()) {
@@ -522,7 +669,7 @@ Color NavigationServer::get_debug_navigation_agent_path_color() const {
void NavigationServer::set_debug_navigation_enable_edge_connections(const bool p_value) {
_debug_navigation_enable_edge_connections = p_value;
- _debug_dirty = true;
+ _navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -543,7 +690,7 @@ bool NavigationServer::get_debug_navigation_enable_edge_connections_xray() const
void NavigationServer::set_debug_navigation_enable_edge_lines(const bool p_value) {
_debug_navigation_enable_edge_lines = p_value;
- _debug_dirty = true;
+ _navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -564,7 +711,7 @@ bool NavigationServer::get_debug_navigation_enable_edge_lines_xray() const {
void NavigationServer::set_debug_navigation_enable_geometry_face_random_color(const bool p_value) {
_debug_navigation_enable_geometry_face_random_color = p_value;
- _debug_dirty = true;
+ _navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -574,7 +721,7 @@ bool NavigationServer::get_debug_navigation_enable_geometry_face_random_color()
void NavigationServer::set_debug_navigation_enable_link_connections(const bool p_value) {
_debug_navigation_enable_link_connections = p_value;
- _debug_dirty = true;
+ _navigation_debug_dirty = true;
call_deferred("_emit_navigation_debug_changed_signal");
}
@@ -593,6 +740,102 @@ bool NavigationServer::get_debug_navigation_enable_link_connections_xray() const
return _debug_navigation_enable_link_connections_xray;
}
+void NavigationServer::set_debug_navigation_avoidance_enable_agents_radius(const bool p_value) {
+ _debug_navigation_avoidance_enable_agents_radius = p_value;
+ _avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer::get_debug_navigation_avoidance_enable_agents_radius() const {
+ return _debug_navigation_avoidance_enable_agents_radius;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value) {
+ _debug_navigation_avoidance_enable_obstacles_radius = p_value;
+ _avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer::get_debug_navigation_avoidance_enable_obstacles_radius() const {
+ return _debug_navigation_avoidance_enable_obstacles_radius;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value) {
+ _debug_navigation_avoidance_enable_obstacles_static = p_value;
+ _avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer::get_debug_navigation_avoidance_enable_obstacles_static() const {
+ return _debug_navigation_avoidance_enable_obstacles_static;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_agents_radius_color(const Color &p_color) {
+ _debug_navigation_avoidance_agents_radius_color = p_color;
+ if (_debug_navigation_avoidance_agents_radius_material.is_valid()) {
+ _debug_navigation_avoidance_agents_radius_material->set_albedo(_debug_navigation_avoidance_agents_radius_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_agents_radius_color() const {
+ return _debug_navigation_avoidance_agents_radius_color;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color) {
+ _debug_navigation_avoidance_obstacles_radius_color = p_color;
+ if (_debug_navigation_avoidance_obstacles_radius_material.is_valid()) {
+ _debug_navigation_avoidance_obstacles_radius_material->set_albedo(_debug_navigation_avoidance_obstacles_radius_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_obstacles_radius_color() const {
+ return _debug_navigation_avoidance_obstacles_radius_color;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color) {
+ _debug_navigation_avoidance_static_obstacle_pushin_face_color = p_color;
+ if (_debug_navigation_avoidance_static_obstacle_pushin_face_material.is_valid()) {
+ _debug_navigation_avoidance_static_obstacle_pushin_face_material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushin_face_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const {
+ return _debug_navigation_avoidance_static_obstacle_pushin_face_color;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color) {
+ _debug_navigation_avoidance_static_obstacle_pushout_face_color = p_color;
+ if (_debug_navigation_avoidance_static_obstacle_pushout_face_material.is_valid()) {
+ _debug_navigation_avoidance_static_obstacle_pushout_face_material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushout_face_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const {
+ return _debug_navigation_avoidance_static_obstacle_pushout_face_color;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color) {
+ _debug_navigation_avoidance_static_obstacle_pushin_edge_color = p_color;
+ if (_debug_navigation_avoidance_static_obstacle_pushin_edge_material.is_valid()) {
+ _debug_navigation_avoidance_static_obstacle_pushin_edge_material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushin_edge_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const {
+ return _debug_navigation_avoidance_static_obstacle_pushin_edge_color;
+}
+
+void NavigationServer::set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color) {
+ _debug_navigation_avoidance_static_obstacle_pushout_edge_color = p_color;
+ if (_debug_navigation_avoidance_static_obstacle_pushout_edge_material.is_valid()) {
+ _debug_navigation_avoidance_static_obstacle_pushout_edge_material->set_albedo(_debug_navigation_avoidance_static_obstacle_pushout_edge_color);
+ }
+}
+
+Color NavigationServer::get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const {
+ return _debug_navigation_avoidance_static_obstacle_pushout_edge_color;
+}
+
void NavigationServer::set_debug_navigation_enable_agent_paths(const bool p_value) {
if (_debug_navigation_enable_agent_paths != p_value) {
_debug_dirty = true;
@@ -623,6 +866,26 @@ bool NavigationServer::get_debug_navigation_enable_agent_paths_xray() const {
return _debug_navigation_enable_agent_paths_xray;
}
+void NavigationServer::set_debug_navigation_enabled(bool p_enabled) {
+ _debug_navigation_enabled = p_enabled;
+ _navigation_debug_dirty = true;
+ call_deferred("_emit_navigation_debug_changed_signal");
+}
+
+bool NavigationServer::get_debug_navigation_enabled() const {
+ return _debug_navigation_enabled;
+}
+
+void NavigationServer::set_debug_avoidance_enabled(bool p_enabled) {
+ _debug_avoidance_enabled = p_enabled;
+ _avoidance_debug_dirty = true;
+ call_deferred("_emit_avoidance_debug_changed_signal");
+}
+
+bool NavigationServer::get_debug_avoidance_enabled() const {
+ return _debug_avoidance_enabled;
+}
+
#endif // DEBUG_ENABLED
void NavigationServer::query_path(const Ref &p_query_parameters, Ref p_query_result) const {
diff --git a/servers/navigation_server.h b/servers/navigation_server.h
index b450a0353..9d78824e4 100644
--- a/servers/navigation_server.h
+++ b/servers/navigation_server.h
@@ -114,6 +114,7 @@ public:
virtual Array map_get_links(RID p_map) const = 0;
virtual Array map_get_regions(RID p_map) const = 0;
virtual Array map_get_agents(RID p_map) const = 0;
+ virtual Array map_get_obstacles(RID p_map) const = 0;
virtual void map_force_update(RID p_map) = 0;
@@ -198,6 +199,12 @@ public:
virtual void agent_set_map(RID p_agent, RID p_map) = 0;
virtual RID agent_get_map(RID p_agent) const = 0;
+ virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) = 0;
+ virtual bool agent_get_avoidance_enabled(RID p_agent) const = 0;
+
+ virtual void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) = 0;
+ virtual bool agent_get_use_3d_avoidance(RID p_agent) const = 0;
+
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
@@ -215,41 +222,53 @@ public:
/// be safe.
virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0;
- /// The minimal amount of time for which this
- /// agent's velocities that are computed by the
- /// simulation are safe with respect to other
- /// agents. The larger this number, the sooner
- /// this agent will respond to the presence of
- /// other agents, but the less freedom this
- /// agent has in choosing its velocities.
- /// Must be positive.
- virtual void agent_set_time_horizon(RID p_agent, real_t p_time) = 0;
+ // Sets the minimum amount of time in seconds that an agent's
+ // must be able to stay on the calculated velocity while still avoiding collisions with agent's
+ // if this value is set to high an agent will often fall back to using a very low velocity just to be safe
+ virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0;
+
+ /// Sets the minimum amount of time in seconds that an agent's
+ // must be able to stay on the calculated velocity while still avoiding collisions with obstacle's
+ // if this value is set to high an agent will often fall back to using a very low velocity just to be safe
+ virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0;
/// The radius of this agent.
/// Must be non-negative.
virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0;
+ virtual void agent_set_height(RID p_agent, real_t p_height) = 0;
/// The maximum speed of this agent.
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0;
- /// Current velocity of the agent
- virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
+ /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
+ virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0;
- /// The new target velocity.
- virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) = 0;
+ /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
+ /// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
+ virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0;
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0;
- /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
- virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) = 0;
-
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const = 0;
/// Callback called at the end of the RVO process
- virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0;
+ virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0;
+
+ virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0;
+ virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0;
+ virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0;
+
+ /// Creates the obstacle.
+ virtual RID obstacle_create() = 0;
+ virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0;
+ virtual RID obstacle_get_map(RID p_obstacle) const = 0;
+ virtual void obstacle_set_height(RID p_obstacle, real_t p_height) = 0;
+ virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) = 0;
+ virtual void obstacle_set_vertices(RID p_obstacle, const Vector &p_vertices) = 0;
+ virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0;
/// Destroy the `RID`
virtual void free(RID p_object) = 0;
@@ -291,6 +310,11 @@ public:
bool get_debug_enabled() const;
#ifdef DEBUG_ENABLED
+ void set_debug_navigation_enabled(bool p_enabled);
+ bool get_debug_navigation_enabled() const;
+
+ void set_debug_avoidance_enabled(bool p_enabled);
+ bool get_debug_avoidance_enabled() const;
void set_debug_navigation_edge_connection_color(const Color &p_color);
Color get_debug_navigation_edge_connection_color() const;
@@ -316,6 +340,24 @@ public:
void set_debug_navigation_agent_path_color(const Color &p_color);
Color get_debug_navigation_agent_path_color() const;
+ void set_debug_navigation_avoidance_agents_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_agents_radius_color() const;
+
+ void set_debug_navigation_avoidance_obstacles_radius_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_obstacles_radius_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_face_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_face_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushin_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushin_edge_color() const;
+
+ void set_debug_navigation_avoidance_static_obstacle_pushout_edge_color(const Color &p_color);
+ Color get_debug_navigation_avoidance_static_obstacle_pushout_edge_color() const;
+
void set_debug_navigation_enable_edge_connections(const bool p_value);
bool get_debug_navigation_enable_edge_connections() const;
@@ -346,6 +388,15 @@ public:
void set_debug_navigation_agent_path_point_size(float p_point_size);
float get_debug_navigation_agent_path_point_size() const;
+ void set_debug_navigation_avoidance_enable_agents_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_agents_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_radius(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_radius() const;
+
+ void set_debug_navigation_avoidance_enable_obstacles_static(const bool p_value);
+ bool get_debug_navigation_avoidance_enable_obstacles_static() const;
+
Ref get_debug_navigation_geometry_face_material();
Ref get_debug_navigation_geometry_edge_material();
Ref get_debug_navigation_geometry_face_disabled_material();
@@ -357,12 +408,26 @@ public:
Ref get_debug_navigation_agent_path_line_material();
Ref get_debug_navigation_agent_path_point_material();
- void _emit_navigation_debug_changed_signal();
+ Ref get_debug_navigation_avoidance_agents_radius_material();
+ Ref get_debug_navigation_avoidance_obstacles_radius_material();
+
+ Ref get_debug_navigation_avoidance_static_obstacle_pushin_face_material();
+ Ref get_debug_navigation_avoidance_static_obstacle_pushout_face_material();
+ Ref get_debug_navigation_avoidance_static_obstacle_pushin_edge_material();
+ Ref get_debug_navigation_avoidance_static_obstacle_pushout_edge_material();
protected:
bool _debug_enabled;
bool _debug_dirty;
+ bool _debug_navigation_enabled;
+ bool _navigation_debug_dirty;
+ void _emit_navigation_debug_changed_signal();
+
+ bool _debug_avoidance_enabled;
+ bool _avoidance_debug_dirty;
+ void _emit_avoidance_debug_changed_signal();
+
Color _debug_navigation_edge_connection_color;
Color _debug_navigation_geometry_edge_color;
Color _debug_navigation_geometry_face_color;
@@ -374,6 +439,14 @@ protected:
float _debug_navigation_agent_path_point_size;
+ Color _debug_navigation_avoidance_agents_radius_color;
+ Color _debug_navigation_avoidance_obstacles_radius_color;
+
+ Color _debug_navigation_avoidance_static_obstacle_pushin_face_color;
+ Color _debug_navigation_avoidance_static_obstacle_pushout_face_color;
+ Color _debug_navigation_avoidance_static_obstacle_pushin_edge_color;
+ Color _debug_navigation_avoidance_static_obstacle_pushout_edge_color;
+
bool _debug_navigation_enable_edge_connections;
bool _debug_navigation_enable_edge_connections_xray;
bool _debug_navigation_enable_edge_lines;
@@ -384,6 +457,10 @@ protected:
bool _debug_navigation_enable_agent_paths;
bool _debug_navigation_enable_agent_paths_xray;
+ bool _debug_navigation_avoidance_enable_agents_radius;
+ bool _debug_navigation_avoidance_enable_obstacles_radius;
+ bool _debug_navigation_avoidance_enable_obstacles_static;
+
Ref _debug_navigation_geometry_edge_material;
Ref