Backport NavigationServer with RVO2 to 3.x
Change the entire navigation system. Remove editor prefix from nav mesh generator class. It is now used for baking at runtime as well. Navigation supports obstacle avoidance now with the RVO2 library. Nav system will also automatically link all nav meshes together to form one overall complete nav map.
This commit is contained in:
parent
571e05d3d3
commit
09bc9eb101
109 changed files with 7910 additions and 2002 deletions
|
@ -360,6 +360,11 @@ Comment: Recast
|
|||
Copyright: 2009, Mikko Mononen
|
||||
License: Zlib
|
||||
|
||||
Files: ./thirdparty/rvo2/
|
||||
Comment: RVO2
|
||||
Copyright: 2016, University of North Carolina at Chapel Hill
|
||||
License: Apache-2.0
|
||||
|
||||
Files: ./thirdparty/squish/
|
||||
Comment: libSquish
|
||||
Copyright: 2006, Simon Brown
|
||||
|
|
|
@ -178,6 +178,7 @@ 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_squish", "Use the built-in squish library", True))
|
||||
opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
|
||||
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
|
||||
|
|
|
@ -53,8 +53,14 @@
|
|||
<member name="Marshalls" type="Marshalls" setter="" getter="">
|
||||
The [Marshalls] singleton.
|
||||
</member>
|
||||
<member name="NavigationMeshGenerator" type="EditorNavigationMeshGenerator" setter="" getter="">
|
||||
The [EditorNavigationMeshGenerator] singleton.
|
||||
<member name="Navigation2DServer" type="Navigation2DServer" setter="" getter="">
|
||||
The [Navigation2DServer] singleton.
|
||||
</member>
|
||||
<member name="NavigationMeshGenerator" type="NavigationMeshGenerator" setter="" getter="">
|
||||
The [NavigationMeshGenerator] singleton.
|
||||
</member>
|
||||
<member name="NavigationServer" type="NavigationServer" setter="" getter="">
|
||||
The [NavigationServer] singleton.
|
||||
</member>
|
||||
<member name="OS" type="OS" setter="" getter="">
|
||||
The [OS] singleton.
|
||||
|
|
|
@ -4,35 +4,34 @@
|
|||
Mesh-based navigation and pathfinding node.
|
||||
</brief_description>
|
||||
<description>
|
||||
Provides navigation and pathfinding within a collection of [NavigationMesh]es. By default, these will be automatically collected from child [NavigationMeshInstance] nodes, but they can also be added on the fly with [method navmesh_add]. In addition to basic pathfinding, this class also assists with aligning navigation agents with the meshes they are navigating on.
|
||||
[b]Note:[/b] The current navigation system has many known issues and will not always return optimal paths as expected. These issues will be fixed in Godot 4.0.
|
||||
Provides navigation and pathfinding within a collection of [NavigationMesh]es. By default, these will be automatically collected from child [NavigationMeshInstance] nodes. In addition to basic pathfinding, this class also assists with aligning navigation agents with the meshes they are navigating on.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_closest_point">
|
||||
<method name="get_closest_point" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the navigation point closest to the point given. Points are in local coordinate space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_closest_point_normal">
|
||||
<method name="get_closest_point_normal" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the surface normal at the navigation point closest to the point given. Useful for rotating a navigation agent according to the navigation mesh it moves on.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_closest_point_owner">
|
||||
<return type="Object" />
|
||||
<method name="get_closest_point_owner" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<argument index="0" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigationMeshInstance]. For meshes added via [method navmesh_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
|
||||
Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigationMeshInstance].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_closest_point_to_segment">
|
||||
<method name="get_closest_point_to_segment" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="start" type="Vector3" />
|
||||
<argument index="1" name="end" type="Vector3" />
|
||||
|
@ -41,42 +40,29 @@
|
|||
Returns the navigation point closest to the given line segment. When enabling [code]use_collision[/code], only considers intersection points between segment and navigation meshes. If multiple intersection points are found, the one closest to the segment start point is returned.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_simple_path">
|
||||
<method name="get_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Returns the object's [RID].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_simple_path" qualifiers="const">
|
||||
<return type="PoolVector3Array" />
|
||||
<argument index="0" name="start" type="Vector3" />
|
||||
<argument index="1" name="end" type="Vector3" />
|
||||
<argument index="2" name="optimize" type="bool" default="true" />
|
||||
<description>
|
||||
Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (radius, height, etc.) are considered in the path calculation, otherwise they are ignored.
|
||||
[b]Note:[/b] This method has known issues and will often return non-optimal paths. These issues will be fixed in Godot 4.0.
|
||||
</description>
|
||||
</method>
|
||||
<method name="navmesh_add">
|
||||
<return type="int" />
|
||||
<argument index="0" name="mesh" type="NavigationMesh" />
|
||||
<argument index="1" name="xform" type="Transform" />
|
||||
<argument index="2" name="owner" type="Object" default="null" />
|
||||
<description>
|
||||
Adds a [NavigationMesh]. Returns an ID for use with [method navmesh_remove] or [method navmesh_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
|
||||
</description>
|
||||
</method>
|
||||
<method name="navmesh_remove">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<description>
|
||||
Removes the [NavigationMesh] with the given ID.
|
||||
</description>
|
||||
</method>
|
||||
<method name="navmesh_set_transform">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<argument index="1" name="xform" type="Transform" />
|
||||
<description>
|
||||
Sets the transform applied to the [NavigationMesh] with the given ID.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="0.3">
|
||||
The XZ plane cell size to use for fields.
|
||||
</member>
|
||||
<member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="5.0">
|
||||
This value is used to detect the near edges to connect compatible regions.
|
||||
</member>
|
||||
<member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector" default="Vector3( 0, 1, 0 )">
|
||||
Defines which direction is up. By default, this is [code](0, 1, 0)[/code], which is the world's "up" direction.
|
||||
</member>
|
||||
|
|
|
@ -4,62 +4,50 @@
|
|||
2D navigation and pathfinding node.
|
||||
</brief_description>
|
||||
<description>
|
||||
Navigation2D provides navigation and pathfinding within a 2D area, specified as a collection of [NavigationPolygon] resources. By default, these are automatically collected from child [NavigationPolygonInstance] nodes, but they can also be added on the fly with [method navpoly_add].
|
||||
[b]Note:[/b] The current navigation system has many known issues and will not always return optimal paths as expected. These issues will be fixed in Godot 4.0.
|
||||
Navigation2D provides navigation and pathfinding within a 2D area, specified as a collection of [NavigationPolygon] resources. By default, these are automatically collected from child [NavigationPolygonInstance] nodes.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_closest_point">
|
||||
<method name="get_closest_point" qualifiers="const">
|
||||
<return type="Vector2" />
|
||||
<argument index="0" name="to_point" type="Vector2" />
|
||||
<description>
|
||||
Returns the navigation point closest to the point given. Points are in local coordinate space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_closest_point_owner">
|
||||
<return type="Object" />
|
||||
<method name="get_closest_point_owner" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<argument index="0" name="to_point" type="Vector2" />
|
||||
<description>
|
||||
Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigationPolygonInstance]. For polygons added via [method navpoly_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
|
||||
Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigationPolygonInstance].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_simple_path">
|
||||
<method name="get_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Returns the object's [RID].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_simple_path" qualifiers="const">
|
||||
<return type="PoolVector2Array" />
|
||||
<argument index="0" name="start" type="Vector2" />
|
||||
<argument index="1" name="end" type="Vector2" />
|
||||
<argument index="2" name="optimize" type="bool" default="true" />
|
||||
<description>
|
||||
Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the path is smoothed by merging path segments where possible.
|
||||
[b]Note:[/b] This method has known issues and will often return non-optimal paths. These issues will be fixed in Godot 4.0.
|
||||
</description>
|
||||
</method>
|
||||
<method name="navpoly_add">
|
||||
<return type="int" />
|
||||
<argument index="0" name="mesh" type="NavigationPolygon" />
|
||||
<argument index="1" name="xform" type="Transform2D" />
|
||||
<argument index="2" name="owner" type="Object" default="null" />
|
||||
<description>
|
||||
Adds a [NavigationPolygon]. Returns an ID for use with [method navpoly_remove] or [method navpoly_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
|
||||
</description>
|
||||
</method>
|
||||
<method name="navpoly_remove">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<description>
|
||||
Removes the [NavigationPolygon] with the given ID.
|
||||
</description>
|
||||
</method>
|
||||
<method name="navpoly_set_transform">
|
||||
<return type="void" />
|
||||
<argument index="0" name="id" type="int" />
|
||||
<argument index="1" name="xform" type="Transform2D" />
|
||||
<description>
|
||||
Sets the transform applied to the [NavigationPolygon] with the given ID.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="10.0">
|
||||
The XY plane cell size to use for fields.
|
||||
</member>
|
||||
<member name="edge_connection_margin" type="float" setter="set_edge_connection_margin" getter="get_edge_connection_margin" default="100.0">
|
||||
This value is used to detect the near edges to connect compatible regions.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
229
doc/classes/Navigation2DServer.xml
Normal file
229
doc/classes/Navigation2DServer.xml
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="Navigation2DServer" inherits="Object" version="3.5">
|
||||
<brief_description>
|
||||
Server interface for low-level 2D navigation access.
|
||||
</brief_description>
|
||||
<description>
|
||||
Navigation2DServer is the server responsible for all 2D navigation. It handles several objects, namely maps, regions and agents.
|
||||
Maps are made up of regions, which are made of navigation polygons. Together, they define the navigable areas in the 2D world. For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [member Navigation.edge_connection_margin] to the respective other edge's vertex.
|
||||
To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity.
|
||||
[b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine.
|
||||
This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="2D Navigation Demo">https://godotengine.org/asset-library/asset/117</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="agent_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Creates the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_is_map_changed" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the map got changed the previous frame.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_callback" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="receiver" type="Object" />
|
||||
<argument index="2" name="method" type="String" />
|
||||
<argument index="3" name="userdata" type="Variant" default="null" />
|
||||
<description>
|
||||
Callback called at the end of the RVO process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_map" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="map" type="RID" />
|
||||
<description>
|
||||
Puts the agent in the map.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_max_neighbors" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="count" type="int" />
|
||||
<description>
|
||||
Sets the maximum number of other agents the agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_max_speed" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="max_speed" type="float" />
|
||||
<description>
|
||||
Sets the maximum speed of the agent. Must be positive.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_neighbor_dist" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="dist" type="float" />
|
||||
<description>
|
||||
Sets the maximum distance to other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_position" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="position" type="Vector2" />
|
||||
<description>
|
||||
Sets the position of the agent in world space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_radius" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="radius" type="float" />
|
||||
<description>
|
||||
Sets the radius of the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_target_velocity" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="target_velocity" type="Vector2" />
|
||||
<description>
|
||||
Sets the new target velocity.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_time_horizon" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<description>
|
||||
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_velocity" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="velocity" type="Vector2" />
|
||||
<description>
|
||||
Sets the current velocity of the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="free" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="object" type="RID" />
|
||||
<description>
|
||||
Destroys the given RID.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Create a new map.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_cell_size" qualifiers="const">
|
||||
<return type="float" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<description>
|
||||
Returns the map cell size.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point" qualifiers="const">
|
||||
<return type="Vector2" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="to_point" type="Vector2" />
|
||||
<description>
|
||||
Returns the point closest to the provided [code]to_point[/code] on the navigation mesh surface.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point_owner" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="to_point" type="Vector2" />
|
||||
<description>
|
||||
Returns the owner region RID for the point returned by [method map_get_closest_point].
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_edge_connection_margin" qualifiers="const">
|
||||
<return type="float" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<description>
|
||||
Returns the edge connection margin of the map. The edge connection margin is a distance used to connect two regions.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_path" qualifiers="const">
|
||||
<return type="PoolVector2Array" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="origin" type="Vector2" />
|
||||
<argument index="2" name="destination" type="Vector2" />
|
||||
<argument index="3" name="optimize" type="bool" />
|
||||
<description>
|
||||
Returns the navigation path to reach the destination from the origin.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_is_active" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="nap" type="RID" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the map is active.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_active" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="active" type="bool" />
|
||||
<description>
|
||||
Sets the map active.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_cell_size" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="cell_size" type="float" />
|
||||
<description>
|
||||
Set the map cell size used to weld the navigation mesh polygons.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_edge_connection_margin" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="margin" type="float" />
|
||||
<description>
|
||||
Set the map edge connection margin used to weld the compatible region edges.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Creates a new region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_map" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="map" type="RID" />
|
||||
<description>
|
||||
Sets the map for the region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_navpoly" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="nav_poly" type="NavigationPolygon" />
|
||||
<description>
|
||||
Sets the navigation mesh for the region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_transform" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="transform" type="Transform2D" />
|
||||
<description>
|
||||
Sets the global transformation for the region.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
148
doc/classes/NavigationAgent.xml
Normal file
148
doc/classes/NavigationAgent.xml
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationAgent" inherits="Node" version="3.5">
|
||||
<brief_description>
|
||||
3D agent used in navigation for collision avoidance.
|
||||
</brief_description>
|
||||
<description>
|
||||
3D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation] node, or using [method set_navigation]. [NavigationAgent] is physics safe.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="distance_to_target" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
Returns the distance to the target location, using the agent's global position. The user must set the target location with [method set_target_location] in order for this to be accurate.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_final_location">
|
||||
<return type="Vector3" />
|
||||
<description>
|
||||
Returns the reachable final location 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.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_nav_path" qualifiers="const">
|
||||
<return type="PoolVector3Array" />
|
||||
<description>
|
||||
Returns the path from start to finish in global coordinates.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_nav_path_index" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns which index the agent is currently on in the navigation path's [PoolVector3Array].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_navigation" qualifiers="const">
|
||||
<return type="Node" />
|
||||
<description>
|
||||
Returns the [Navigation] node that the agent is using for its navigation system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_next_location">
|
||||
<return type="Vector3" />
|
||||
<description>
|
||||
Returns a [Vector3] in global coordinates, that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the origin of the agent's parent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_target_location" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<description>
|
||||
Returns the user-defined target location (set with [method set_target_location]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_navigation_finished">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the navigation path's final location has been reached.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_target_reachable">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the target location is reachable. The target location is set using [method set_target_location].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_target_reached" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the target location is reached. The target location is set using [method set_target_location]. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation">
|
||||
<return type="void" />
|
||||
<argument index="0" name="navigation" type="Node" />
|
||||
<description>
|
||||
Sets the [Navigation] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation] node.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_target_location">
|
||||
<return type="void" />
|
||||
<argument index="0" name="location" type="Vector3" />
|
||||
<description>
|
||||
Sets the user desired final location. This will clear the current navigation path.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_velocity">
|
||||
<return type="void" />
|
||||
<argument index="0" name="velocity" type="Vector3" />
|
||||
<description>
|
||||
Sends the given 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.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="agent_height_offset" type="float" setter="set_agent_height_offset" getter="get_agent_height_offset" default="0.0">
|
||||
The agent height offset to match the navigation mesh height.
|
||||
</member>
|
||||
<member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="true">
|
||||
Ignores collisions on the Y axis. Must be [code]true[/code] to move on a horizontal plane.
|
||||
</member>
|
||||
<member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
|
||||
The maximum number of neighbors for the agent to consider.
|
||||
</member>
|
||||
<member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="10.0">
|
||||
The maximum speed that an agent can move.
|
||||
</member>
|
||||
<member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="50.0">
|
||||
The distance to search for other agents.
|
||||
</member>
|
||||
<member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
|
||||
The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
|
||||
</member>
|
||||
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
|
||||
The radius of the agent.
|
||||
</member>
|
||||
<member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
|
||||
The distance threshold before a target is considered to be reached. This will allow an agent to not have to hit a point on the path exactly, but in the area.
|
||||
</member>
|
||||
<member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="5.0">
|
||||
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithim, 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.
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="navigation_finished">
|
||||
<description>
|
||||
Notifies when the final location is reached.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="path_changed">
|
||||
<description>
|
||||
Notifies when the navigation path changes. This can be triggered by the navigation system or by the user changing the path.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="target_reached">
|
||||
<description>
|
||||
Notifies when the player-defined target, set with [method set_target_location], is reached.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="velocity_computed">
|
||||
<argument index="0" name="safe_velocity" type="Vector3" />
|
||||
<description>
|
||||
Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity].
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
142
doc/classes/NavigationAgent2D.xml
Normal file
142
doc/classes/NavigationAgent2D.xml
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationAgent2D" inherits="Node" version="3.5">
|
||||
<brief_description>
|
||||
2D agent used in navigation for collision avoidance.
|
||||
</brief_description>
|
||||
<description>
|
||||
2D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationAgent2D] is physics safe.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="distance_to_target" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
Returns the distance to the target location, using the agent's global position. The user must set the target location with [method set_target_location] in order for this to be accurate.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_final_location">
|
||||
<return type="Vector2" />
|
||||
<description>
|
||||
Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_nav_path" qualifiers="const">
|
||||
<return type="PoolVector2Array" />
|
||||
<description>
|
||||
Returns the path from start to finish in global coordinates.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_nav_path_index" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns which index the agent is currently on in the navigation path's [PoolVector2Array].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_navigation" qualifiers="const">
|
||||
<return type="Node" />
|
||||
<description>
|
||||
Returns the [Navigation2D] node that the agent is using for its navigation system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_next_location">
|
||||
<return type="Vector2" />
|
||||
<description>
|
||||
Returns a [Vector2] in global coordinates, that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_target_location" qualifiers="const">
|
||||
<return type="Vector2" />
|
||||
<description>
|
||||
Returns the user-defined target location (set with [method set_target_location]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_navigation_finished">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the navigation path's final location has been reached.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_target_reachable">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the target location is reachable. The target location is set using [method set_target_location].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_target_reached" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the target location is reached. The target location is set using [method set_target_location]. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation">
|
||||
<return type="void" />
|
||||
<argument index="0" name="navigation" type="Node" />
|
||||
<description>
|
||||
Sets the [Navigation2D] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation2D] node.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_target_location">
|
||||
<return type="void" />
|
||||
<argument index="0" name="location" type="Vector2" />
|
||||
<description>
|
||||
Sets the user desired final location. This will clear the current navigation path.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_velocity">
|
||||
<return type="void" />
|
||||
<argument index="0" name="velocity" type="Vector2" />
|
||||
<description>
|
||||
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.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
|
||||
The maximum number of neighbors for the agent to consider.
|
||||
</member>
|
||||
<member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="200.0">
|
||||
The maximum speed that an agent can move.
|
||||
</member>
|
||||
<member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="500.0">
|
||||
The distance to search for other agents.
|
||||
</member>
|
||||
<member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
|
||||
The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceded, it recalculates the ideal path.
|
||||
</member>
|
||||
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="10.0">
|
||||
The radius of the agent.
|
||||
</member>
|
||||
<member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
|
||||
The distance threshold before a target is considered to be reached. This will allow an agent to not have to hit a point on the path exactly, but in the area.
|
||||
</member>
|
||||
<member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="20.0">
|
||||
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithim, 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.
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="navigation_finished">
|
||||
<description>
|
||||
Notifies when the final location is reached.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="path_changed">
|
||||
<description>
|
||||
Notifies when the navigation path changes. This can be triggered by the navigation system or by the user changing the path.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="target_reached">
|
||||
<description>
|
||||
Notifies when the player-defined target, set with [method set_target_location], is reached.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="velocity_computed">
|
||||
<argument index="0" name="safe_velocity" type="Vector2" />
|
||||
<description>
|
||||
Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity].
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
|
@ -1,8 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="EditorNavigationMeshGenerator" inherits="Object" version="3.5">
|
||||
<class name="NavigationMeshGenerator" inherits="Object" version="3.5">
|
||||
<brief_description>
|
||||
This class is responsible for creating and clearing navigation meshes.
|
||||
</brief_description>
|
||||
<description>
|
||||
This class is responsible for creating and clearing navigation meshes.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -12,12 +14,14 @@
|
|||
<argument index="0" name="nav_mesh" type="NavigationMesh" />
|
||||
<argument index="1" name="root_node" type="Node" />
|
||||
<description>
|
||||
Bakes the navigation mesh. This will allow you to use pathfinding with the navigation system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear">
|
||||
<return type="void" />
|
||||
<argument index="0" name="nav_mesh" type="NavigationMesh" />
|
||||
<description>
|
||||
Clears the navigation mesh.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
|
@ -1,23 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationMeshInstance" inherits="Spatial" version="3.5">
|
||||
<brief_description>
|
||||
Node that instances navigation meshes into a scenario.
|
||||
An instance of a [NavigationMesh].
|
||||
</brief_description>
|
||||
<description>
|
||||
NavigationMeshInstance is a node that takes a [NavigationMesh] resource and adds it to the current scenario by creating an instance of it.
|
||||
An instance of a [NavigationMesh]. It tells the [Navigation] node what can be navigated and what cannot, based on the [NavigationMesh] resource. This should be a child of a [Navigation] node.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="bake_navigation_mesh">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Bakes the [NavigationMesh]. The baking is done in a seperate thread because navigation baking is not a cheap operation. This can be done at runtime. When it is completed, it automatically sets the new [NavigationMesh].
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
|
||||
If [code]true[/code], the navigation mesh will be used by [Navigation].
|
||||
Determines if the [NavigationMeshInstance] is enabled or disabled.
|
||||
</member>
|
||||
<member name="navmesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh">
|
||||
The [NavigationMesh] resource for the instance.
|
||||
The [NavigationMesh] resource to use.
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="bake_finished">
|
||||
<description>
|
||||
Notifies when the navigation mesh bake operation is completed.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="navigation_mesh_changed">
|
||||
<description>
|
||||
Notifies when the [NavigationMesh] has changed.
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
36
doc/classes/NavigationObstacle.xml
Normal file
36
doc/classes/NavigationObstacle.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationObstacle" inherits="Node" version="3.5">
|
||||
<brief_description>
|
||||
3D obstacle used in navigation for collision avoidance.
|
||||
</brief_description>
|
||||
<description>
|
||||
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.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_navigation" qualifiers="const">
|
||||
<return type="Node" />
|
||||
<description>
|
||||
Returns the [Navigation] node that the obstacle is using for its navigation system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation">
|
||||
<return type="void" />
|
||||
<argument index="0" name="navigation" type="Node" />
|
||||
<description>
|
||||
Sets the [Navigation] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation] node.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
|
||||
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
|
||||
</member>
|
||||
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
|
||||
The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
36
doc/classes/NavigationObstacle2D.xml
Normal file
36
doc/classes/NavigationObstacle2D.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationObstacle2D" inherits="Node" version="3.5">
|
||||
<brief_description>
|
||||
2D obstacle used in navigation for collision avoidance.
|
||||
</brief_description>
|
||||
<description>
|
||||
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]. [NavigationObstacle] is physics safe.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_navigation" qualifiers="const">
|
||||
<return type="Node" />
|
||||
<description>
|
||||
Returns the [Navigation2D] node that the obstacle is using for its navigation system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation">
|
||||
<return type="void" />
|
||||
<argument index="0" name="navigation" type="Node" />
|
||||
<description>
|
||||
Sets the [Navigation2D] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation2D] node.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
|
||||
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
|
||||
</member>
|
||||
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
|
||||
The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
286
doc/classes/NavigationServer.xml
Normal file
286
doc/classes/NavigationServer.xml
Normal file
|
@ -0,0 +1,286 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="NavigationServer" inherits="Object" version="3.5">
|
||||
<brief_description>
|
||||
Server interface for low-level 3D navigation access.
|
||||
</brief_description>
|
||||
<description>
|
||||
NavigationServer is the server responsible for all 3D navigation. It handles several objects, namely maps, regions and agents.
|
||||
Maps are made up of regions, which are made of navigation meshes. Together, they define the navigable areas in the 3D world. For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [member Navigation.edge_connection_margin] to the respective other edge's vertex.
|
||||
To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity.
|
||||
[b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine.
|
||||
This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="agent_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Creates the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_is_map_changed" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the map got changed the previous frame.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_callback" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="receiver" type="Object" />
|
||||
<argument index="2" name="method" type="String" />
|
||||
<argument index="3" name="userdata" type="Variant" default="null" />
|
||||
<description>
|
||||
Callback called at the end of the RVO process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_map" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="map" type="RID" />
|
||||
<description>
|
||||
Puts the agent in the map.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_max_neighbors" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="count" type="int" />
|
||||
<description>
|
||||
Sets the maximum number of other agents the agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_max_speed" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="max_speed" type="float" />
|
||||
<description>
|
||||
Sets the maximum speed of the agent. Must be positive.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_neighbor_dist" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="dist" type="float" />
|
||||
<description>
|
||||
Sets the maximum distance to other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_position" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="position" type="Vector3" />
|
||||
<description>
|
||||
Sets the position of the agent in world space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_radius" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="radius" type="float" />
|
||||
<description>
|
||||
Sets the radius of the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_target_velocity" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="target_velocity" type="Vector3" />
|
||||
<description>
|
||||
Sets the new target velocity.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_time_horizon" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<description>
|
||||
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
|
||||
</description>
|
||||
</method>
|
||||
<method name="agent_set_velocity" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="agent" type="RID" />
|
||||
<argument index="1" name="velocity" type="Vector3" />
|
||||
<description>
|
||||
Sets the current velocity of the agent.
|
||||
</description>
|
||||
</method>
|
||||
<method name="free" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="object" type="RID" />
|
||||
<description>
|
||||
Destroys the given RID.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Create a new map.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_cell_size" qualifiers="const">
|
||||
<return type="float" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<description>
|
||||
Returns the map cell size.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the point closest to the provided [code]to_point[/code] on the navigation mesh surface.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point_normal" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the normal for the point returned by [method map_get_closest_point].
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point_owner" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="to_point" type="Vector3" />
|
||||
<description>
|
||||
Returns the owner region RID for the point returned by [method map_get_closest_point].
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_closest_point_to_segment" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="start" type="Vector3" />
|
||||
<argument index="2" name="end" type="Vector3" />
|
||||
<argument index="3" name="use_collision" type="bool" default="false" />
|
||||
<description>
|
||||
Returns the closest point between the navigation surface and the segment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_edge_connection_margin" qualifiers="const">
|
||||
<return type="float" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<description>
|
||||
Returns the edge connection margin of the map. This distance is the minimum vertex distance needed to connect two edges from different regions.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_path" qualifiers="const">
|
||||
<return type="PoolVector3Array" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="origin" type="Vector3" />
|
||||
<argument index="2" name="destination" type="Vector3" />
|
||||
<argument index="3" name="optimize" type="bool" />
|
||||
<description>
|
||||
Returns the navigation path to reach the destination from the origin.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_get_up" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<description>
|
||||
Returns the map's up direction.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_is_active" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="nap" type="RID" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the map is active.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_active" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="active" type="bool" />
|
||||
<description>
|
||||
Sets the map active.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_cell_size" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="cell_size" type="float" />
|
||||
<description>
|
||||
Set the map cell size used to weld the navigation mesh polygons.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_edge_connection_margin" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="margin" type="float" />
|
||||
<description>
|
||||
Set the map edge connection margin used to weld the compatible region edges.
|
||||
</description>
|
||||
</method>
|
||||
<method name="map_set_up" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="map" type="RID" />
|
||||
<argument index="1" name="up" type="Vector3" />
|
||||
<description>
|
||||
Sets the map up direction.
|
||||
</description>
|
||||
</method>
|
||||
<method name="process">
|
||||
<return type="void" />
|
||||
<argument index="0" name="delta_time" type="float" />
|
||||
<description>
|
||||
Process the collision avoidance agents.
|
||||
The result of this process is needed by the physics server, so this must be called in the main thread.
|
||||
[b]Note:[/b] This function is not thread safe.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_bake_navmesh" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="mesh" type="NavigationMesh" />
|
||||
<argument index="1" name="node" type="Node" />
|
||||
<description>
|
||||
Bakes the navigation mesh.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_create" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Creates a new region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_map" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="map" type="RID" />
|
||||
<description>
|
||||
Sets the map for the region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_navmesh" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="nav_mesh" type="NavigationMesh" />
|
||||
<description>
|
||||
Sets the navigation mesh for the region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="region_set_transform" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="region" type="RID" />
|
||||
<argument index="1" name="transform" type="Transform" />
|
||||
<description>
|
||||
Sets the global transformation for the region.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_active" qualifiers="const">
|
||||
<return type="void" />
|
||||
<argument index="0" name="active" type="bool" />
|
||||
<description>
|
||||
Control activation of this server.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
|
@ -64,6 +64,8 @@
|
|||
#include "scene/gui/texture_progress.h"
|
||||
#include "scene/gui/tool_button.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
#include "servers/navigation_server.h"
|
||||
#include "servers/physics_2d_server.h"
|
||||
|
||||
#include "editor/audio_stream_preview.h"
|
||||
|
@ -5743,6 +5745,8 @@ EditorNode::EditorNode() {
|
|||
VisualServer::get_singleton()->textures_keep_original(true);
|
||||
VisualServer::get_singleton()->set_debug_generate_wireframes(true);
|
||||
|
||||
NavigationServer::get_singleton()->set_active(false); // no nav by default if editor
|
||||
|
||||
PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor
|
||||
Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor
|
||||
ScriptServer::set_scripting_enabled(false); // no scripting by default if editor
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
#include "editor/editor_scale.h"
|
||||
#include "scene/3d/collision_shape.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "scene/3d/physics_body.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "spatial_editor_plugin.h"
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "editor/editor_settings.h"
|
||||
#include "main/main.h"
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "scene/3d/physics_body.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/gui/tool_button.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
// Used to test for GLES3 support.
|
||||
#ifndef SERVER_ENABLED
|
||||
|
@ -2375,6 +2376,7 @@ ProjectManager::ProjectManager() {
|
|||
}
|
||||
|
||||
// Turn off some servers we aren't going to be using in the Project Manager.
|
||||
NavigationServer::get_singleton()->set_active(false);
|
||||
PhysicsServer::get_singleton()->set_active(false);
|
||||
Physics2DServer::get_singleton()->set_active(false);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "scene/3d/light.h"
|
||||
#include "scene/3d/listener.h"
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "scene/3d/occluder.h"
|
||||
#include "scene/3d/particles.h"
|
||||
#include "scene/3d/physics_joint.h"
|
||||
|
|
|
@ -66,6 +66,8 @@
|
|||
#include "servers/arvr_server.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "servers/camera_server.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
#include "servers/navigation_server.h"
|
||||
#include "servers/physics_2d_server.h"
|
||||
#include "servers/physics_server.h"
|
||||
#include "servers/register_server_types.h"
|
||||
|
@ -110,6 +112,8 @@ static ARVRServer *arvr_server = nullptr;
|
|||
static PhysicsServer *physics_server = nullptr;
|
||||
static Physics2DServer *physics_2d_server = nullptr;
|
||||
static VisualServerCallbacks *visual_server_callbacks = nullptr;
|
||||
static NavigationServer *navigation_server = nullptr;
|
||||
static Navigation2DServer *navigation_2d_server = nullptr;
|
||||
|
||||
// We error out if setup2() doesn't turn this true
|
||||
static bool _start_success = false;
|
||||
|
@ -214,6 +218,19 @@ void finalize_physics() {
|
|||
memdelete(physics_2d_server);
|
||||
}
|
||||
|
||||
void initialize_navigation_server() {
|
||||
ERR_FAIL_COND(navigation_server != nullptr);
|
||||
navigation_server = NavigationServerManager::new_default_server();
|
||||
navigation_2d_server = memnew(Navigation2DServer);
|
||||
}
|
||||
|
||||
void finalize_navigation_server() {
|
||||
memdelete(navigation_server);
|
||||
navigation_server = nullptr;
|
||||
memdelete(navigation_2d_server);
|
||||
navigation_2d_server = nullptr;
|
||||
}
|
||||
|
||||
//#define DEBUG_INIT
|
||||
#ifdef DEBUG_INIT
|
||||
#define MAIN_PRINT(m_txt) print_line(m_txt)
|
||||
|
@ -1523,6 +1540,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
|||
camera_server = CameraServer::create();
|
||||
|
||||
initialize_physics();
|
||||
initialize_navigation_server();
|
||||
register_server_singletons();
|
||||
|
||||
register_driver_types();
|
||||
|
@ -2230,6 +2248,7 @@ bool Main::iteration() {
|
|||
break;
|
||||
}
|
||||
|
||||
NavigationServer::get_singleton_mut()->process(frame_slice * time_scale);
|
||||
message_queue->flush();
|
||||
|
||||
PhysicsServer::get_singleton()->step(frame_slice * time_scale);
|
||||
|
@ -2429,6 +2448,7 @@ void Main::cleanup(bool p_force) {
|
|||
|
||||
OS::get_singleton()->finalize();
|
||||
finalize_physics();
|
||||
finalize_navigation_server();
|
||||
|
||||
if (packed_data) {
|
||||
memdelete(packed_data);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "scene/resources/mesh_library.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/navigation_server.h"
|
||||
#include "servers/visual_server.h"
|
||||
|
||||
bool GridMap::_set(const StringName &p_name, const Variant &p_value) {
|
||||
|
@ -406,12 +407,10 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
|
|||
}
|
||||
|
||||
//erase navigation
|
||||
if (navigation) {
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
|
||||
navigation->navmesh_remove(E->get().id);
|
||||
}
|
||||
g.navmesh_ids.clear();
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
|
||||
NavigationServer::get_singleton()->free(E->get().region);
|
||||
}
|
||||
g.navmesh_ids.clear();
|
||||
|
||||
//erase multimeshes
|
||||
|
||||
|
@ -490,9 +489,11 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
|
|||
nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item);
|
||||
|
||||
if (navigation) {
|
||||
nm.id = navigation->navmesh_add(navmesh, xform, this);
|
||||
} else {
|
||||
nm.id = -1;
|
||||
RID region = NavigationServer::get_singleton()->region_create();
|
||||
NavigationServer::get_singleton()->region_set_navmesh(region, navmesh);
|
||||
NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * nm.xform);
|
||||
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
nm.region = region;
|
||||
}
|
||||
g.navmesh_ids[E->get()] = nm;
|
||||
}
|
||||
|
@ -579,10 +580,14 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
|
|||
|
||||
if (navigation && mesh_library.is_valid()) {
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
|
||||
if (cell_map.has(F->key()) && F->get().id < 0) {
|
||||
if (cell_map.has(F->key()) && F->get().region.is_valid() == false) {
|
||||
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F->key()].item);
|
||||
if (nm.is_valid()) {
|
||||
F->get().id = navigation->navmesh_add(nm, F->get().xform, this);
|
||||
RID region = NavigationServer::get_singleton()->region_create();
|
||||
NavigationServer::get_singleton()->region_set_navmesh(region, nm);
|
||||
NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * F->get().xform);
|
||||
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
F->get().region = region;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -605,9 +610,9 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
|
|||
|
||||
if (navigation) {
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
|
||||
if (F->get().id >= 0) {
|
||||
navigation->navmesh_remove(F->get().id);
|
||||
F->get().id = -1;
|
||||
if (F->get().region.is_valid()) {
|
||||
NavigationServer::get_singleton()->free(F->get().region);
|
||||
F->get().region = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,13 +637,11 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
|
|||
g.static_body = RID();
|
||||
}
|
||||
|
||||
//erase navigation
|
||||
if (navigation) {
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
|
||||
navigation->navmesh_remove(E->get().id);
|
||||
}
|
||||
g.navmesh_ids.clear();
|
||||
// Erase navigation
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
|
||||
NavigationServer::get_singleton()->free(E->get().region);
|
||||
}
|
||||
g.navmesh_ids.clear();
|
||||
|
||||
//erase multimeshes
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ class GridMap : public Spatial {
|
|||
*/
|
||||
struct Octant {
|
||||
struct NavMesh {
|
||||
int id;
|
||||
RID region;
|
||||
Transform xform;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_recast = env_modules.Clone()
|
||||
env_navigation = env_modules.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
# Recast
|
||||
if env["builtin_recast"]:
|
||||
thirdparty_dir = "#thirdparty/recastnavigation/Recast/"
|
||||
thirdparty_sources = [
|
||||
|
@ -26,18 +27,36 @@ if env["builtin_recast"]:
|
|||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_recast.Prepend(CPPPATH=[thirdparty_dir + "/Include"])
|
||||
env_navigation.Prepend(CPPPATH=[thirdparty_dir + "Include"])
|
||||
|
||||
env_thirdparty = env_recast.Clone()
|
||||
env_thirdparty = env_navigation.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env.modules_sources += thirdparty_obj
|
||||
|
||||
# RVO2
|
||||
if env["builtin_rvo2"]:
|
||||
thirdparty_dir = "#thirdparty/rvo2/"
|
||||
thirdparty_sources = [
|
||||
"Agent.cpp",
|
||||
"KdTree.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
|
||||
|
||||
|
||||
# Godot source files
|
||||
|
||||
module_obj = []
|
||||
|
||||
env_recast.add_source_files(module_obj, "*.cpp")
|
||||
env_navigation.add_source_files(module_obj, "*.cpp")
|
||||
|
||||
env.modules_sources += module_obj
|
||||
|
||||
# Needed to force rebuilding the module files when the thirdparty library is updated.
|
|
@ -1,5 +1,5 @@
|
|||
def can_build(env, platform):
|
||||
return env["tools"]
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
503
modules/navigation/godot_navigation_server.cpp
Normal file
503
modules/navigation/godot_navigation_server.cpp
Normal file
|
@ -0,0 +1,503 @@
|
|||
/*************************************************************************/
|
||||
/* godot_navigation_server.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "godot_navigation_server.h"
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
#include "navigation_mesh_generator.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
/// Creates a struct for each function and a function that once called creates
|
||||
/// an instance of that struct with the submited parameters.
|
||||
/// Then, that struct is stored in an array; the `sync` function consume that array.
|
||||
|
||||
#define COMMAND_1(F_NAME, T_0, D_0) \
|
||||
struct MERGE(F_NAME, _command) : public SetCommand { \
|
||||
T_0 d_0; \
|
||||
MERGE(F_NAME, _command) \
|
||||
(T_0 p_d_0) : \
|
||||
d_0(p_d_0) {} \
|
||||
virtual void exec(GodotNavigationServer *server) { \
|
||||
server->MERGE(_cmd_, F_NAME)(d_0); \
|
||||
} \
|
||||
}; \
|
||||
void GodotNavigationServer::F_NAME(T_0 D_0) const { \
|
||||
auto cmd = memnew(MERGE(F_NAME, _command)( \
|
||||
D_0)); \
|
||||
add_command(cmd); \
|
||||
} \
|
||||
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0)
|
||||
|
||||
#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
|
||||
struct MERGE(F_NAME, _command) : public SetCommand { \
|
||||
T_0 d_0; \
|
||||
T_1 d_1; \
|
||||
MERGE(F_NAME, _command) \
|
||||
( \
|
||||
T_0 p_d_0, \
|
||||
T_1 p_d_1) : \
|
||||
d_0(p_d_0), \
|
||||
d_1(p_d_1) {} \
|
||||
virtual void exec(GodotNavigationServer *server) { \
|
||||
server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
|
||||
} \
|
||||
}; \
|
||||
void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
|
||||
auto cmd = memnew(MERGE(F_NAME, _command)( \
|
||||
D_0, \
|
||||
D_1)); \
|
||||
add_command(cmd); \
|
||||
} \
|
||||
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
|
||||
|
||||
#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
|
||||
struct MERGE(F_NAME, _command) : public SetCommand { \
|
||||
T_0 d_0; \
|
||||
T_1 d_1; \
|
||||
T_2 d_2; \
|
||||
T_3 d_3; \
|
||||
MERGE(F_NAME, _command) \
|
||||
( \
|
||||
T_0 p_d_0, \
|
||||
T_1 p_d_1, \
|
||||
T_2 p_d_2, \
|
||||
T_3 p_d_3) : \
|
||||
d_0(p_d_0), \
|
||||
d_1(p_d_1), \
|
||||
d_2(p_d_2), \
|
||||
d_3(p_d_3) {} \
|
||||
virtual void exec(GodotNavigationServer *server) { \
|
||||
server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
|
||||
} \
|
||||
}; \
|
||||
void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
|
||||
auto cmd = memnew(MERGE(F_NAME, _command)( \
|
||||
D_0, \
|
||||
D_1, \
|
||||
D_2, \
|
||||
D_3)); \
|
||||
add_command(cmd); \
|
||||
} \
|
||||
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
|
||||
|
||||
GodotNavigationServer::GodotNavigationServer() :
|
||||
NavigationServer(),
|
||||
active(true) {
|
||||
}
|
||||
|
||||
GodotNavigationServer::~GodotNavigationServer() {
|
||||
flush_queries();
|
||||
}
|
||||
|
||||
void GodotNavigationServer::add_command(SetCommand *command) const {
|
||||
auto mut_this = const_cast<GodotNavigationServer *>(this);
|
||||
{
|
||||
MutexLock lock(commands_mutex);
|
||||
mut_this->commands.push_back(command);
|
||||
}
|
||||
}
|
||||
|
||||
RID GodotNavigationServer::map_create() const {
|
||||
auto mut_this = const_cast<GodotNavigationServer *>(this);
|
||||
MutexLock lock(mut_this->operations_mutex);
|
||||
NavMap *space = memnew(NavMap);
|
||||
RID rid = map_owner.make_rid(space);
|
||||
space->set_self(rid);
|
||||
return rid;
|
||||
}
|
||||
|
||||
COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
if (p_active) {
|
||||
if (!map_is_active(p_map)) {
|
||||
active_maps.push_back(map);
|
||||
}
|
||||
} else {
|
||||
active_maps.erase(map);
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotNavigationServer::map_is_active(RID p_map) const {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, false);
|
||||
|
||||
return active_maps.find(map) >= 0;
|
||||
}
|
||||
|
||||
COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
map->set_up(p_up);
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer::map_get_up(RID p_map) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, Vector3());
|
||||
|
||||
return map->get_up();
|
||||
}
|
||||
|
||||
COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
map->set_cell_size(p_cell_size);
|
||||
}
|
||||
|
||||
real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, 0);
|
||||
|
||||
return map->get_cell_size();
|
||||
}
|
||||
|
||||
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
map->set_edge_connection_margin(p_connection_margin);
|
||||
}
|
||||
|
||||
real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, 0);
|
||||
|
||||
return map->get_edge_connection_margin();
|
||||
}
|
||||
|
||||
Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
|
||||
|
||||
return map->get_path(p_origin, p_destination, p_optimize);
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, Vector3());
|
||||
|
||||
return map->get_closest_point_to_segment(p_from, p_to, p_use_collision);
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer::map_get_closest_point(RID p_map, const Vector3 &p_point) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, Vector3());
|
||||
|
||||
return map->get_closest_point(p_point);
|
||||
}
|
||||
|
||||
Vector3 GodotNavigationServer::map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, Vector3());
|
||||
|
||||
return map->get_closest_point_normal(p_point);
|
||||
}
|
||||
|
||||
RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const {
|
||||
const NavMap *map = map_owner.getornull(p_map);
|
||||
ERR_FAIL_COND_V(map == nullptr, RID());
|
||||
|
||||
return map->get_closest_point_owner(p_point);
|
||||
}
|
||||
|
||||
RID GodotNavigationServer::region_create() const {
|
||||
auto mut_this = const_cast<GodotNavigationServer *>(this);
|
||||
MutexLock lock(mut_this->operations_mutex);
|
||||
NavRegion *reg = memnew(NavRegion);
|
||||
RID rid = region_owner.make_rid(reg);
|
||||
reg->set_self(rid);
|
||||
return rid;
|
||||
}
|
||||
|
||||
COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
|
||||
NavRegion *region = region_owner.get(p_region);
|
||||
ERR_FAIL_COND(region == nullptr);
|
||||
|
||||
if (region->get_map() != nullptr) {
|
||||
if (region->get_map()->get_self() == p_map) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
region->get_map()->remove_region(region);
|
||||
region->set_map(nullptr);
|
||||
}
|
||||
|
||||
if (p_map.is_valid()) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
map->add_region(region);
|
||||
region->set_map(map);
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform) {
|
||||
NavRegion *region = region_owner.get(p_region);
|
||||
ERR_FAIL_COND(region == nullptr);
|
||||
|
||||
region->set_transform(p_transform);
|
||||
}
|
||||
|
||||
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
|
||||
NavRegion *region = region_owner.get(p_region);
|
||||
ERR_FAIL_COND(region == nullptr);
|
||||
|
||||
region->set_mesh(p_nav_mesh);
|
||||
}
|
||||
|
||||
void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const {
|
||||
ERR_FAIL_COND(r_mesh.is_null());
|
||||
ERR_FAIL_COND(p_node == nullptr);
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
NavigationMeshGenerator::get_singleton()->clear(r_mesh);
|
||||
NavigationMeshGenerator::get_singleton()->bake(r_mesh, p_node);
|
||||
#endif
|
||||
}
|
||||
|
||||
RID GodotNavigationServer::agent_create() const {
|
||||
auto mut_this = const_cast<GodotNavigationServer *>(this);
|
||||
MutexLock lock(mut_this->operations_mutex);
|
||||
RvoAgent *agent = memnew(RvoAgent());
|
||||
RID rid = agent_owner.make_rid(agent);
|
||||
agent->set_self(rid);
|
||||
return rid;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
if (agent->get_map()) {
|
||||
if (agent->get_map()->get_self() == p_map) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
agent->get_map()->remove_agent(agent);
|
||||
}
|
||||
|
||||
agent->set_map(nullptr);
|
||||
|
||||
if (p_map.is_valid()) {
|
||||
NavMap *map = map_owner.get(p_map);
|
||||
ERR_FAIL_COND(map == nullptr);
|
||||
|
||||
agent->set_map(map);
|
||||
map->add_agent(agent);
|
||||
|
||||
if (agent->has_callback()) {
|
||||
map->set_agent_as_controlled(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->neighborDist_ = p_dist;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->maxNeighbors_ = p_count;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->timeHorizon_ = p_time;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->radius_ = p_radius;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->maxSpeed_ = p_max_speed;
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
|
||||
}
|
||||
|
||||
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
|
||||
RvoAgent *agent = agent_owner.get(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.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->get_agent()->ignore_y_ = p_ignore;
|
||||
}
|
||||
|
||||
bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND_V(agent == nullptr, false);
|
||||
|
||||
return agent->is_map_changed();
|
||||
}
|
||||
|
||||
COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) {
|
||||
RvoAgent *agent = agent_owner.get(p_agent);
|
||||
ERR_FAIL_COND(agent == nullptr);
|
||||
|
||||
agent->set_callback(p_receiver == nullptr ? 0 : p_receiver->get_instance_id(), p_method, p_udata);
|
||||
|
||||
if (agent->get_map()) {
|
||||
if (p_receiver == nullptr) {
|
||||
agent->get_map()->remove_agent_as_controlled(agent);
|
||||
} else {
|
||||
agent->get_map()->set_agent_as_controlled(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_1(free, RID, p_object) {
|
||||
if (map_owner.owns(p_object)) {
|
||||
NavMap *map = map_owner.get(p_object);
|
||||
|
||||
// Removes any assigned region
|
||||
std::vector<NavRegion *> regions = map->get_regions();
|
||||
for (size_t i(0); i < regions.size(); i++) {
|
||||
map->remove_region(regions[i]);
|
||||
regions[i]->set_map(nullptr);
|
||||
}
|
||||
|
||||
// Remove any assigned agent
|
||||
std::vector<RvoAgent *> agents = map->get_agents();
|
||||
for (size_t i(0); i < agents.size(); i++) {
|
||||
map->remove_agent(agents[i]);
|
||||
agents[i]->set_map(nullptr);
|
||||
}
|
||||
|
||||
active_maps.erase(map);
|
||||
map_owner.free(p_object);
|
||||
memdelete(map);
|
||||
|
||||
} else if (region_owner.owns(p_object)) {
|
||||
NavRegion *region = region_owner.get(p_object);
|
||||
|
||||
// Removes this region from the map if assigned
|
||||
if (region->get_map() != nullptr) {
|
||||
region->get_map()->remove_region(region);
|
||||
region->set_map(nullptr);
|
||||
}
|
||||
|
||||
region_owner.free(p_object);
|
||||
memdelete(region);
|
||||
|
||||
} else if (agent_owner.owns(p_object)) {
|
||||
RvoAgent *agent = agent_owner.get(p_object);
|
||||
|
||||
// Removes this agent from the map if assigned
|
||||
if (agent->get_map() != nullptr) {
|
||||
agent->get_map()->remove_agent(agent);
|
||||
agent->set_map(nullptr);
|
||||
}
|
||||
|
||||
agent_owner.free(p_object);
|
||||
memdelete(agent);
|
||||
|
||||
} else {
|
||||
ERR_FAIL_COND("Invalid ID.");
|
||||
}
|
||||
}
|
||||
|
||||
void GodotNavigationServer::set_active(bool p_active) const {
|
||||
auto mut_this = const_cast<GodotNavigationServer *>(this);
|
||||
MutexLock lock(mut_this->operations_mutex);
|
||||
mut_this->active = p_active;
|
||||
}
|
||||
|
||||
void GodotNavigationServer::flush_queries() {
|
||||
// In C++ we can't be sure that this is performed in the main thread
|
||||
// even with mutable functions.
|
||||
MutexLock lock(commands_mutex);
|
||||
MutexLock lock2(operations_mutex);
|
||||
for (size_t i(0); i < commands.size(); i++) {
|
||||
commands[i]->exec(this);
|
||||
memdelete(commands[i]);
|
||||
}
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
void GodotNavigationServer::process(real_t p_delta_time) {
|
||||
flush_queries();
|
||||
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In c++ we can't be sure that this is performed in the main thread
|
||||
// even with mutable functions.
|
||||
MutexLock lock(operations_mutex);
|
||||
for (int i(0); i < active_maps.size(); i++) {
|
||||
active_maps[i]->sync();
|
||||
active_maps[i]->step(p_delta_time);
|
||||
active_maps[i]->dispatch_callbacks();
|
||||
}
|
||||
}
|
||||
|
||||
#undef COMMAND_1
|
||||
#undef COMMAND_2
|
||||
#undef COMMAND_4
|
139
modules/navigation/godot_navigation_server.h
Normal file
139
modules/navigation/godot_navigation_server.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*************************************************************************/
|
||||
/* godot_navigation_server.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef GODOT_NAVIGATION_SERVER_H
|
||||
#define GODOT_NAVIGATION_SERVER_H
|
||||
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
#include "nav_map.h"
|
||||
#include "nav_region.h"
|
||||
#include "rvo_agent.h"
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
/// The commands are functions executed during the `sync` phase.
|
||||
|
||||
#define MERGE_INTERNAL(A, B) A##B
|
||||
#define MERGE(A, B) MERGE_INTERNAL(A, B)
|
||||
|
||||
#define COMMAND_1(F_NAME, T_0, D_0) \
|
||||
virtual void F_NAME(T_0 D_0) const; \
|
||||
void MERGE(_cmd_, F_NAME)(T_0 D_0)
|
||||
|
||||
#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
|
||||
virtual void F_NAME(T_0 D_0, T_1 D_1) const; \
|
||||
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
|
||||
|
||||
#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
|
||||
virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) const; \
|
||||
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
|
||||
|
||||
class GodotNavigationServer;
|
||||
|
||||
struct SetCommand {
|
||||
virtual ~SetCommand() {}
|
||||
virtual void exec(GodotNavigationServer *server) = 0;
|
||||
};
|
||||
|
||||
class GodotNavigationServer : public NavigationServer {
|
||||
Mutex commands_mutex;
|
||||
/// Mutex used to make any operation threadsafe.
|
||||
Mutex operations_mutex;
|
||||
|
||||
std::vector<SetCommand *> commands;
|
||||
|
||||
mutable RID_Owner<NavMap> map_owner;
|
||||
mutable RID_Owner<NavRegion> region_owner;
|
||||
mutable RID_Owner<RvoAgent> agent_owner;
|
||||
|
||||
bool active;
|
||||
Vector<NavMap *> active_maps;
|
||||
|
||||
public:
|
||||
GodotNavigationServer();
|
||||
virtual ~GodotNavigationServer();
|
||||
|
||||
void add_command(SetCommand *command) const;
|
||||
|
||||
virtual RID map_create() const;
|
||||
COMMAND_2(map_set_active, RID, p_map, bool, p_active);
|
||||
virtual bool map_is_active(RID p_map) const;
|
||||
|
||||
COMMAND_2(map_set_up, RID, p_map, Vector3, p_up);
|
||||
virtual Vector3 map_get_up(RID p_map) const;
|
||||
|
||||
COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size);
|
||||
virtual real_t map_get_cell_size(RID p_map) const;
|
||||
|
||||
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
|
||||
virtual real_t map_get_edge_connection_margin(RID p_map) const;
|
||||
|
||||
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
|
||||
|
||||
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const;
|
||||
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const;
|
||||
virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const;
|
||||
virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const;
|
||||
|
||||
virtual RID region_create() const;
|
||||
COMMAND_2(region_set_map, RID, p_region, RID, p_map);
|
||||
COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform);
|
||||
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
|
||||
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const;
|
||||
|
||||
virtual RID agent_create() const;
|
||||
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
|
||||
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
|
||||
COMMAND_2(agent_set_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_radius, RID, p_agent, real_t, p_radius);
|
||||
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_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, Object *, p_receiver, StringName, p_method, Variant, p_udata, Variant());
|
||||
|
||||
COMMAND_1(free, RID, p_object);
|
||||
|
||||
virtual void set_active(bool p_active) const;
|
||||
void flush_queries();
|
||||
virtual void process(real_t p_delta_time);
|
||||
};
|
||||
|
||||
#undef COMMAND_1
|
||||
#undef COMMAND_2
|
||||
#undef COMMAND_4_DEF
|
||||
|
||||
#endif // GODOT_NAVIGATION_SERVER_H
|
784
modules/navigation/nav_map.cpp
Normal file
784
modules/navigation/nav_map.cpp
Normal file
|
@ -0,0 +1,784 @@
|
|||
/*************************************************************************/
|
||||
/* nav_map.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "nav_map.h"
|
||||
|
||||
#include "core/os/threaded_array_processor.h"
|
||||
#include "nav_region.h"
|
||||
#include "rvo_agent.h"
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
#define USE_ENTRY_POINT
|
||||
|
||||
NavMap::NavMap() :
|
||||
up(0, 1, 0),
|
||||
cell_size(0.3),
|
||||
edge_connection_margin(5.0),
|
||||
regenerate_polygons(true),
|
||||
regenerate_links(true),
|
||||
agents_dirty(false),
|
||||
deltatime(0.0),
|
||||
map_update_id(0) {}
|
||||
|
||||
void NavMap::set_up(Vector3 p_up) {
|
||||
up = p_up;
|
||||
regenerate_polygons = true;
|
||||
}
|
||||
|
||||
void NavMap::set_cell_size(float p_cell_size) {
|
||||
cell_size = p_cell_size;
|
||||
regenerate_polygons = true;
|
||||
}
|
||||
|
||||
void NavMap::set_edge_connection_margin(float p_edge_connection_margin) {
|
||||
edge_connection_margin = p_edge_connection_margin;
|
||||
regenerate_links = true;
|
||||
}
|
||||
|
||||
gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
|
||||
const int x = int(Math::floor(p_pos.x / cell_size));
|
||||
const int y = int(Math::floor(p_pos.y / cell_size));
|
||||
const int z = int(Math::floor(p_pos.z / cell_size));
|
||||
|
||||
gd::PointKey p;
|
||||
p.key = 0;
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
p.z = z;
|
||||
return p;
|
||||
}
|
||||
|
||||
Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
|
||||
const gd::Polygon *begin_poly = NULL;
|
||||
const gd::Polygon *end_poly = NULL;
|
||||
Vector3 begin_point;
|
||||
Vector3 end_point;
|
||||
float begin_d = 1e20;
|
||||
float end_d = 1e20;
|
||||
|
||||
// Find the initial poly and the end poly on this map.
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
const gd::Polygon &p = polygons[i];
|
||||
|
||||
// For each point cast a face and check the distance between the origin/destination
|
||||
for (size_t point_id = 2; point_id < p.points.size(); point_id++) {
|
||||
Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
|
||||
Vector3 spoint = f.get_closest_point_to(p_origin);
|
||||
float dpoint = spoint.distance_to(p_origin);
|
||||
if (dpoint < begin_d) {
|
||||
begin_d = dpoint;
|
||||
begin_poly = &p;
|
||||
begin_point = spoint;
|
||||
}
|
||||
|
||||
spoint = f.get_closest_point_to(p_destination);
|
||||
dpoint = spoint.distance_to(p_destination);
|
||||
if (dpoint < end_d) {
|
||||
end_d = dpoint;
|
||||
end_poly = &p;
|
||||
end_point = spoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin_poly || !end_poly) {
|
||||
// No path
|
||||
return Vector<Vector3>();
|
||||
}
|
||||
|
||||
if (begin_poly == end_poly) {
|
||||
Vector<Vector3> path;
|
||||
path.resize(2);
|
||||
path.write[0] = begin_point;
|
||||
path.write[1] = end_point;
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<gd::NavigationPoly> navigation_polys;
|
||||
navigation_polys.reserve(polygons.size() * 0.75);
|
||||
|
||||
// The elements indices in the `navigation_polys`.
|
||||
int least_cost_id(-1);
|
||||
List<uint32_t> open_list;
|
||||
bool found_route = false;
|
||||
|
||||
navigation_polys.push_back(gd::NavigationPoly(begin_poly));
|
||||
{
|
||||
least_cost_id = 0;
|
||||
gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
|
||||
least_cost_poly->self_id = least_cost_id;
|
||||
least_cost_poly->entry = begin_point;
|
||||
}
|
||||
|
||||
open_list.push_back(0);
|
||||
|
||||
const gd::Polygon *reachable_end = NULL;
|
||||
float reachable_d = 1e30;
|
||||
bool is_reachable = true;
|
||||
|
||||
while (found_route == false) {
|
||||
{
|
||||
// Takes the current least_cost_poly neighbors and compute the traveled_distance of each
|
||||
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
|
||||
gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
|
||||
|
||||
const gd::Edge &edge = least_cost_poly->poly->edges[i];
|
||||
if (!edge.other_polygon)
|
||||
continue;
|
||||
|
||||
#ifdef USE_ENTRY_POINT
|
||||
Vector3 edge_line[2] = {
|
||||
least_cost_poly->poly->points[i].pos,
|
||||
least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos
|
||||
};
|
||||
|
||||
const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, edge_line);
|
||||
const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
|
||||
#else
|
||||
const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance;
|
||||
#endif
|
||||
|
||||
auto it = std::find(
|
||||
navigation_polys.begin(),
|
||||
navigation_polys.end(),
|
||||
gd::NavigationPoly(edge.other_polygon));
|
||||
|
||||
if (it != navigation_polys.end()) {
|
||||
// Oh this was visited already, can we win the cost?
|
||||
if (it->traveled_distance > new_distance) {
|
||||
it->prev_navigation_poly_id = least_cost_id;
|
||||
it->back_navigation_edge = edge.other_edge;
|
||||
it->traveled_distance = new_distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
it->entry = new_entry;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Add to open neighbours
|
||||
|
||||
navigation_polys.push_back(gd::NavigationPoly(edge.other_polygon));
|
||||
gd::NavigationPoly *np = &navigation_polys[navigation_polys.size() - 1];
|
||||
|
||||
np->self_id = navigation_polys.size() - 1;
|
||||
np->prev_navigation_poly_id = least_cost_id;
|
||||
np->back_navigation_edge = edge.other_edge;
|
||||
np->traveled_distance = new_distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
np->entry = new_entry;
|
||||
#endif
|
||||
open_list.push_back(navigation_polys.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the least cost polygon from the open list so we can advance.
|
||||
open_list.erase(least_cost_id);
|
||||
|
||||
if (open_list.size() == 0) {
|
||||
// When the open list is empty at this point the End Polygon is not reachable
|
||||
// so use the further reachable polygon
|
||||
ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons");
|
||||
is_reachable = false;
|
||||
if (reachable_end == NULL) {
|
||||
// The path is not found and there is not a way out.
|
||||
break;
|
||||
}
|
||||
|
||||
// Set as end point the furthest reachable point.
|
||||
end_poly = reachable_end;
|
||||
end_d = 1e20;
|
||||
for (size_t point_id = 2; point_id < end_poly->points.size(); point_id++) {
|
||||
Face3 f(end_poly->points[point_id - 2].pos, end_poly->points[point_id - 1].pos, end_poly->points[point_id].pos);
|
||||
Vector3 spoint = f.get_closest_point_to(p_destination);
|
||||
float dpoint = spoint.distance_to(p_destination);
|
||||
if (dpoint < end_d) {
|
||||
end_point = spoint;
|
||||
end_d = dpoint;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset open and navigation_polys
|
||||
gd::NavigationPoly np = navigation_polys[0];
|
||||
navigation_polys.clear();
|
||||
navigation_polys.push_back(np);
|
||||
open_list.clear();
|
||||
open_list.push_back(0);
|
||||
|
||||
reachable_end = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now take the new least_cost_poly from the open list.
|
||||
least_cost_id = -1;
|
||||
float least_cost = 1e30;
|
||||
|
||||
for (auto element = open_list.front(); element != NULL; element = element->next()) {
|
||||
gd::NavigationPoly *np = &navigation_polys[element->get()];
|
||||
float cost = np->traveled_distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
cost += np->entry.distance_to(end_point);
|
||||
#else
|
||||
cost += np->poly->center.distance_to(end_point);
|
||||
#endif
|
||||
if (cost < least_cost) {
|
||||
least_cost_id = np->self_id;
|
||||
least_cost = cost;
|
||||
}
|
||||
}
|
||||
|
||||
// Stores the further reachable end polygon, in case our goal is not reachable.
|
||||
if (is_reachable) {
|
||||
float d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
|
||||
if (reachable_d > d) {
|
||||
reachable_d = d;
|
||||
reachable_end = navigation_polys[least_cost_id].poly;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_BREAK(least_cost_id == -1);
|
||||
|
||||
// Check if we reached the end
|
||||
if (navigation_polys[least_cost_id].poly == end_poly) {
|
||||
// Yep, done!!
|
||||
found_route = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_route) {
|
||||
Vector<Vector3> path;
|
||||
if (p_optimize) {
|
||||
// String pulling
|
||||
|
||||
gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id];
|
||||
Vector3 apex_point = end_point;
|
||||
Vector3 portal_left = apex_point;
|
||||
Vector3 portal_right = apex_point;
|
||||
gd::NavigationPoly *left_poly = apex_poly;
|
||||
gd::NavigationPoly *right_poly = apex_poly;
|
||||
gd::NavigationPoly *p = apex_poly;
|
||||
|
||||
path.push_back(end_point);
|
||||
|
||||
while (p) {
|
||||
Vector3 left;
|
||||
Vector3 right;
|
||||
|
||||
#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b)))
|
||||
|
||||
if (p->poly == begin_poly) {
|
||||
left = begin_point;
|
||||
right = begin_point;
|
||||
} else {
|
||||
int prev = p->back_navigation_edge;
|
||||
int prev_n = (p->back_navigation_edge + 1) % p->poly->points.size();
|
||||
left = p->poly->points[prev].pos;
|
||||
right = p->poly->points[prev_n].pos;
|
||||
|
||||
//if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
|
||||
if (p->poly->clockwise) {
|
||||
SWAP(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
|
||||
if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
|
||||
//process
|
||||
if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
|
||||
left_poly = p;
|
||||
portal_left = left;
|
||||
} else {
|
||||
clip_path(navigation_polys, path, apex_poly, portal_right, right_poly);
|
||||
|
||||
apex_point = portal_right;
|
||||
p = right_poly;
|
||||
left_poly = p;
|
||||
apex_poly = p;
|
||||
portal_left = apex_point;
|
||||
portal_right = apex_point;
|
||||
path.push_back(apex_point);
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
|
||||
//process
|
||||
if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
|
||||
right_poly = p;
|
||||
portal_right = right;
|
||||
} else {
|
||||
clip_path(navigation_polys, path, apex_poly, portal_left, left_poly);
|
||||
|
||||
apex_point = portal_left;
|
||||
p = left_poly;
|
||||
right_poly = p;
|
||||
apex_poly = p;
|
||||
portal_right = apex_point;
|
||||
portal_left = apex_point;
|
||||
path.push_back(apex_point);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->prev_navigation_poly_id != -1)
|
||||
p = &navigation_polys[p->prev_navigation_poly_id];
|
||||
else
|
||||
// The end
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
if (path[path.size() - 1] != begin_point)
|
||||
path.push_back(begin_point);
|
||||
|
||||
path.invert();
|
||||
|
||||
} else {
|
||||
path.push_back(end_point);
|
||||
|
||||
// Add mid points
|
||||
int np_id = least_cost_id;
|
||||
while (np_id != -1) {
|
||||
#ifdef USE_ENTRY_POINT
|
||||
Vector3 point = navigation_polys[np_id].entry;
|
||||
#else
|
||||
int prev = navigation_polys[np_id].back_navigation_edge;
|
||||
int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size();
|
||||
Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5;
|
||||
#endif
|
||||
|
||||
path.push_back(point);
|
||||
np_id = navigation_polys[np_id].prev_navigation_poly_id;
|
||||
}
|
||||
|
||||
path.invert();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
return Vector<Vector3>();
|
||||
}
|
||||
|
||||
Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
|
||||
bool use_collision = p_use_collision;
|
||||
Vector3 closest_point;
|
||||
real_t closest_point_d = 1e20;
|
||||
|
||||
// Find the initial poly and the end poly on this map.
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
const gd::Polygon &p = polygons[i];
|
||||
|
||||
// For each point cast a face and check the distance to the segment
|
||||
for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) {
|
||||
const Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
|
||||
Vector3 inters;
|
||||
if (f.intersects_segment(p_from, p_to, &inters)) {
|
||||
const real_t d = closest_point_d = p_from.distance_to(inters);
|
||||
if (use_collision == false) {
|
||||
closest_point = inters;
|
||||
use_collision = true;
|
||||
closest_point_d = d;
|
||||
} else if (closest_point_d > d) {
|
||||
closest_point = inters;
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_collision == false) {
|
||||
for (size_t point_id = 0; point_id < p.points.size(); point_id += 1) {
|
||||
Vector3 a, b;
|
||||
|
||||
Geometry::get_closest_points_between_segments(
|
||||
p_from,
|
||||
p_to,
|
||||
p.points[point_id].pos,
|
||||
p.points[(point_id + 1) % p.points.size()].pos,
|
||||
a,
|
||||
b);
|
||||
|
||||
const real_t d = a.distance_to(b);
|
||||
if (d < closest_point_d) {
|
||||
closest_point_d = d;
|
||||
closest_point = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
Vector3 NavMap::get_closest_point(const Vector3 &p_point) const {
|
||||
// TODO this is really not optimal, please redesign the API to directly return all this data
|
||||
|
||||
Vector3 closest_point;
|
||||
real_t closest_point_d = 1e20;
|
||||
|
||||
// Find the initial poly and the end poly on this map.
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
const gd::Polygon &p = polygons[i];
|
||||
|
||||
// For each point cast a face and check the distance to the point
|
||||
for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) {
|
||||
const Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
|
||||
const Vector3 inters = f.get_closest_point_to(p_point);
|
||||
const real_t d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
Vector3 NavMap::get_closest_point_normal(const Vector3 &p_point) const {
|
||||
// TODO this is really not optimal, please redesign the API to directly return all this data
|
||||
|
||||
Vector3 closest_point;
|
||||
Vector3 closest_point_normal;
|
||||
real_t closest_point_d = 1e20;
|
||||
|
||||
// Find the initial poly and the end poly on this map.
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
const gd::Polygon &p = polygons[i];
|
||||
|
||||
// For each point cast a face and check the distance to the point
|
||||
for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) {
|
||||
const Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
|
||||
const Vector3 inters = f.get_closest_point_to(p_point);
|
||||
const real_t d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_normal = f.get_plane().normal;
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point_normal;
|
||||
}
|
||||
|
||||
RID NavMap::get_closest_point_owner(const Vector3 &p_point) const {
|
||||
// TODO this is really not optimal, please redesign the API to directly return all this data
|
||||
|
||||
Vector3 closest_point;
|
||||
RID closest_point_owner;
|
||||
real_t closest_point_d = 1e20;
|
||||
|
||||
// Find the initial poly and the end poly on this map.
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
const gd::Polygon &p = polygons[i];
|
||||
|
||||
// For each point cast a face and check the distance to the point
|
||||
for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) {
|
||||
const Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
|
||||
const Vector3 inters = f.get_closest_point_to(p_point);
|
||||
const real_t d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_owner = p.owner->get_self();
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point_owner;
|
||||
}
|
||||
|
||||
void NavMap::add_region(NavRegion *p_region) {
|
||||
regions.push_back(p_region);
|
||||
regenerate_links = true;
|
||||
}
|
||||
|
||||
void NavMap::remove_region(NavRegion *p_region) {
|
||||
std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region);
|
||||
if (it != regions.end()) {
|
||||
regions.erase(it);
|
||||
regenerate_links = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool NavMap::has_agent(RvoAgent *agent) const {
|
||||
return std::find(agents.begin(), agents.end(), agent) != agents.end();
|
||||
}
|
||||
|
||||
void NavMap::add_agent(RvoAgent *agent) {
|
||||
if (!has_agent(agent)) {
|
||||
agents.push_back(agent);
|
||||
agents_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::remove_agent(RvoAgent *agent) {
|
||||
remove_agent_as_controlled(agent);
|
||||
auto it = std::find(agents.begin(), agents.end(), agent);
|
||||
if (it != agents.end()) {
|
||||
agents.erase(it);
|
||||
agents_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::set_agent_as_controlled(RvoAgent *agent) {
|
||||
const bool exist = std::find(controlled_agents.begin(), controlled_agents.end(), agent) != controlled_agents.end();
|
||||
if (!exist) {
|
||||
ERR_FAIL_COND(!has_agent(agent));
|
||||
controlled_agents.push_back(agent);
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
|
||||
auto it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
|
||||
if (it != controlled_agents.end()) {
|
||||
controlled_agents.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::sync() {
|
||||
if (regenerate_polygons) {
|
||||
for (size_t r(0); r < regions.size(); r++) {
|
||||
regions[r]->scratch_polygons();
|
||||
}
|
||||
regenerate_links = true;
|
||||
}
|
||||
|
||||
for (size_t r(0); r < regions.size(); r++) {
|
||||
if (regions[r]->sync()) {
|
||||
regenerate_links = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (regenerate_links) {
|
||||
// Copy all region polygons in the map.
|
||||
int count = 0;
|
||||
for (size_t r(0); r < regions.size(); r++) {
|
||||
count += regions[r]->get_polygons().size();
|
||||
}
|
||||
|
||||
polygons.resize(count);
|
||||
count = 0;
|
||||
|
||||
for (size_t r(0); r < regions.size(); r++) {
|
||||
std::copy(
|
||||
regions[r]->get_polygons().data(),
|
||||
regions[r]->get_polygons().data() + regions[r]->get_polygons().size(),
|
||||
polygons.begin() + count);
|
||||
|
||||
count += regions[r]->get_polygons().size();
|
||||
}
|
||||
|
||||
// Connects the `Edges` of all the `Polygons` of all `Regions` each other.
|
||||
Map<gd::EdgeKey, gd::Connection> connections;
|
||||
|
||||
for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
|
||||
gd::Polygon &poly(polygons[poly_id]);
|
||||
|
||||
for (size_t p(0); p < poly.points.size(); p++) {
|
||||
int next_point = (p + 1) % poly.points.size();
|
||||
gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
|
||||
|
||||
Map<gd::EdgeKey, gd::Connection>::Element *connection = connections.find(ek);
|
||||
if (!connection) {
|
||||
// Nothing yet
|
||||
gd::Connection c;
|
||||
c.A = &poly;
|
||||
c.A_edge = p;
|
||||
c.B = NULL;
|
||||
c.B_edge = -1;
|
||||
connections[ek] = c;
|
||||
|
||||
} else if (connection->get().B == NULL) {
|
||||
CRASH_COND(connection->get().A == NULL); // Unreachable
|
||||
|
||||
// Connect the two Polygons by this edge
|
||||
connection->get().B = &poly;
|
||||
connection->get().B_edge = p;
|
||||
|
||||
connection->get().A->edges[connection->get().A_edge].this_edge = connection->get().A_edge;
|
||||
connection->get().A->edges[connection->get().A_edge].other_polygon = connection->get().B;
|
||||
connection->get().A->edges[connection->get().A_edge].other_edge = connection->get().B_edge;
|
||||
|
||||
connection->get().B->edges[connection->get().B_edge].this_edge = connection->get().B_edge;
|
||||
connection->get().B->edges[connection->get().B_edge].other_polygon = connection->get().A;
|
||||
connection->get().B->edges[connection->get().B_edge].other_edge = connection->get().A_edge;
|
||||
} else {
|
||||
// The edge is already connected with another edge, skip.
|
||||
ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the Navigation's `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Takes all the free edges.
|
||||
std::vector<gd::FreeEdge> free_edges;
|
||||
free_edges.reserve(connections.size());
|
||||
|
||||
for (auto connection_element = connections.front(); connection_element; connection_element = connection_element->next()) {
|
||||
if (connection_element->get().B == NULL) {
|
||||
CRASH_COND(connection_element->get().A == NULL); // Unreachable
|
||||
CRASH_COND(connection_element->get().A_edge < 0); // Unreachable
|
||||
|
||||
// This is a free edge
|
||||
uint32_t id(free_edges.size());
|
||||
free_edges.push_back(gd::FreeEdge());
|
||||
free_edges[id].is_free = true;
|
||||
free_edges[id].poly = connection_element->get().A;
|
||||
free_edges[id].edge_id = connection_element->get().A_edge;
|
||||
uint32_t point_0(free_edges[id].edge_id);
|
||||
uint32_t point_1((free_edges[id].edge_id + 1) % free_edges[id].poly->points.size());
|
||||
Vector3 pos_0 = free_edges[id].poly->points[point_0].pos;
|
||||
Vector3 pos_1 = free_edges[id].poly->points[point_1].pos;
|
||||
Vector3 relative = pos_1 - pos_0;
|
||||
free_edges[id].edge_center = (pos_0 + pos_1) / 2.0;
|
||||
free_edges[id].edge_dir = relative.normalized();
|
||||
free_edges[id].edge_len_squared = relative.length_squared();
|
||||
}
|
||||
}
|
||||
|
||||
const float ecm_squared(edge_connection_margin * edge_connection_margin);
|
||||
#define LEN_TOLLERANCE 0.1
|
||||
#define DIR_TOLLERANCE 0.9
|
||||
// In front of tollerance
|
||||
#define IFO_TOLLERANCE 0.5
|
||||
|
||||
// Find the compatible near edges.
|
||||
//
|
||||
// Note:
|
||||
// Considering that the edges must be compatible (for obvious reasons)
|
||||
// to be connected, create new polygons to remove that small gap is
|
||||
// not really useful and would result in wasteful computation during
|
||||
// connection, integration and path finding.
|
||||
for (size_t i(0); i < free_edges.size(); i++) {
|
||||
if (!free_edges[i].is_free) {
|
||||
continue;
|
||||
}
|
||||
gd::FreeEdge &edge = free_edges[i];
|
||||
for (size_t y(0); y < free_edges.size(); y++) {
|
||||
gd::FreeEdge &other_edge = free_edges[y];
|
||||
if (i == y || !other_edge.is_free || edge.poly->owner == other_edge.poly->owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 rel_centers = other_edge.edge_center - edge.edge_center;
|
||||
if (ecm_squared > rel_centers.length_squared() // Are enough closer?
|
||||
&& ABS(edge.edge_len_squared - other_edge.edge_len_squared) < LEN_TOLLERANCE // Are the same length?
|
||||
&& ABS(edge.edge_dir.dot(other_edge.edge_dir)) > DIR_TOLLERANCE // Are alligned?
|
||||
&& ABS(rel_centers.normalized().dot(edge.edge_dir)) < IFO_TOLLERANCE // Are one in front the other?
|
||||
) {
|
||||
// The edges can be connected
|
||||
edge.is_free = false;
|
||||
other_edge.is_free = false;
|
||||
|
||||
edge.poly->edges[edge.edge_id].this_edge = edge.edge_id;
|
||||
edge.poly->edges[edge.edge_id].other_edge = other_edge.edge_id;
|
||||
edge.poly->edges[edge.edge_id].other_polygon = other_edge.poly;
|
||||
|
||||
other_edge.poly->edges[other_edge.edge_id].this_edge = other_edge.edge_id;
|
||||
other_edge.poly->edges[other_edge.edge_id].other_edge = edge.edge_id;
|
||||
other_edge.poly->edges[other_edge.edge_id].other_polygon = edge.poly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (regenerate_links) {
|
||||
map_update_id = map_update_id + 1 % 9999999;
|
||||
}
|
||||
|
||||
if (agents_dirty) {
|
||||
std::vector<RVO::Agent *> raw_agents;
|
||||
raw_agents.reserve(agents.size());
|
||||
for (size_t i(0); i < agents.size(); i++)
|
||||
raw_agents.push_back(agents[i]->get_agent());
|
||||
rvo.buildAgentTree(raw_agents);
|
||||
}
|
||||
|
||||
regenerate_polygons = false;
|
||||
regenerate_links = false;
|
||||
agents_dirty = false;
|
||||
}
|
||||
|
||||
void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
|
||||
(*(agent + index))->get_agent()->computeNeighbors(&rvo);
|
||||
(*(agent + index))->get_agent()->computeNewVelocity(deltatime);
|
||||
}
|
||||
|
||||
void NavMap::step(real_t p_deltatime) {
|
||||
deltatime = p_deltatime;
|
||||
if (controlled_agents.size() > 0) {
|
||||
thread_process_array(
|
||||
controlled_agents.size(),
|
||||
this,
|
||||
&NavMap::compute_single_step,
|
||||
controlled_agents.data());
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::dispatch_callbacks() {
|
||||
for (int i(0); i < static_cast<int>(controlled_agents.size()); i++) {
|
||||
controlled_agents[i]->dispatch_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
|
||||
Vector3 from = path[path.size() - 1];
|
||||
|
||||
if (from.distance_to(p_to_point) < CMP_EPSILON)
|
||||
return;
|
||||
Plane cut_plane;
|
||||
cut_plane.normal = (from - p_to_point).cross(up);
|
||||
if (cut_plane.normal == Vector3())
|
||||
return;
|
||||
cut_plane.normal.normalize();
|
||||
cut_plane.d = cut_plane.normal.dot(from);
|
||||
|
||||
while (from_poly != p_to_poly) {
|
||||
int back_nav_edge = from_poly->back_navigation_edge;
|
||||
Vector3 a = from_poly->poly->points[back_nav_edge].pos;
|
||||
Vector3 b = from_poly->poly->points[(back_nav_edge + 1) % from_poly->poly->points.size()].pos;
|
||||
|
||||
ERR_FAIL_COND(from_poly->prev_navigation_poly_id == -1);
|
||||
from_poly = &p_navigation_polys[from_poly->prev_navigation_poly_id];
|
||||
|
||||
if (a.distance_to(b) > CMP_EPSILON) {
|
||||
Vector3 inters;
|
||||
if (cut_plane.intersects_segment(a, b, &inters)) {
|
||||
if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) {
|
||||
path.push_back(inters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
modules/navigation/nav_map.h
Normal file
140
modules/navigation/nav_map.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*************************************************************************/
|
||||
/* nav_map.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef RVO_SPACE_H
|
||||
#define RVO_SPACE_H
|
||||
|
||||
#include "nav_rid.h"
|
||||
|
||||
#include "core/math/math_defs.h"
|
||||
#include "nav_utils.h"
|
||||
#include <KdTree.h>
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
class NavRegion;
|
||||
class RvoAgent;
|
||||
class NavRegion;
|
||||
|
||||
class NavMap : public NavRid {
|
||||
/// Map Up
|
||||
Vector3 up;
|
||||
|
||||
/// To find the polygons edges the vertices are displaced in a grid where
|
||||
/// each cell has the following cell_size.
|
||||
real_t cell_size;
|
||||
|
||||
/// This value is used to detect the near edges to connect.
|
||||
real_t edge_connection_margin;
|
||||
|
||||
bool regenerate_polygons;
|
||||
bool regenerate_links;
|
||||
|
||||
std::vector<NavRegion *> regions;
|
||||
|
||||
/// Map polygons
|
||||
std::vector<gd::Polygon> polygons;
|
||||
|
||||
/// Rvo world
|
||||
RVO::KdTree rvo;
|
||||
|
||||
/// Is agent array modified?
|
||||
bool agents_dirty;
|
||||
|
||||
/// All the Agents (even the controlled one)
|
||||
std::vector<RvoAgent *> agents;
|
||||
|
||||
/// Controlled agents
|
||||
std::vector<RvoAgent *> controlled_agents;
|
||||
|
||||
/// Physics delta time
|
||||
real_t deltatime;
|
||||
|
||||
/// Change the id each time the map is updated.
|
||||
uint32_t map_update_id;
|
||||
|
||||
public:
|
||||
NavMap();
|
||||
|
||||
void set_up(Vector3 p_up);
|
||||
Vector3 get_up() const {
|
||||
return up;
|
||||
}
|
||||
|
||||
void set_cell_size(float p_cell_size);
|
||||
float get_cell_size() const {
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
void set_edge_connection_margin(float p_edge_connection_margin);
|
||||
float get_edge_connection_margin() const {
|
||||
return edge_connection_margin;
|
||||
}
|
||||
|
||||
gd::PointKey get_point_key(const Vector3 &p_pos) const;
|
||||
|
||||
Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
|
||||
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const;
|
||||
Vector3 get_closest_point(const Vector3 &p_point) const;
|
||||
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
|
||||
RID get_closest_point_owner(const Vector3 &p_point) const;
|
||||
|
||||
void add_region(NavRegion *p_region);
|
||||
void remove_region(NavRegion *p_region);
|
||||
const std::vector<NavRegion *> &get_regions() const {
|
||||
return regions;
|
||||
}
|
||||
|
||||
bool has_agent(RvoAgent *agent) const;
|
||||
void add_agent(RvoAgent *agent);
|
||||
void remove_agent(RvoAgent *agent);
|
||||
const std::vector<RvoAgent *> &get_agents() const {
|
||||
return agents;
|
||||
}
|
||||
|
||||
void set_agent_as_controlled(RvoAgent *agent);
|
||||
void remove_agent_as_controlled(RvoAgent *agent);
|
||||
|
||||
uint32_t get_map_update_id() const {
|
||||
return map_update_id;
|
||||
}
|
||||
|
||||
void sync();
|
||||
void step(real_t p_deltatime);
|
||||
void dispatch_callbacks();
|
||||
|
||||
private:
|
||||
void compute_single_step(uint32_t index, RvoAgent **agent);
|
||||
void clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
|
||||
};
|
||||
|
||||
#endif // RVO_SPACE_H
|
134
modules/navigation/nav_region.cpp
Normal file
134
modules/navigation/nav_region.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*************************************************************************/
|
||||
/* nav_region.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "nav_region.h"
|
||||
|
||||
#include "nav_map.h"
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
NavRegion::NavRegion() :
|
||||
map(NULL),
|
||||
polygons_dirty(true) {
|
||||
}
|
||||
|
||||
void NavRegion::set_map(NavMap *p_map) {
|
||||
map = p_map;
|
||||
polygons_dirty = true;
|
||||
}
|
||||
|
||||
void NavRegion::set_transform(Transform p_transform) {
|
||||
transform = p_transform;
|
||||
polygons_dirty = true;
|
||||
}
|
||||
|
||||
void NavRegion::set_mesh(Ref<NavigationMesh> p_mesh) {
|
||||
mesh = p_mesh;
|
||||
polygons_dirty = true;
|
||||
}
|
||||
|
||||
bool NavRegion::sync() {
|
||||
bool something_changed = polygons_dirty /* || something_dirty? */;
|
||||
|
||||
update_polygons();
|
||||
|
||||
return something_changed;
|
||||
}
|
||||
|
||||
void NavRegion::update_polygons() {
|
||||
if (!polygons_dirty) {
|
||||
return;
|
||||
}
|
||||
polygons.clear();
|
||||
polygons_dirty = false;
|
||||
|
||||
if (map == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mesh.is_null())
|
||||
return;
|
||||
|
||||
PoolVector<Vector3> vertices = mesh->get_vertices();
|
||||
int len = vertices.size();
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
PoolVector<Vector3>::Read vertices_r = vertices.read();
|
||||
|
||||
polygons.resize(mesh->get_polygon_count());
|
||||
|
||||
// Build
|
||||
for (size_t i(0); i < polygons.size(); i++) {
|
||||
gd::Polygon &p = polygons[i];
|
||||
p.owner = this;
|
||||
|
||||
Vector<int> mesh_poly = mesh->get_polygon(i);
|
||||
const int *indices = mesh_poly.ptr();
|
||||
bool valid(true);
|
||||
p.points.resize(mesh_poly.size());
|
||||
p.edges.resize(mesh_poly.size());
|
||||
|
||||
Vector3 center;
|
||||
float sum(0);
|
||||
|
||||
for (int j(0); j < mesh_poly.size(); j++) {
|
||||
int idx = indices[j];
|
||||
if (idx < 0 || idx >= len) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3 point_position = transform.xform(vertices_r[idx]);
|
||||
p.points[j].pos = point_position;
|
||||
p.points[j].key = map->get_point_key(point_position);
|
||||
|
||||
center += point_position; // Composing the center of the polygon
|
||||
|
||||
if (j >= 2) {
|
||||
Vector3 epa = transform.xform(vertices_r[indices[j - 2]]);
|
||||
Vector3 epb = transform.xform(vertices_r[indices[j - 1]]);
|
||||
|
||||
sum += map->get_up().dot((epb - epa).cross(point_position - epa));
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
ERR_BREAK_MSG(!valid, "The navigation mesh set in this region is not valid!");
|
||||
}
|
||||
|
||||
p.clockwise = sum > 0;
|
||||
if (mesh_poly.size() != 0) {
|
||||
p.center = center / float(mesh_poly.size());
|
||||
}
|
||||
}
|
||||
}
|
89
modules/navigation/nav_region.h
Normal file
89
modules/navigation/nav_region.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*************************************************************************/
|
||||
/* nav_region.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAV_REGION_H
|
||||
#define NAV_REGION_H
|
||||
|
||||
#include "nav_rid.h"
|
||||
|
||||
#include "nav_utils.h"
|
||||
#include "scene/3d/navigation.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
class NavMap;
|
||||
class NavRegion;
|
||||
|
||||
class NavRegion : public NavRid {
|
||||
NavMap *map;
|
||||
Transform transform;
|
||||
Ref<NavigationMesh> mesh;
|
||||
|
||||
bool polygons_dirty;
|
||||
|
||||
/// Cache
|
||||
std::vector<gd::Polygon> polygons;
|
||||
|
||||
public:
|
||||
NavRegion();
|
||||
|
||||
void scratch_polygons() {
|
||||
polygons_dirty = true;
|
||||
}
|
||||
|
||||
void set_map(NavMap *p_map);
|
||||
NavMap *get_map() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
void set_transform(Transform transform);
|
||||
const Transform &get_transform() const {
|
||||
return transform;
|
||||
}
|
||||
|
||||
void set_mesh(Ref<NavigationMesh> p_mesh);
|
||||
const Ref<NavigationMesh> get_mesh() const {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::vector<gd::Polygon> const &get_polygons() const {
|
||||
return polygons;
|
||||
}
|
||||
|
||||
bool sync();
|
||||
|
||||
private:
|
||||
void update_polygons();
|
||||
};
|
||||
|
||||
#endif // NAV_REGION_H
|
48
modules/navigation/nav_rid.h
Normal file
48
modules/navigation/nav_rid.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*************************************************************************/
|
||||
/* nav_rid.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAV_RID_H
|
||||
#define NAV_RID_H
|
||||
|
||||
#include "core/rid.h"
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
class NavRid : public RID_Data {
|
||||
RID self;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
|
||||
_FORCE_INLINE_ RID get_self() const { return self; }
|
||||
};
|
||||
|
||||
#endif // NAV_RID_H
|
166
modules/navigation/nav_utils.h
Normal file
166
modules/navigation/nav_utils.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*************************************************************************/
|
||||
/* nav_utils.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAV_UTILS_H
|
||||
#define NAV_UTILS_H
|
||||
|
||||
#include "core/math/vector3.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
class NavRegion;
|
||||
|
||||
namespace gd {
|
||||
struct Polygon;
|
||||
|
||||
union PointKey {
|
||||
struct {
|
||||
int64_t x : 21;
|
||||
int64_t y : 22;
|
||||
int64_t z : 21;
|
||||
};
|
||||
|
||||
uint64_t key;
|
||||
bool operator<(const PointKey &p_key) const { return key < p_key.key; }
|
||||
};
|
||||
|
||||
struct EdgeKey {
|
||||
PointKey a;
|
||||
PointKey b;
|
||||
|
||||
bool operator<(const EdgeKey &p_key) const {
|
||||
return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
|
||||
}
|
||||
|
||||
EdgeKey(const PointKey &p_a = PointKey(), const PointKey &p_b = PointKey()) :
|
||||
a(p_a),
|
||||
b(p_b) {
|
||||
if (a.key > b.key) {
|
||||
SWAP(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Point {
|
||||
Vector3 pos;
|
||||
PointKey key;
|
||||
};
|
||||
|
||||
struct Edge {
|
||||
/// This edge ID
|
||||
int this_edge;
|
||||
|
||||
/// Other Polygon
|
||||
Polygon *other_polygon;
|
||||
|
||||
/// The other `Polygon` at this edge id has this `Polygon`.
|
||||
int other_edge;
|
||||
|
||||
Edge() {
|
||||
this_edge = -1;
|
||||
other_polygon = NULL;
|
||||
other_edge = -1;
|
||||
}
|
||||
};
|
||||
|
||||
struct Polygon {
|
||||
NavRegion *owner;
|
||||
|
||||
/// The points of this `Polygon`
|
||||
std::vector<Point> points;
|
||||
|
||||
/// Are the points clockwise ?
|
||||
bool clockwise;
|
||||
|
||||
/// The edges of this `Polygon`
|
||||
std::vector<Edge> edges;
|
||||
|
||||
/// The center of this `Polygon`
|
||||
Vector3 center;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
Polygon *A;
|
||||
int A_edge;
|
||||
Polygon *B;
|
||||
int B_edge;
|
||||
|
||||
Connection() {
|
||||
A = NULL;
|
||||
B = NULL;
|
||||
A_edge = -1;
|
||||
B_edge = -1;
|
||||
}
|
||||
};
|
||||
|
||||
struct NavigationPoly {
|
||||
uint32_t self_id;
|
||||
/// This poly.
|
||||
const Polygon *poly;
|
||||
/// The previous navigation poly (id in the `navigation_poly` array).
|
||||
int prev_navigation_poly_id;
|
||||
/// The edge id in this `Poly` to reach the `prev_navigation_poly_id`.
|
||||
uint32_t back_navigation_edge;
|
||||
/// The entry location of this poly.
|
||||
Vector3 entry;
|
||||
/// The distance to the destination.
|
||||
float traveled_distance;
|
||||
|
||||
NavigationPoly(const Polygon *p_poly) :
|
||||
self_id(0),
|
||||
poly(p_poly),
|
||||
prev_navigation_poly_id(-1),
|
||||
back_navigation_edge(0),
|
||||
traveled_distance(0.0) {
|
||||
}
|
||||
|
||||
bool operator==(const NavigationPoly &other) const {
|
||||
return this->poly == other.poly;
|
||||
}
|
||||
|
||||
bool operator!=(const NavigationPoly &other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
struct FreeEdge {
|
||||
bool is_free;
|
||||
Polygon *poly;
|
||||
uint32_t edge_id;
|
||||
Vector3 edge_center;
|
||||
Vector3 edge_dir;
|
||||
float edge_len_squared;
|
||||
};
|
||||
} // namespace gd
|
||||
|
||||
#endif // NAV_UTILS_H
|
|
@ -28,10 +28,12 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "navigation_mesh_editor_plugin.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "navigation_mesh_generator.h"
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
|
@ -54,22 +56,21 @@ void NavigationMeshEditor::_bake_pressed() {
|
|||
button_bake->set_pressed(false);
|
||||
|
||||
ERR_FAIL_COND(!node);
|
||||
const String conf_warning = node->get_configuration_warning();
|
||||
if (!conf_warning.empty()) {
|
||||
err_dialog->set_text(conf_warning);
|
||||
if (!node->get_navigation_mesh().is_valid()) {
|
||||
err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work."));
|
||||
err_dialog->popup_centered_minsize();
|
||||
return;
|
||||
}
|
||||
|
||||
EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
|
||||
EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
|
||||
NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
|
||||
NavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
|
||||
|
||||
node->update_gizmo();
|
||||
}
|
||||
|
||||
void NavigationMeshEditor::_clear_pressed() {
|
||||
if (node) {
|
||||
EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
|
||||
NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
|
||||
}
|
||||
|
||||
button_bake->set_pressed(false);
|
||||
|
@ -149,3 +150,5 @@ NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) {
|
|||
|
||||
NavigationMeshEditorPlugin::~NavigationMeshEditorPlugin() {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -28,12 +28,15 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_MESH_GENERATOR_PLUGIN_H
|
||||
#define NAVIGATION_MESH_GENERATOR_PLUGIN_H
|
||||
#ifndef NAVIGATION_MESH_EDITOR_PLUGIN_H
|
||||
#define NAVIGATION_MESH_EDITOR_PLUGIN_H
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "navigation_mesh_generator.h"
|
||||
|
||||
class NavigationMeshInstance;
|
||||
|
||||
class NavigationMeshEditor : public Control {
|
||||
friend class NavigationMeshEditorPlugin;
|
||||
|
@ -80,4 +83,6 @@ public:
|
|||
~NavigationMeshEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_MESH_GENERATOR_PLUGIN_H
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -28,13 +28,16 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "navigation_mesh_generator.h"
|
||||
|
||||
#include "core/math/convex_hull.h"
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "navigation_mesh_generator.h"
|
||||
//#include "core/math/quick_hull.h"
|
||||
//#include "core/math/convex_hull.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "scene/3d/collision_shape.h"
|
||||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/3d/multimesh_instance.h"
|
||||
#include "scene/3d/physics_body.h"
|
||||
#include "scene/resources/box_shape.h"
|
||||
#include "scene/resources/capsule_shape.h"
|
||||
|
@ -47,6 +50,12 @@
|
|||
#include "scene/resources/sphere_shape.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_CSG_ENABLED
|
||||
#include "modules/csg/csg_shape.h"
|
||||
#endif
|
||||
|
@ -54,19 +63,19 @@
|
|||
#include "modules/gridmap/grid_map.h"
|
||||
#endif
|
||||
|
||||
EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = nullptr;
|
||||
NavigationMeshGenerator *NavigationMeshGenerator::singleton = NULL;
|
||||
|
||||
void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
|
||||
p_verticies.push_back(p_vec3.x);
|
||||
p_verticies.push_back(p_vec3.y);
|
||||
p_verticies.push_back(p_vec3.z);
|
||||
void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices) {
|
||||
p_vertices.push_back(p_vec3.x);
|
||||
p_vertices.push_back(p_vec3.y);
|
||||
p_vertices.push_back(p_vec3.z);
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
|
||||
void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
|
||||
int current_vertex_count;
|
||||
|
||||
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
|
||||
current_vertex_count = p_verticies.size() / 3;
|
||||
current_vertex_count = p_vertices.size() / 3;
|
||||
|
||||
if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
|
||||
continue;
|
||||
|
@ -93,7 +102,7 @@ void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Tra
|
|||
PoolVector<int>::Read ir = mesh_indices.read();
|
||||
|
||||
for (int j = 0; j < mesh_vertices.size(); j++) {
|
||||
_add_vertex(p_xform.xform(vr[j]), p_verticies);
|
||||
_add_vertex(p_xform.xform(vr[j]), p_vertices);
|
||||
}
|
||||
|
||||
for (int j = 0; j < face_count; j++) {
|
||||
|
@ -105,9 +114,9 @@ void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Tra
|
|||
} else {
|
||||
face_count = mesh_vertices.size() / 3;
|
||||
for (int j = 0; j < face_count; j++) {
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 0]), p_verticies);
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 2]), p_verticies);
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 1]), p_verticies);
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 0]), p_vertices);
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 2]), p_vertices);
|
||||
_add_vertex(p_xform.xform(vr[j * 3 + 1]), p_vertices);
|
||||
|
||||
p_indices.push_back(current_vertex_count + (j * 3 + 0));
|
||||
p_indices.push_back(current_vertex_count + (j * 3 + 1));
|
||||
|
@ -117,14 +126,14 @@ void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Tra
|
|||
}
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
|
||||
void NavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
|
||||
int face_count = p_faces.size() / 3;
|
||||
int current_vertex_count = p_verticies.size() / 3;
|
||||
int current_vertex_count = p_vertices.size() / 3;
|
||||
|
||||
for (int j = 0; j < face_count; j++) {
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_verticies);
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_verticies);
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_verticies);
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_vertices);
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_vertices);
|
||||
_add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_vertices);
|
||||
|
||||
p_indices.push_back(current_vertex_count + (j * 3 + 0));
|
||||
p_indices.push_back(current_vertex_count + (j * 3 + 2));
|
||||
|
@ -132,12 +141,27 @@ void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces,
|
|||
}
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
|
||||
void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
|
||||
if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
|
||||
MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(p_node);
|
||||
Ref<Mesh> mesh = mesh_instance->get_mesh();
|
||||
if (mesh.is_valid()) {
|
||||
_add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_verticies, p_indices);
|
||||
_add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<MultiMeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
|
||||
MultiMeshInstance *multimesh_instance = Object::cast_to<MultiMeshInstance>(p_node);
|
||||
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
|
||||
Ref<Mesh> mesh = multimesh->get_mesh();
|
||||
if (mesh.is_valid()) {
|
||||
int n = multimesh->get_visible_instance_count();
|
||||
if (n == -1) {
|
||||
n = multimesh->get_instance_count();
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
_add_mesh(mesh, p_accumulated_transform * multimesh->get_instance_transform(i), p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +172,7 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
|
|||
if (!meshes.empty()) {
|
||||
Ref<Mesh> mesh = meshes[1];
|
||||
if (mesh.is_valid()) {
|
||||
_add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices);
|
||||
_add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +230,7 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
|
|||
|
||||
ConcavePolygonShape *concave_polygon = Object::cast_to<ConcavePolygonShape>(*s);
|
||||
if (concave_polygon) {
|
||||
_add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices);
|
||||
_add_faces(concave_polygon->get_faces(), transform, p_vertices, p_indices);
|
||||
}
|
||||
|
||||
ConvexPolygonShape *convex_polygon = Object::cast_to<ConvexPolygonShape>(*s);
|
||||
|
@ -229,12 +253,12 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
|
|||
}
|
||||
}
|
||||
|
||||
_add_faces(faces, transform, p_verticies, p_indices);
|
||||
_add_faces(faces, transform, p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
_add_mesh(mesh, transform, p_verticies, p_indices);
|
||||
_add_mesh(mesh, transform, p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +273,7 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
|
|||
for (int i = 0; i < meshes.size(); i += 2) {
|
||||
Ref<Mesh> mesh = meshes[i + 1];
|
||||
if (mesh.is_valid()) {
|
||||
_add_mesh(mesh, p_accumulated_transform * xform * meshes[i], p_verticies, p_indices);
|
||||
_add_mesh(mesh, p_accumulated_transform * xform * meshes[i], p_vertices, p_indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,12 +286,12 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
|
|||
|
||||
if (p_recurse_children) {
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
_parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
|
||||
_parse_geometry(p_accumulated_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
|
||||
void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
|
||||
PoolVector<Vector3> nav_vertices;
|
||||
|
||||
for (int i = 0; i < p_detail_mesh->nverts; i++) {
|
||||
|
@ -294,11 +318,24 @@ void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_me
|
|||
}
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
|
||||
rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh,
|
||||
Vector<float> &vertices, Vector<int> &indices) {
|
||||
void NavigationMeshGenerator::_build_recast_navigation_mesh(
|
||||
Ref<NavigationMesh> p_nav_mesh,
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorProgress *ep,
|
||||
#endif
|
||||
rcHeightfield *hf,
|
||||
rcCompactHeightfield *chf,
|
||||
rcContourSet *cset,
|
||||
rcPolyMesh *poly_mesh,
|
||||
rcPolyMeshDetail *detail_mesh,
|
||||
Vector<float> &vertices,
|
||||
Vector<int> &indices) {
|
||||
rcContext ctx;
|
||||
ep->step(TTR("Setting up Configuration..."), 1);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Setting up Configuration..."), 1);
|
||||
#endif
|
||||
|
||||
const float *verts = vertices.ptr();
|
||||
const int nverts = vertices.size() / 3;
|
||||
|
@ -332,16 +369,25 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
|
|||
cfg.bmax[1] = bmax[1];
|
||||
cfg.bmax[2] = bmax[2];
|
||||
|
||||
ep->step(TTR("Calculating grid size..."), 2);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Calculating grid size..."), 2);
|
||||
#endif
|
||||
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
|
||||
|
||||
ep->step(TTR("Creating heightfield..."), 3);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Creating heightfield..."), 3);
|
||||
#endif
|
||||
hf = rcAllocHeightfield();
|
||||
|
||||
ERR_FAIL_COND(!hf);
|
||||
ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
|
||||
|
||||
ep->step(TTR("Marking walkable triangles..."), 4);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Marking walkable triangles..."), 4);
|
||||
#endif
|
||||
{
|
||||
Vector<unsigned char> tri_areas;
|
||||
tri_areas.resize(ntris);
|
||||
|
@ -364,7 +410,10 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
|
|||
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
|
||||
}
|
||||
|
||||
ep->step(TTR("Constructing compact heightfield..."), 5);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Constructing compact heightfield..."), 5);
|
||||
#endif
|
||||
|
||||
chf = rcAllocCompactHeightfield();
|
||||
|
||||
|
@ -372,12 +421,20 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
|
|||
ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf));
|
||||
|
||||
rcFreeHeightField(hf);
|
||||
hf = nullptr;
|
||||
hf = 0;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Eroding walkable area..."), 6);
|
||||
#endif
|
||||
|
||||
ep->step(TTR("Eroding walkable area..."), 6);
|
||||
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
|
||||
|
||||
ep->step(TTR("Partitioning..."), 7);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Partitioning..."), 7);
|
||||
#endif
|
||||
|
||||
if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
|
||||
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
|
||||
ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
|
||||
|
@ -387,14 +444,20 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
|
|||
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea));
|
||||
}
|
||||
|
||||
ep->step(TTR("Creating contours..."), 8);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Creating contours..."), 8);
|
||||
#endif
|
||||
|
||||
cset = rcAllocContourSet();
|
||||
|
||||
ERR_FAIL_COND(!cset);
|
||||
ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
|
||||
|
||||
ep->step(TTR("Creating polymesh..."), 9);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Creating polymesh..."), 9);
|
||||
#endif
|
||||
|
||||
poly_mesh = rcAllocPolyMesh();
|
||||
ERR_FAIL_COND(!poly_mesh);
|
||||
|
@ -405,41 +468,46 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
|
|||
ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh));
|
||||
|
||||
rcFreeCompactHeightfield(chf);
|
||||
chf = nullptr;
|
||||
chf = 0;
|
||||
rcFreeContourSet(cset);
|
||||
cset = nullptr;
|
||||
cset = 0;
|
||||
|
||||
ep->step(TTR("Converting to native navigation mesh..."), 10);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Converting to native navigation mesh..."), 10);
|
||||
#endif
|
||||
|
||||
_convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_nav_mesh);
|
||||
|
||||
rcFreePolyMesh(poly_mesh);
|
||||
poly_mesh = nullptr;
|
||||
poly_mesh = 0;
|
||||
rcFreePolyMeshDetail(detail_mesh);
|
||||
detail_mesh = nullptr;
|
||||
detail_mesh = 0;
|
||||
}
|
||||
|
||||
EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() {
|
||||
NavigationMeshGenerator *NavigationMeshGenerator::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() {
|
||||
NavigationMeshGenerator::NavigationMeshGenerator() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() {
|
||||
NavigationMeshGenerator::~NavigationMeshGenerator() {
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
ERR_PRINT("Invoking EditorNavigationMeshGenerator::bake(...) in-game is not supported in Godot 3.2 or below. Aborting bake...");
|
||||
return;
|
||||
void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
|
||||
ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid Navigation Mesh");
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorProgress *ep(NULL);
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
ep = memnew(EditorProgress("bake", TTR("Navigation Mesh Generator Setup:"), 11));
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!p_nav_mesh.is_valid());
|
||||
|
||||
EditorProgress ep("bake", TTR("Navigation Mesh Generator Setup:"), 11);
|
||||
ep.step(TTR("Parsing Geometry..."), 0);
|
||||
if (ep)
|
||||
ep->step(TTR("Parsing Geometry..."), 0);
|
||||
#endif
|
||||
|
||||
Vector<float> vertices;
|
||||
Vector<int> indices;
|
||||
|
@ -467,34 +535,54 @@ void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p
|
|||
rcPolyMesh *poly_mesh = nullptr;
|
||||
rcPolyMeshDetail *detail_mesh = nullptr;
|
||||
|
||||
_build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices);
|
||||
_build_recast_navigation_mesh(
|
||||
p_nav_mesh,
|
||||
#ifdef TOOLS_ENABLED
|
||||
ep,
|
||||
#endif
|
||||
hf,
|
||||
chf,
|
||||
cset,
|
||||
poly_mesh,
|
||||
detail_mesh,
|
||||
vertices,
|
||||
indices);
|
||||
|
||||
rcFreeHeightField(hf);
|
||||
hf = nullptr;
|
||||
hf = 0;
|
||||
|
||||
rcFreeCompactHeightfield(chf);
|
||||
chf = nullptr;
|
||||
chf = 0;
|
||||
|
||||
rcFreeContourSet(cset);
|
||||
cset = nullptr;
|
||||
cset = 0;
|
||||
|
||||
rcFreePolyMesh(poly_mesh);
|
||||
poly_mesh = nullptr;
|
||||
poly_mesh = 0;
|
||||
|
||||
rcFreePolyMeshDetail(detail_mesh);
|
||||
detail_mesh = nullptr;
|
||||
detail_mesh = 0;
|
||||
}
|
||||
ep.step(TTR("Done!"), 11);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (ep)
|
||||
ep->step(TTR("Done!"), 11);
|
||||
|
||||
if (ep)
|
||||
memdelete(ep);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
|
||||
void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
|
||||
if (p_nav_mesh.is_valid()) {
|
||||
p_nav_mesh->clear_polygons();
|
||||
p_nav_mesh->set_vertices(PoolVector<Vector3>());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNavigationMeshGenerator::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &EditorNavigationMeshGenerator::bake);
|
||||
ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &EditorNavigationMeshGenerator::clear);
|
||||
void NavigationMeshGenerator::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &NavigationMeshGenerator::bake);
|
||||
ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &NavigationMeshGenerator::clear);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,37 +31,53 @@
|
|||
#ifndef NAVIGATION_MESH_GENERATOR_H
|
||||
#define NAVIGATION_MESH_GENERATOR_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
|
||||
#include <Recast.h>
|
||||
|
||||
class EditorNavigationMeshGenerator : public Object {
|
||||
GDCLASS(EditorNavigationMeshGenerator, Object);
|
||||
#ifdef TOOLS_ENABLED
|
||||
struct EditorProgress;
|
||||
#endif
|
||||
|
||||
static EditorNavigationMeshGenerator *singleton;
|
||||
class NavigationMeshGenerator : public Object {
|
||||
GDCLASS(NavigationMeshGenerator, Object);
|
||||
|
||||
static NavigationMeshGenerator *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies);
|
||||
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
|
||||
static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
|
||||
static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
|
||||
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
|
||||
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
|
||||
static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
|
||||
static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
|
||||
|
||||
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
|
||||
static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
|
||||
rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh,
|
||||
rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices);
|
||||
static void _build_recast_navigation_mesh(
|
||||
Ref<NavigationMesh> p_nav_mesh,
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorProgress *ep,
|
||||
#endif
|
||||
rcHeightfield *hf,
|
||||
rcCompactHeightfield *chf,
|
||||
rcContourSet *cset,
|
||||
rcPolyMesh *poly_mesh,
|
||||
rcPolyMeshDetail *detail_mesh,
|
||||
Vector<float> &vertices,
|
||||
Vector<int> &indices);
|
||||
|
||||
public:
|
||||
static EditorNavigationMeshGenerator *get_singleton();
|
||||
static NavigationMeshGenerator *get_singleton();
|
||||
|
||||
EditorNavigationMeshGenerator();
|
||||
~EditorNavigationMeshGenerator();
|
||||
NavigationMeshGenerator();
|
||||
~NavigationMeshGenerator();
|
||||
|
||||
void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
|
||||
void clear(Ref<NavigationMesh> p_nav_mesh);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // NAVIGATION_MESH_GENERATOR_H
|
|
@ -30,30 +30,46 @@
|
|||
|
||||
#include "register_types.h"
|
||||
|
||||
#include "navigation_mesh_editor_plugin.h"
|
||||
#include "core/engine.h"
|
||||
#include "godot_navigation_server.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNavigationMeshGenerator *_nav_mesh_generator = nullptr;
|
||||
#ifndef _3D_DISABLED
|
||||
#include "navigation_mesh_generator.h"
|
||||
#endif
|
||||
|
||||
void register_recast_types() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
ClassDB::APIType prev_api = ClassDB::get_current_api();
|
||||
ClassDB::set_current_api(ClassDB::API_EDITOR);
|
||||
#include "navigation_mesh_editor_plugin.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
NavigationMeshGenerator *_nav_mesh_generator = nullptr;
|
||||
#endif
|
||||
|
||||
NavigationServer *new_server() {
|
||||
return memnew(GodotNavigationServer);
|
||||
}
|
||||
|
||||
void register_navigation_types() {
|
||||
NavigationServerManager::set_default_server(new_server);
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
_nav_mesh_generator = memnew(NavigationMeshGenerator);
|
||||
ClassDB::register_class<NavigationMeshGenerator>();
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
|
||||
_nav_mesh_generator = memnew(EditorNavigationMeshGenerator);
|
||||
|
||||
ClassDB::register_class<EditorNavigationMeshGenerator>();
|
||||
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", EditorNavigationMeshGenerator::get_singleton()));
|
||||
|
||||
ClassDB::set_current_api(prev_api);
|
||||
#endif
|
||||
}
|
||||
|
||||
void unregister_recast_types() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
void unregister_navigation_types() {
|
||||
#ifndef _3D_DISABLED
|
||||
if (_nav_mesh_generator) {
|
||||
memdelete(_nav_mesh_generator);
|
||||
}
|
|
@ -28,10 +28,9 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef RECAST_REGISTER_TYPES_H
|
||||
#define RECAST_REGISTER_TYPES_H
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
void register_recast_types();
|
||||
void unregister_recast_types();
|
||||
|
||||
#endif // RECAST_REGISTER_TYPES_H
|
||||
void register_navigation_types();
|
||||
void unregister_navigation_types();
|
84
modules/navigation/rvo_agent.cpp
Normal file
84
modules/navigation/rvo_agent.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*************************************************************************/
|
||||
/* rvo_agent.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "rvo_agent.h"
|
||||
|
||||
#include "nav_map.h"
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
RvoAgent::RvoAgent() :
|
||||
map(NULL) {
|
||||
callback.id = ObjectID(0);
|
||||
}
|
||||
|
||||
void RvoAgent::set_map(NavMap *p_map) {
|
||||
map = p_map;
|
||||
}
|
||||
|
||||
bool RvoAgent::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 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;
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
callback.id = ObjectID(0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
77
modules/navigation/rvo_agent.h
Normal file
77
modules/navigation/rvo_agent.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*************************************************************************/
|
||||
/* rvo_agent.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef RVO_AGENT_H
|
||||
#define RVO_AGENT_H
|
||||
|
||||
#include "core/object.h"
|
||||
#include "nav_rid.h"
|
||||
#include <Agent.h>
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
class NavMap;
|
||||
|
||||
class RvoAgent : public NavRid {
|
||||
struct AvoidanceComputedCallback {
|
||||
ObjectID id;
|
||||
StringName method;
|
||||
Variant udata;
|
||||
Variant new_velocity;
|
||||
};
|
||||
|
||||
NavMap *map;
|
||||
RVO::Agent agent;
|
||||
AvoidanceComputedCallback callback;
|
||||
uint32_t map_update_id;
|
||||
|
||||
public:
|
||||
RvoAgent();
|
||||
|
||||
void set_map(NavMap *p_map);
|
||||
NavMap *get_map() {
|
||||
return map;
|
||||
}
|
||||
|
||||
RVO::Agent *get_agent() {
|
||||
return &agent;
|
||||
}
|
||||
|
||||
bool is_map_changed();
|
||||
|
||||
void set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
|
||||
bool has_callback() const;
|
||||
|
||||
void dispatch_callback();
|
||||
};
|
||||
|
||||
#endif // RVO_AGENT_H
|
|
@ -30,674 +30,64 @@
|
|||
|
||||
#include "navigation_2d.h"
|
||||
|
||||
#define USE_ENTRY_POINT
|
||||
|
||||
void Navigation2D::_navpoly_link(int p_id) {
|
||||
ERR_FAIL_COND(!navpoly_map.has(p_id));
|
||||
NavMesh &nm = navpoly_map[p_id];
|
||||
ERR_FAIL_COND(nm.linked);
|
||||
|
||||
PoolVector<Vector2> vertices = nm.navpoly->get_vertices();
|
||||
int len = vertices.size();
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PoolVector<Vector2>::Read r = vertices.read();
|
||||
|
||||
for (int i = 0; i < nm.navpoly->get_polygon_count(); i++) {
|
||||
//build
|
||||
|
||||
List<Polygon>::Element *P = nm.polygons.push_back(Polygon());
|
||||
Polygon &p = P->get();
|
||||
p.owner = &nm;
|
||||
|
||||
Vector<int> poly = nm.navpoly->get_polygon(i);
|
||||
int plen = poly.size();
|
||||
const int *indices = poly.ptr();
|
||||
bool valid = true;
|
||||
p.edges.resize(plen);
|
||||
|
||||
Vector2 center;
|
||||
float sum = 0;
|
||||
|
||||
for (int j = 0; j < plen; j++) {
|
||||
int idx = indices[j];
|
||||
if (idx < 0 || idx >= len) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Polygon::Edge e;
|
||||
Vector2 ep = nm.xform.xform(r[idx]);
|
||||
center += ep;
|
||||
e.point = _get_point(ep);
|
||||
p.edges.write[j] = e;
|
||||
|
||||
int idxn = indices[(j + 1) % plen];
|
||||
if (idxn < 0 || idxn >= len) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Vector2 epn = nm.xform.xform(r[idxn]);
|
||||
|
||||
sum += (epn.x - ep.x) * (epn.y + ep.y);
|
||||
}
|
||||
|
||||
p.clockwise = sum > 0;
|
||||
|
||||
if (!valid) {
|
||||
nm.polygons.pop_back();
|
||||
ERR_CONTINUE(!valid);
|
||||
}
|
||||
|
||||
p.center = center / plen;
|
||||
|
||||
//connect
|
||||
|
||||
for (int j = 0; j < plen; j++) {
|
||||
int next = (j + 1) % plen;
|
||||
EdgeKey ek(p.edges[j].point, p.edges[next].point);
|
||||
|
||||
Map<EdgeKey, Connection>::Element *C = connections.find(ek);
|
||||
if (!C) {
|
||||
Connection c;
|
||||
c.A = &p;
|
||||
c.A_edge = j;
|
||||
c.B = nullptr;
|
||||
c.B_edge = -1;
|
||||
connections[ek] = c;
|
||||
} else {
|
||||
if (C->get().B != nullptr) {
|
||||
ConnectionPending pending;
|
||||
pending.polygon = &p;
|
||||
pending.edge = j;
|
||||
p.edges.write[j].P = C->get().pending.push_back(pending);
|
||||
continue;
|
||||
}
|
||||
|
||||
C->get().B = &p;
|
||||
C->get().B_edge = j;
|
||||
C->get().A->edges.write[C->get().A_edge].C = &p;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = j;
|
||||
p.edges.write[j].C = C->get().A;
|
||||
p.edges.write[j].C_edge = C->get().A_edge;
|
||||
//connection successful.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm.linked = true;
|
||||
}
|
||||
|
||||
void Navigation2D::_navpoly_unlink(int p_id) {
|
||||
ERR_FAIL_COND(!navpoly_map.has(p_id));
|
||||
NavMesh &nm = navpoly_map[p_id];
|
||||
ERR_FAIL_COND(!nm.linked);
|
||||
|
||||
for (List<Polygon>::Element *E = nm.polygons.front(); E; E = E->next()) {
|
||||
Polygon &p = E->get();
|
||||
|
||||
int ec = p.edges.size();
|
||||
Polygon::Edge *edges = p.edges.ptrw();
|
||||
|
||||
for (int i = 0; i < ec; i++) {
|
||||
int next = (i + 1) % ec;
|
||||
|
||||
EdgeKey ek(edges[i].point, edges[next].point);
|
||||
Map<EdgeKey, Connection>::Element *C = connections.find(ek);
|
||||
ERR_CONTINUE(!C);
|
||||
|
||||
if (edges[i].P) {
|
||||
C->get().pending.erase(edges[i].P);
|
||||
edges[i].P = nullptr;
|
||||
|
||||
} else if (C->get().B) {
|
||||
//disconnect
|
||||
|
||||
C->get().B->edges.write[C->get().B_edge].C = nullptr;
|
||||
C->get().B->edges.write[C->get().B_edge].C_edge = -1;
|
||||
C->get().A->edges.write[C->get().A_edge].C = nullptr;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = -1;
|
||||
|
||||
if (C->get().A == &E->get()) {
|
||||
C->get().A = C->get().B;
|
||||
C->get().A_edge = C->get().B_edge;
|
||||
}
|
||||
C->get().B = nullptr;
|
||||
C->get().B_edge = -1;
|
||||
|
||||
if (C->get().pending.size()) {
|
||||
//reconnect if something is pending
|
||||
ConnectionPending cp = C->get().pending.front()->get();
|
||||
C->get().pending.pop_front();
|
||||
|
||||
C->get().B = cp.polygon;
|
||||
C->get().B_edge = cp.edge;
|
||||
C->get().A->edges.write[C->get().A_edge].C = cp.polygon;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge;
|
||||
cp.polygon->edges.write[cp.edge].C = C->get().A;
|
||||
cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge;
|
||||
cp.polygon->edges.write[cp.edge].P = nullptr;
|
||||
}
|
||||
|
||||
} else {
|
||||
connections.erase(C);
|
||||
//erase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm.polygons.clear();
|
||||
|
||||
nm.linked = false;
|
||||
}
|
||||
|
||||
int Navigation2D::navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) {
|
||||
ERR_FAIL_COND_V(p_mesh.is_null(), -1);
|
||||
|
||||
int id = last_id++;
|
||||
NavMesh nm;
|
||||
nm.linked = false;
|
||||
nm.navpoly = p_mesh;
|
||||
nm.xform = p_xform;
|
||||
nm.owner = p_owner;
|
||||
navpoly_map[id] = nm;
|
||||
|
||||
_navpoly_link(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void Navigation2D::navpoly_set_transform(int p_id, const Transform2D &p_xform) {
|
||||
ERR_FAIL_COND(!navpoly_map.has(p_id));
|
||||
NavMesh &nm = navpoly_map[p_id];
|
||||
if (nm.xform == p_xform) {
|
||||
return; //bleh
|
||||
}
|
||||
_navpoly_unlink(p_id);
|
||||
nm.xform = p_xform;
|
||||
_navpoly_link(p_id);
|
||||
}
|
||||
void Navigation2D::navpoly_remove(int p_id) {
|
||||
ERR_FAIL_COND(!navpoly_map.has(p_id));
|
||||
_navpoly_unlink(p_id);
|
||||
navpoly_map.erase(p_id);
|
||||
}
|
||||
|
||||
Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) {
|
||||
Polygon *begin_poly = nullptr;
|
||||
Polygon *end_poly = nullptr;
|
||||
Vector2 begin_point;
|
||||
Vector2 end_point;
|
||||
float begin_d = 1e20;
|
||||
float end_d = 1e20;
|
||||
|
||||
//look for point inside triangle
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
if (begin_d || end_d) {
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
if (begin_d > 0) {
|
||||
if (Geometry::is_point_in_triangle(p_start, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
|
||||
begin_poly = &p;
|
||||
begin_point = p_start;
|
||||
begin_d = 0;
|
||||
if (end_d == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end_d > 0) {
|
||||
if (Geometry::is_point_in_triangle(p_end, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
|
||||
end_poly = &p;
|
||||
end_point = p_end;
|
||||
end_d = 0;
|
||||
if (begin_d == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.prev_edge = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//start or end not inside triangle.. look for closest segment :|
|
||||
if (begin_d || end_d) {
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
int es = p.edges.size();
|
||||
for (int i = 0; i < es; i++) {
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(p.edges[i].point),
|
||||
_get_vertex(p.edges[(i + 1) % es].point)
|
||||
};
|
||||
|
||||
if (begin_d > 0) {
|
||||
Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_start, edge);
|
||||
float d = spoint.distance_to(p_start);
|
||||
if (d < begin_d) {
|
||||
begin_poly = &p;
|
||||
begin_point = spoint;
|
||||
begin_d = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (end_d > 0) {
|
||||
Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_end, edge);
|
||||
float d = spoint.distance_to(p_end);
|
||||
if (d < end_d) {
|
||||
end_poly = &p;
|
||||
end_point = spoint;
|
||||
end_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin_poly || !end_poly) {
|
||||
return Vector<Vector2>(); //no path
|
||||
}
|
||||
|
||||
if (begin_poly == end_poly) {
|
||||
Vector<Vector2> path;
|
||||
path.resize(2);
|
||||
path.write[0] = begin_point;
|
||||
path.write[1] = end_point;
|
||||
return path;
|
||||
}
|
||||
|
||||
bool found_route = false;
|
||||
|
||||
List<Polygon *> open_list;
|
||||
|
||||
begin_poly->entry = p_start;
|
||||
|
||||
for (int i = 0; i < begin_poly->edges.size(); i++) {
|
||||
if (begin_poly->edges[i].C) {
|
||||
begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(begin_poly->edges[i].point),
|
||||
_get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point)
|
||||
};
|
||||
|
||||
Vector2 entry = Geometry::get_closest_point_to_segment_2d(begin_poly->entry, edge);
|
||||
begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry);
|
||||
begin_poly->edges[i].C->entry = entry;
|
||||
#else
|
||||
begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center);
|
||||
#endif
|
||||
open_list.push_back(begin_poly->edges[i].C);
|
||||
|
||||
if (begin_poly->edges[i].C == end_poly) {
|
||||
found_route = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!found_route) {
|
||||
if (open_list.size() == 0) {
|
||||
break;
|
||||
}
|
||||
//check open list
|
||||
|
||||
List<Polygon *>::Element *least_cost_poly = nullptr;
|
||||
float least_cost = 1e30;
|
||||
|
||||
//this could be faster (cache previous results)
|
||||
for (List<Polygon *>::Element *E = open_list.front(); E; E = E->next()) {
|
||||
Polygon *p = E->get();
|
||||
|
||||
float cost = p->distance;
|
||||
|
||||
#ifdef USE_ENTRY_POINT
|
||||
int es = p->edges.size();
|
||||
|
||||
float shortest_distance = 1e30;
|
||||
|
||||
for (int i = 0; i < es; i++) {
|
||||
Polygon::Edge &e = p->edges.write[i];
|
||||
|
||||
if (!e.C) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(p->edges[i].point),
|
||||
_get_vertex(p->edges[(i + 1) % es].point)
|
||||
};
|
||||
|
||||
Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge);
|
||||
float dist = p->entry.distance_to(edge_point);
|
||||
if (dist < shortest_distance) {
|
||||
shortest_distance = dist;
|
||||
}
|
||||
}
|
||||
|
||||
cost += shortest_distance;
|
||||
#else
|
||||
cost += p->center.distance_to(end_point);
|
||||
#endif
|
||||
if (cost < least_cost) {
|
||||
least_cost_poly = E;
|
||||
least_cost = cost;
|
||||
}
|
||||
}
|
||||
|
||||
Polygon *p = least_cost_poly->get();
|
||||
//open the neighbours for search
|
||||
int es = p->edges.size();
|
||||
|
||||
for (int i = 0; i < es; i++) {
|
||||
Polygon::Edge &e = p->edges.write[i];
|
||||
|
||||
if (!e.C) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef USE_ENTRY_POINT
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(p->edges[i].point),
|
||||
_get_vertex(p->edges[(i + 1) % es].point)
|
||||
};
|
||||
|
||||
Vector2 edge_entry = Geometry::get_closest_point_to_segment_2d(p->entry, edge);
|
||||
float distance = p->entry.distance_to(edge_entry) + p->distance;
|
||||
|
||||
#else
|
||||
|
||||
float distance = p->center.distance_to(e.C->center) + p->distance;
|
||||
|
||||
#endif
|
||||
|
||||
if (e.C->prev_edge != -1) {
|
||||
//oh this was visited already, can we win the cost?
|
||||
|
||||
if (e.C->distance > distance) {
|
||||
e.C->prev_edge = e.C_edge;
|
||||
e.C->distance = distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
e.C->entry = edge_entry;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
//add to open neighbours
|
||||
|
||||
e.C->prev_edge = e.C_edge;
|
||||
e.C->distance = distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
e.C->entry = edge_entry;
|
||||
#endif
|
||||
|
||||
open_list.push_back(e.C);
|
||||
|
||||
if (e.C == end_poly) {
|
||||
//oh my reached end! stop algorithm
|
||||
found_route = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_route) {
|
||||
break;
|
||||
}
|
||||
|
||||
open_list.erase(least_cost_poly);
|
||||
}
|
||||
|
||||
if (found_route) {
|
||||
Vector<Vector2> path;
|
||||
|
||||
if (p_optimize) {
|
||||
//string pulling
|
||||
|
||||
Vector2 apex_point = end_point;
|
||||
Vector2 portal_left = apex_point;
|
||||
Vector2 portal_right = apex_point;
|
||||
Polygon *left_poly = end_poly;
|
||||
Polygon *right_poly = end_poly;
|
||||
Polygon *p = end_poly;
|
||||
|
||||
while (p) {
|
||||
Vector2 left;
|
||||
Vector2 right;
|
||||
|
||||
//#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) )
|
||||
#define CLOCK_TANGENT(m_a, m_b, m_c) ((((m_a).x - (m_c).x) * ((m_b).y - (m_c).y) - ((m_b).x - (m_c).x) * ((m_a).y - (m_c).y)))
|
||||
|
||||
if (p == begin_poly) {
|
||||
left = begin_point;
|
||||
right = begin_point;
|
||||
} else {
|
||||
int prev = p->prev_edge;
|
||||
int prev_n = (p->prev_edge + 1) % p->edges.size();
|
||||
left = _get_vertex(p->edges[prev].point);
|
||||
right = _get_vertex(p->edges[prev_n].point);
|
||||
|
||||
if (p->clockwise) {
|
||||
SWAP(left, right);
|
||||
}
|
||||
/*if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5) < 0){
|
||||
SWAP(left,right);
|
||||
}*/
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
|
||||
/*
|
||||
print_line("-----\nAPEX: "+(apex_point-end_point));
|
||||
print_line("LEFT:");
|
||||
print_line("\tPortal: "+(portal_left-end_point));
|
||||
print_line("\tPoint: "+(left-end_point));
|
||||
print_line("\tLeft Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_left,left)));
|
||||
print_line("\tLeft Distance: "+rtos(portal_left.distance_squared_to(apex_point)));
|
||||
print_line("\tLeft Test: "+rtos(CLOCK_TANGENT(apex_point,left,portal_right)));
|
||||
print_line("RIGHT:");
|
||||
print_line("\tPortal: "+(portal_right-end_point));
|
||||
print_line("\tPoint: "+(right-end_point));
|
||||
print_line("\tRight Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_right,right)));
|
||||
print_line("\tRight Distance: "+rtos(portal_right.distance_squared_to(apex_point)));
|
||||
print_line("\tRight Test: "+rtos(CLOCK_TANGENT(apex_point,right,portal_left)));
|
||||
*/
|
||||
|
||||
if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
|
||||
//process
|
||||
if (portal_left.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
|
||||
left_poly = p;
|
||||
portal_left = left;
|
||||
} else {
|
||||
apex_point = portal_right;
|
||||
p = right_poly;
|
||||
left_poly = p;
|
||||
portal_left = apex_point;
|
||||
portal_right = apex_point;
|
||||
if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point)) {
|
||||
path.push_back(apex_point);
|
||||
}
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
|
||||
//process
|
||||
if (portal_right.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
|
||||
right_poly = p;
|
||||
portal_right = right;
|
||||
} else {
|
||||
apex_point = portal_left;
|
||||
p = left_poly;
|
||||
right_poly = p;
|
||||
portal_right = apex_point;
|
||||
portal_left = apex_point;
|
||||
if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point)) {
|
||||
path.push_back(apex_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p != begin_poly) {
|
||||
p = p->edges[p->prev_edge].C;
|
||||
} else {
|
||||
p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//midpoints
|
||||
Polygon *p = end_poly;
|
||||
|
||||
while (true) {
|
||||
int prev = p->prev_edge;
|
||||
int prev_n = (p->prev_edge + 1) % p->edges.size();
|
||||
Vector2 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5;
|
||||
path.push_back(point);
|
||||
p = p->edges[prev].C;
|
||||
if (p == begin_poly) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!path.size() || !path[path.size() - 1].is_equal_approx(begin_point)) {
|
||||
path.push_back(begin_point); // Add the begin point
|
||||
} else {
|
||||
path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
|
||||
}
|
||||
|
||||
path.invert();
|
||||
|
||||
if (path.size() <= 1 || !path[path.size() - 1].is_equal_approx(end_point)) {
|
||||
path.push_back(end_point); // Add the end point
|
||||
} else {
|
||||
path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
return Vector<Vector2>();
|
||||
}
|
||||
|
||||
Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) {
|
||||
Vector2 closest_point = Vector2();
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
|
||||
return p_point; //inside triangle, nothing else to discuss
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
int es = p.edges.size();
|
||||
for (int i = 0; i < es; i++) {
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(p.edges[i].point),
|
||||
_get_vertex(p.edges[(i + 1) % es].point)
|
||||
};
|
||||
|
||||
Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge);
|
||||
float d = spoint.distance_squared_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = spoint;
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) {
|
||||
Object *owner = nullptr;
|
||||
Vector2 closest_point = Vector2();
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
|
||||
return E->get().owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
int es = p.edges.size();
|
||||
for (int i = 0; i < es; i++) {
|
||||
Vector2 edge[2] = {
|
||||
_get_vertex(p.edges[i].point),
|
||||
_get_vertex(p.edges[(i + 1) % es].point)
|
||||
};
|
||||
|
||||
Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge);
|
||||
float d = spoint.distance_squared_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = spoint;
|
||||
closest_point_d = d;
|
||||
owner = E->get().owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
#include "servers/navigation_2d_server.h"
|
||||
|
||||
void Navigation2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove);
|
||||
ClassDB::bind_method(D_METHOD("get_rid"), &Navigation2D::get_rid);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation2D::get_closest_point);
|
||||
ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation2D::get_closest_point_owner);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation2D::set_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation2D::get_cell_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation2D::set_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation2D::get_edge_connection_margin);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
|
||||
}
|
||||
|
||||
void Navigation2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
Navigation2DServer::get_singleton()->map_set_active(map, true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
Navigation2DServer::get_singleton()->map_set_active(map, false);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Navigation2D::set_cell_size(float p_cell_size) {
|
||||
cell_size = p_cell_size;
|
||||
Navigation2DServer::get_singleton()->map_set_cell_size(map, cell_size);
|
||||
}
|
||||
|
||||
void Navigation2D::set_edge_connection_margin(float p_edge_connection_margin) {
|
||||
edge_connection_margin = p_edge_connection_margin;
|
||||
Navigation2DServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin);
|
||||
}
|
||||
|
||||
Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) const {
|
||||
return Navigation2DServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize);
|
||||
}
|
||||
|
||||
Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) const {
|
||||
return Navigation2DServer::get_singleton()->map_get_closest_point(map, p_point);
|
||||
}
|
||||
|
||||
RID Navigation2D::get_closest_point_owner(const Vector2 &p_point) const {
|
||||
return Navigation2DServer::get_singleton()->map_get_closest_point_owner(map, p_point);
|
||||
}
|
||||
|
||||
Navigation2D::Navigation2D() {
|
||||
ERR_FAIL_COND(sizeof(Point) != 8);
|
||||
cell_size = 1; // one pixel
|
||||
last_id = 1;
|
||||
map = Navigation2DServer::get_singleton()->map_create();
|
||||
set_cell_size(10); // Ten pixels
|
||||
set_edge_connection_margin(100);
|
||||
}
|
||||
|
||||
Navigation2D::~Navigation2D() {
|
||||
Navigation2DServer::get_singleton()->free(map);
|
||||
}
|
||||
|
|
|
@ -37,129 +37,35 @@
|
|||
class Navigation2D : public Node2D {
|
||||
GDCLASS(Navigation2D, Node2D);
|
||||
|
||||
union Point {
|
||||
struct {
|
||||
int64_t x : 32;
|
||||
int64_t y : 32;
|
||||
};
|
||||
|
||||
uint64_t key;
|
||||
bool operator<(const Point &p_key) const { return key < p_key.key; }
|
||||
};
|
||||
|
||||
struct EdgeKey {
|
||||
Point a;
|
||||
Point b;
|
||||
|
||||
bool operator<(const EdgeKey &p_key) const {
|
||||
return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
|
||||
};
|
||||
|
||||
EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
|
||||
a(p_a),
|
||||
b(p_b) {
|
||||
if (a.key > b.key) {
|
||||
SWAP(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NavMesh;
|
||||
struct Polygon;
|
||||
|
||||
struct ConnectionPending {
|
||||
Polygon *polygon;
|
||||
int edge;
|
||||
};
|
||||
|
||||
struct Polygon {
|
||||
struct Edge {
|
||||
Point point;
|
||||
Polygon *C; //connection
|
||||
int C_edge;
|
||||
List<ConnectionPending>::Element *P;
|
||||
Edge() {
|
||||
C = nullptr;
|
||||
C_edge = -1;
|
||||
P = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Edge> edges;
|
||||
|
||||
Vector2 center;
|
||||
Vector2 entry;
|
||||
|
||||
float distance;
|
||||
int prev_edge;
|
||||
|
||||
bool clockwise;
|
||||
|
||||
NavMesh *owner;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
Polygon *A;
|
||||
int A_edge;
|
||||
Polygon *B;
|
||||
int B_edge;
|
||||
|
||||
List<ConnectionPending> pending;
|
||||
|
||||
Connection() {
|
||||
A = nullptr;
|
||||
B = nullptr;
|
||||
A_edge = -1;
|
||||
B_edge = -1;
|
||||
}
|
||||
};
|
||||
|
||||
Map<EdgeKey, Connection> connections;
|
||||
|
||||
struct NavMesh {
|
||||
Object *owner;
|
||||
Transform2D xform;
|
||||
bool linked;
|
||||
Ref<NavigationPolygon> navpoly;
|
||||
List<Polygon> polygons;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Point _get_point(const Vector2 &p_pos) const {
|
||||
int x = int(Math::floor(p_pos.x / cell_size));
|
||||
int y = int(Math::floor(p_pos.y / cell_size));
|
||||
|
||||
Point p;
|
||||
p.key = 0;
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
return p;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector2 _get_vertex(const Point &p_point) const {
|
||||
return Vector2(p_point.x, p_point.y) * cell_size;
|
||||
}
|
||||
|
||||
void _navpoly_link(int p_id);
|
||||
void _navpoly_unlink(int p_id);
|
||||
|
||||
float cell_size;
|
||||
Map<int, NavMesh> navpoly_map;
|
||||
int last_id;
|
||||
RID map;
|
||||
real_t cell_size;
|
||||
real_t edge_connection_margin;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
//API should be as dynamic as possible
|
||||
int navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = nullptr);
|
||||
void navpoly_set_transform(int p_id, const Transform2D &p_xform);
|
||||
void navpoly_remove(int p_id);
|
||||
RID get_rid() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
Vector<Vector2> get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true);
|
||||
Vector2 get_closest_point(const Vector2 &p_point);
|
||||
Object *get_closest_point_owner(const Vector2 &p_point);
|
||||
void set_cell_size(float p_cell_size);
|
||||
float get_cell_size() const {
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
void set_edge_connection_margin(float p_edge_connection_margin);
|
||||
float get_edge_connection_margin() const {
|
||||
return edge_connection_margin;
|
||||
}
|
||||
|
||||
Vector<Vector2> get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true) const;
|
||||
Vector2 get_closest_point(const Vector2 &p_point) const;
|
||||
RID get_closest_point_owner(const Vector2 &p_point) const;
|
||||
|
||||
Navigation2D();
|
||||
~Navigation2D();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_2D_H
|
||||
|
|
355
scene/2d/navigation_agent_2d.cpp
Normal file
355
scene/2d/navigation_agent_2d.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_agent_2d.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "navigation_agent_2d.h"
|
||||
|
||||
#include "core/engine.h"
|
||||
#include "scene/2d/navigation_2d.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
|
||||
void NavigationAgent2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent2D::set_target_desired_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent2D::get_target_desired_distance);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent2D::set_radius);
|
||||
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent2D::get_radius);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent2D::set_navigation_node);
|
||||
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent2D::get_navigation_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent2D::set_neighbor_dist);
|
||||
ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent2D::get_neighbor_dist);
|
||||
|
||||
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_max_speed", "max_speed"), &NavigationAgent2D::set_max_speed);
|
||||
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent2D::get_max_speed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location);
|
||||
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location);
|
||||
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location);
|
||||
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
|
||||
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity);
|
||||
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("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);
|
||||
ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent2D::get_final_location);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done);
|
||||
|
||||
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, "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::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("path_changed"));
|
||||
ADD_SIGNAL(MethodInfo("target_reached"));
|
||||
ADD_SIGNAL(MethodInfo("navigation_finished"));
|
||||
ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR2, "safe_velocity")));
|
||||
}
|
||||
|
||||
void NavigationAgent2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
agent_parent = Object::cast_to<Node2D>(get_parent());
|
||||
|
||||
Navigation2DServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done");
|
||||
|
||||
// Search the navigation node and set it
|
||||
{
|
||||
Navigation2D *nav = nullptr;
|
||||
Node *p = get_parent();
|
||||
while (p != nullptr) {
|
||||
nav = Object::cast_to<Navigation2D>(p);
|
||||
if (nav != nullptr) {
|
||||
p = nullptr;
|
||||
} else {
|
||||
p = p->get_parent();
|
||||
}
|
||||
}
|
||||
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
set_physics_process_internal(true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
agent_parent = nullptr;
|
||||
set_navigation(nullptr);
|
||||
set_physics_process_internal(false);
|
||||
// Want to call ready again when the node enters the tree again. We're not using enter_tree notification because
|
||||
// the navigation map may not be ready at that time. This fixes issues with taking the agent out of the scene tree.
|
||||
request_ready();
|
||||
} break;
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (agent_parent) {
|
||||
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().get_origin());
|
||||
_check_distance_to_target();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationAgent2D::NavigationAgent2D() :
|
||||
agent_parent(nullptr),
|
||||
navigation(nullptr),
|
||||
agent(RID()),
|
||||
target_desired_distance(1.0),
|
||||
path_max_distance(3.0),
|
||||
velocity_submitted(false),
|
||||
target_reached(false),
|
||||
navigation_finished(true) {
|
||||
agent = Navigation2DServer::get_singleton()->agent_create();
|
||||
set_neighbor_dist(500.0);
|
||||
set_max_neighbors(10);
|
||||
set_time_horizon(20.0);
|
||||
set_radius(10.0);
|
||||
set_max_speed(200.0);
|
||||
}
|
||||
|
||||
NavigationAgent2D::~NavigationAgent2D() {
|
||||
Navigation2DServer::get_singleton()->free(agent);
|
||||
agent = RID(); // Pointless
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_navigation(Navigation2D *p_nav) {
|
||||
if (navigation == p_nav) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
navigation = p_nav;
|
||||
Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_navigation_node(Node *p_nav) {
|
||||
Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav);
|
||||
ERR_FAIL_NULL(nav);
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
Node *NavigationAgent2D::get_navigation_node() const {
|
||||
return Object::cast_to<Node>(navigation);
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
|
||||
target_desired_distance = p_dd;
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_radius(real_t p_radius) {
|
||||
radius = p_radius;
|
||||
Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_neighbor_dist(real_t p_dist) {
|
||||
neighbor_dist = p_dist;
|
||||
Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_max_neighbors(int p_count) {
|
||||
max_neighbors = 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_max_speed(real_t p_max_speed) {
|
||||
max_speed = p_max_speed;
|
||||
Navigation2DServer::get_singleton()->agent_set_max_speed(agent, max_speed);
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_path_max_distance(real_t p_pmd) {
|
||||
path_max_distance = p_pmd;
|
||||
}
|
||||
|
||||
real_t NavigationAgent2D::get_path_max_distance() {
|
||||
return path_max_distance;
|
||||
}
|
||||
|
||||
void NavigationAgent2D::set_target_location(Vector2 p_location) {
|
||||
target_location = p_location;
|
||||
navigation_path.clear();
|
||||
target_reached = false;
|
||||
navigation_finished = false;
|
||||
update_frame_id = 0;
|
||||
}
|
||||
|
||||
Vector2 NavigationAgent2D::get_target_location() const {
|
||||
return target_location;
|
||||
}
|
||||
|
||||
Vector2 NavigationAgent2D::get_next_location() {
|
||||
update_navigation();
|
||||
if (navigation_path.size() == 0) {
|
||||
ERR_FAIL_COND_V(agent_parent == nullptr, Vector2());
|
||||
return agent_parent->get_global_transform().get_origin();
|
||||
} else {
|
||||
return navigation_path[nav_path_index];
|
||||
}
|
||||
}
|
||||
|
||||
real_t NavigationAgent2D::distance_to_target() const {
|
||||
ERR_FAIL_COND_V(agent_parent == nullptr, 0.0);
|
||||
return agent_parent->get_global_transform().get_origin().distance_to(target_location);
|
||||
}
|
||||
|
||||
bool NavigationAgent2D::is_target_reached() const {
|
||||
return target_reached;
|
||||
}
|
||||
|
||||
bool NavigationAgent2D::is_target_reachable() {
|
||||
return target_desired_distance >= get_final_location().distance_to(target_location);
|
||||
}
|
||||
|
||||
bool NavigationAgent2D::is_navigation_finished() {
|
||||
update_navigation();
|
||||
return navigation_finished;
|
||||
}
|
||||
|
||||
Vector2 NavigationAgent2D::get_final_location() {
|
||||
update_navigation();
|
||||
if (navigation_path.size() == 0) {
|
||||
return Vector2();
|
||||
}
|
||||
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);
|
||||
velocity_submitted = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
String NavigationAgent2D::get_configuration_warning() const {
|
||||
if (!Object::cast_to<Node2D>(get_parent())) {
|
||||
return TTR("The NavigationAgent2D can be used only under a Node2D node");
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
void NavigationAgent2D::update_navigation() {
|
||||
if (agent_parent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (navigation == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_frame_id = Engine::get_singleton()->get_physics_frames();
|
||||
|
||||
Vector2 o = agent_parent->get_global_transform().get_origin();
|
||||
|
||||
bool reload_path = false;
|
||||
|
||||
if (Navigation2DServer::get_singleton()->agent_is_map_changed(agent)) {
|
||||
reload_path = true;
|
||||
} else if (navigation_path.size() == 0) {
|
||||
reload_path = true;
|
||||
} else {
|
||||
// Check if too far from the navigation path
|
||||
if (nav_path_index > 0) {
|
||||
Vector2 segment[2];
|
||||
segment[0] = navigation_path[nav_path_index - 1];
|
||||
segment[1] = navigation_path[nav_path_index];
|
||||
Vector2 p = Geometry::get_closest_point_to_segment_2d(o, segment);
|
||||
if (o.distance_to(p) >= path_max_distance) {
|
||||
// To faraway, reload path
|
||||
reload_path = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_path) {
|
||||
navigation_path = Navigation2DServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true);
|
||||
navigation_finished = false;
|
||||
nav_path_index = 0;
|
||||
emit_signal("path_changed");
|
||||
}
|
||||
|
||||
if (navigation_path.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we can advance the navigation path
|
||||
if (navigation_finished == false) {
|
||||
// Advances to the next far away location.
|
||||
while (o.distance_to(navigation_path[nav_path_index]) < target_desired_distance) {
|
||||
nav_path_index += 1;
|
||||
if (nav_path_index == navigation_path.size()) {
|
||||
_check_distance_to_target();
|
||||
nav_path_index -= 1;
|
||||
navigation_finished = true;
|
||||
emit_signal("navigation_finished");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationAgent2D::_check_distance_to_target() {
|
||||
if (!target_reached) {
|
||||
if (distance_to_target() < target_desired_distance) {
|
||||
emit_signal("target_reached");
|
||||
target_reached = true;
|
||||
}
|
||||
}
|
||||
}
|
151
scene/2d/navigation_agent_2d.h
Normal file
151
scene/2d/navigation_agent_2d.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_agent_2d.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_AGENT_2D_H
|
||||
#define NAVIGATION_AGENT_2D_H
|
||||
|
||||
#include "core/vector.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Node2D;
|
||||
class Navigation2D;
|
||||
|
||||
class NavigationAgent2D : public Node {
|
||||
GDCLASS(NavigationAgent2D, Node);
|
||||
|
||||
Node2D *agent_parent;
|
||||
Navigation2D *navigation;
|
||||
|
||||
RID agent;
|
||||
|
||||
real_t target_desired_distance;
|
||||
real_t radius;
|
||||
real_t neighbor_dist;
|
||||
int max_neighbors;
|
||||
real_t time_horizon;
|
||||
real_t max_speed;
|
||||
|
||||
real_t path_max_distance;
|
||||
|
||||
Vector2 target_location;
|
||||
Vector<Vector2> navigation_path;
|
||||
int nav_path_index;
|
||||
bool velocity_submitted;
|
||||
Vector2 prev_safe_velocity;
|
||||
/// The submitted target velocity
|
||||
Vector2 target_velocity;
|
||||
bool target_reached;
|
||||
bool navigation_finished;
|
||||
// No initialized on purpose
|
||||
uint32_t update_frame_id;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
NavigationAgent2D();
|
||||
virtual ~NavigationAgent2D();
|
||||
|
||||
void set_navigation(Navigation2D *p_nav);
|
||||
const Navigation2D *get_navigation() const {
|
||||
return navigation;
|
||||
}
|
||||
|
||||
void set_navigation_node(Node *p_nav);
|
||||
Node *get_navigation_node() const;
|
||||
|
||||
RID get_rid() const {
|
||||
return agent;
|
||||
}
|
||||
|
||||
void set_target_desired_distance(real_t p_dd);
|
||||
real_t get_target_desired_distance() const {
|
||||
return target_desired_distance;
|
||||
}
|
||||
|
||||
void set_radius(real_t p_radius);
|
||||
real_t get_radius() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
void set_neighbor_dist(real_t p_dist);
|
||||
real_t get_neighbor_dist() const {
|
||||
return neighbor_dist;
|
||||
}
|
||||
|
||||
void set_max_neighbors(int p_count);
|
||||
int get_max_neighbors() const {
|
||||
return max_neighbors;
|
||||
}
|
||||
|
||||
void set_time_horizon(real_t p_time);
|
||||
real_t get_time_horizon() const {
|
||||
return time_horizon;
|
||||
}
|
||||
|
||||
void set_max_speed(real_t p_max_speed);
|
||||
real_t get_max_speed() const {
|
||||
return max_speed;
|
||||
}
|
||||
|
||||
void set_path_max_distance(real_t p_pmd);
|
||||
real_t get_path_max_distance();
|
||||
|
||||
void set_target_location(Vector2 p_location);
|
||||
Vector2 get_target_location() const;
|
||||
|
||||
Vector2 get_next_location();
|
||||
|
||||
Vector<Vector2> get_nav_path() const {
|
||||
return navigation_path;
|
||||
}
|
||||
|
||||
int get_nav_path_index() const {
|
||||
return nav_path_index;
|
||||
}
|
||||
|
||||
real_t distance_to_target() const;
|
||||
bool is_target_reached() const;
|
||||
bool is_target_reachable();
|
||||
bool is_navigation_finished();
|
||||
Vector2 get_final_location();
|
||||
|
||||
void set_velocity(Vector2 p_velocity);
|
||||
void _avoidance_done(Vector3 p_new_velocity);
|
||||
|
||||
virtual String get_configuration_warning() const;
|
||||
|
||||
private:
|
||||
void update_navigation();
|
||||
void _check_distance_to_target();
|
||||
};
|
||||
|
||||
#endif
|
196
scene/2d/navigation_obstacle_2d.cpp
Normal file
196
scene/2d/navigation_obstacle_2d.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_obstacle_2d.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "navigation_obstacle_2d.h"
|
||||
|
||||
#include "scene/2d/collision_shape_2d.h"
|
||||
#include "scene/2d/navigation_2d.h"
|
||||
#include "scene/2d/physics_body_2d.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
|
||||
void NavigationObstacle2D::_bind_methods() {
|
||||
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");
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "radius") {
|
||||
if (estimate_radius) {
|
||||
p_property.usage = PROPERTY_USAGE_NOEDITOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
initialize_agent();
|
||||
parent_node2d = Object::cast_to<Node2D>(get_parent());
|
||||
|
||||
// Search the navigation node and set it
|
||||
{
|
||||
Navigation2D *nav = nullptr;
|
||||
Node *p = get_parent();
|
||||
while (p != nullptr) {
|
||||
nav = Object::cast_to<Navigation2D>(p);
|
||||
if (nav != nullptr) {
|
||||
p = nullptr;
|
||||
} else {
|
||||
p = p->get_parent();
|
||||
}
|
||||
}
|
||||
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
set_physics_process_internal(true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
set_navigation(nullptr);
|
||||
set_physics_process_internal(false);
|
||||
request_ready(); // required to solve an issue with losing the navigation
|
||||
} break;
|
||||
case NOTIFICATION_PARENTED: {
|
||||
parent_node2d = Object::cast_to<Node2D>(get_parent());
|
||||
reevaluate_agent_radius();
|
||||
}
|
||||
case NOTIFICATION_UNPARENTED: {
|
||||
parent_node2d = nullptr;
|
||||
}
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (parent_node2d) {
|
||||
Navigation2DServer::get_singleton()->agent_set_position(agent, parent_node2d->get_global_transform().get_origin());
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationObstacle2D::NavigationObstacle2D() :
|
||||
navigation(nullptr),
|
||||
agent(RID()) {
|
||||
agent = Navigation2DServer::get_singleton()->agent_create();
|
||||
}
|
||||
|
||||
NavigationObstacle2D::~NavigationObstacle2D() {
|
||||
Navigation2DServer::get_singleton()->free(agent);
|
||||
agent = RID(); // Pointless
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) {
|
||||
if (navigation == p_nav) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
navigation = p_nav;
|
||||
Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
|
||||
Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav);
|
||||
ERR_FAIL_COND(nav == nullptr);
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
Node *NavigationObstacle2D::get_navigation_node() const {
|
||||
return Object::cast_to<Node>(navigation);
|
||||
}
|
||||
|
||||
String NavigationObstacle2D::get_configuration_warning() const {
|
||||
if (!Object::cast_to<Node2D>(get_parent())) {
|
||||
return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
|
||||
}
|
||||
|
||||
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_agent_radius()) {
|
||||
Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
|
||||
} else if (parent_node2d) {
|
||||
Navigation2DServer::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
|
||||
}
|
||||
}
|
||||
|
||||
real_t NavigationObstacle2D::estimate_agent_radius() const {
|
||||
if (parent_node2d) {
|
||||
// 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<CollisionShape2D>(parent_node2d->get_child(i));
|
||||
if (cs) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Vector2 s = parent_node2d->get_global_transform().get_scale();
|
||||
radius *= MAX(s.x, s.y);
|
||||
|
||||
if (radius > 0.0) {
|
||||
return radius;
|
||||
}
|
||||
}
|
||||
return 1.0; // Never a 0 radius
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
|
||||
estimate_radius = p_estimate_radius;
|
||||
_change_notify();
|
||||
reevaluate_agent_radius();
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::set_radius(real_t p_radius) {
|
||||
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
|
||||
radius = p_radius;
|
||||
reevaluate_agent_radius();
|
||||
}
|
86
scene/2d/navigation_obstacle_2d.h
Normal file
86
scene/2d/navigation_obstacle_2d.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_obstacle_2d.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_OBSTACLE_2D_H
|
||||
#define NAVIGATION_OBSTACLE_2D_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Navigation2D;
|
||||
|
||||
class NavigationObstacle2D : public Node {
|
||||
GDCLASS(NavigationObstacle2D, Node);
|
||||
|
||||
Navigation2D *navigation;
|
||||
Node2D *parent_node2d = nullptr;
|
||||
|
||||
RID agent;
|
||||
bool estimate_radius = true;
|
||||
real_t radius = 1.0;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
NavigationObstacle2D();
|
||||
virtual ~NavigationObstacle2D();
|
||||
|
||||
void set_navigation(Navigation2D *p_nav);
|
||||
const Navigation2D *get_navigation() const {
|
||||
return navigation;
|
||||
}
|
||||
|
||||
void set_navigation_node(Node *p_nav);
|
||||
Node *get_navigation_node() const;
|
||||
|
||||
RID get_rid() const {
|
||||
return agent;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
void initialize_agent();
|
||||
void reevaluate_agent_radius();
|
||||
real_t estimate_agent_radius() const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -32,7 +32,9 @@
|
|||
|
||||
#include "core/core_string_names.h"
|
||||
#include "core/engine.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "navigation_2d.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
|
||||
#include "thirdparty/misc/triangulator.h"
|
||||
|
||||
|
@ -80,6 +82,10 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double
|
|||
#endif
|
||||
|
||||
void NavigationPolygon::set_vertices(const PoolVector<Vector2> &p_vertices) {
|
||||
{
|
||||
MutexLock lock(navmesh_generation);
|
||||
navmesh.unref();
|
||||
}
|
||||
vertices = p_vertices;
|
||||
rect_cache_dirty = true;
|
||||
}
|
||||
|
@ -89,6 +95,10 @@ PoolVector<Vector2> NavigationPolygon::get_vertices() const {
|
|||
}
|
||||
|
||||
void NavigationPolygon::_set_polygons(const Array &p_array) {
|
||||
{
|
||||
MutexLock lock(navmesh_generation);
|
||||
navmesh.unref();
|
||||
}
|
||||
polygons.resize(p_array.size());
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
polygons.write[i].indices = p_array[i];
|
||||
|
@ -127,6 +137,10 @@ void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
|
|||
Polygon polygon;
|
||||
polygon.indices = p_polygon;
|
||||
polygons.push_back(polygon);
|
||||
{
|
||||
MutexLock lock(navmesh_generation);
|
||||
navmesh.unref();
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationPolygon::add_outline_at_index(const PoolVector<Vector2> &p_outline, int p_index) {
|
||||
|
@ -143,6 +157,34 @@ Vector<int> NavigationPolygon::get_polygon(int p_idx) {
|
|||
}
|
||||
void NavigationPolygon::clear_polygons() {
|
||||
polygons.clear();
|
||||
{
|
||||
MutexLock lock(navmesh_generation);
|
||||
navmesh.unref();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<NavigationMesh> NavigationPolygon::get_mesh() {
|
||||
MutexLock lock(navmesh_generation);
|
||||
if (navmesh.is_null()) {
|
||||
navmesh.instance();
|
||||
PoolVector<Vector3> verts;
|
||||
{
|
||||
verts.resize(get_vertices().size());
|
||||
PoolVector<Vector3>::Write w = verts.write();
|
||||
|
||||
PoolVector<Vector2>::Read r = get_vertices().read();
|
||||
|
||||
for (int i(0); i < get_vertices().size(); i++) {
|
||||
w[i] = Vector3(r[i].x, 0.0, r[i].y);
|
||||
}
|
||||
}
|
||||
navmesh->set_vertices(verts);
|
||||
|
||||
for (int i(0); i < get_polygon_count(); i++) {
|
||||
navmesh->add_polygon(get_polygon(i));
|
||||
}
|
||||
}
|
||||
return navmesh;
|
||||
}
|
||||
|
||||
void NavigationPolygon::add_outline(const PoolVector<Vector2> &p_outline) {
|
||||
|
@ -176,6 +218,10 @@ void NavigationPolygon::clear_outlines() {
|
|||
rect_cache_dirty = true;
|
||||
}
|
||||
void NavigationPolygon::make_polygons_from_outlines() {
|
||||
{
|
||||
MutexLock lock(navmesh_generation);
|
||||
navmesh.unref();
|
||||
}
|
||||
List<TriangulatorPoly> in_poly, out_poly;
|
||||
|
||||
Vector2 outside_point(-1e10, -1e10);
|
||||
|
@ -305,6 +351,9 @@ NavigationPolygon::NavigationPolygon() :
|
|||
rect_cache_dirty(true) {
|
||||
}
|
||||
|
||||
NavigationPolygon::~NavigationPolygon() {
|
||||
}
|
||||
|
||||
void NavigationPolygonInstance::set_enabled(bool p_enabled) {
|
||||
if (enabled == p_enabled) {
|
||||
return;
|
||||
|
@ -316,15 +365,10 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
|
|||
}
|
||||
|
||||
if (!enabled) {
|
||||
if (nav_id != -1) {
|
||||
navigation->navpoly_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
Navigation2DServer::get_singleton()->region_set_map(region, RID());
|
||||
} else {
|
||||
if (navigation) {
|
||||
if (navpoly.is_valid()) {
|
||||
nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
|
||||
}
|
||||
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,8 +399,8 @@ void NavigationPolygonInstance::_notification(int p_what) {
|
|||
while (c) {
|
||||
navigation = Object::cast_to<Navigation2D>(c);
|
||||
if (navigation) {
|
||||
if (enabled && navpoly.is_valid()) {
|
||||
nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
|
||||
if (enabled) {
|
||||
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -366,17 +410,12 @@ void NavigationPolygonInstance::_notification(int p_what) {
|
|||
|
||||
} break;
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
if (navigation && nav_id != -1) {
|
||||
navigation->navpoly_set_transform(nav_id, get_relative_transform_to_parent(navigation));
|
||||
}
|
||||
Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform());
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (navigation) {
|
||||
if (nav_id != -1) {
|
||||
navigation->navpoly_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
Navigation2DServer::get_singleton()->region_set_map(region, RID());
|
||||
}
|
||||
navigation = nullptr;
|
||||
} break;
|
||||
|
@ -431,24 +470,18 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg
|
|||
return;
|
||||
}
|
||||
|
||||
if (navigation && nav_id != -1) {
|
||||
navigation->navpoly_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
|
||||
if (navpoly.is_valid()) {
|
||||
navpoly->disconnect(CoreStringNames::get_singleton()->changed, this, "_navpoly_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_changed();
|
||||
|
||||
if (navigation && navpoly.is_valid() && enabled) {
|
||||
nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
|
||||
}
|
||||
|
||||
_change_notify("navpoly");
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
@ -506,8 +539,13 @@ void NavigationPolygonInstance::_bind_methods() {
|
|||
}
|
||||
|
||||
NavigationPolygonInstance::NavigationPolygonInstance() {
|
||||
navigation = nullptr;
|
||||
nav_id = -1;
|
||||
enabled = true;
|
||||
set_notify_transform(true);
|
||||
region = Navigation2DServer::get_singleton()->region_create();
|
||||
|
||||
navigation = nullptr;
|
||||
}
|
||||
|
||||
NavigationPolygonInstance::~NavigationPolygonInstance() {
|
||||
Navigation2DServer::get_singleton()->free(region);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define NAVIGATION_POLYGON_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/resources/navigation_mesh.h"
|
||||
|
||||
class NavigationPolygon : public Resource {
|
||||
GDCLASS(NavigationPolygon, Resource);
|
||||
|
@ -46,6 +47,10 @@ class NavigationPolygon : public Resource {
|
|||
mutable Rect2 item_rect;
|
||||
mutable bool rect_cache_dirty;
|
||||
|
||||
Mutex navmesh_generation;
|
||||
// Navigation mesh
|
||||
Ref<NavigationMesh> navmesh;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
@ -80,7 +85,10 @@ public:
|
|||
Vector<int> get_polygon(int p_idx);
|
||||
void clear_polygons();
|
||||
|
||||
Ref<NavigationMesh> get_mesh();
|
||||
|
||||
NavigationPolygon();
|
||||
~NavigationPolygon();
|
||||
};
|
||||
|
||||
class Navigation2D;
|
||||
|
@ -89,7 +97,7 @@ class NavigationPolygonInstance : public Node2D {
|
|||
GDCLASS(NavigationPolygonInstance, Node2D);
|
||||
|
||||
bool enabled;
|
||||
int nav_id;
|
||||
RID region;
|
||||
Navigation2D *navigation;
|
||||
Ref<NavigationPolygon> navpoly;
|
||||
|
||||
|
@ -114,6 +122,7 @@ public:
|
|||
String get_configuration_warning() const;
|
||||
|
||||
NavigationPolygonInstance();
|
||||
~NavigationPolygonInstance();
|
||||
};
|
||||
|
||||
#endif // NAVIGATIONPOLYGON_H
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "core/method_bind_ext.gen.inc"
|
||||
#include "core/os/os.h"
|
||||
#include "scene/2d/area_2d.h"
|
||||
#include "servers/navigation_2d_server.h"
|
||||
#include "servers/physics_2d_server.h"
|
||||
|
||||
int TileMap::_get_quadrant_size() const {
|
||||
|
@ -79,7 +80,7 @@ void TileMap::_notification(int p_what) {
|
|||
Quadrant &q = E->get();
|
||||
if (navigation) {
|
||||
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
|
||||
navigation->navpoly_remove(F->get().id);
|
||||
Navigation2DServer::get_singleton()->region_set_map(F->get().region, RID());
|
||||
}
|
||||
q.navpoly_ids.clear();
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ void TileMap::_update_quadrant_transform() {
|
|||
|
||||
if (navigation) {
|
||||
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
|
||||
navigation->navpoly_set_transform(F->get().id, nav_rel * F->get().xform);
|
||||
Navigation2DServer::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,7 +377,7 @@ void TileMap::update_dirty_quadrants() {
|
|||
|
||||
if (navigation) {
|
||||
for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
|
||||
navigation->navpoly_remove(E->get().id);
|
||||
Navigation2DServer::get_singleton()->region_set_map(E->get().region, RID());
|
||||
}
|
||||
q.navpoly_ids.clear();
|
||||
}
|
||||
|
@ -616,10 +617,13 @@ void TileMap::update_dirty_quadrants() {
|
|||
xform.set_origin(offset.floor() + q.pos);
|
||||
_fix_cell_transform(xform, c, npoly_ofs, s);
|
||||
|
||||
int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
|
||||
RID region = Navigation2DServer::get_singleton()->region_create();
|
||||
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
Navigation2DServer::get_singleton()->region_set_transform(region, nav_rel * xform);
|
||||
Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly);
|
||||
|
||||
Quadrant::NavPoly np;
|
||||
np.id = pid;
|
||||
np.region = region;
|
||||
np.xform = xform;
|
||||
q.navpoly_ids[E->key()] = np;
|
||||
|
||||
|
@ -813,7 +817,7 @@ void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
|
|||
|
||||
if (navigation) {
|
||||
for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
|
||||
navigation->navpoly_remove(E->get().id);
|
||||
Navigation2DServer::get_singleton()->region_set_map(E->get().region, RID());
|
||||
}
|
||||
q.navpoly_ids.clear();
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ private:
|
|||
SelfList<Quadrant> dirty_list;
|
||||
|
||||
struct NavPoly {
|
||||
int id;
|
||||
RID region;
|
||||
Transform2D xform;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,650 +30,49 @@
|
|||
|
||||
#include "navigation.h"
|
||||
|
||||
#define USE_ENTRY_POINT
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
void Navigation::_navmesh_link(int p_id) {
|
||||
ERR_FAIL_COND(!navmesh_map.has(p_id));
|
||||
NavMesh &nm = navmesh_map[p_id];
|
||||
ERR_FAIL_COND(nm.linked);
|
||||
ERR_FAIL_COND(nm.navmesh.is_null());
|
||||
|
||||
PoolVector<Vector3> vertices = nm.navmesh->get_vertices();
|
||||
int len = vertices.size();
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PoolVector<Vector3>::Read r = vertices.read();
|
||||
|
||||
for (int i = 0; i < nm.navmesh->get_polygon_count(); i++) {
|
||||
//build
|
||||
|
||||
List<Polygon>::Element *P = nm.polygons.push_back(Polygon());
|
||||
Polygon &p = P->get();
|
||||
p.owner = &nm;
|
||||
|
||||
Vector<int> poly = nm.navmesh->get_polygon(i);
|
||||
int plen = poly.size();
|
||||
const int *indices = poly.ptr();
|
||||
bool valid = true;
|
||||
p.edges.resize(plen);
|
||||
|
||||
Vector3 center;
|
||||
float sum = 0;
|
||||
|
||||
for (int j = 0; j < plen; j++) {
|
||||
int idx = indices[j];
|
||||
if (idx < 0 || idx >= len) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Polygon::Edge e;
|
||||
Vector3 ep = nm.xform.xform(r[idx]);
|
||||
center += ep;
|
||||
e.point = _get_point(ep);
|
||||
p.edges.write[j] = e;
|
||||
|
||||
if (j >= 2) {
|
||||
Vector3 epa = nm.xform.xform(r[indices[j - 2]]);
|
||||
Vector3 epb = nm.xform.xform(r[indices[j - 1]]);
|
||||
|
||||
sum += up.dot((epb - epa).cross(ep - epa));
|
||||
}
|
||||
}
|
||||
|
||||
p.clockwise = sum > 0;
|
||||
|
||||
if (!valid) {
|
||||
nm.polygons.pop_back();
|
||||
ERR_CONTINUE(!valid);
|
||||
}
|
||||
|
||||
p.center = center;
|
||||
if (plen != 0) {
|
||||
p.center /= plen;
|
||||
}
|
||||
|
||||
//connect
|
||||
|
||||
for (int j = 0; j < plen; j++) {
|
||||
int next = (j + 1) % plen;
|
||||
EdgeKey ek(p.edges[j].point, p.edges[next].point);
|
||||
|
||||
Map<EdgeKey, Connection>::Element *C = connections.find(ek);
|
||||
if (!C) {
|
||||
Connection c;
|
||||
c.A = &p;
|
||||
c.A_edge = j;
|
||||
c.B = nullptr;
|
||||
c.B_edge = -1;
|
||||
connections[ek] = c;
|
||||
} else {
|
||||
if (C->get().B != nullptr) {
|
||||
ConnectionPending pending;
|
||||
pending.polygon = &p;
|
||||
pending.edge = j;
|
||||
p.edges.write[j].P = C->get().pending.push_back(pending);
|
||||
continue;
|
||||
}
|
||||
|
||||
C->get().B = &p;
|
||||
C->get().B_edge = j;
|
||||
C->get().A->edges.write[C->get().A_edge].C = &p;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = j;
|
||||
p.edges.write[j].C = C->get().A;
|
||||
p.edges.write[j].C_edge = C->get().A_edge;
|
||||
//connection successful.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm.linked = true;
|
||||
Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const {
|
||||
return NavigationServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize);
|
||||
}
|
||||
|
||||
void Navigation::_navmesh_unlink(int p_id) {
|
||||
ERR_FAIL_COND(!navmesh_map.has(p_id));
|
||||
NavMesh &nm = navmesh_map[p_id];
|
||||
ERR_FAIL_COND(!nm.linked);
|
||||
|
||||
for (List<Polygon>::Element *E = nm.polygons.front(); E; E = E->next()) {
|
||||
Polygon &p = E->get();
|
||||
|
||||
int edge_count = p.edges.size();
|
||||
Polygon::Edge *edges = p.edges.ptrw();
|
||||
|
||||
for (int i = 0; i < edge_count; i++) {
|
||||
int next = (i + 1) % edge_count;
|
||||
EdgeKey ek(edges[i].point, edges[next].point);
|
||||
Map<EdgeKey, Connection>::Element *C = connections.find(ek);
|
||||
|
||||
ERR_CONTINUE(!C);
|
||||
|
||||
if (edges[i].P) {
|
||||
C->get().pending.erase(edges[i].P);
|
||||
edges[i].P = nullptr;
|
||||
} else if (C->get().B) {
|
||||
//disconnect
|
||||
|
||||
C->get().B->edges.write[C->get().B_edge].C = nullptr;
|
||||
C->get().B->edges.write[C->get().B_edge].C_edge = -1;
|
||||
C->get().A->edges.write[C->get().A_edge].C = nullptr;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = -1;
|
||||
|
||||
if (C->get().A == &E->get()) {
|
||||
C->get().A = C->get().B;
|
||||
C->get().A_edge = C->get().B_edge;
|
||||
}
|
||||
C->get().B = nullptr;
|
||||
C->get().B_edge = -1;
|
||||
|
||||
if (C->get().pending.size()) {
|
||||
//reconnect if something is pending
|
||||
ConnectionPending cp = C->get().pending.front()->get();
|
||||
C->get().pending.pop_front();
|
||||
|
||||
C->get().B = cp.polygon;
|
||||
C->get().B_edge = cp.edge;
|
||||
C->get().A->edges.write[C->get().A_edge].C = cp.polygon;
|
||||
C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge;
|
||||
cp.polygon->edges.write[cp.edge].C = C->get().A;
|
||||
cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge;
|
||||
cp.polygon->edges.write[cp.edge].P = nullptr;
|
||||
}
|
||||
|
||||
} else {
|
||||
connections.erase(C);
|
||||
//erase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm.polygons.clear();
|
||||
|
||||
nm.linked = false;
|
||||
Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const {
|
||||
return NavigationServer::get_singleton()->map_get_closest_point_to_segment(map, p_from, p_to, p_use_collision);
|
||||
}
|
||||
|
||||
int Navigation::navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) {
|
||||
int id = last_id++;
|
||||
NavMesh nm;
|
||||
nm.linked = false;
|
||||
nm.navmesh = p_mesh;
|
||||
nm.xform = p_xform;
|
||||
nm.owner = p_owner;
|
||||
navmesh_map[id] = nm;
|
||||
|
||||
_navmesh_link(id);
|
||||
|
||||
return id;
|
||||
Vector3 Navigation::get_closest_point(const Vector3 &p_point) const {
|
||||
return NavigationServer::get_singleton()->map_get_closest_point(map, p_point);
|
||||
}
|
||||
|
||||
void Navigation::navmesh_set_transform(int p_id, const Transform &p_xform) {
|
||||
ERR_FAIL_COND(!navmesh_map.has(p_id));
|
||||
NavMesh &nm = navmesh_map[p_id];
|
||||
if (nm.xform == p_xform) {
|
||||
return; //bleh
|
||||
}
|
||||
_navmesh_unlink(p_id);
|
||||
nm.xform = p_xform;
|
||||
_navmesh_link(p_id);
|
||||
}
|
||||
void Navigation::navmesh_remove(int p_id) {
|
||||
ERR_FAIL_COND_MSG(!navmesh_map.has(p_id), "Trying to remove nonexisting navmesh with id: " + itos(p_id));
|
||||
_navmesh_unlink(p_id);
|
||||
navmesh_map.erase(p_id);
|
||||
Vector3 Navigation::get_closest_point_normal(const Vector3 &p_point) const {
|
||||
return NavigationServer::get_singleton()->map_get_closest_point_normal(map, p_point);
|
||||
}
|
||||
|
||||
void Navigation::_clip_path(Vector<Vector3> &path, Polygon *from_poly, const Vector3 &p_to_point, Polygon *p_to_poly) {
|
||||
Vector3 from = path[path.size() - 1];
|
||||
|
||||
if (from.distance_to(p_to_point) < CMP_EPSILON) {
|
||||
return;
|
||||
}
|
||||
Plane cut_plane;
|
||||
cut_plane.normal = (from - p_to_point).cross(up);
|
||||
if (cut_plane.normal == Vector3()) {
|
||||
return;
|
||||
}
|
||||
cut_plane.normal.normalize();
|
||||
cut_plane.d = cut_plane.normal.dot(from);
|
||||
|
||||
while (from_poly != p_to_poly) {
|
||||
int edge_count = from_poly->edges.size();
|
||||
ERR_FAIL_COND_MSG(edge_count == 0, "Polygon has no edges.");
|
||||
int pe = from_poly->prev_edge;
|
||||
int next = (pe + 1) % edge_count;
|
||||
Vector3 a = _get_vertex(from_poly->edges[pe].point);
|
||||
Vector3 b = _get_vertex(from_poly->edges[next].point);
|
||||
|
||||
from_poly = from_poly->edges[pe].C;
|
||||
ERR_FAIL_COND(!from_poly);
|
||||
|
||||
if (a.distance_to(b) > CMP_EPSILON) {
|
||||
Vector3 inters;
|
||||
if (cut_plane.intersects_segment(a, b, &inters)) {
|
||||
if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) {
|
||||
path.push_back(inters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) {
|
||||
Polygon *begin_poly = nullptr;
|
||||
Polygon *end_poly = nullptr;
|
||||
Vector3 begin_point;
|
||||
Vector3 end_point;
|
||||
float begin_d = 1e20;
|
||||
float end_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
|
||||
Vector3 spoint = f.get_closest_point_to(p_start);
|
||||
float dpoint = spoint.distance_to(p_start);
|
||||
if (dpoint < begin_d) {
|
||||
begin_d = dpoint;
|
||||
begin_poly = &p;
|
||||
begin_point = spoint;
|
||||
}
|
||||
|
||||
spoint = f.get_closest_point_to(p_end);
|
||||
dpoint = spoint.distance_to(p_end);
|
||||
if (dpoint < end_d) {
|
||||
end_d = dpoint;
|
||||
end_poly = &p;
|
||||
end_point = spoint;
|
||||
}
|
||||
}
|
||||
|
||||
p.prev_edge = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin_poly || !end_poly) {
|
||||
return Vector<Vector3>(); //no path
|
||||
}
|
||||
|
||||
if (begin_poly == end_poly) {
|
||||
Vector<Vector3> path;
|
||||
path.resize(2);
|
||||
path.write[0] = begin_point;
|
||||
path.write[1] = end_point;
|
||||
return path;
|
||||
}
|
||||
|
||||
bool found_route = false;
|
||||
|
||||
List<Polygon *> open_list;
|
||||
int begin_edge_count = begin_poly->edges.size();
|
||||
|
||||
for (int i = 0; i < begin_edge_count; i++) {
|
||||
if (begin_poly->edges[i].C) {
|
||||
begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
int next = (i + 1) % begin_edge_count;
|
||||
Vector3 edge[2] = {
|
||||
_get_vertex(begin_poly->edges[i].point),
|
||||
_get_vertex(begin_poly->edges[next].point)
|
||||
};
|
||||
|
||||
Vector3 entry = Geometry::get_closest_point_to_segment(begin_poly->entry, edge);
|
||||
begin_poly->edges[i].C->distance = begin_point.distance_to(entry);
|
||||
begin_poly->edges[i].C->entry = entry;
|
||||
#else
|
||||
begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center);
|
||||
#endif
|
||||
open_list.push_back(begin_poly->edges[i].C);
|
||||
}
|
||||
}
|
||||
|
||||
while (!found_route) {
|
||||
if (open_list.size() == 0) {
|
||||
break;
|
||||
}
|
||||
//check open list
|
||||
|
||||
List<Polygon *>::Element *least_cost_poly = nullptr;
|
||||
float least_cost = 1e30;
|
||||
|
||||
//this could be faster (cache previous results)
|
||||
for (List<Polygon *>::Element *E = open_list.front(); E; E = E->next()) {
|
||||
Polygon *p = E->get();
|
||||
|
||||
float cost = p->distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
cost += p->entry.distance_to(end_point);
|
||||
#else
|
||||
cost += p->center.distance_to(end_point);
|
||||
#endif
|
||||
if (cost < least_cost) {
|
||||
least_cost_poly = E;
|
||||
least_cost = cost;
|
||||
}
|
||||
}
|
||||
|
||||
Polygon *p = least_cost_poly->get();
|
||||
//open the neighbours for search
|
||||
|
||||
if (p == end_poly) {
|
||||
//oh my reached end! stop algorithm
|
||||
found_route = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int edge_count = p->edges.size();
|
||||
for (int i = 0; i < edge_count; i++) {
|
||||
Polygon::Edge &e = p->edges.write[i];
|
||||
|
||||
if (!e.C) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef USE_ENTRY_POINT
|
||||
int next = (i + 1) % edge_count;
|
||||
Vector3 edge[2] = {
|
||||
_get_vertex(p->edges[i].point),
|
||||
_get_vertex(p->edges[next].point)
|
||||
};
|
||||
|
||||
Vector3 entry = Geometry::get_closest_point_to_segment(p->entry, edge);
|
||||
float distance = p->entry.distance_to(entry) + p->distance;
|
||||
#else
|
||||
float distance = p->center.distance_to(e.C->center) + p->distance;
|
||||
#endif
|
||||
|
||||
if (e.C->prev_edge != -1) {
|
||||
//oh this was visited already, can we win the cost?
|
||||
|
||||
if (e.C->distance > distance) {
|
||||
e.C->prev_edge = e.C_edge;
|
||||
e.C->distance = distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
e.C->entry = entry;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
//add to open neighbours
|
||||
|
||||
e.C->prev_edge = e.C_edge;
|
||||
e.C->distance = distance;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
e.C->entry = entry;
|
||||
#endif
|
||||
open_list.push_back(e.C);
|
||||
}
|
||||
}
|
||||
|
||||
open_list.erase(least_cost_poly);
|
||||
}
|
||||
|
||||
if (found_route) {
|
||||
Vector<Vector3> path;
|
||||
|
||||
if (p_optimize) {
|
||||
//string pulling
|
||||
|
||||
Polygon *apex_poly = end_poly;
|
||||
Vector3 apex_point = end_point;
|
||||
Vector3 portal_left = apex_point;
|
||||
Vector3 portal_right = apex_point;
|
||||
Polygon *left_poly = end_poly;
|
||||
Polygon *right_poly = end_poly;
|
||||
Polygon *p = end_poly;
|
||||
path.push_back(end_point);
|
||||
|
||||
while (p) {
|
||||
Vector3 left;
|
||||
Vector3 right;
|
||||
|
||||
#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b)))
|
||||
|
||||
if (p == begin_poly) {
|
||||
left = begin_point;
|
||||
right = begin_point;
|
||||
} else {
|
||||
int edge_count = p->edges.size();
|
||||
ERR_FAIL_COND_V_MSG(edge_count == 0, Vector<Vector3>(), "Polygon has no edges.");
|
||||
int prev = p->prev_edge;
|
||||
int prev_n = (p->prev_edge + 1) % edge_count;
|
||||
left = _get_vertex(p->edges[prev].point);
|
||||
right = _get_vertex(p->edges[prev_n].point);
|
||||
|
||||
//if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
|
||||
if (p->clockwise) {
|
||||
SWAP(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
bool skip = false;
|
||||
|
||||
if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
|
||||
//process
|
||||
if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
|
||||
left_poly = p;
|
||||
portal_left = left;
|
||||
} else {
|
||||
_clip_path(path, apex_poly, portal_right, right_poly);
|
||||
|
||||
apex_point = portal_right;
|
||||
p = right_poly;
|
||||
left_poly = p;
|
||||
apex_poly = p;
|
||||
portal_left = apex_point;
|
||||
portal_right = apex_point;
|
||||
path.push_back(apex_point);
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
|
||||
//process
|
||||
if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
|
||||
right_poly = p;
|
||||
portal_right = right;
|
||||
} else {
|
||||
_clip_path(path, apex_poly, portal_left, left_poly);
|
||||
|
||||
apex_point = portal_left;
|
||||
p = left_poly;
|
||||
right_poly = p;
|
||||
apex_poly = p;
|
||||
portal_right = apex_point;
|
||||
portal_left = apex_point;
|
||||
path.push_back(apex_point);
|
||||
}
|
||||
}
|
||||
|
||||
if (p != begin_poly) {
|
||||
p = p->edges[p->prev_edge].C;
|
||||
} else {
|
||||
p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (path[path.size() - 1] != begin_point) {
|
||||
path.push_back(begin_point);
|
||||
}
|
||||
|
||||
path.invert();
|
||||
|
||||
} else {
|
||||
//midpoints
|
||||
Polygon *p = end_poly;
|
||||
|
||||
path.push_back(end_point);
|
||||
while (true) {
|
||||
int prev = p->prev_edge;
|
||||
#ifdef USE_ENTRY_POINT
|
||||
Vector3 point = p->entry;
|
||||
#else
|
||||
int edge_count = p->edges.size();
|
||||
ERR_FAIL_COND_V_MSG(edge_count == 0, Vector<Vector3>(), "Polygon has no edges.");
|
||||
int prev_n = (p->prev_edge + 1) % edge_count;
|
||||
Vector3 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5;
|
||||
#endif
|
||||
path.push_back(point);
|
||||
p = p->edges[prev].C;
|
||||
if (p == begin_poly) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
path.push_back(begin_point);
|
||||
|
||||
path.invert();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
return Vector<Vector3>();
|
||||
}
|
||||
|
||||
Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool &p_use_collision) {
|
||||
bool use_collision = p_use_collision;
|
||||
Vector3 closest_point;
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
|
||||
Vector3 inters;
|
||||
if (f.intersects_segment(p_from, p_to, &inters)) {
|
||||
if (!use_collision) {
|
||||
closest_point = inters;
|
||||
use_collision = true;
|
||||
closest_point_d = p_from.distance_to(inters);
|
||||
} else if (closest_point_d > inters.distance_to(p_from)) {
|
||||
closest_point = inters;
|
||||
closest_point_d = p_from.distance_to(inters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_collision) {
|
||||
int edge_count = p.edges.size();
|
||||
for (int i = 0; i < edge_count; i++) {
|
||||
Vector3 a, b;
|
||||
int next = (i + 1) % edge_count;
|
||||
Geometry::get_closest_points_between_segments(p_from, p_to, _get_vertex(p.edges[i].point), _get_vertex(p.edges[next].point), a, b);
|
||||
|
||||
float d = a.distance_to(b);
|
||||
if (d < closest_point_d) {
|
||||
closest_point_d = d;
|
||||
closest_point = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
Vector3 Navigation::get_closest_point(const Vector3 &p_point) {
|
||||
Vector3 closest_point;
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
|
||||
Vector3 inters = f.get_closest_point_to(p_point);
|
||||
float d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_d = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
Vector3 Navigation::get_closest_point_normal(const Vector3 &p_point) {
|
||||
Vector3 closest_point;
|
||||
Vector3 closest_normal;
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
|
||||
Vector3 inters = f.get_closest_point_to(p_point);
|
||||
float d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_d = d;
|
||||
closest_normal = f.get_plane().normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_normal;
|
||||
}
|
||||
|
||||
Object *Navigation::get_closest_point_owner(const Vector3 &p_point) {
|
||||
Vector3 closest_point;
|
||||
Object *owner = nullptr;
|
||||
float closest_point_d = 1e20;
|
||||
|
||||
for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
|
||||
if (!E->get().linked) {
|
||||
continue;
|
||||
}
|
||||
for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
|
||||
Polygon &p = F->get();
|
||||
for (int i = 2; i < p.edges.size(); i++) {
|
||||
Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
|
||||
Vector3 inters = f.get_closest_point_to(p_point);
|
||||
float d = inters.distance_to(p_point);
|
||||
if (d < closest_point_d) {
|
||||
closest_point = inters;
|
||||
closest_point_d = d;
|
||||
owner = E->get().owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return owner;
|
||||
RID Navigation::get_closest_point_owner(const Vector3 &p_point) const {
|
||||
return NavigationServer::get_singleton()->map_get_closest_point_owner(map, p_point);
|
||||
}
|
||||
|
||||
void Navigation::set_up_vector(const Vector3 &p_up) {
|
||||
up = p_up;
|
||||
NavigationServer::get_singleton()->map_set_up(map, up);
|
||||
}
|
||||
|
||||
Vector3 Navigation::get_up_vector() const {
|
||||
return up;
|
||||
}
|
||||
|
||||
void Navigation::set_cell_size(float p_cell_size) {
|
||||
cell_size = p_cell_size;
|
||||
NavigationServer::get_singleton()->map_set_cell_size(map, cell_size);
|
||||
}
|
||||
|
||||
void Navigation::set_edge_connection_margin(float p_edge_connection_margin) {
|
||||
edge_connection_margin = p_edge_connection_margin;
|
||||
NavigationServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin);
|
||||
}
|
||||
|
||||
void Navigation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("navmesh_add", "mesh", "xform", "owner"), &Navigation::navmesh_add, DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("navmesh_set_transform", "id", "xform"), &Navigation::navmesh_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("navmesh_remove", "id"), &Navigation::navmesh_remove);
|
||||
ClassDB::bind_method(D_METHOD("get_rid"), &Navigation::get_rid);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation::get_simple_path, DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "start", "end", "use_collision"), &Navigation::get_closest_point_to_segment, DEFVAL(false));
|
||||
|
@ -684,12 +83,37 @@ void Navigation::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_up_vector", "up"), &Navigation::set_up_vector);
|
||||
ClassDB::bind_method(D_METHOD("get_up_vector"), &Navigation::get_up_vector);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation::set_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation::get_cell_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation::set_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation::get_edge_connection_margin);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
|
||||
}
|
||||
|
||||
void Navigation::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
NavigationServer::get_singleton()->map_set_active(map, true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
NavigationServer::get_singleton()->map_set_active(map, false);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Navigation::Navigation() {
|
||||
ERR_FAIL_COND(sizeof(Point) != 8);
|
||||
cell_size = 0.01; //one centimeter
|
||||
last_id = 1;
|
||||
map = NavigationServer::get_singleton()->map_create();
|
||||
|
||||
set_cell_size(0.3);
|
||||
set_edge_connection_margin(5.0); // Five meters, depends alot on the agents radius
|
||||
|
||||
up = Vector3(0, 1, 0);
|
||||
}
|
||||
|
||||
Navigation::~Navigation() {
|
||||
NavigationServer::get_singleton()->free(map);
|
||||
}
|
||||
|
|
|
@ -31,145 +31,48 @@
|
|||
#ifndef NAVIGATION_H
|
||||
#define NAVIGATION_H
|
||||
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
|
||||
class Navigation : public Spatial {
|
||||
GDCLASS(Navigation, Spatial);
|
||||
|
||||
union Point {
|
||||
struct {
|
||||
int64_t x : 21;
|
||||
int64_t y : 22;
|
||||
int64_t z : 21;
|
||||
};
|
||||
|
||||
uint64_t key;
|
||||
bool operator<(const Point &p_key) const { return key < p_key.key; }
|
||||
};
|
||||
|
||||
struct EdgeKey {
|
||||
Point a;
|
||||
Point b;
|
||||
|
||||
bool operator<(const EdgeKey &p_key) const {
|
||||
return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
|
||||
};
|
||||
|
||||
EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
|
||||
a(p_a),
|
||||
b(p_b) {
|
||||
if (a.key > b.key) {
|
||||
SWAP(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NavMesh;
|
||||
struct Polygon;
|
||||
|
||||
struct ConnectionPending {
|
||||
Polygon *polygon;
|
||||
int edge;
|
||||
};
|
||||
|
||||
struct Polygon {
|
||||
struct Edge {
|
||||
Point point;
|
||||
Polygon *C; //connection
|
||||
int C_edge;
|
||||
List<ConnectionPending>::Element *P;
|
||||
Edge() {
|
||||
C = nullptr;
|
||||
C_edge = -1;
|
||||
P = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Edge> edges;
|
||||
|
||||
Vector3 center;
|
||||
Vector3 entry;
|
||||
|
||||
float distance;
|
||||
int prev_edge;
|
||||
bool clockwise;
|
||||
|
||||
NavMesh *owner;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
Polygon *A;
|
||||
int A_edge;
|
||||
Polygon *B;
|
||||
int B_edge;
|
||||
|
||||
List<ConnectionPending> pending;
|
||||
|
||||
Connection() {
|
||||
A = nullptr;
|
||||
B = nullptr;
|
||||
A_edge = -1;
|
||||
B_edge = -1;
|
||||
}
|
||||
};
|
||||
|
||||
Map<EdgeKey, Connection> connections;
|
||||
|
||||
struct NavMesh {
|
||||
Object *owner;
|
||||
Transform xform;
|
||||
bool linked;
|
||||
Ref<NavigationMesh> navmesh;
|
||||
List<Polygon> polygons;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Point _get_point(const Vector3 &p_pos) const {
|
||||
int x = int(Math::floor(p_pos.x / cell_size));
|
||||
int y = int(Math::floor(p_pos.y / cell_size));
|
||||
int z = int(Math::floor(p_pos.z / cell_size));
|
||||
|
||||
Point p;
|
||||
p.key = 0;
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
p.z = z;
|
||||
return p;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector3 _get_vertex(const Point &p_point) const {
|
||||
return Vector3(p_point.x, p_point.y, p_point.z) * cell_size;
|
||||
}
|
||||
|
||||
void _navmesh_link(int p_id);
|
||||
void _navmesh_unlink(int p_id);
|
||||
|
||||
float cell_size;
|
||||
Map<int, NavMesh> navmesh_map;
|
||||
int last_id;
|
||||
RID map;
|
||||
|
||||
Vector3 up;
|
||||
void _clip_path(Vector<Vector3> &path, Polygon *from_poly, const Vector3 &p_to_point, Polygon *p_to_poly);
|
||||
real_t cell_size;
|
||||
real_t edge_connection_margin;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
RID get_rid() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
void set_up_vector(const Vector3 &p_up);
|
||||
Vector3 get_up_vector() const;
|
||||
|
||||
//API should be as dynamic as possible
|
||||
int navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = nullptr);
|
||||
void navmesh_set_transform(int p_id, const Transform &p_xform);
|
||||
void navmesh_remove(int p_id);
|
||||
void set_cell_size(float p_cell_size);
|
||||
float get_cell_size() const {
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
Vector<Vector3> get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize = true);
|
||||
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool &p_use_collision = false);
|
||||
Vector3 get_closest_point(const Vector3 &p_point);
|
||||
Vector3 get_closest_point_normal(const Vector3 &p_point);
|
||||
Object *get_closest_point_owner(const Vector3 &p_point);
|
||||
void set_edge_connection_margin(float p_edge_connection_margin);
|
||||
float get_edge_connection_margin() const {
|
||||
return edge_connection_margin;
|
||||
}
|
||||
|
||||
Vector<Vector3> get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize = true) const;
|
||||
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const;
|
||||
Vector3 get_closest_point(const Vector3 &p_point) const;
|
||||
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
|
||||
RID get_closest_point_owner(const Vector3 &p_point) const;
|
||||
|
||||
Navigation();
|
||||
~Navigation();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_H
|
||||
|
|
375
scene/3d/navigation_agent.cpp
Normal file
375
scene/3d/navigation_agent.cpp
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_agent.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "navigation_agent.h"
|
||||
|
||||
#include "core/engine.h"
|
||||
#include "scene/3d/navigation.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
void NavigationAgent::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent::set_target_desired_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent::get_target_desired_distance);
|
||||
|
||||
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_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_navigation", "navigation"), &NavigationAgent::set_navigation_node);
|
||||
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent::get_navigation_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent::set_neighbor_dist);
|
||||
ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent::get_neighbor_dist);
|
||||
|
||||
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_max_speed", "max_speed"), &NavigationAgent::set_max_speed);
|
||||
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent::get_max_speed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent::set_path_max_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent::get_path_max_distance);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent::set_target_location);
|
||||
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent::get_target_location);
|
||||
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent::get_next_location);
|
||||
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
|
||||
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent::set_velocity);
|
||||
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("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_location"), &NavigationAgent::get_final_location);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent::_avoidance_done);
|
||||
|
||||
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, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_radius", "get_radius");
|
||||
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, "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::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("path_changed"));
|
||||
ADD_SIGNAL(MethodInfo("target_reached"));
|
||||
ADD_SIGNAL(MethodInfo("navigation_finished"));
|
||||
ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity")));
|
||||
}
|
||||
|
||||
void NavigationAgent::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
agent_parent = Object::cast_to<Spatial>(get_parent());
|
||||
|
||||
NavigationServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done");
|
||||
|
||||
// Search the navigation node and set it
|
||||
{
|
||||
Navigation *nav = nullptr;
|
||||
Node *p = get_parent();
|
||||
while (p != nullptr) {
|
||||
nav = Object::cast_to<Navigation>(p);
|
||||
if (nav != nullptr) {
|
||||
p = nullptr;
|
||||
} else {
|
||||
p = p->get_parent();
|
||||
}
|
||||
}
|
||||
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
set_physics_process_internal(true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
agent_parent = nullptr;
|
||||
set_navigation(nullptr);
|
||||
set_physics_process_internal(false);
|
||||
// Want to call ready again when the node enters the tree again. We're not using enter_tree notification because
|
||||
// the navigation map may not be ready at that time. This fixes issues with taking the agent out of the scene tree.
|
||||
request_ready();
|
||||
} break;
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (agent_parent) {
|
||||
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
|
||||
_check_distance_to_target();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationAgent::NavigationAgent() :
|
||||
agent_parent(nullptr),
|
||||
navigation(nullptr),
|
||||
agent(RID()),
|
||||
target_desired_distance(1.0),
|
||||
navigation_height_offset(0.0),
|
||||
path_max_distance(3.0),
|
||||
velocity_submitted(false),
|
||||
target_reached(false),
|
||||
navigation_finished(true) {
|
||||
agent = NavigationServer::get_singleton()->agent_create();
|
||||
set_neighbor_dist(50.0);
|
||||
set_max_neighbors(10);
|
||||
set_time_horizon(5.0);
|
||||
set_radius(1.0);
|
||||
set_max_speed(10.0);
|
||||
set_ignore_y(true);
|
||||
}
|
||||
|
||||
NavigationAgent::~NavigationAgent() {
|
||||
NavigationServer::get_singleton()->free(agent);
|
||||
agent = RID(); // Pointless
|
||||
}
|
||||
|
||||
void NavigationAgent::set_navigation(Navigation *p_nav) {
|
||||
if (navigation == p_nav) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
navigation = p_nav;
|
||||
NavigationServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
||||
}
|
||||
|
||||
void NavigationAgent::set_navigation_node(Node *p_nav) {
|
||||
Navigation *nav = Object::cast_to<Navigation>(p_nav);
|
||||
ERR_FAIL_NULL(nav);
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
Node *NavigationAgent::get_navigation_node() const {
|
||||
return Object::cast_to<Node>(navigation);
|
||||
}
|
||||
|
||||
void NavigationAgent::set_target_desired_distance(real_t p_dd) {
|
||||
target_desired_distance = p_dd;
|
||||
}
|
||||
|
||||
void NavigationAgent::set_radius(real_t p_radius) {
|
||||
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_ignore_y(bool p_ignore_y) {
|
||||
ignore_y = p_ignore_y;
|
||||
NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y);
|
||||
}
|
||||
|
||||
void NavigationAgent::set_neighbor_dist(real_t p_dist) {
|
||||
neighbor_dist = p_dist;
|
||||
NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
|
||||
}
|
||||
|
||||
void NavigationAgent::set_max_neighbors(int p_count) {
|
||||
max_neighbors = 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_max_speed(real_t p_max_speed) {
|
||||
max_speed = p_max_speed;
|
||||
NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
|
||||
}
|
||||
|
||||
void NavigationAgent::set_path_max_distance(real_t p_pmd) {
|
||||
path_max_distance = p_pmd;
|
||||
}
|
||||
|
||||
real_t NavigationAgent::get_path_max_distance() {
|
||||
return path_max_distance;
|
||||
}
|
||||
|
||||
void NavigationAgent::set_target_location(Vector3 p_location) {
|
||||
target_location = p_location;
|
||||
navigation_path.clear();
|
||||
target_reached = false;
|
||||
navigation_finished = false;
|
||||
update_frame_id = 0;
|
||||
}
|
||||
|
||||
Vector3 NavigationAgent::get_target_location() const {
|
||||
return target_location;
|
||||
}
|
||||
|
||||
Vector3 NavigationAgent::get_next_location() {
|
||||
update_navigation();
|
||||
if (navigation_path.size() == 0) {
|
||||
ERR_FAIL_COND_V(agent_parent == nullptr, Vector3());
|
||||
return agent_parent->get_global_transform().origin;
|
||||
} else {
|
||||
return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
|
||||
}
|
||||
}
|
||||
|
||||
real_t NavigationAgent::distance_to_target() const {
|
||||
ERR_FAIL_COND_V(agent_parent == nullptr, 0.0);
|
||||
return agent_parent->get_global_transform().origin.distance_to(target_location);
|
||||
}
|
||||
|
||||
bool NavigationAgent::is_target_reached() const {
|
||||
return target_reached;
|
||||
}
|
||||
|
||||
bool NavigationAgent::is_target_reachable() {
|
||||
return target_desired_distance >= get_final_location().distance_to(target_location);
|
||||
}
|
||||
|
||||
bool NavigationAgent::is_navigation_finished() {
|
||||
update_navigation();
|
||||
return navigation_finished;
|
||||
}
|
||||
|
||||
Vector3 NavigationAgent::get_final_location() {
|
||||
update_navigation();
|
||||
if (navigation_path.size() == 0) {
|
||||
return Vector3();
|
||||
}
|
||||
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);
|
||||
velocity_submitted = true;
|
||||
}
|
||||
|
||||
void NavigationAgent::_avoidance_done(Vector3 p_new_velocity) {
|
||||
prev_safe_velocity = p_new_velocity;
|
||||
|
||||
if (!velocity_submitted) {
|
||||
target_velocity = Vector3();
|
||||
return;
|
||||
}
|
||||
velocity_submitted = false;
|
||||
|
||||
emit_signal("velocity_computed", p_new_velocity);
|
||||
}
|
||||
|
||||
String NavigationAgent::get_configuration_warning() const {
|
||||
if (!Object::cast_to<Spatial>(get_parent())) {
|
||||
return TTR("The NavigationAgent can be used only under a spatial node.");
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
void NavigationAgent::update_navigation() {
|
||||
if (agent_parent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (navigation == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_frame_id = Engine::get_singleton()->get_physics_frames();
|
||||
|
||||
Vector3 o = agent_parent->get_global_transform().origin;
|
||||
|
||||
bool reload_path = false;
|
||||
|
||||
if (NavigationServer::get_singleton()->agent_is_map_changed(agent)) {
|
||||
reload_path = true;
|
||||
} else if (navigation_path.size() == 0) {
|
||||
reload_path = true;
|
||||
} else {
|
||||
// Check if too far from the navigation path
|
||||
if (nav_path_index > 0) {
|
||||
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;
|
||||
Vector3 p = Geometry::get_closest_point_to_segment(o, segment);
|
||||
if (o.distance_to(p) >= path_max_distance) {
|
||||
// To faraway, reload path
|
||||
reload_path = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_path) {
|
||||
navigation_path = NavigationServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true);
|
||||
navigation_finished = false;
|
||||
nav_path_index = 0;
|
||||
emit_signal("path_changed");
|
||||
}
|
||||
|
||||
if (navigation_path.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we can advance the navigation path
|
||||
if (navigation_finished == false) {
|
||||
// Advances to the next far away location.
|
||||
while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < target_desired_distance) {
|
||||
nav_path_index += 1;
|
||||
if (nav_path_index == navigation_path.size()) {
|
||||
_check_distance_to_target();
|
||||
nav_path_index -= 1;
|
||||
navigation_finished = true;
|
||||
emit_signal("navigation_finished");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationAgent::_check_distance_to_target() {
|
||||
if (!target_reached) {
|
||||
if (distance_to_target() < target_desired_distance) {
|
||||
target_reached = true;
|
||||
emit_signal("target_reached");
|
||||
}
|
||||
}
|
||||
}
|
163
scene/3d/navigation_agent.h
Normal file
163
scene/3d/navigation_agent.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_agent.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_AGENT_H
|
||||
#define NAVIGATION_AGENT_H
|
||||
|
||||
#include "core/vector.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Spatial;
|
||||
class Navigation;
|
||||
|
||||
class NavigationAgent : public Node {
|
||||
GDCLASS(NavigationAgent, Node);
|
||||
|
||||
Spatial *agent_parent;
|
||||
Navigation *navigation;
|
||||
|
||||
RID agent;
|
||||
|
||||
real_t target_desired_distance;
|
||||
real_t radius;
|
||||
real_t navigation_height_offset;
|
||||
bool ignore_y;
|
||||
real_t neighbor_dist;
|
||||
int max_neighbors;
|
||||
real_t time_horizon;
|
||||
real_t max_speed;
|
||||
|
||||
real_t path_max_distance;
|
||||
|
||||
Vector3 target_location;
|
||||
Vector<Vector3> navigation_path;
|
||||
int nav_path_index;
|
||||
bool velocity_submitted;
|
||||
Vector3 prev_safe_velocity;
|
||||
/// The submitted target velocity
|
||||
Vector3 target_velocity;
|
||||
bool target_reached;
|
||||
bool navigation_finished;
|
||||
// No initialized on purpose
|
||||
uint32_t update_frame_id;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
NavigationAgent();
|
||||
virtual ~NavigationAgent();
|
||||
|
||||
void set_navigation(Navigation *p_nav);
|
||||
const Navigation *get_navigation() const {
|
||||
return navigation;
|
||||
}
|
||||
|
||||
void set_navigation_node(Node *p_nav);
|
||||
Node *get_navigation_node() const;
|
||||
|
||||
RID get_rid() const {
|
||||
return agent;
|
||||
}
|
||||
|
||||
void set_target_desired_distance(real_t p_dd);
|
||||
real_t get_target_desired_distance() const {
|
||||
return target_desired_distance;
|
||||
}
|
||||
|
||||
void set_radius(real_t p_radius);
|
||||
real_t get_radius() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
void set_agent_height_offset(real_t p_hh);
|
||||
real_t get_agent_height_offset() const {
|
||||
return navigation_height_offset;
|
||||
}
|
||||
|
||||
void set_ignore_y(bool p_ignore_y);
|
||||
bool get_ignore_y() const {
|
||||
return ignore_y;
|
||||
}
|
||||
|
||||
void set_neighbor_dist(real_t p_dist);
|
||||
real_t get_neighbor_dist() const {
|
||||
return neighbor_dist;
|
||||
}
|
||||
|
||||
void set_max_neighbors(int p_count);
|
||||
int get_max_neighbors() const {
|
||||
return max_neighbors;
|
||||
}
|
||||
|
||||
void set_time_horizon(real_t p_time);
|
||||
real_t get_time_horizon() const {
|
||||
return time_horizon;
|
||||
}
|
||||
|
||||
void set_max_speed(real_t p_max_speed);
|
||||
real_t get_max_speed() const {
|
||||
return max_speed;
|
||||
}
|
||||
|
||||
void set_path_max_distance(real_t p_pmd);
|
||||
real_t get_path_max_distance();
|
||||
|
||||
void set_target_location(Vector3 p_location);
|
||||
Vector3 get_target_location() const;
|
||||
|
||||
Vector3 get_next_location();
|
||||
|
||||
Vector<Vector3> get_nav_path() const {
|
||||
return navigation_path;
|
||||
}
|
||||
|
||||
int get_nav_path_index() const {
|
||||
return nav_path_index;
|
||||
}
|
||||
|
||||
real_t distance_to_target() const;
|
||||
bool is_target_reached() const;
|
||||
bool is_target_reachable();
|
||||
bool is_navigation_finished();
|
||||
Vector3 get_final_location();
|
||||
|
||||
void set_velocity(Vector3 p_velocity);
|
||||
void _avoidance_done(Vector3 p_new_velocity);
|
||||
|
||||
virtual String get_configuration_warning() const;
|
||||
|
||||
private:
|
||||
void update_navigation();
|
||||
void _check_distance_to_target();
|
||||
};
|
||||
|
||||
#endif
|
239
scene/3d/navigation_mesh_instance.cpp
Normal file
239
scene/3d/navigation_mesh_instance.cpp
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_mesh_instance.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "navigation_mesh_instance.h"
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "mesh_instance.h"
|
||||
#include "navigation.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
void NavigationMeshInstance::set_enabled(bool p_enabled) {
|
||||
if (enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
enabled = p_enabled;
|
||||
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
NavigationServer::get_singleton()->region_set_map(region, RID());
|
||||
} else {
|
||||
if (navigation) {
|
||||
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_view) {
|
||||
MeshInstance *dm = Object::cast_to<MeshInstance>(debug_view);
|
||||
if (is_enabled()) {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_material());
|
||||
} else {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
|
||||
}
|
||||
}
|
||||
|
||||
update_gizmo();
|
||||
}
|
||||
|
||||
bool NavigationMeshInstance::is_enabled() const {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void NavigationMeshInstance::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
Spatial *c = this;
|
||||
while (c) {
|
||||
navigation = Object::cast_to<Navigation>(c);
|
||||
if (navigation) {
|
||||
if (enabled) {
|
||||
NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
c = c->get_parent_spatial();
|
||||
}
|
||||
|
||||
if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
|
||||
MeshInstance *dm = memnew(MeshInstance);
|
||||
dm->set_mesh(navmesh->get_debug_mesh());
|
||||
if (is_enabled()) {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_material());
|
||||
} else {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
|
||||
}
|
||||
add_child(dm);
|
||||
debug_view = dm;
|
||||
}
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
NavigationServer::get_singleton()->region_set_transform(region, get_global_transform());
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (navigation) {
|
||||
NavigationServer::get_singleton()->region_set_map(region, RID());
|
||||
}
|
||||
|
||||
if (debug_view) {
|
||||
debug_view->queue_delete();
|
||||
debug_view = nullptr;
|
||||
}
|
||||
navigation = nullptr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) {
|
||||
if (p_navmesh == navmesh)
|
||||
return;
|
||||
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->remove_change_receptor(this);
|
||||
}
|
||||
|
||||
navmesh = p_navmesh;
|
||||
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->add_change_receptor(this);
|
||||
}
|
||||
|
||||
NavigationServer::get_singleton()->region_set_navmesh(region, p_navmesh);
|
||||
|
||||
if (debug_view && navmesh.is_valid()) {
|
||||
Object::cast_to<MeshInstance>(debug_view)->set_mesh(navmesh->get_debug_mesh());
|
||||
}
|
||||
|
||||
emit_signal("navigation_mesh_changed");
|
||||
|
||||
update_gizmo();
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
||||
Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const {
|
||||
return navmesh;
|
||||
}
|
||||
|
||||
struct BakeThreadsArgs {
|
||||
NavigationMeshInstance *nav_region;
|
||||
};
|
||||
|
||||
void _bake_navigation_mesh(void *p_user_data) {
|
||||
BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
|
||||
|
||||
if (args->nav_region->get_navigation_mesh().is_valid()) {
|
||||
Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh()->duplicate();
|
||||
|
||||
NavigationServer::get_singleton()->region_bake_navmesh(nav_mesh, args->nav_region);
|
||||
args->nav_region->call_deferred("_bake_finished", nav_mesh);
|
||||
memdelete(args);
|
||||
} else {
|
||||
ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
|
||||
args->nav_region->call_deferred("_bake_finished", Ref<NavigationMesh>());
|
||||
memdelete(args);
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::bake_navigation_mesh() {
|
||||
ERR_FAIL_COND_MSG(bake_thread.is_started(), "Navigation Mesh Bake thread is already baking a Navigation Mesh. Unable to start another bake request.");
|
||||
|
||||
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
|
||||
args->nav_region = this;
|
||||
|
||||
bake_thread.start(_bake_navigation_mesh, args);
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
|
||||
set_navigation_mesh(p_nav_mesh);
|
||||
bake_thread.wait_to_finish();
|
||||
emit_signal("bake_finished");
|
||||
}
|
||||
|
||||
String NavigationMeshInstance::get_configuration_warning() const {
|
||||
if (!is_visible_in_tree() || !is_inside_tree()) {
|
||||
return String();
|
||||
}
|
||||
|
||||
if (!navmesh.is_valid()) {
|
||||
return TTR("A NavigationMesh resource must be set or created for this node to work.");
|
||||
}
|
||||
const Spatial *c = this;
|
||||
while (c) {
|
||||
if (Object::cast_to<Navigation>(c)) {
|
||||
return String();
|
||||
}
|
||||
|
||||
c = Object::cast_to<Spatial>(c->get_parent());
|
||||
}
|
||||
|
||||
return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navmesh"), &NavigationMeshInstance::set_navigation_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationMeshInstance::get_navigation_mesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationMeshInstance::bake_navigation_mesh);
|
||||
ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("navigation_mesh_changed"));
|
||||
ADD_SIGNAL(MethodInfo("bake_finished"));
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) {
|
||||
update_gizmo();
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
||||
NavigationMeshInstance::NavigationMeshInstance() {
|
||||
set_notify_transform(true);
|
||||
region = NavigationServer::get_singleton()->region_create();
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
NavigationMeshInstance::~NavigationMeshInstance() {
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->remove_change_receptor(this);
|
||||
}
|
||||
NavigationServer::get_singleton()->free(region);
|
||||
}
|
74
scene/3d/navigation_mesh_instance.h
Normal file
74
scene/3d/navigation_mesh_instance.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_mesh_instance.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_MESH_INSTANCE_H
|
||||
#define NAVIGATION_MESH_INSTANCE_H
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/navigation_mesh.h"
|
||||
|
||||
class Navigation;
|
||||
|
||||
class NavigationMeshInstance : public Spatial {
|
||||
GDCLASS(NavigationMeshInstance, Spatial);
|
||||
|
||||
bool enabled;
|
||||
RID region;
|
||||
Ref<NavigationMesh> navmesh;
|
||||
|
||||
Navigation *navigation = nullptr;
|
||||
Node *debug_view = nullptr;
|
||||
Thread bake_thread;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
void _changed_callback(Object *p_changed, const char *p_prop);
|
||||
|
||||
public:
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const;
|
||||
|
||||
void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
|
||||
Ref<NavigationMesh> get_navigation_mesh() const;
|
||||
|
||||
/// Bakes the navigation mesh in a dedicated thread; once done, automatically
|
||||
/// sets the new navigation mesh and emits a signal
|
||||
void bake_navigation_mesh();
|
||||
void _bake_finished(Ref<NavigationMesh> p_nav_mesh);
|
||||
|
||||
String get_configuration_warning() const;
|
||||
|
||||
NavigationMeshInstance();
|
||||
~NavigationMeshInstance();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_MESH_INSTANCE_H
|
204
scene/3d/navigation_obstacle.cpp
Normal file
204
scene/3d/navigation_obstacle.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_obstacle.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "navigation_obstacle.h"
|
||||
|
||||
#include "scene/3d/collision_shape.h"
|
||||
#include "scene/3d/navigation.h"
|
||||
#include "scene/3d/physics_body.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
void NavigationObstacle::_bind_methods() {
|
||||
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");
|
||||
}
|
||||
|
||||
void NavigationObstacle::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "radius") {
|
||||
if (estimate_radius) {
|
||||
p_property.usage = PROPERTY_USAGE_NOEDITOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationObstacle::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
initialize_agent();
|
||||
parent_spatial = Object::cast_to<Spatial>(get_parent());
|
||||
|
||||
// Search the navigation node and set it
|
||||
{
|
||||
Navigation *nav = nullptr;
|
||||
Node *p = get_parent();
|
||||
while (p != nullptr) {
|
||||
nav = Object::cast_to<Navigation>(p);
|
||||
if (nav != nullptr) {
|
||||
p = nullptr;
|
||||
} else {
|
||||
p = p->get_parent();
|
||||
}
|
||||
}
|
||||
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
set_physics_process_internal(true);
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
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<Spatial>(get_parent());
|
||||
reevaluate_agent_radius();
|
||||
} break;
|
||||
case NOTIFICATION_UNPARENTED: {
|
||||
parent_spatial = nullptr;
|
||||
} break;
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (parent_spatial) {
|
||||
NavigationServer::get_singleton()->agent_set_position(agent, parent_spatial->get_global_transform().origin);
|
||||
}
|
||||
|
||||
PhysicsBody *rigid = Object::cast_to<PhysicsBody>(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);
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationObstacle::NavigationObstacle() :
|
||||
navigation(nullptr),
|
||||
agent(RID()) {
|
||||
agent = NavigationServer::get_singleton()->agent_create();
|
||||
}
|
||||
|
||||
NavigationObstacle::~NavigationObstacle() {
|
||||
NavigationServer::get_singleton()->free(agent);
|
||||
agent = RID(); // Pointless
|
||||
}
|
||||
|
||||
void NavigationObstacle::set_navigation(Navigation *p_nav) {
|
||||
if (navigation == p_nav) {
|
||||
return; // Pointless
|
||||
}
|
||||
|
||||
navigation = p_nav;
|
||||
NavigationServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
||||
}
|
||||
|
||||
void NavigationObstacle::set_navigation_node(Node *p_nav) {
|
||||
Navigation *nav = Object::cast_to<Navigation>(p_nav);
|
||||
ERR_FAIL_COND(nav);
|
||||
set_navigation(nav);
|
||||
}
|
||||
|
||||
Node *NavigationObstacle::get_navigation_node() const {
|
||||
return Object::cast_to<Node>(navigation);
|
||||
}
|
||||
|
||||
String NavigationObstacle::get_configuration_warning() const {
|
||||
if (!Object::cast_to<Spatial>(get_parent())) {
|
||||
return TTR("The NavigationObstacle only serves to provide collision avoidance to a spatial object.");
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
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) {
|
||||
NavigationServer::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
|
||||
}
|
||||
}
|
||||
|
||||
real_t NavigationObstacle::estimate_agent_radius() const {
|
||||
if (parent_spatial) {
|
||||
// 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<CollisionShape>(parent_spatial->get_child(i));
|
||||
if (cs) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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_radius(real_t p_radius) {
|
||||
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
|
||||
radius = p_radius;
|
||||
reevaluate_agent_radius();
|
||||
}
|
87
scene/3d/navigation_obstacle.h
Normal file
87
scene/3d/navigation_obstacle.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_obstacle.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef NAVIGATION_OBSTACLE_H
|
||||
#define NAVIGATION_OBSTACLE_H
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Navigation;
|
||||
|
||||
class NavigationObstacle : public Node {
|
||||
GDCLASS(NavigationObstacle, Node);
|
||||
|
||||
Spatial *parent_spatial = nullptr;
|
||||
Navigation *navigation;
|
||||
|
||||
RID agent;
|
||||
bool estimate_radius = true;
|
||||
real_t radius = 1.0;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
NavigationObstacle();
|
||||
virtual ~NavigationObstacle();
|
||||
|
||||
void set_navigation(Navigation *p_nav);
|
||||
const Navigation *get_navigation() const {
|
||||
return navigation;
|
||||
}
|
||||
|
||||
void set_navigation_node(Node *p_nav);
|
||||
Node *get_navigation_node() const;
|
||||
|
||||
RID get_rid() const {
|
||||
return agent;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
void initialize_agent();
|
||||
void reevaluate_agent_radius();
|
||||
real_t estimate_agent_radius() const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -46,6 +46,7 @@
|
|||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/navigation_server.h"
|
||||
#include "servers/physics_2d_server.h"
|
||||
#include "servers/physics_server.h"
|
||||
#include "viewport.h"
|
||||
|
@ -894,6 +895,7 @@ void SceneTree::set_pause(bool p_enabled) {
|
|||
return;
|
||||
}
|
||||
pause = p_enabled;
|
||||
NavigationServer::get_singleton()->set_active(!p_enabled);
|
||||
PhysicsServer::get_singleton()->set_active(!p_enabled);
|
||||
Physics2DServer::get_singleton()->set_active(!p_enabled);
|
||||
if (get_root()) {
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
#include "scene/2d/mesh_instance_2d.h"
|
||||
#include "scene/2d/multimesh_instance_2d.h"
|
||||
#include "scene/2d/navigation_2d.h"
|
||||
#include "scene/2d/navigation_agent_2d.h"
|
||||
#include "scene/2d/navigation_obstacle_2d.h"
|
||||
#include "scene/2d/parallax_background.h"
|
||||
#include "scene/2d/parallax_layer.h"
|
||||
#include "scene/2d/particles_2d.h"
|
||||
|
@ -66,6 +68,8 @@
|
|||
#include "scene/2d/touch_screen_button.h"
|
||||
#include "scene/2d/visibility_notifier_2d.h"
|
||||
#include "scene/2d/y_sort.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/3d/world_environment.h"
|
||||
#include "scene/animation/animation_blend_space_1d.h"
|
||||
#include "scene/animation/animation_blend_space_2d.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
|
@ -147,6 +151,7 @@
|
|||
#include "scene/resources/material.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/mesh_data_tool.h"
|
||||
#include "scene/resources/navigation_mesh.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/resources/particles_material.h"
|
||||
#include "scene/resources/physics_material.h"
|
||||
|
@ -170,9 +175,6 @@
|
|||
#include "scene/resources/world_2d.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/3d/world_environment.h"
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
#include "scene/3d/area.h"
|
||||
#include "scene/3d/arvr_nodes.h"
|
||||
|
@ -191,7 +193,9 @@
|
|||
#include "scene/3d/mesh_instance.h"
|
||||
#include "scene/3d/multimesh_instance.h"
|
||||
#include "scene/3d/navigation.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_agent.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "scene/3d/navigation_obstacle.h"
|
||||
#include "scene/3d/occluder.h"
|
||||
#include "scene/3d/particles.h"
|
||||
#include "scene/3d/path.h"
|
||||
|
@ -489,9 +493,15 @@ void register_scene_types() {
|
|||
ClassDB::register_class<ConeTwistJoint>();
|
||||
ClassDB::register_class<Generic6DOFJoint>();
|
||||
|
||||
ClassDB::register_class<Navigation>();
|
||||
ClassDB::register_class<NavigationMeshInstance>();
|
||||
ClassDB::register_class<NavigationAgent>();
|
||||
ClassDB::register_class<NavigationObstacle>();
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
||||
#endif
|
||||
ClassDB::register_class<NavigationMesh>();
|
||||
|
||||
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF_NOVAL("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
|
||||
|
||||
|
@ -740,6 +750,8 @@ void register_scene_types() {
|
|||
ClassDB::register_class<Navigation2D>();
|
||||
ClassDB::register_class<NavigationPolygon>();
|
||||
ClassDB::register_class<NavigationPolygonInstance>();
|
||||
ClassDB::register_class<NavigationAgent2D>();
|
||||
ClassDB::register_class<NavigationObstacle2D>();
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ Vector<Vector3> BoxShape::get_debug_mesh_lines() {
|
|||
return lines;
|
||||
}
|
||||
|
||||
real_t BoxShape::get_enclosing_radius() const {
|
||||
return extents.length();
|
||||
}
|
||||
|
||||
void BoxShape::_update_shape() {
|
||||
PhysicsServer::get_singleton()->shape_set_data(get_shape(), extents);
|
||||
Shape::_update_shape();
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
Vector3 get_extents() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
BoxShape();
|
||||
};
|
||||
|
|
|
@ -66,6 +66,10 @@ Vector<Vector3> CapsuleShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t CapsuleShape::get_enclosing_radius() const {
|
||||
return radius + height * 0.5;
|
||||
}
|
||||
|
||||
void CapsuleShape::_update_shape() {
|
||||
Dictionary d;
|
||||
d["radius"] = radius;
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
float get_height() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
CapsuleShape();
|
||||
};
|
||||
|
|
|
@ -98,6 +98,10 @@ Rect2 CapsuleShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t CapsuleShape2D::get_enclosing_radius() const {
|
||||
return radius + height * 0.5;
|
||||
}
|
||||
|
||||
void CapsuleShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleShape2D::set_radius);
|
||||
ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleShape2D::get_radius);
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
CapsuleShape2D();
|
||||
};
|
||||
|
|
|
@ -65,6 +65,10 @@ Rect2 CircleShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t CircleShape2D::get_enclosing_radius() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
|
||||
Vector<Vector2> points;
|
||||
for (int i = 0; i < 24; i++) {
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
CircleShape2D();
|
||||
};
|
||||
|
|
|
@ -60,6 +60,16 @@ Vector<Vector3> ConcavePolygonShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t ConcavePolygonShape::get_enclosing_radius() const {
|
||||
PoolVector<Vector3> data = get_faces();
|
||||
PoolVector<Vector3>::Read read = data.read();
|
||||
real_t r = 0;
|
||||
for (int i(0); i < data.size(); i++) {
|
||||
r = MAX(read[i].length_squared(), r);
|
||||
}
|
||||
return Math::sqrt(r);
|
||||
}
|
||||
|
||||
void ConcavePolygonShape::_update_shape() {
|
||||
Shape::_update_shape();
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
PoolVector<Vector3> get_faces() const;
|
||||
|
||||
Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
ConcavePolygonShape();
|
||||
};
|
||||
|
|
|
@ -94,6 +94,16 @@ Rect2 ConcavePolygonShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t ConcavePolygonShape2D::get_enclosing_radius() const {
|
||||
PoolVector<Vector2> data = get_segments();
|
||||
PoolVector<Vector2>::Read read = data.read();
|
||||
real_t r = 0;
|
||||
for (int i(0); i < data.size(); i++) {
|
||||
r = MAX(read[i].length_squared(), r);
|
||||
}
|
||||
return Math::sqrt(r);
|
||||
}
|
||||
|
||||
void ConcavePolygonShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_segments", "segments"), &ConcavePolygonShape2D::set_segments);
|
||||
ClassDB::bind_method(D_METHOD("get_segments"), &ConcavePolygonShape2D::get_segments);
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
ConcavePolygonShape2D();
|
||||
};
|
||||
|
|
|
@ -53,6 +53,16 @@ Vector<Vector3> ConvexPolygonShape::get_debug_mesh_lines() {
|
|||
return Vector<Vector3>();
|
||||
}
|
||||
|
||||
real_t ConvexPolygonShape::get_enclosing_radius() const {
|
||||
PoolVector<Vector3> data = get_points();
|
||||
PoolVector<Vector3>::Read read = data.read();
|
||||
real_t r = 0;
|
||||
for (int i(0); i < data.size(); i++) {
|
||||
r = MAX(read[i].length_squared(), r);
|
||||
}
|
||||
return Math::sqrt(r);
|
||||
}
|
||||
|
||||
void ConvexPolygonShape::_update_shape() {
|
||||
PhysicsServer::get_singleton()->shape_set_data(get_shape(), points);
|
||||
Shape::_update_shape();
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
PoolVector<Vector3> get_points() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
ConvexPolygonShape();
|
||||
};
|
||||
|
|
|
@ -99,6 +99,14 @@ Rect2 ConvexPolygonShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t ConvexPolygonShape2D::get_enclosing_radius() const {
|
||||
real_t r = 0;
|
||||
for (int i(0); i < get_points().size(); i++) {
|
||||
r = MAX(get_points()[i].length_squared(), r);
|
||||
}
|
||||
return Math::sqrt(r);
|
||||
}
|
||||
|
||||
ConvexPolygonShape2D::ConvexPolygonShape2D() :
|
||||
Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) {
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
ConvexPolygonShape2D();
|
||||
};
|
||||
|
|
|
@ -59,6 +59,10 @@ Vector<Vector3> CylinderShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t CylinderShape::get_enclosing_radius() const {
|
||||
return Vector2(radius, height * 0.5).length();
|
||||
}
|
||||
|
||||
void CylinderShape::_update_shape() {
|
||||
Dictionary d;
|
||||
d["radius"] = radius;
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
float get_height() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
CylinderShape();
|
||||
};
|
||||
|
|
|
@ -80,6 +80,10 @@ Vector<Vector3> HeightMapShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t HeightMapShape::get_enclosing_radius() const {
|
||||
return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length();
|
||||
}
|
||||
|
||||
void HeightMapShape::_update_shape() {
|
||||
Dictionary d;
|
||||
d["width"] = map_width;
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
PoolRealArray get_map_data() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
HeightMapShape();
|
||||
};
|
||||
|
|
|
@ -93,6 +93,10 @@ Rect2 LineShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t LineShape2D::get_enclosing_radius() const {
|
||||
return d;
|
||||
}
|
||||
|
||||
void LineShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal);
|
||||
ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal);
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
LineShape2D();
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "core/map.h"
|
||||
#include "core/resource.h"
|
||||
#include "mesh.h"
|
||||
#include "scene/3d/navigation_mesh.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
#include "shape.h"
|
||||
|
||||
class MeshLibrary : public Resource {
|
||||
|
|
|
@ -29,12 +29,8 @@
|
|||
/*************************************************************************/
|
||||
|
||||
#include "navigation_mesh.h"
|
||||
#include "mesh_instance.h"
|
||||
#include "navigation.h"
|
||||
|
||||
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
|
||||
ERR_FAIL_COND(p_mesh.is_null());
|
||||
|
||||
vertices = PoolVector<Vector3>();
|
||||
clear_polygons();
|
||||
|
||||
|
@ -557,191 +553,3 @@ NavigationMesh::NavigationMesh() {
|
|||
filter_ledge_spans = false;
|
||||
filter_walkable_low_height_spans = false;
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::set_enabled(bool p_enabled) {
|
||||
if (enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
enabled = p_enabled;
|
||||
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
if (nav_id != -1) {
|
||||
navigation->navmesh_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
} else {
|
||||
if (navigation) {
|
||||
if (navmesh.is_valid()) {
|
||||
nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_view) {
|
||||
MeshInstance *dm = Object::cast_to<MeshInstance>(debug_view);
|
||||
if (is_enabled()) {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_material());
|
||||
} else {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
|
||||
}
|
||||
}
|
||||
|
||||
update_gizmo();
|
||||
}
|
||||
|
||||
bool NavigationMeshInstance::is_enabled() const {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void NavigationMeshInstance::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
Spatial *c = this;
|
||||
while (c) {
|
||||
navigation = Object::cast_to<Navigation>(c);
|
||||
if (navigation) {
|
||||
if (enabled && navmesh.is_valid()) {
|
||||
nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
c = c->get_parent_spatial();
|
||||
}
|
||||
|
||||
if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
|
||||
MeshInstance *dm = memnew(MeshInstance);
|
||||
dm->set_mesh(navmesh->get_debug_mesh());
|
||||
if (is_enabled()) {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_material());
|
||||
} else {
|
||||
dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
|
||||
}
|
||||
add_child(dm);
|
||||
debug_view = dm;
|
||||
}
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
if (navigation && nav_id != -1) {
|
||||
navigation->navmesh_set_transform(nav_id, get_relative_transform(navigation));
|
||||
}
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (navigation) {
|
||||
if (nav_id != -1) {
|
||||
navigation->navmesh_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_view) {
|
||||
debug_view->queue_delete();
|
||||
debug_view = nullptr;
|
||||
}
|
||||
navigation = nullptr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) {
|
||||
if (p_navmesh == navmesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigation && nav_id != -1) {
|
||||
navigation->navmesh_remove(nav_id);
|
||||
nav_id = -1;
|
||||
}
|
||||
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->remove_change_receptor(this);
|
||||
}
|
||||
|
||||
navmesh = p_navmesh;
|
||||
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->add_change_receptor(this);
|
||||
}
|
||||
|
||||
if (navigation && navmesh.is_valid() && enabled) {
|
||||
nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
|
||||
}
|
||||
|
||||
if (debug_view && navmesh.is_valid()) {
|
||||
Object::cast_to<MeshInstance>(debug_view)->set_mesh(navmesh->get_debug_mesh());
|
||||
}
|
||||
|
||||
update_gizmo();
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
||||
Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const {
|
||||
return navmesh;
|
||||
}
|
||||
|
||||
String NavigationMeshInstance::get_configuration_warning() const {
|
||||
if (!is_visible_in_tree() || !is_inside_tree()) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String warning = Spatial::get_configuration_warning();
|
||||
if (!navmesh.is_valid()) {
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("A NavigationMesh resource must be set or created for this node to work.");
|
||||
return warning;
|
||||
}
|
||||
const Spatial *c = this;
|
||||
while (c) {
|
||||
if (Object::cast_to<Navigation>(c)) {
|
||||
return warning;
|
||||
}
|
||||
|
||||
c = Object::cast_to<Spatial>(c->get_parent());
|
||||
}
|
||||
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navmesh"), &NavigationMeshInstance::set_navigation_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationMeshInstance::get_navigation_mesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
}
|
||||
|
||||
void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) {
|
||||
update_gizmo();
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
||||
NavigationMeshInstance::NavigationMeshInstance() {
|
||||
debug_view = nullptr;
|
||||
navigation = nullptr;
|
||||
nav_id = -1;
|
||||
enabled = true;
|
||||
set_notify_transform(true);
|
||||
}
|
||||
|
||||
NavigationMeshInstance::~NavigationMeshInstance() {
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->remove_change_receptor(this);
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef NAVIGATION_MESH_H
|
||||
#define NAVIGATION_MESH_H
|
||||
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
||||
class Mesh;
|
||||
|
@ -195,34 +194,4 @@ VARIANT_ENUM_CAST(NavigationMesh::SamplePartitionType);
|
|||
VARIANT_ENUM_CAST(NavigationMesh::ParsedGeometryType);
|
||||
VARIANT_ENUM_CAST(NavigationMesh::SourceGeometryMode);
|
||||
|
||||
class Navigation;
|
||||
|
||||
class NavigationMeshInstance : public Spatial {
|
||||
GDCLASS(NavigationMeshInstance, Spatial);
|
||||
|
||||
bool enabled;
|
||||
int nav_id;
|
||||
Navigation *navigation;
|
||||
Ref<NavigationMesh> navmesh;
|
||||
|
||||
Node *debug_view;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
void _changed_callback(Object *p_changed, const char *p_prop);
|
||||
|
||||
public:
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const;
|
||||
|
||||
void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
|
||||
Ref<NavigationMesh> get_navigation_mesh() const;
|
||||
|
||||
String get_configuration_warning() const;
|
||||
|
||||
NavigationMeshInstance();
|
||||
~NavigationMeshInstance();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_MESH_H
|
|
@ -46,6 +46,10 @@ public:
|
|||
Plane get_plane() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const {
|
||||
// Should be infinite?
|
||||
return 0;
|
||||
};
|
||||
|
||||
PlaneShape();
|
||||
};
|
||||
|
|
|
@ -40,6 +40,10 @@ Vector<Vector3> RayShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t RayShape::get_enclosing_radius() const {
|
||||
return length;
|
||||
}
|
||||
|
||||
void RayShape::_update_shape() {
|
||||
Dictionary d;
|
||||
d["length"] = length;
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
bool get_slips_on_slope() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
RayShape();
|
||||
};
|
||||
|
|
|
@ -72,6 +72,10 @@ Rect2 RectangleShape2D::get_rect() const {
|
|||
return Rect2(-extents, extents * 2.0);
|
||||
}
|
||||
|
||||
real_t RectangleShape2D::get_enclosing_radius() const {
|
||||
return extents.length();
|
||||
}
|
||||
|
||||
void RectangleShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &RectangleShape2D::set_extents);
|
||||
ClassDB::bind_method(D_METHOD("get_extents"), &RectangleShape2D::get_extents);
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
RectangleShape2D();
|
||||
};
|
||||
|
|
|
@ -74,6 +74,10 @@ Rect2 SegmentShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t SegmentShape2D::get_enclosing_radius() const {
|
||||
return (a + b).length();
|
||||
}
|
||||
|
||||
void SegmentShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_a", "a"), &SegmentShape2D::set_a);
|
||||
ClassDB::bind_method(D_METHOD("get_a"), &SegmentShape2D::get_a);
|
||||
|
@ -126,6 +130,10 @@ Rect2 RayShape2D::get_rect() const {
|
|||
return rect;
|
||||
}
|
||||
|
||||
real_t RayShape2D::get_enclosing_radius() const {
|
||||
return length;
|
||||
}
|
||||
|
||||
void RayShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length);
|
||||
ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length);
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
SegmentShape2D();
|
||||
};
|
||||
|
@ -79,6 +80,7 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color);
|
||||
virtual Rect2 get_rect() const;
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
RayShape2D();
|
||||
};
|
||||
|
|
|
@ -56,6 +56,8 @@ public:
|
|||
|
||||
Ref<ArrayMesh> get_debug_mesh();
|
||||
virtual Vector<Vector3> get_debug_mesh_lines() = 0; // { return Vector<Vector3>(); }
|
||||
/// Returns the radius of a sphere that fully enclose this shape
|
||||
virtual real_t get_enclosing_radius() const = 0;
|
||||
|
||||
void add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p_xform);
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
|
||||
virtual void draw(const RID &p_to_rid, const Color &p_color) {}
|
||||
virtual Rect2 get_rect() const { return Rect2(); }
|
||||
/// Returns the radius of a sphere that fully enclose this shape
|
||||
virtual real_t get_enclosing_radius() const = 0;
|
||||
virtual RID get_rid() const;
|
||||
|
||||
bool is_collision_outline_enabled();
|
||||
|
|
|
@ -53,6 +53,10 @@ Vector<Vector3> SphereShape::get_debug_mesh_lines() {
|
|||
return points;
|
||||
}
|
||||
|
||||
real_t SphereShape::get_enclosing_radius() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
void SphereShape::_update_shape() {
|
||||
PhysicsServer::get_singleton()->shape_set_data(get_shape(), radius);
|
||||
Shape::_update_shape();
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
float get_radius() const;
|
||||
|
||||
virtual Vector<Vector3> get_debug_mesh_lines();
|
||||
virtual real_t get_enclosing_radius() const;
|
||||
|
||||
SphereShape();
|
||||
};
|
||||
|
|
230
servers/navigation_2d_server.cpp
Normal file
230
servers/navigation_2d_server.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_2d_server.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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 "servers/navigation_2d_server.h"
|
||||
|
||||
#include "core/math/transform.h"
|
||||
#include "core/math/transform_2d.h"
|
||||
#include "servers/navigation_server.h"
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
Navigation2DServer *Navigation2DServer::singleton = nullptr;
|
||||
|
||||
#define FORWARD_0_C(FUNC_NAME) \
|
||||
Navigation2DServer::FUNC_NAME() \
|
||||
const { \
|
||||
return NavigationServer::get_singleton()->FUNC_NAME(); \
|
||||
}
|
||||
|
||||
#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0) { \
|
||||
return NavigationServer::get_singleton_mut()->FUNC_NAME(CONV_0(D_0)); \
|
||||
}
|
||||
|
||||
#define FORWARD_1_C(FUNC_NAME, T_0, D_0, CONV_0) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0) \
|
||||
const { \
|
||||
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
|
||||
}
|
||||
|
||||
#define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1) \
|
||||
const { \
|
||||
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
|
||||
}
|
||||
|
||||
#define FORWARD_2_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1) \
|
||||
const { \
|
||||
return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \
|
||||
}
|
||||
|
||||
#define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
|
||||
const { \
|
||||
return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3))); \
|
||||
}
|
||||
|
||||
#define FORWARD_4_C(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
|
||||
Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
|
||||
const { \
|
||||
return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \
|
||||
}
|
||||
|
||||
RID rid_to_rid(const RID d) {
|
||||
return d;
|
||||
}
|
||||
bool bool_to_bool(const bool d) {
|
||||
return d;
|
||||
}
|
||||
int int_to_int(const int d) {
|
||||
return d;
|
||||
}
|
||||
real_t real_to_real(const real_t d) {
|
||||
return d;
|
||||
}
|
||||
Vector3 v2_to_v3(const Vector2 d) {
|
||||
return Vector3(d.x, 0.0, d.y);
|
||||
}
|
||||
Vector2 v3_to_v2(const Vector3 &d) {
|
||||
return Vector2(d.x, d.z);
|
||||
}
|
||||
Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
|
||||
Vector<Vector2> nd;
|
||||
nd.resize(d.size());
|
||||
for (int i(0); i < nd.size(); i++) {
|
||||
nd.write[i] = v3_to_v2(d[i]);
|
||||
}
|
||||
return nd;
|
||||
}
|
||||
Transform trf2_to_trf3(const Transform2D &d) {
|
||||
Vector3 o(v2_to_v3(d.get_origin()));
|
||||
Basis b;
|
||||
b.rotate(Vector3(0, 1, 0), d.get_rotation());
|
||||
return Transform(b, o);
|
||||
}
|
||||
Object *obj_to_obj(Object *d) {
|
||||
return d;
|
||||
}
|
||||
StringName sn_to_sn(StringName &d) {
|
||||
return d;
|
||||
}
|
||||
Variant var_to_var(Variant &d) {
|
||||
return d;
|
||||
}
|
||||
Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
|
||||
if (d.is_valid()) {
|
||||
return d->get_mesh();
|
||||
} else {
|
||||
return Ref<NavigationMesh>();
|
||||
}
|
||||
}
|
||||
|
||||
void Navigation2DServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create);
|
||||
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active);
|
||||
ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &Navigation2DServer::map_is_active);
|
||||
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &Navigation2DServer::map_set_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &Navigation2DServer::map_get_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &Navigation2DServer::map_get_path);
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &Navigation2DServer::map_get_closest_point);
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &Navigation2DServer::map_get_closest_point_owner);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("region_create"), &Navigation2DServer::region_create);
|
||||
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &Navigation2DServer::region_set_map);
|
||||
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &Navigation2DServer::region_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &Navigation2DServer::region_set_navpoly);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("agent_create"), &Navigation2DServer::agent_create);
|
||||
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &Navigation2DServer::agent_set_map);
|
||||
ClassDB::bind_method(D_METHOD("agent_set_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_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", "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", "receiver", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("free", "object"), &Navigation2DServer::free);
|
||||
}
|
||||
|
||||
Navigation2DServer::Navigation2DServer() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
Navigation2DServer::~Navigation2DServer() {
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
RID FORWARD_0_C(map_create);
|
||||
|
||||
void FORWARD_2_C(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
|
||||
|
||||
bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
|
||||
|
||||
void FORWARD_2_C(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
|
||||
real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
|
||||
|
||||
void FORWARD_2_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
|
||||
real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
|
||||
|
||||
Vector<Vector2> FORWARD_4_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool);
|
||||
|
||||
Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
|
||||
RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3);
|
||||
|
||||
RID FORWARD_0_C(region_create);
|
||||
void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
|
||||
|
||||
void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
|
||||
|
||||
void Navigation2DServer::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const {
|
||||
NavigationServer::get_singleton()->region_set_navmesh(p_region, poly_to_mesh(p_nav_mesh));
|
||||
}
|
||||
|
||||
RID Navigation2DServer::agent_create() const {
|
||||
RID agent = NavigationServer::get_singleton()->agent_create();
|
||||
NavigationServer::get_singleton()->agent_set_ignore_y(agent, true);
|
||||
return agent;
|
||||
}
|
||||
|
||||
void FORWARD_2_C(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
|
||||
|
||||
void FORWARD_2_C(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
|
||||
|
||||
void FORWARD_2_C(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
|
||||
|
||||
void FORWARD_2_C(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
|
||||
|
||||
void FORWARD_2_C(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
|
||||
|
||||
void FORWARD_2_C(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
|
||||
|
||||
void FORWARD_2_C(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
|
||||
|
||||
void FORWARD_2_C(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
|
||||
|
||||
void FORWARD_2_C(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
|
||||
|
||||
void FORWARD_2_C(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
|
||||
|
||||
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
|
||||
|
||||
void FORWARD_4_C(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, rid_to_rid, obj_to_obj, sn_to_sn, var_to_var);
|
||||
|
||||
void FORWARD_1_C(free, RID, p_object, rid_to_rid);
|
163
servers/navigation_2d_server.h
Normal file
163
servers/navigation_2d_server.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_2d_server.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
#ifndef NAVIGATION_2D_SERVER_H
|
||||
#define NAVIGATION_2D_SERVER_H
|
||||
|
||||
#include "core/object.h"
|
||||
#include "core/rid.h"
|
||||
#include "scene/2d/navigation_polygon.h"
|
||||
|
||||
// This server exposes the 3D `NavigationServer` features in the 2D world.
|
||||
class Navigation2DServer : public Object {
|
||||
GDCLASS(Navigation2DServer, Object);
|
||||
|
||||
static Navigation2DServer *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
/// Thread safe, can be used across many threads.
|
||||
static const Navigation2DServer *get_singleton() { return singleton; }
|
||||
|
||||
/// MUST be used in single thread!
|
||||
static Navigation2DServer *get_singleton_mut() { return singleton; }
|
||||
|
||||
/// Create a new map.
|
||||
virtual RID map_create() const;
|
||||
|
||||
/// Set map active.
|
||||
virtual void map_set_active(RID p_map, bool p_active) const;
|
||||
|
||||
/// Returns true if the map is active.
|
||||
virtual bool map_is_active(RID p_map) const;
|
||||
|
||||
/// Set the map cell size used to weld the navigation mesh polygons.
|
||||
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const;
|
||||
|
||||
/// Returns the map cell size.
|
||||
virtual real_t map_get_cell_size(RID p_map) const;
|
||||
|
||||
/// Set the map edge connection margin used to weld the compatible region edges.
|
||||
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const;
|
||||
|
||||
/// Returns the edge connection margin of this map.
|
||||
virtual real_t map_get_edge_connection_margin(RID p_map) const;
|
||||
|
||||
/// Returns the navigation path to reach the destination from the origin.
|
||||
virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize) const;
|
||||
|
||||
virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const;
|
||||
virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const;
|
||||
|
||||
/// Creates a new region.
|
||||
virtual RID region_create() const;
|
||||
|
||||
/// Set the map of this region.
|
||||
virtual void region_set_map(RID p_region, RID p_map) const;
|
||||
|
||||
/// Set the global transformation of this region.
|
||||
virtual void region_set_transform(RID p_region, Transform2D p_transform) const;
|
||||
|
||||
/// Set the navigation poly of this region.
|
||||
virtual void region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const;
|
||||
|
||||
/// Creates the agent.
|
||||
virtual RID agent_create() const;
|
||||
|
||||
/// Put the agent in the map.
|
||||
virtual void agent_set_map(RID p_agent, RID p_map) const;
|
||||
|
||||
/// The maximum distance (center point to
|
||||
/// center point) to other agents this agent
|
||||
/// takes into account in the navigation. The
|
||||
/// larger this number, the longer the running
|
||||
/// time of the simulation. If the number is too
|
||||
/// low, the simulation will not be safe.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) const;
|
||||
|
||||
/// The maximum number of other agents this
|
||||
/// agent takes into account in the navigation.
|
||||
/// The larger this number, the longer the
|
||||
/// running time of the simulation. If the
|
||||
/// number is too low, the simulation will not
|
||||
/// be safe.
|
||||
virtual void agent_set_max_neighbors(RID p_agent, int p_count) const;
|
||||
|
||||
/// 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) const;
|
||||
|
||||
/// The radius of this agent.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_radius(RID p_agent, real_t p_radius) const;
|
||||
|
||||
/// The maximum speed of this agent.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) const;
|
||||
|
||||
/// Current velocity of the agent
|
||||
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) const;
|
||||
|
||||
/// The new target velocity.
|
||||
virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) const;
|
||||
|
||||
/// Position of the agent in world space.
|
||||
virtual void agent_set_position(RID p_agent, Vector2 p_position) const;
|
||||
|
||||
/// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
|
||||
virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) const;
|
||||
|
||||
/// 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, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const;
|
||||
|
||||
/// Destroy the `RID`
|
||||
virtual void free(RID p_object) const;
|
||||
|
||||
Navigation2DServer();
|
||||
virtual ~Navigation2DServer();
|
||||
};
|
||||
|
||||
#endif
|
106
servers/navigation_server.cpp
Normal file
106
servers/navigation_server.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_server.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
#include "navigation_server.h"
|
||||
|
||||
NavigationServer *NavigationServer::singleton = nullptr;
|
||||
|
||||
void NavigationServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer::map_create);
|
||||
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer::map_set_active);
|
||||
ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &NavigationServer::map_is_active);
|
||||
ClassDB::bind_method(D_METHOD("map_set_up", "map", "up"), &NavigationServer::map_set_up);
|
||||
ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer::map_get_up);
|
||||
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer::map_set_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer::map_get_cell_size);
|
||||
ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer::map_set_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer::map_get_edge_connection_margin);
|
||||
ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &NavigationServer::map_get_path);
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer::map_get_closest_point_to_segment, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer::map_get_closest_point);
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer::map_get_closest_point_normal);
|
||||
ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer::map_get_closest_point_owner);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer::region_create);
|
||||
ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer::region_set_map);
|
||||
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer::region_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer::region_set_navmesh);
|
||||
ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer::region_bake_navmesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer::agent_create);
|
||||
ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer::agent_set_map);
|
||||
ClassDB::bind_method(D_METHOD("agent_set_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_radius", "agent", "radius"), &NavigationServer::agent_set_radius);
|
||||
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", "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", "receiver", "method", "userdata"), &NavigationServer::agent_set_callback, DEFVAL(Variant()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("free", "object"), &NavigationServer::free);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer::set_active);
|
||||
ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer::process);
|
||||
}
|
||||
|
||||
const NavigationServer *NavigationServer::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
NavigationServer *NavigationServer::get_singleton_mut() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
NavigationServer::NavigationServer() {
|
||||
ERR_FAIL_COND(singleton != nullptr);
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
NavigationServer::~NavigationServer() {
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
NavigationServerCallback NavigationServerManager::create_callback = nullptr;
|
||||
|
||||
void NavigationServerManager::set_default_server(NavigationServerCallback p_callback) {
|
||||
create_callback = p_callback;
|
||||
}
|
||||
|
||||
NavigationServer *NavigationServerManager::new_default_server() {
|
||||
ERR_FAIL_COND_V(create_callback == nullptr, nullptr);
|
||||
return create_callback();
|
||||
}
|
199
servers/navigation_server.h
Normal file
199
servers/navigation_server.h
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*************************************************************************/
|
||||
/* navigation_server.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
@author AndreaCatania
|
||||
*/
|
||||
|
||||
#ifndef NAVIGATION_SERVER_H
|
||||
#define NAVIGATION_SERVER_H
|
||||
|
||||
#include "core/object.h"
|
||||
#include "core/rid.h"
|
||||
#include "scene/3d/navigation_mesh_instance.h"
|
||||
|
||||
/// This server uses the concept of internal mutability.
|
||||
/// All the constant functions can be called in multithread because internally
|
||||
/// the server takes care to schedule the functions access.
|
||||
///
|
||||
/// Note: All the `set` functions are commands executed during the `sync` phase,
|
||||
/// don't expect that a change is immediately propagated.
|
||||
class NavigationServer : public Object {
|
||||
GDCLASS(NavigationServer, Object);
|
||||
|
||||
static NavigationServer *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
/// Thread safe, can be used across many threads.
|
||||
static const NavigationServer *get_singleton();
|
||||
|
||||
/// MUST be used in single thread!
|
||||
static NavigationServer *get_singleton_mut();
|
||||
|
||||
/// Create a new map.
|
||||
virtual RID map_create() const = 0;
|
||||
|
||||
/// Set map active.
|
||||
virtual void map_set_active(RID p_map, bool p_active) const = 0;
|
||||
|
||||
/// Returns true if the map is active.
|
||||
virtual bool map_is_active(RID p_map) const = 0;
|
||||
|
||||
/// Set the map UP direction.
|
||||
virtual void map_set_up(RID p_map, Vector3 p_up) const = 0;
|
||||
|
||||
/// Returns the map UP direction.
|
||||
virtual Vector3 map_get_up(RID p_map) const = 0;
|
||||
|
||||
/// Set the map cell size used to weld the navigation mesh polygons.
|
||||
virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const = 0;
|
||||
|
||||
/// Returns the map cell size.
|
||||
virtual real_t map_get_cell_size(RID p_map) const = 0;
|
||||
|
||||
/// Set the map edge connection margin used to weld the compatible region edges.
|
||||
virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const = 0;
|
||||
|
||||
/// Returns the edge connection margin of this map.
|
||||
virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
|
||||
|
||||
/// Returns the navigation path to reach the destination from the origin.
|
||||
virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const = 0;
|
||||
|
||||
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const = 0;
|
||||
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const = 0;
|
||||
virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const = 0;
|
||||
virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const = 0;
|
||||
|
||||
/// Creates a new region.
|
||||
virtual RID region_create() const = 0;
|
||||
|
||||
/// Set the map of this region.
|
||||
virtual void region_set_map(RID p_region, RID p_map) const = 0;
|
||||
|
||||
/// Set the global transformation of this region.
|
||||
virtual void region_set_transform(RID p_region, Transform p_transform) const = 0;
|
||||
|
||||
/// Set the navigation mesh of this region.
|
||||
virtual void region_set_navmesh(RID p_region, Ref<NavigationMesh> p_nav_mesh) const = 0;
|
||||
|
||||
/// Bake the navigation mesh
|
||||
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const = 0;
|
||||
|
||||
/// Creates the agent.
|
||||
virtual RID agent_create() const = 0;
|
||||
|
||||
/// Put the agent in the map.
|
||||
virtual void agent_set_map(RID p_agent, RID p_map) const = 0;
|
||||
|
||||
/// The maximum distance (center point to
|
||||
/// center point) to other agents this agent
|
||||
/// takes into account in the navigation. The
|
||||
/// larger this number, the longer the running
|
||||
/// time of the simulation. If the number is too
|
||||
/// low, the simulation will not be safe.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) const = 0;
|
||||
|
||||
/// The maximum number of other agents this
|
||||
/// agent takes into account in the navigation.
|
||||
/// The larger this number, the longer the
|
||||
/// running time of the simulation. If the
|
||||
/// number is too low, the simulation will not
|
||||
/// be safe.
|
||||
virtual void agent_set_max_neighbors(RID p_agent, int p_count) const = 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) const = 0;
|
||||
|
||||
/// The radius of this agent.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_radius(RID p_agent, real_t p_radius) const = 0;
|
||||
|
||||
/// The maximum speed of this agent.
|
||||
/// Must be non-negative.
|
||||
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) const = 0;
|
||||
|
||||
/// Current velocity of the agent
|
||||
virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) const = 0;
|
||||
|
||||
/// The new target velocity.
|
||||
virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) const = 0;
|
||||
|
||||
/// Position of the agent in world space.
|
||||
virtual void agent_set_position(RID p_agent, Vector3 p_position) const = 0;
|
||||
|
||||
/// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
|
||||
virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) const = 0;
|
||||
|
||||
/// 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, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const = 0;
|
||||
|
||||
/// Destroy the `RID`
|
||||
virtual void free(RID p_object) const = 0;
|
||||
|
||||
/// Control activation of this server.
|
||||
virtual void set_active(bool p_active) const = 0;
|
||||
|
||||
/// Process the collision avoidance agents.
|
||||
/// The result of this process is needed by the physics server,
|
||||
/// so this must be called in the main thread.
|
||||
/// Note: This function is not thread safe.
|
||||
virtual void process(real_t delta_time) = 0;
|
||||
|
||||
NavigationServer();
|
||||
virtual ~NavigationServer();
|
||||
};
|
||||
|
||||
typedef NavigationServer *(*NavigationServerCallback)();
|
||||
|
||||
/// Manager used for the server singleton registration
|
||||
class NavigationServerManager {
|
||||
static NavigationServerCallback create_callback;
|
||||
|
||||
public:
|
||||
static void set_default_server(NavigationServerCallback p_callback);
|
||||
static NavigationServer *new_default_server();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -57,6 +57,8 @@
|
|||
#include "audio_server.h"
|
||||
#include "camera/camera_feed.h"
|
||||
#include "camera_server.h"
|
||||
#include "navigation_2d_server.h"
|
||||
#include "navigation_server.h"
|
||||
#include "physics/physics_server_sw.h"
|
||||
#include "physics_2d/physics_2d_server_sw.h"
|
||||
#include "physics_2d/physics_2d_server_wrap_mt.h"
|
||||
|
@ -112,6 +114,8 @@ void register_server_types() {
|
|||
ClassDB::register_class<AudioServer>();
|
||||
ClassDB::register_virtual_class<PhysicsServer>();
|
||||
ClassDB::register_virtual_class<Physics2DServer>();
|
||||
ClassDB::register_virtual_class<NavigationServer>();
|
||||
ClassDB::register_virtual_class<Navigation2DServer>();
|
||||
ClassDB::register_class<ARVRServer>();
|
||||
ClassDB::register_class<CameraServer>();
|
||||
|
||||
|
@ -209,6 +213,8 @@ void register_server_singletons() {
|
|||
Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer", PhysicsServer::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("Physics2DServer", Physics2DServer::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer", NavigationServer::get_singleton_mut()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("Navigation2DServer", Navigation2DServer::get_singleton_mut()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("ARVRServer", ARVRServer::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton()));
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue