diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml deleted file mode 100644 index 9b61585a086a..000000000000 --- a/doc/classes/Navigation.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - Mesh-based navigation and pathfinding node. - - - [i]Deprecated.[/i] [Navigation] node and [method get_simple_path] are deprecated and will be removed in a future version. Use [method NavigationServer.map_get_path] instead. - 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. - - - https://godotengine.org/asset-library/asset/124 - - - - - - - Returns the navigation point closest to the point given. Points are in local coordinate space. - - - - - - - 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. - - - - - - - Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigationMeshInstance]. - - - - - - - - - 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. - - - - - - Returns the [RID] of the navigation map on the [NavigationServer]. - - - - - - - - - [i]Deprecated.[/i] [Navigation] node and [method get_simple_path] are deprecated and will be removed in a future version. Use [method NavigationServer.map_get_path] instead. - 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. - - - - - - The cell height to use for fields. - - - The XZ plane cell size to use for fields. - - - This value is used to detect the near edges to connect compatible regions. - - - A bitfield determining all navigation map layers the navigation can use on a [method Navigation.get_simple_path] path query. - - - Defines which direction is up. By default, this is [code](0, 1, 0)[/code], which is the world's "up" direction. - - - - - - - Emitted when a navigation map is updated, when a region moves or is modified. - - - - - - diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml deleted file mode 100644 index 64688ca3f10c..000000000000 --- a/doc/classes/Navigation2D.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - 2D navigation and pathfinding node. - - - [i]Deprecated.[/i] [Navigation2D] node and [method get_simple_path] are deprecated and will be removed in a future version. Use [method Navigation2DServer.map_get_path] instead. - 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. - - - https://godotengine.org/asset-library/asset/117 - - - - - - - Returns the navigation point closest to the point given. Points are in local coordinate space. - - - - - - - Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigationPolygonInstance]. - - - - - - Returns the object's [RID]. - - - - - - - - - [i]Deprecated.[/i] [Navigation2D] node and [method get_simple_path] are deprecated and will be removed in a future version. Use [method Navigation2DServer.map_get_path] instead. - 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. - - - - - - The XY plane cell size to use for fields. - - - This value is used to detect the near edges to connect compatible regions. - - - A bitfield determining all navigation map layers the navigation can use on a [method Navigation2D.get_simple_path] path query. - - - - - diff --git a/doc/classes/NavigationAgent.xml b/doc/classes/NavigationAgent.xml index a46bc6d54c4c..446e0d1d6a7b 100644 --- a/doc/classes/NavigationAgent.xml +++ b/doc/classes/NavigationAgent.xml @@ -4,7 +4,7 @@ 3D agent used in navigation for collision avoidance. - 3D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World] navigation map. If this node is a child of a [Navigation] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent] is physics safe. + 3D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World] navigation map. [NavigationAgent] is physics safe. [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. [b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues. @@ -35,10 +35,11 @@ Returns which index the agent is currently on in the navigation path's [PoolVector3Array]. - - + + + - Returns the [Navigation] node that the agent is using for its navigation system. + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. @@ -77,11 +78,12 @@ Returns [code]true[/code] if [member target_location] is reached. 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]. - + - + + - Sets the [Navigation] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation] node. + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. @@ -124,7 +126,7 @@ The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. - + The 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. @@ -137,7 +139,7 @@ The user-defined target location. Setting this property will clear the current navigation path. - + The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive. diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index 73c59e862a3e..dea87a9e2187 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -4,7 +4,7 @@ 2D agent used in navigation for collision avoidance. - 2D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World2D] navigation map. If this node is a child of a [Navigation2D] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent2D] is physics safe. + 2D agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World2D] navigation map. [NavigationAgent2D] is physics safe. [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. [b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues. @@ -35,10 +35,11 @@ Returns which index the agent is currently on in the navigation path's [PoolVector2Array]. - - + + + - Returns the [Navigation2D] node that the agent is using for its navigation system. + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. @@ -77,11 +78,12 @@ Returns [code]true[/code] if [member target_location] is reached. 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]. - + - + + - Sets the [Navigation2D] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation2D] node. + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. @@ -106,7 +108,7 @@ The maximum number of neighbors for the agent to consider. - + The maximum speed that an agent can move. @@ -115,23 +117,23 @@ The distance to search for other agents. - + The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. - + The 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. The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_dist]). Does not affect normal pathfinding. - + The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update. The user-defined target location. Setting this property will clear the current navigation path. - + The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive. diff --git a/doc/classes/NavigationMeshInstance.xml b/doc/classes/NavigationMeshInstance.xml index 62faccee80ef..87de3be97214 100644 --- a/doc/classes/NavigationMeshInstance.xml +++ b/doc/classes/NavigationMeshInstance.xml @@ -4,8 +4,8 @@ An instance of a [NavigationMesh]. - An instance of a [NavigationMesh]. It tells the [Navigation] node what can be navigated and what cannot, based on the [NavigationMesh] resource. - By default this node will register to the default [World] navigation map. If this node is a child of a [Navigation] node it will register to the navigation map of the navigation node. + A region of the navigation map. It tells the [NavigationServer] what can be navigated and what cannot, based on its [NavigationMesh] resource. + By default this node will register to the default [World] navigation map. Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method NavigationServer.map_set_edge_connection_margin]. [b]Note:[/b] Overlapping two regions' navmeshes is not enough for connecting two regions. They must share a similar edge. The cost of entering this region from another region can be controlled with the [member enter_cost] value. @@ -22,11 +22,26 @@ Bakes the [NavigationMesh]. If [code]on_thread[/code] is set to [code]true[/code] (default), the baking is done on a separate thread. Baking on separate thread is useful because navigation baking is not a cheap operation. When it is completed, it automatically sets the new [NavigationMesh]. Please note that baking on separate thread may be very slow if geometry is parsed from meshes as async access to each mesh involves heavy synchronization. Also, please note that baking on a separate thread is automatically disabled on operating systems that cannot use threads (such as HTML5 with threads disabled). + + + + + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. + + Returns the [RID] of this region on the [NavigationServer]. Combined with [method NavigationServer.map_get_closest_point_owner] can be used to identify the [NavigationMeshInstance] closest to a point on the merged navigation map. + + + + + + + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. + diff --git a/doc/classes/NavigationObstacle.xml b/doc/classes/NavigationObstacle.xml index 14e232703256..e87d499b34e9 100644 --- a/doc/classes/NavigationObstacle.xml +++ b/doc/classes/NavigationObstacle.xml @@ -4,16 +4,16 @@ 3D obstacle used in navigation for collision avoidance. - 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. + 3D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. [NavigationObstacle] is physics safe. Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently. - - + + - Returns the [Navigation] node that the obstacle is using for its navigation system. + Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer. @@ -22,11 +22,11 @@ Returns the [RID] of this obstacle on the [NavigationServer]. - + - + - Sets the [Navigation] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation] node. + Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer. diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml index fa24155e5cf2..fbd2ff300760 100644 --- a/doc/classes/NavigationObstacle2D.xml +++ b/doc/classes/NavigationObstacle2D.xml @@ -4,16 +4,16 @@ 2D obstacle used in navigation for collision avoidance. - 2D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationObstacle2D] is physics safe. + 2D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. [NavigationObstacle2D] is physics safe. Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently. - - + + - Returns the [Navigation2D] node that the obstacle is using for its navigation system. + Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the agent on the NavigationServer. @@ -22,11 +22,11 @@ Returns the [RID] of this obstacle on the [Navigation2DServer]. - + - + - Sets the [Navigation2D] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation2D] node. + Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]agent[/code] on the NavigationServer. diff --git a/doc/classes/NavigationPolygonInstance.xml b/doc/classes/NavigationPolygonInstance.xml index 9bebe5b639ce..b610032bb8c1 100644 --- a/doc/classes/NavigationPolygonInstance.xml +++ b/doc/classes/NavigationPolygonInstance.xml @@ -5,7 +5,7 @@ A region of the navigation map. It tells the [Navigation2DServer] what can be navigated and what cannot, based on its [NavigationPolygon] resource. - By default this node will register to the default [World2D] navigation map. If this node is a child of a [Navigation2D] node it will register to the navigation map of the navigation node. + By default this node will register to the default [World2D] navigation map. Two regions can be connected to each other if they share a similar edge. You can set the minimum distance between two vertices required to connect two edges by using [method Navigation2DServer.map_set_edge_connection_margin]. [b]Note:[/b] Overlapping two regions' polygons is not enough for connecting two regions. They must share a similar edge. The pathfinding cost of entering this region from another region can be controlled with the [member enter_cost] value. @@ -15,12 +15,27 @@ + + + + + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. + + Returns the [RID] of this region on the [Navigation2DServer]. Combined with [method Navigation2DServer.map_get_closest_point_owner] can be used to identify the [NavigationPolygonInstance] closest to a point on the merged navigation map. + + + + + + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. + + diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml index b704b6fd5b12..17d0b746448f 100644 --- a/doc/classes/NavigationServer.xml +++ b/doc/classes/NavigationServer.xml @@ -7,7 +7,7 @@ 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. [b]Note:[/b] Most NavigationServer changes take effect after the next physics frame and not immediately. This includes all changes made to maps, regions or agents by navigation related Nodes in the SceneTree or made through scripts. - For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [member Navigation.edge_connection_margin] to the respective other edge's vertex. + For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [code]edge_connection_margin[/code] 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. [b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues. diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index c16e60960894..d4324cadcd36 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -34,7 +34,7 @@ #include "editor/editor_node.h" #include "scene/3d/collision_shape.h" #include "scene/3d/mesh_instance.h" -#include "scene/3d/navigation.h" +#include "scene/3d/navigation_mesh_instance.h" #include "scene/3d/physics_body.h" #include "scene/3d/vehicle_body.h" #include "scene/animation/animation_player.h" diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h index df3188d2e90c..ca73cdd123b6 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.h +++ b/editor/plugins/navigation_polygon_editor_plugin.h @@ -32,7 +32,8 @@ #define NAVIGATION_POLYGON_EDITOR_PLUGIN_H #include "editor/plugins/abstract_polygon_2d_editor.h" -#include "scene/2d/navigation_polygon.h" +#include "scene/2d/navigation_polygon_instance.h" +#include "scene/resources/navigation_polygon.h" class NavigationPolygonEditor : public AbstractPolygon2DEditor { GDCLASS(NavigationPolygonEditor, AbstractPolygon2DEditor); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index dcb8672ad216..85cdf8e7a1b6 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "scene/2d/navigation_polygon_instance.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/sprite.h" diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 01672907b740..b405626c4552 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -77,6 +77,13 @@ Returns an array of [Transform] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space. + + + + Returns the [RID] of the navigation map this GridMap node uses for its cell baked navigation meshes. + This function returns always the map set on the GridMap node and not the map on the NavigationServer. If the map is changed directly with the NavigationServer API the GridMap node will not be aware of the map change. + + @@ -150,6 +157,13 @@ Sets an individual bit on the [member collision_mask]. + + + + + Sets the [RID] of the navigation map this GridMap node should use for its cell baked navigation meshes. + + diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index b1ccc8053ac0..54b133b8ff58 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -222,6 +222,27 @@ bool GridMap::is_baking_navigation() { return bake_navigation; } +void GridMap::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + for (Map::Element *E = octant_map.front(); E; E = E->next()) { + Octant *g = E->get(); + for (Map::Element *F = g->navmesh_ids.front(); F; F = F->next()) { + if (F->get().region.is_valid()) { + NavigationServer::get_singleton()->region_set_map(F->get().region, map_override); + } + } + } +} + +RID GridMap::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world()->get_navigation_map(); + } + return RID(); +} + void GridMap::set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; _recreate_octant_data(); @@ -479,6 +500,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { } if (E->get().navmesh_debug_instance.is_valid()) { VS::get_singleton()->free(E->get().navmesh_debug_instance); + E->get().navmesh_debug_instance = RID(); } } g.navmesh_ids.clear(); @@ -565,8 +587,8 @@ bool GridMap::_octant_update(const OctantKey &p_key) { NavigationServer::get_singleton()->region_set_navmesh(region, navmesh); NavigationServer::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform); if (is_inside_tree()) { - if (navigation) { - NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); + if (map_override.is_valid()) { + NavigationServer::get_singleton()->region_set_map(region, map_override); } else { NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map()); } @@ -681,8 +703,8 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { NavigationServer::get_singleton()->region_set_navigation_layers(region, navigation_layers); NavigationServer::get_singleton()->region_set_navmesh(region, nm); NavigationServer::get_singleton()->region_set_transform(region, get_global_transform() * E->get().xform); - if (navigation) { - NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); + if (map_override.is_valid()) { + NavigationServer::get_singleton()->region_set_map(region, map_override); } else { NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map()); } @@ -765,16 +787,6 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) { void GridMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - Spatial *c = this; - while (c) { - navigation = Object::cast_to(c); - if (navigation) { - break; - } - - c = Object::cast_to(c->get_parent()); - } - last_transform = get_global_transform(); for (Map::Element *E = octant_map.front(); E; E = E->next()) { @@ -809,8 +821,6 @@ void GridMap::_notification(int p_what) { _octant_exit_world(E->key()); } - navigation = nullptr; - //_queue_octants_dirty(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS); //_update_octants_callback(); //_update_area_instances(); @@ -928,6 +938,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation); ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &GridMap::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &GridMap::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &GridMap::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers); @@ -1234,7 +1247,6 @@ GridMap::GridMap() { clip_above = true; cell_scale = 1.0; - navigation = nullptr; set_notify_transform(true); recreating_octants = false; diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 02969eb47806..efe439bfe83c 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -31,7 +31,6 @@ #ifndef GRID_MAP_H #define GRID_MAP_H -#include "scene/3d/navigation.h" #include "scene/3d/spatial.h" #include "scene/resources/mesh_library.h" #include "scene/resources/multimesh.h" @@ -137,6 +136,7 @@ class GridMap : public Spatial { uint32_t collision_mask; Ref physics_material; bool bake_navigation = false; + RID map_override; uint32_t navigation_layers = 1; Transform last_transform; @@ -146,7 +146,6 @@ class GridMap : public Spatial { int octant_size; bool center_x, center_y, center_z; float cell_scale; - Navigation *navigation; bool clip; bool clip_above; @@ -233,6 +232,9 @@ class GridMap : public Spatial { void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers(); diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index 3eac985e6f15..ca4ffdf516f1 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -31,13 +31,11 @@ #ifndef NAV_REGION_H #define NAV_REGION_H -#include "scene/3d/navigation.h" +#include "scene/resources/navigation_mesh.h" #include "nav_rid.h" #include "nav_utils.h" -#include - class NavMap; class NavRegion; diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp deleted file mode 100644 index eaf0cabed2c0..000000000000 --- a/scene/2d/navigation_2d.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************/ -/* navigation_2d.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "navigation_2d.h" - -#include "servers/navigation_2d_server.h" - -void Navigation2D::_bind_methods() { - 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); - - ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &Navigation2D::set_navigation_layers); - ClassDB::bind_method(D_METHOD("get_navigation_layers"), &Navigation2D::get_navigation_layers); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); -} - -void Navigation2D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - Navigation2DServer::get_singleton()->map_set_active(map, true); - } break; - // FIXME 3.5 with this old navigation 2d node only - // if the node gets deleted this exit causes annoying error prints in debug - // It tries to deactivate a map that itself has sent a free command to the server. - //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 Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) const { - WARN_DEPRECATED_MSG("'Navigation2D' node and 'Navigation2D.get_simple_path()' are deprecated and will be removed in a future version. Use 'Navigation2DServer.map_get_path()' instead."); - return Navigation2DServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize, navigation_layers); -} - -String Navigation2D::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); - - if (warning != String()) { - warning += "\n\n"; - } - warning += TTR("'Navigation2D' node and 'Navigation2D.get_simple_path()' are deprecated and will be removed in a future version. Use 'Navigation2DServer.map_get_path()' instead."); - - return warning; -} - -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); -} - -void Navigation2D::set_navigation_layers(uint32_t p_navigation_layers) { - navigation_layers = p_navigation_layers; -} - -uint32_t Navigation2D::get_navigation_layers() const { - return navigation_layers; -} - -Navigation2D::Navigation2D() { - map = Navigation2DServer::get_singleton()->map_create(); - Navigation2DServer::get_singleton()->map_set_active(map, true); - Navigation2DServer::get_singleton()->map_set_cell_size(map, get_cell_size()); - Navigation2DServer::get_singleton()->map_set_edge_connection_margin(map, get_edge_connection_margin()); -} - -Navigation2D::~Navigation2D() { - Navigation2DServer::get_singleton()->free(map); -} diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 95bfa79c9e9e..e1eec435d5bd 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -31,7 +31,6 @@ #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() { @@ -49,9 +48,6 @@ void NavigationAgent2D::_bind_methods() { 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); @@ -70,6 +66,9 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent2D::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent2D::get_navigation_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent2D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent2D::get_navigation_layer_value); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent2D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent2D::get_navigation_map); @@ -112,26 +111,17 @@ void NavigationAgent2D::_bind_methods() { void NavigationAgent2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POST_ENTER_TREE: { - // Search the navigation node and set it - { - Navigation2D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - set_navigation(nav); - } // need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready. // cannot use READY as ready does not get called if Node is readded to SceneTree set_agent_parent(get_parent()); set_physics_process_internal(true); } break; + case NOTIFICATION_EXIT_TREE: { + set_agent_parent(nullptr); + set_physics_process_internal(false); + } break; + case NOTIFICATION_PARENTED: { if (is_inside_tree() && (get_parent() != agent_parent)) { // only react to PARENTED notifications when already inside_tree and parent changed, e.g. users switch nodes around @@ -167,11 +157,7 @@ void NavigationAgent2D::_notification(int p_what) { map_before_pause = RID(); } } break; - case NOTIFICATION_EXIT_TREE: { - set_agent_parent(nullptr); - set_navigation(nullptr); - set_physics_process_internal(false); - } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { if (avoidance_enabled) { @@ -185,25 +171,17 @@ void NavigationAgent2D::_notification(int p_what) { } } -NavigationAgent2D::NavigationAgent2D() : - agent_parent(nullptr), - navigation(nullptr), - agent(RID()), - avoidance_enabled(false), - target_desired_distance(1.0), - path_max_distance(3.0), - velocity_submitted(false), - target_reached(false), - navigation_finished(true) { +NavigationAgent2D::NavigationAgent2D() { 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); + Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); + Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); + Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, time_horizon); + Navigation2DServer::get_singleton()->agent_set_radius(agent, radius); + Navigation2DServer::get_singleton()->agent_set_max_speed(agent, max_speed); } NavigationAgent2D::~NavigationAgent2D() { + ERR_FAIL_NULL(Navigation2DServer::get_singleton()); Navigation2DServer::get_singleton()->free(agent); agent = RID(); // Pointless } @@ -229,8 +207,6 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { agent_parent = Object::cast_to(p_agent_parent); if (map_override.is_valid()) { Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_override); - } else if (navigation != nullptr) { - Navigation2DServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid()); } else { // no navigation node found in parent nodes, use default navigation map from world resource Navigation2DServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map()); @@ -243,25 +219,6 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { } } -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(p_nav); - ERR_FAIL_NULL(nav); - set_navigation(nav); -} - -Node *NavigationAgent2D::get_navigation_node() const { - return Object::cast_to(navigation); -} - void NavigationAgent2D::set_navigation_layers(uint32_t p_navigation_layers) { bool _navigation_layers_changed = navigation_layers != p_navigation_layers; navigation_layers = p_navigation_layers; @@ -274,8 +231,31 @@ uint32_t NavigationAgent2D::get_navigation_layers() const { return navigation_layers; } +void NavigationAgent2D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + set_navigation_layers(_navigation_layers); +} + +bool NavigationAgent2D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + void NavigationAgent2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + Navigation2DServer::get_singleton()->agent_set_map(agent, map_override); _request_repath(); } @@ -283,8 +263,6 @@ void NavigationAgent2D::set_navigation_map(RID p_navigation_map) { RID NavigationAgent2D::get_navigation_map() const { if (map_override.is_valid()) { return map_override; - } else if (navigation != nullptr) { - return navigation->get_rid(); } else if (agent_parent != nullptr) { return agent_parent->get_world_2d()->get_navigation_map(); } @@ -292,39 +270,76 @@ RID NavigationAgent2D::get_navigation_map() const { } void NavigationAgent2D::set_path_desired_distance(real_t p_dd) { + if (Math::is_equal_approx(path_desired_distance, p_dd)) { + return; + } + path_desired_distance = p_dd; } void NavigationAgent2D::set_target_desired_distance(real_t p_dd) { + if (Math::is_equal_approx(target_desired_distance, p_dd)) { + return; + } + target_desired_distance = p_dd; } void NavigationAgent2D::set_radius(real_t p_radius) { + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + Navigation2DServer::get_singleton()->agent_set_radius(agent, radius); } void NavigationAgent2D::set_neighbor_dist(real_t p_dist) { + if (Math::is_equal_approx(neighbor_dist, p_dist)) { + return; + } + neighbor_dist = p_dist; + Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); } void NavigationAgent2D::set_max_neighbors(int p_count) { + if (max_neighbors == p_count) { + return; + } + max_neighbors = p_count; + Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); } void NavigationAgent2D::set_time_horizon(real_t p_time) { + if (Math::is_equal_approx(time_horizon, p_time)) { + return; + } + time_horizon = p_time; + Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, time_horizon); } void NavigationAgent2D::set_max_speed(real_t p_max_speed) { + if (Math::is_equal_approx(max_speed, p_max_speed)) { + return; + } + 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) { + if (Math::is_equal_approx(path_max_distance, p_pmd)) { + return; + } + path_max_distance = p_pmd; } @@ -448,8 +463,6 @@ void NavigationAgent2D::update_navigation() { if (reload_path) { if (map_override.is_valid()) { navigation_path = Navigation2DServer::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); - } else if (navigation != nullptr) { - navigation_path = Navigation2DServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true, navigation_layers); } else { navigation_path = Navigation2DServer::get_singleton()->map_get_path(agent_parent->get_world_2d()->get_navigation_map(), o, target_location, true, navigation_layers); } diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 4dd96f3a4650..4052a61b3d06 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -35,13 +35,11 @@ #include "scene/main/node.h" class Node2D; -class Navigation2D; class NavigationAgent2D : public Node { GDCLASS(NavigationAgent2D, Node); Node2D *agent_parent = nullptr; - Navigation2D *navigation = nullptr; RID agent; RID map_before_pause; @@ -50,15 +48,14 @@ class NavigationAgent2D : public Node { bool avoidance_enabled = false; uint32_t navigation_layers = 1; - real_t path_desired_distance = 1.0; - real_t target_desired_distance = 1.0; - real_t radius = 0.0; - real_t neighbor_dist = 0.0; - int max_neighbors = 0; - real_t time_horizon = 0.0; - real_t max_speed = 0.0; - - real_t path_max_distance = 3.0; + real_t path_desired_distance = 20.0; + real_t target_desired_distance = 10.0; + real_t radius = 10.0; + real_t neighbor_dist = 500.0; + int max_neighbors = 10; + real_t time_horizon = 1.0; + real_t max_speed = 100.0; + real_t path_max_distance = 100.0; Vector2 target_location; Vector navigation_path; @@ -70,7 +67,7 @@ class NavigationAgent2D : public Node { bool target_reached = false; bool navigation_finished = true; // No initialized on purpose - uint32_t update_frame_id = UINT32_MAX; + uint32_t update_frame_id = 0; protected: static void _bind_methods(); @@ -80,14 +77,6 @@ class NavigationAgent2D : public Node { 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; } @@ -100,6 +89,9 @@ class NavigationAgent2D : public Node { void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + void set_navigation_map(RID p_navigation_map); RID get_navigation_map() const; diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index e21f888a6429..658d0cf4314c 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -31,15 +31,14 @@ #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("get_rid"), &NavigationObstacle2D::get_rid); - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle2D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle2D::get_navigation_node); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle2D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle2D::get_navigation_map); 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); @@ -60,39 +59,23 @@ void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const { void NavigationObstacle2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - parent_node2d = Object::cast_to(get_parent()); - reevaluate_agent_radius(); - - // Search the navigation node and set it - { - Navigation2D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); - } - + case NOTIFICATION_POST_ENTER_TREE: { + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_navigation(nullptr); + set_agent_parent(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(get_parent()); - reevaluate_agent_radius(); + if (is_inside_tree() && (get_parent() != parent_node2d)) { + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } } break; case NOTIFICATION_UNPARENTED: { - parent_node2d = nullptr; + set_agent_parent(nullptr); + set_physics_process_internal(false); } break; case NOTIFICATION_PAUSED: { if (parent_node2d && !parent_node2d->can_process()) { @@ -121,42 +104,17 @@ void NavigationObstacle2D::_notification(int p_what) { } } -NavigationObstacle2D::NavigationObstacle2D() : - navigation(nullptr), - agent(RID()) { +NavigationObstacle2D::NavigationObstacle2D() { agent = Navigation2DServer::get_singleton()->agent_create(); initialize_agent(); } NavigationObstacle2D::~NavigationObstacle2D() { + ERR_FAIL_NULL(Navigation2DServer::get_singleton()); Navigation2DServer::get_singleton()->free(agent); agent = RID(); // Pointless } -void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) { - if (navigation == p_nav && navigation != nullptr) { - return; // Pointless - } - - navigation = p_nav; - - if (navigation != nullptr) { - Navigation2DServer::get_singleton()->agent_set_map(agent, navigation->get_rid()); - } else if (parent_node2d && parent_node2d->is_inside_tree()) { - Navigation2DServer::get_singleton()->agent_set_map(agent, parent_node2d->get_world_2d()->get_navigation_map()); - } -} - -void NavigationObstacle2D::set_navigation_node(Node *p_nav) { - Navigation2D *nav = Object::cast_to(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationObstacle2D::get_navigation_node() const { - return Object::cast_to(navigation); -} - String NavigationObstacle2D::get_configuration_warning() const { String warning = Node::get_configuration_warning(); @@ -226,6 +184,44 @@ real_t NavigationObstacle2D::estimate_agent_radius() const { return 1.0; // Never a 0 radius } +void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) { + if (parent_node2d == p_agent_parent) { + return; + } + + if (Object::cast_to(p_agent_parent) != nullptr) { + parent_node2d = Object::cast_to(p_agent_parent); + if (map_override.is_valid()) { + Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + Navigation2DServer::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map()); + } + reevaluate_agent_radius(); + } else { + parent_node2d = nullptr; + Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + Navigation2DServer::get_singleton()->agent_set_map(agent, map_override); +} + +RID NavigationObstacle2D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (parent_node2d != nullptr) { + return parent_node2d->get_world_2d()->get_navigation_map(); + } + return RID(); +} + void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) { estimate_radius = p_estimate_radius; _change_notify(); diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 9d70bcf16510..0d0361c01bef 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -34,15 +34,13 @@ #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; RID map_before_pause; + RID map_override; bool estimate_radius = true; real_t radius = 1.0; @@ -56,18 +54,15 @@ class NavigationObstacle2D : public Node { 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_agent_parent(Node *p_agent_parent); + + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_estimate_radius(bool p_estimate_radius); bool is_radius_estimated() const { return estimate_radius; diff --git a/scene/2d/navigation_polygon_instance.cpp b/scene/2d/navigation_polygon_instance.cpp new file mode 100644 index 000000000000..36d1b5245d59 --- /dev/null +++ b/scene/2d/navigation_polygon_instance.cpp @@ -0,0 +1,308 @@ +/**************************************************************************/ +/* navigation_polygon_instance.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "navigation_polygon_instance.h" + +#include "core/core_string_names.h" +#include "core/engine.h" +#include "core/os/mutex.h" +#include "servers/navigation_2d_server.h" + +#include "thirdparty/misc/triangulator.h" + +void NavigationPolygonInstance::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + enabled = p_enabled; + + if (!is_inside_tree()) { + return; + } + + if (!enabled) { + Navigation2DServer::get_singleton()->region_set_map(region, RID()); + Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed"); + } else { + Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed"); + } + + if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { + update(); + } +} + +bool NavigationPolygonInstance::is_enabled() const { + return enabled; +} + +void NavigationPolygonInstance::set_navigation_layers(uint32_t p_navigation_layers) { + navigation_layers = p_navigation_layers; + Navigation2DServer::get_singleton()->region_set_navigation_layers(region, navigation_layers); +} + +uint32_t NavigationPolygonInstance::get_navigation_layers() const { + return navigation_layers; +} + +void NavigationPolygonInstance::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationPolygonInstance::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationPolygonInstance::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + enter_cost = MAX(p_enter_cost, 0.0); + Navigation2DServer::get_singleton()->region_set_enter_cost(region, p_enter_cost); +} + +real_t NavigationPolygonInstance::get_enter_cost() const { + return enter_cost; +} + +void NavigationPolygonInstance::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + travel_cost = MAX(p_travel_cost, 0.0); + Navigation2DServer::get_singleton()->region_set_travel_cost(region, travel_cost); +} + +real_t NavigationPolygonInstance::get_travel_cost() const { + return travel_cost; +} + +RID NavigationPolygonInstance::get_region_rid() const { + return region; +} + +///////////////////////////// +#ifdef TOOLS_ENABLED +Rect2 NavigationPolygonInstance::_edit_get_rect() const { + return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); +} + +bool NavigationPolygonInstance::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} +#endif + +void NavigationPolygonInstance::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (enabled) { + Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed"); + } + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { + Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform()); + } break; + + case NOTIFICATION_EXIT_TREE: { + Navigation2DServer::get_singleton()->region_set_map(region, RID()); + if (enabled) { + Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed"); + } + } break; + + case NOTIFICATION_DRAW: { + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { + PoolVector verts = navpoly->get_vertices(); + if (verts.size() < 3) { + return; + } + + Color color; + if (enabled) { + color = get_tree()->get_debug_navigation_color(); + } else { + color = get_tree()->get_debug_navigation_disabled_color(); + } + Color doors_color = color.lightened(0.2); + + RandomPCG rand; + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector polygon = navpoly->get_polygon(i); + + Vector vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], verts.size()); + vertices.write[j] = verts[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector colors; + colors.push_back(random_variation_color); + + VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), vertices, colors); + } + + // Draw the region + Transform2D xform = get_global_transform(); + const Navigation2DServer *ns = Navigation2DServer::get_singleton(); + real_t radius = ns->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()); + radius = radius * 0.5; + for (int i = 0; i < ns->region_get_connections_count(region); i++) { + // Two main points + Vector2 a = ns->region_get_connection_pathway_start(region, i); + a = xform.affine_inverse().xform(a); + Vector2 b = ns->region_get_connection_pathway_end(region, i); + b = xform.affine_inverse().xform(b); + draw_line(a, b, doors_color); + + // Draw a circle to illustrate the margins. + real_t angle = a.angle_to_point(b); + draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color); + draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); + } + } + } break; + } +} + +void NavigationPolygonInstance::set_navigation_polygon(const Ref &p_navpoly) { + if (p_navpoly == navpoly) { + return; + } + + 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(); + + _change_notify("navpoly"); + update_configuration_warning(); +} + +Ref NavigationPolygonInstance::get_navigation_polygon() const { + return navpoly; +} + +void NavigationPolygonInstance::_navpoly_changed() { + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { + update(); + } + if (navpoly.is_valid()) { + Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly); + } +} + +void NavigationPolygonInstance::_map_changed(RID p_map) { + if (is_inside_tree() && enabled && (get_world_2d()->get_navigation_map() == p_map)) { + update(); + } +} + +String NavigationPolygonInstance::get_configuration_warning() const { + String warning = Node2D::get_configuration_warning(); + + if (!navpoly.is_valid()) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); + } + + return warning; +} + +void NavigationPolygonInstance::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navpoly"), &NavigationPolygonInstance::set_navigation_polygon); + ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationPolygonInstance::get_navigation_polygon); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationPolygonInstance::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationPolygonInstance::is_enabled); + + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPolygonInstance::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPolygonInstance::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationPolygonInstance::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationPolygonInstance::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationPolygonInstance::get_region_rid); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationPolygonInstance::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationPolygonInstance::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationPolygonInstance::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationPolygonInstance::get_travel_cost); + + ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "travel_cost"), "set_travel_cost", "get_travel_cost"); + + ClassDB::bind_method(D_METHOD("_map_changed"), &NavigationPolygonInstance::_map_changed); +} + +NavigationPolygonInstance::NavigationPolygonInstance() { + set_notify_transform(true); + region = Navigation2DServer::get_singleton()->region_create(); + Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost()); + Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost()); +} + +NavigationPolygonInstance::~NavigationPolygonInstance() { + Navigation2DServer::get_singleton()->free(region); +} diff --git a/scene/2d/navigation_2d.h b/scene/2d/navigation_polygon_instance.h similarity index 65% rename from scene/2d/navigation_2d.h rename to scene/2d/navigation_polygon_instance.h index 2631f2c41840..3b5f20420f0e 100644 --- a/scene/2d/navigation_2d.h +++ b/scene/2d/navigation_polygon_instance.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* navigation_2d.h */ +/* navigation_polygon_instance.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,50 +28,61 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef NAVIGATION_2D_H -#define NAVIGATION_2D_H +#ifndef NAVIGATION_POLYGON_INSTANCE_H +#define NAVIGATION_POLYGON_INSTANCE_H -#include "scene/2d/navigation_polygon.h" #include "scene/2d/node_2d.h" +#include "scene/resources/navigation_polygon.h" -class Navigation2D : public Node2D { - GDCLASS(Navigation2D, Node2D); +class NavigationPolygonInstance : public Node2D { + GDCLASS(NavigationPolygonInstance, Node2D); + + bool enabled = true; + RID region; + Ref navpoly; + + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; - RID map; - real_t cell_size = 1.0; - real_t edge_connection_margin = 1.0; uint32_t navigation_layers = 1; + void _navpoly_changed(); + void _map_changed(RID p_map); + protected: - static void _bind_methods(); void _notification(int p_what); + static void _bind_methods(); public: - RID get_rid() const { - return map; - } +#ifdef TOOLS_ENABLED + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; +#endif - void set_cell_size(float p_cell_size); - float get_cell_size() const { - return cell_size; - } + void set_enabled(bool p_enabled); + bool is_enabled() const; void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; - void set_edge_connection_margin(float p_edge_connection_margin); - float get_edge_connection_margin() const { - return edge_connection_margin; - } + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + RID get_region_rid() const; + + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const; + + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const; - Vector 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; + void set_navigation_polygon(const Ref &p_navpoly); + Ref get_navigation_polygon() const; - virtual String get_configuration_warning() const; + String get_configuration_warning() const; - Navigation2D(); - ~Navigation2D(); + NavigationPolygonInstance(); + ~NavigationPolygonInstance(); }; -#endif // NAVIGATION_2D_H +#endif // NAVIGATION_POLYGON_INSTANCE_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 85ce57424f55..bb95b7bed0fd 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -59,18 +59,6 @@ int TileMap::_get_quadrant_size() const { void TileMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Node2D *c = this; - while (c) { - navigation = Object::cast_to(c); - if (navigation) { - // only for <3.5 backward compatibility - bake_navigation = true; - break; - } - - c = Object::cast_to(c->get_parent()); - } - if (use_parent) { _clear_quadrants(); collision_parent = Object::cast_to(get_parent()); @@ -108,7 +96,6 @@ void TileMap::_notification(int p_what) { } collision_parent = nullptr; - navigation = nullptr; } break; @@ -167,15 +154,6 @@ void TileMap::_update_quadrant_transform() { local_transform = get_transform(); } - Transform2D nav_rel; - if (bake_navigation) { - if (navigation) { - nav_rel = get_relative_transform_to_parent(navigation); - } else { - nav_rel = get_global_transform(); - } - } - for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); Transform2D xform; @@ -188,7 +166,7 @@ void TileMap::_update_quadrant_transform() { if (bake_navigation) { for (Map::Element *F = q.navpoly_ids.front(); F; F = F->next()) { - Navigation2DServer::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform); + Navigation2DServer::get_singleton()->region_set_transform(F->get().region, global_transform * F->get().xform); } } @@ -353,14 +331,7 @@ void TileMap::update_dirty_quadrants() { VisualServer *vs = VisualServer::get_singleton(); Physics2DServer *ps = Physics2DServer::get_singleton(); Vector2 tofs = get_cell_draw_offset(); - Transform2D nav_rel; - if (bake_navigation) { - if (navigation) { - nav_rel = get_relative_transform_to_parent(navigation); - } else { - nav_rel = get_global_transform(); - } - } + Transform2D global_transform = get_global_transform(); Vector2 qofs; @@ -663,13 +634,13 @@ void TileMap::update_dirty_quadrants() { _fix_cell_transform(xform, c, npoly_ofs, s); RID region = Navigation2DServer::get_singleton()->region_create(); - if (navigation) { - Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); + if (map_override.is_valid()) { + Navigation2DServer::get_singleton()->region_set_map(region, map_override); } else { Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); } Navigation2DServer::get_singleton()->region_set_navigation_layers(region, navigation_layers); - Navigation2DServer::get_singleton()->region_set_transform(region, nav_rel * xform); + Navigation2DServer::get_singleton()->region_set_transform(region, global_transform * xform); Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly); Quadrant::NavPoly np; @@ -1475,6 +1446,32 @@ uint32_t TileMap::get_navigation_layers() { return navigation_layers; } +void TileMap::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + if (bake_navigation) { + for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { + Quadrant &q = E->get(); + for (Map::Element *F = q.navpoly_ids.front(); F; F = F->next()) { + Navigation2DServer::get_singleton()->region_set_map(F->get().region, map_override); + } + } + } +} + +RID TileMap::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_2d()->get_navigation_map(); + } + return RID(); +} + uint32_t TileMap::get_collision_layer() const { return collision_layer; } @@ -2006,7 +2003,6 @@ TileMap::TileMap() { use_parent = false; collision_parent = nullptr; use_kinematic = false; - navigation = nullptr; y_sort_mode = false; compatibility_mode = false; centered_textures = false; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index a3d2b82885a7..00e0bb435dee 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -33,7 +33,6 @@ #include "core/self_list.h" #include "core/vset.h" -#include "scene/2d/navigation_2d.h" #include "scene/2d/node_2d.h" #include "scene/resources/tile_set.h" @@ -78,7 +77,7 @@ class TileMap : public Node2D { bool use_parent; CollisionObject2D *collision_parent; bool use_kinematic; - Navigation2D *navigation; + RID map_override; bool bake_navigation = false; uint32_t navigation_layers = 1; bool show_collision = false; @@ -311,6 +310,9 @@ class TileMap : public Node2D { void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers(); + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_mode(Mode p_mode); Mode get_mode() const; diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp deleted file mode 100644 index f0a37f6187b1..000000000000 --- a/scene/3d/navigation.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************/ -/* navigation.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "navigation.h" - -#include "servers/navigation_server.h" - -Vector Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const { - WARN_DEPRECATED_MSG("'Navigation' node and 'Navigation.get_simple_path()' are deprecated and will be removed in a future version. Use 'NavigationServer.map_get_path()' instead."); - return NavigationServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize, navigation_layers); -} - -String Navigation::get_configuration_warning() const { - String warning = Spatial::get_configuration_warning(); - - if (warning != String()) { - warning += "\n\n"; - } - warning += TTR("'Navigation' node and 'Navigation.get_simple_path()' are deprecated and will be removed in a future version. Use 'NavigationServer.map_get_path()' instead."); - - return warning; -} - -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); -} - -Vector3 Navigation::get_closest_point(const Vector3 &p_point) const { - return NavigationServer::get_singleton()->map_get_closest_point(map, p_point); -} - -Vector3 Navigation::get_closest_point_normal(const Vector3 &p_point) const { - return NavigationServer::get_singleton()->map_get_closest_point_normal(map, p_point); -} - -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_cell_height(float p_cell_height) { - cell_height = p_cell_height; - NavigationServer::get_singleton()->map_set_cell_height(map, cell_height); -} - -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::set_navigation_layers(uint32_t p_navigation_layers) { - navigation_layers = p_navigation_layers; -} - -uint32_t Navigation::get_navigation_layers() const { - return navigation_layers; -} - -void Navigation::_bind_methods() { - 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)); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation::get_closest_point); - ClassDB::bind_method(D_METHOD("get_closest_point_normal", "to_point"), &Navigation::get_closest_point_normal); - ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation::get_closest_point_owner); - - 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_cell_height", "cell_height"), &Navigation::set_cell_height); - ClassDB::bind_method(D_METHOD("get_cell_height"), &Navigation::get_cell_height); - - ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation::set_edge_connection_margin); - ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation::get_edge_connection_margin); - - ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &Navigation::set_navigation_layers); - ClassDB::bind_method(D_METHOD("get_navigation_layers"), &Navigation::get_navigation_layers); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_height"), "set_cell_height", "get_cell_height"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - - ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::_RID, "map"))); -} - -void Navigation::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - NavigationServer::get_singleton()->map_set_active(map, true); - } break; - case NOTIFICATION_EXIT_TREE: { - // FIXME 3.5 with this old navigation node only - // if the node gets deleted this exit causes annoying error prints in debug - // It tries to deactivate a map that itself has sent a free command to the server. - //NavigationServer::get_singleton()->map_set_active(map, false); - } break; - } -} - -Navigation::Navigation() { - map = NavigationServer::get_singleton()->map_create(); - NavigationServer::get_singleton()->map_set_active(map, true); - NavigationServer::get_singleton()->map_set_up(map, get_up_vector()); - NavigationServer::get_singleton()->map_set_cell_size(map, get_cell_size()); - NavigationServer::get_singleton()->map_set_cell_height(map, get_cell_height()); - NavigationServer::get_singleton()->map_set_edge_connection_margin(map, get_edge_connection_margin()); -} - -Navigation::~Navigation() { - NavigationServer::get_singleton()->free(map); -} diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h deleted file mode 100644 index f7689fb84a79..000000000000 --- a/scene/3d/navigation.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************/ -/* navigation.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef NAVIGATION_H -#define NAVIGATION_H - -#include "scene/3d/navigation_mesh_instance.h" -#include "scene/3d/spatial.h" - -class Navigation : public Spatial { - GDCLASS(Navigation, Spatial); - - RID map; - - Vector3 up = Vector3(0, 1, 0); - real_t cell_size = 0.25; - real_t cell_height = 0.25; - real_t edge_connection_margin = 0.25; - - uint32_t navigation_layers = 1; - -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; - - void set_cell_size(float p_cell_size); - float get_cell_size() const { - return cell_size; - } - - void set_cell_height(float p_cell_height); - float get_cell_height() const { - return cell_height; - } - - void set_navigation_layers(uint32_t p_navigation_layers); - uint32_t get_navigation_layers() const; - - void set_edge_connection_margin(float p_edge_connection_margin); - float get_edge_connection_margin() const { - return edge_connection_margin; - } - - Vector 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; - - virtual String get_configuration_warning() const; - - Navigation(); - ~Navigation(); -}; - -#endif // NAVIGATION_H diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp index e30903ad06d8..093a7f08fe6d 100644 --- a/scene/3d/navigation_agent.cpp +++ b/scene/3d/navigation_agent.cpp @@ -31,7 +31,6 @@ #include "navigation_agent.h" #include "core/engine.h" -#include "scene/3d/navigation.h" #include "servers/navigation_server.h" void NavigationAgent::_bind_methods() { @@ -55,9 +54,6 @@ void NavigationAgent::_bind_methods() { 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); @@ -76,6 +72,9 @@ void NavigationAgent::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent::get_navigation_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationAgent::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationAgent::get_navigation_layer_value); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent::get_navigation_map); @@ -120,20 +119,6 @@ void NavigationAgent::_bind_methods() { void NavigationAgent::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POST_ENTER_TREE: { - // Search the navigation node and set it - { - Navigation *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - set_navigation(nav); - } // need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready. // cannot use READY as ready does not get called if Node is readded to SceneTree set_agent_parent(get_parent()); @@ -159,7 +144,6 @@ void NavigationAgent::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { set_agent_parent(nullptr); - set_navigation(nullptr); set_physics_process_internal(false); } break; @@ -198,21 +182,27 @@ void NavigationAgent::_notification(int p_what) { NavigationAgent::NavigationAgent() { 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); + NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); + NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); + NavigationServer::get_singleton()->agent_set_time_horizon(agent, time_horizon); + NavigationServer::get_singleton()->agent_set_radius(agent, radius); + NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed); + NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y); } NavigationAgent::~NavigationAgent() { + ERR_FAIL_NULL(NavigationServer::get_singleton()); NavigationServer::get_singleton()->free(agent); agent = RID(); // Pointless } void NavigationAgent::set_avoidance_enabled(bool p_enabled) { + if (avoidance_enabled == p_enabled) { + return; + } + avoidance_enabled = p_enabled; + if (avoidance_enabled) { NavigationServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); } else { @@ -224,26 +214,10 @@ bool NavigationAgent::get_avoidance_enabled() const { return avoidance_enabled; } -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(p_nav); - ERR_FAIL_NULL(nav); - set_navigation(nav); -} - -Node *NavigationAgent::get_navigation_node() const { - return Object::cast_to(navigation); -} - void NavigationAgent::set_agent_parent(Node *p_agent_parent) { + if (agent_parent == p_agent_parent) { + return; + } // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); if (Object::cast_to(p_agent_parent) != nullptr) { @@ -251,34 +225,60 @@ void NavigationAgent::set_agent_parent(Node *p_agent_parent) { agent_parent = Object::cast_to(p_agent_parent); if (map_override.is_valid()) { NavigationServer::get_singleton()->agent_set_map(get_rid(), map_override); - } else if (navigation != nullptr) { - NavigationServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid()); } else { // no navigation node found in parent nodes, use default navigation map from world resource NavigationServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world()->get_navigation_map()); } + // create new avoidance callback if enabled - set_avoidance_enabled(avoidance_enabled); + if (avoidance_enabled) { + NavigationServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); + } } else { agent_parent = nullptr; NavigationServer::get_singleton()->agent_set_map(get_rid(), RID()); } } -void NavigationAgent::set_navigation_layers(uint32_t p_layers) { - bool _navigation_layers_changed = navigation_layers != p_layers; - navigation_layers = p_layers; - if (_navigation_layers_changed) { - _request_repath(); +void NavigationAgent::set_navigation_layers(uint32_t p_navigation_layers) { + if (navigation_layers == p_navigation_layers) { + return; } + + navigation_layers = p_navigation_layers; + + _request_repath(); } uint32_t NavigationAgent::get_navigation_layers() const { return navigation_layers; } +void NavigationAgent::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + uint32_t _navigation_layers = get_navigation_layers(); + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + set_navigation_layers(_navigation_layers); +} + +bool NavigationAgent::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + void NavigationAgent::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + NavigationServer::get_singleton()->agent_set_map(agent, map_override); _request_repath(); } @@ -293,48 +293,94 @@ RID NavigationAgent::get_navigation_map() const { } void NavigationAgent::set_path_desired_distance(real_t p_dd) { + if (Math::is_equal_approx(path_desired_distance, p_dd)) { + return; + } + path_desired_distance = p_dd; } void NavigationAgent::set_target_desired_distance(real_t p_dd) { + if (Math::is_equal_approx(target_desired_distance, p_dd)) { + return; + } + target_desired_distance = p_dd; } void NavigationAgent::set_radius(real_t p_radius) { + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + NavigationServer::get_singleton()->agent_set_radius(agent, radius); } void NavigationAgent::set_agent_height_offset(real_t p_hh) { + if (Math::is_equal_approx(navigation_height_offset, p_hh)) { + return; + } + navigation_height_offset = p_hh; } void NavigationAgent::set_ignore_y(bool p_ignore_y) { + if (ignore_y == p_ignore_y) { + return; + } + ignore_y = p_ignore_y; + NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y); } void NavigationAgent::set_neighbor_dist(real_t p_dist) { + if (Math::is_equal_approx(neighbor_dist, p_dist)) { + return; + } + neighbor_dist = p_dist; + NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist); } void NavigationAgent::set_max_neighbors(int p_count) { + if (max_neighbors == p_count) { + return; + } + max_neighbors = p_count; + NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); } void NavigationAgent::set_time_horizon(real_t p_time) { + if (Math::is_equal_approx(time_horizon, p_time)) { + return; + } + time_horizon = p_time; + NavigationServer::get_singleton()->agent_set_time_horizon(agent, time_horizon); } void NavigationAgent::set_max_speed(real_t p_max_speed) { + if (Math::is_equal_approx(max_speed, p_max_speed)) { + return; + } + 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) { + if (Math::is_equal_approx(path_max_distance, p_pmd)) { + return; + } + path_max_distance = p_pmd; } @@ -459,8 +505,6 @@ void NavigationAgent::update_navigation() { if (reload_path) { if (map_override.is_valid()) { navigation_path = NavigationServer::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); - } else if (navigation != nullptr) { - navigation_path = NavigationServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true, navigation_layers); } else { navigation_path = NavigationServer::get_singleton()->map_get_path(agent_parent->get_world()->get_navigation_map(), o, target_location, true, navigation_layers); } diff --git a/scene/3d/navigation_agent.h b/scene/3d/navigation_agent.h index 70abb159f947..0e3d5befb360 100644 --- a/scene/3d/navigation_agent.h +++ b/scene/3d/navigation_agent.h @@ -35,13 +35,11 @@ #include "scene/main/node.h" class Spatial; -class Navigation; class NavigationAgent : public Node { GDCLASS(NavigationAgent, Node); Spatial *agent_parent = nullptr; - Navigation *navigation = nullptr; RID agent; RID map_before_pause; @@ -52,15 +50,14 @@ class NavigationAgent : public Node { real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; - real_t radius = 0.0; + real_t radius = 0.5; real_t navigation_height_offset = 0.0; - bool ignore_y = false; - real_t neighbor_dist = 0.0; - int max_neighbors = 0; - real_t time_horizon = 0.0; - real_t max_speed = 0.0; - - real_t path_max_distance = 3.0; + bool ignore_y = true; + real_t neighbor_dist = 50.0; + int max_neighbors = 10; + real_t time_horizon = 1.0; + real_t max_speed = 10.0; + real_t path_max_distance = 5.0; Vector3 target_location; Vector navigation_path; @@ -82,14 +79,6 @@ class NavigationAgent : public Node { 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; } @@ -102,6 +91,9 @@ class NavigationAgent : public Node { void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + void set_navigation_map(RID p_navigation_map); RID get_navigation_map() const; diff --git a/scene/3d/navigation_mesh_instance.cpp b/scene/3d/navigation_mesh_instance.cpp index 74e78ccd42e8..742c37e93c13 100644 --- a/scene/3d/navigation_mesh_instance.cpp +++ b/scene/3d/navigation_mesh_instance.cpp @@ -33,7 +33,6 @@ #include "core/os/os.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) { @@ -49,11 +48,7 @@ void NavigationMeshInstance::set_enabled(bool p_enabled) { if (!enabled) { NavigationServer::get_singleton()->region_set_map(region, RID()); } else { - if (navigation) { - NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } else { - NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map()); - } + NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map()); } if (debug_view) { @@ -81,6 +76,28 @@ uint32_t NavigationMeshInstance::get_navigation_layers() const { return navigation_layers; } +void NavigationMeshInstance::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationMeshInstance::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + void NavigationMeshInstance::set_enter_cost(real_t p_enter_cost) { ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); enter_cost = MAX(p_enter_cost, 0.0); @@ -110,21 +127,7 @@ RID NavigationMeshInstance::get_region_rid() const { void NavigationMeshInstance::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Spatial *c = this; - while (c) { - navigation = Object::cast_to(c); - if (navigation) { - if (enabled) { - NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - - c = c->get_parent_spatial(); - } - - if (enabled && navigation == nullptr) { - // did not find a valid navigation node parent, fallback to default navigation map on world resource + if (enabled) { NavigationServer::get_singleton()->region_set_map(region, get_world()->get_navigation_map()); } @@ -146,15 +149,12 @@ void NavigationMeshInstance::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { - if (navigation) { - NavigationServer::get_singleton()->region_set_map(region, RID()); - } + NavigationServer::get_singleton()->region_set_map(region, RID()); if (debug_view) { debug_view->queue_delete(); debug_view = nullptr; } - navigation = nullptr; } break; } } @@ -267,6 +267,9 @@ void NavigationMeshInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationMeshInstance::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationMeshInstance::get_navigation_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationMeshInstance::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationMeshInstance::get_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationMeshInstance::get_region_rid); ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationMeshInstance::set_enter_cost); @@ -305,5 +308,6 @@ NavigationMeshInstance::~NavigationMeshInstance() { if (navmesh.is_valid()) { navmesh->remove_change_receptor(this); } + ERR_FAIL_NULL(NavigationServer::get_singleton()); NavigationServer::get_singleton()->free(region); } diff --git a/scene/3d/navigation_mesh_instance.h b/scene/3d/navigation_mesh_instance.h index 56830206b1b4..671256a96270 100644 --- a/scene/3d/navigation_mesh_instance.h +++ b/scene/3d/navigation_mesh_instance.h @@ -35,8 +35,6 @@ #include "scene/resources/mesh.h" #include "scene/resources/navigation_mesh.h" -class Navigation; - class NavigationMeshInstance : public Spatial { GDCLASS(NavigationMeshInstance, Spatial); @@ -48,7 +46,6 @@ class NavigationMeshInstance : public Spatial { real_t travel_cost = 1.0; uint32_t navigation_layers = 1; - Navigation *navigation = nullptr; Node *debug_view = nullptr; Thread bake_thread; @@ -64,6 +61,9 @@ class NavigationMeshInstance : public Spatial { void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + RID get_region_rid() const; void set_enter_cost(real_t p_enter_cost); diff --git a/scene/3d/navigation_obstacle.cpp b/scene/3d/navigation_obstacle.cpp index 6b6db6548ced..1e4a5294e3c3 100644 --- a/scene/3d/navigation_obstacle.cpp +++ b/scene/3d/navigation_obstacle.cpp @@ -31,15 +31,15 @@ #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("get_rid"), &NavigationObstacle::get_rid); - 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("set_navigation_map", "navigation_map"), &NavigationObstacle::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle::get_navigation_map); + ClassDB::bind_method(D_METHOD("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); @@ -59,28 +59,24 @@ void NavigationObstacle::_validate_property(PropertyInfo &p_property) const { void NavigationObstacle::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - parent_spatial = Object::cast_to(get_parent()); - reevaluate_agent_radius(); - - // Search the navigation node and set it - { - Navigation *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); - } - + case NOTIFICATION_POST_ENTER_TREE: { + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; + case NOTIFICATION_EXIT_TREE: { + set_agent_parent(nullptr); + set_physics_process_internal(false); + } break; + case NOTIFICATION_PARENTED: { + if (is_inside_tree() && (get_parent() != parent_spatial)) { + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } + } break; + case NOTIFICATION_UNPARENTED: { + set_agent_parent(nullptr); + set_physics_process_internal(false); + } break; case NOTIFICATION_PAUSED: { if (parent_spatial && !parent_spatial->can_process()) { map_before_pause = NavigationServer::get_singleton()->agent_get_map(get_rid()); @@ -99,70 +95,33 @@ void NavigationObstacle::_notification(int p_what) { map_before_pause = RID(); } } 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(get_parent()); - reevaluate_agent_radius(); - } break; - case NOTIFICATION_UNPARENTED: { - parent_spatial = nullptr; - } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (parent_spatial && parent_spatial->is_inside_tree()) { NavigationServer::get_singleton()->agent_set_position(agent, parent_spatial->get_global_transform().origin); - } - PhysicsBody *rigid = Object::cast_to(get_parent()); - if (rigid) { - Vector3 v = rigid->get_linear_velocity(); - NavigationServer::get_singleton()->agent_set_velocity(agent, v); - NavigationServer::get_singleton()->agent_set_target_velocity(agent, v); + PhysicsBody *rigid = Object::cast_to(get_parent()); + if (rigid) { + Vector3 v = rigid->get_linear_velocity(); + NavigationServer::get_singleton()->agent_set_velocity(agent, v); + NavigationServer::get_singleton()->agent_set_target_velocity(agent, v); + } } } break; } } -NavigationObstacle::NavigationObstacle() : - navigation(nullptr), - agent(RID()) { +NavigationObstacle::NavigationObstacle() { agent = NavigationServer::get_singleton()->agent_create(); initialize_agent(); } NavigationObstacle::~NavigationObstacle() { + ERR_FAIL_NULL(NavigationServer::get_singleton()); NavigationServer::get_singleton()->free(agent); agent = RID(); // Pointless } -void NavigationObstacle::set_navigation(Navigation *p_nav) { - if (navigation == p_nav && navigation != nullptr) { - return; // Pointless - } - - navigation = p_nav; - - if (navigation != nullptr) { - NavigationServer::get_singleton()->agent_set_map(agent, navigation->get_rid()); - } else if (parent_spatial && parent_spatial->is_inside_tree()) { - NavigationServer::get_singleton()->agent_set_map(agent, parent_spatial->get_world()->get_navigation_map()); - } -} - -void NavigationObstacle::set_navigation_node(Node *p_nav) { - Navigation *nav = Object::cast_to(p_nav); - ERR_FAIL_NULL(nav); - set_navigation(nav); -} - -Node *NavigationObstacle::get_navigation_node() const { - return Object::cast_to(navigation); -} - String NavigationObstacle::get_configuration_warning() const { String warning = Node::get_configuration_warning(); @@ -232,14 +191,62 @@ real_t NavigationObstacle::estimate_agent_radius() const { return 1.0; // Never a 0 radius } +void NavigationObstacle::set_agent_parent(Node *p_agent_parent) { + if (parent_spatial == p_agent_parent) { + return; + } + + if (Object::cast_to(p_agent_parent) != nullptr) { + parent_spatial = Object::cast_to(p_agent_parent); + if (map_override.is_valid()) { + NavigationServer::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + NavigationServer::get_singleton()->agent_set_map(get_rid(), parent_spatial->get_world()->get_navigation_map()); + } + reevaluate_agent_radius(); + } else { + parent_spatial = nullptr; + NavigationServer::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationObstacle::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + + map_override = p_navigation_map; + + NavigationServer::get_singleton()->agent_set_map(agent, map_override); +} + +RID NavigationObstacle::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (parent_spatial != nullptr) { + return parent_spatial->get_world()->get_navigation_map(); + } + return RID(); +} + void NavigationObstacle::set_estimate_radius(bool p_estimate_radius) { + if (estimate_radius == p_estimate_radius) { + return; + } + 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."); + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + reevaluate_agent_radius(); } diff --git a/scene/3d/navigation_obstacle.h b/scene/3d/navigation_obstacle.h index afd55024b720..42375ff54605 100644 --- a/scene/3d/navigation_obstacle.h +++ b/scene/3d/navigation_obstacle.h @@ -34,16 +34,14 @@ #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; RID map_before_pause; + RID map_override; bool estimate_radius = true; real_t radius = 1.0; @@ -57,18 +55,15 @@ class NavigationObstacle : public Node { 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_agent_parent(Node *p_agent_parent); + + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_estimate_radius(bool p_estimate_radius); bool is_radius_estimated() const { return estimate_radius; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3d2f67a6c258..5d4d884f3bb3 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -50,9 +50,9 @@ #include "scene/2d/listener_2d.h" #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/navigation_polygon_instance.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" #include "scene/2d/particles_2d.h" @@ -198,7 +198,6 @@ #include "scene/3d/merge_group.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/multimesh_instance.h" -#include "scene/3d/navigation.h" #include "scene/3d/navigation_agent.h" #include "scene/3d/navigation_mesh_instance.h" #include "scene/3d/navigation_obstacle.h" @@ -462,7 +461,6 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -514,7 +512,6 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -775,7 +772,6 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -793,6 +789,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("ImageSkyBox", "PanoramaSky"); ClassDB::add_compatibility_class("FixedSpatialMaterial", "SpatialMaterial"); ClassDB::add_compatibility_class("Mesh", "ArrayMesh"); + ClassDB::add_compatibility_class("Navigation", "Spatial"); + ClassDB::add_compatibility_class("Navigation2D", "Node2D"); #endif diff --git a/scene/2d/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp similarity index 53% rename from scene/2d/navigation_polygon.cpp rename to scene/resources/navigation_polygon.cpp index 46d1026d2766..163006354d87 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/resources/navigation_polygon.cpp @@ -33,7 +33,6 @@ #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" @@ -360,276 +359,3 @@ NavigationPolygon::NavigationPolygon() { NavigationPolygon::~NavigationPolygon() { } - -void NavigationPolygonInstance::set_enabled(bool p_enabled) { - if (enabled == p_enabled) { - return; - } - enabled = p_enabled; - - if (!is_inside_tree()) { - return; - } - - if (!enabled) { - Navigation2DServer::get_singleton()->region_set_map(region, RID()); - Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed"); - } else { - if (navigation != nullptr) { - Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } else { - Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); - } - Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed"); - } - - if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) { - update(); - } -} - -bool NavigationPolygonInstance::is_enabled() const { - return enabled; -} - -void NavigationPolygonInstance::set_navigation_layers(uint32_t p_navigation_layers) { - navigation_layers = p_navigation_layers; - Navigation2DServer::get_singleton()->region_set_navigation_layers(region, navigation_layers); -} - -uint32_t NavigationPolygonInstance::get_navigation_layers() const { - return navigation_layers; -} - -void NavigationPolygonInstance::set_enter_cost(real_t p_enter_cost) { - ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); - enter_cost = MAX(p_enter_cost, 0.0); - Navigation2DServer::get_singleton()->region_set_enter_cost(region, p_enter_cost); -} - -real_t NavigationPolygonInstance::get_enter_cost() const { - return enter_cost; -} - -void NavigationPolygonInstance::set_travel_cost(real_t p_travel_cost) { - ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); - travel_cost = MAX(p_travel_cost, 0.0); - Navigation2DServer::get_singleton()->region_set_travel_cost(region, travel_cost); -} - -real_t NavigationPolygonInstance::get_travel_cost() const { - return travel_cost; -} - -RID NavigationPolygonInstance::get_region_rid() const { - return region; -} - -///////////////////////////// -#ifdef TOOLS_ENABLED -Rect2 NavigationPolygonInstance::_edit_get_rect() const { - return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); -} - -bool NavigationPolygonInstance::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; -} -#endif - -void NavigationPolygonInstance::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - Node2D *c = this; - while (c) { - navigation = Object::cast_to(c); - if (navigation) { - if (enabled) { - Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - c = Object::cast_to(c->get_parent()); - } - if (enabled && navigation == nullptr) { - // did not find a valid navigation node parent, fallback to default navigation map on world resource - Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); - } - if (enabled) { - Navigation2DServer::get_singleton_mut()->connect("map_changed", this, "_map_changed"); - } - } break; - - case NOTIFICATION_TRANSFORM_CHANGED: { - Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform()); - } break; - - case NOTIFICATION_EXIT_TREE: { - if (navigation) { - Navigation2DServer::get_singleton()->region_set_map(region, RID()); - } - navigation = nullptr; - if (enabled) { - Navigation2DServer::get_singleton_mut()->disconnect("map_changed", this, "_map_changed"); - } - } break; - - case NOTIFICATION_DRAW: { - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint()) && navpoly.is_valid()) { - PoolVector verts = navpoly->get_vertices(); - if (verts.size() < 3) { - return; - } - - Color color; - if (enabled) { - color = get_tree()->get_debug_navigation_color(); - } else { - color = get_tree()->get_debug_navigation_disabled_color(); - } - Color doors_color = color.lightened(0.2); - - RandomPCG rand; - - for (int i = 0; i < navpoly->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector polygon = navpoly->get_polygon(i); - - Vector vertices; - vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], verts.size()); - vertices.write[j] = verts[polygon[j]]; - } - - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); - random_variation_color.a = color.a; - Vector colors; - colors.push_back(random_variation_color); - - VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), vertices, colors); - } - - // Draw the region - Transform2D xform = get_global_transform(); - const Navigation2DServer *ns = Navigation2DServer::get_singleton(); - real_t radius = 1.0; - if (navigation != nullptr) { - radius = Navigation2DServer::get_singleton()->map_get_edge_connection_margin(navigation->get_rid()); - } else { - radius = Navigation2DServer::get_singleton()->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()); - } - radius = radius * 0.5; - for (int i = 0; i < ns->region_get_connections_count(region); i++) { - // Two main points - Vector2 a = ns->region_get_connection_pathway_start(region, i); - a = xform.affine_inverse().xform(a); - Vector2 b = ns->region_get_connection_pathway_end(region, i); - b = xform.affine_inverse().xform(b); - draw_line(a, b, doors_color); - - // Draw a circle to illustrate the margins. - real_t angle = a.angle_to_point(b); - draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color); - draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); - } - } - } break; - } -} - -void NavigationPolygonInstance::set_navigation_polygon(const Ref &p_navpoly) { - if (p_navpoly == navpoly) { - return; - } - - 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(); - - _change_notify("navpoly"); - update_configuration_warning(); -} - -Ref NavigationPolygonInstance::get_navigation_polygon() const { - return navpoly; -} - -void NavigationPolygonInstance::_navpoly_changed() { - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { - update(); - } - if (navpoly.is_valid()) { - Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly); - } -} - -void NavigationPolygonInstance::_map_changed(RID p_map) { - if (navigation != nullptr && enabled && (navigation->get_rid() == p_map)) { - update(); - } else if (is_inside_tree() && enabled && (get_world_2d()->get_navigation_map() == p_map)) { - update(); - } -} - -String NavigationPolygonInstance::get_configuration_warning() const { - String warning = Node2D::get_configuration_warning(); - - if (!navpoly.is_valid()) { - if (warning != String()) { - warning += "\n\n"; - } - warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); - } - - return warning; -} - -void NavigationPolygonInstance::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navpoly"), &NavigationPolygonInstance::set_navigation_polygon); - ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationPolygonInstance::get_navigation_polygon); - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationPolygonInstance::set_enabled); - ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationPolygonInstance::is_enabled); - - ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationPolygonInstance::set_navigation_layers); - ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationPolygonInstance::get_navigation_layers); - - ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationPolygonInstance::get_region_rid); - - ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationPolygonInstance::set_enter_cost); - ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationPolygonInstance::get_enter_cost); - - ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationPolygonInstance::set_travel_cost); - ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationPolygonInstance::get_travel_cost); - - ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "enter_cost"), "set_enter_cost", "get_enter_cost"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "travel_cost"), "set_travel_cost", "get_travel_cost"); - - ClassDB::bind_method(D_METHOD("_map_changed"), &NavigationPolygonInstance::_map_changed); -} - -NavigationPolygonInstance::NavigationPolygonInstance() { - set_notify_transform(true); - region = Navigation2DServer::get_singleton()->region_create(); - Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost()); - Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost()); -} - -NavigationPolygonInstance::~NavigationPolygonInstance() { - Navigation2DServer::get_singleton()->free(region); -} diff --git a/scene/2d/navigation_polygon.h b/scene/resources/navigation_polygon.h similarity index 76% rename from scene/2d/navigation_polygon.h rename to scene/resources/navigation_polygon.h index 56dbec5e42e7..ec60f7c102c4 100644 --- a/scene/2d/navigation_polygon.h +++ b/scene/resources/navigation_polygon.h @@ -91,55 +91,4 @@ class NavigationPolygon : public Resource { ~NavigationPolygon(); }; -class Navigation2D; - -class NavigationPolygonInstance : public Node2D { - GDCLASS(NavigationPolygonInstance, Node2D); - - bool enabled = true; - RID region; - Navigation2D *navigation = nullptr; - Ref navpoly; - - real_t enter_cost = 0.0; - real_t travel_cost = 1.0; - - uint32_t navigation_layers = 1; - - void _navpoly_changed(); - void _map_changed(RID p_map); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: -#ifdef TOOLS_ENABLED - virtual Rect2 _edit_get_rect() const; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; -#endif - - void set_enabled(bool p_enabled); - bool is_enabled() const; - - void set_navigation_layers(uint32_t p_navigation_layers); - uint32_t get_navigation_layers() const; - - RID get_region_rid() const; - - void set_enter_cost(real_t p_enter_cost); - real_t get_enter_cost() const; - - void set_travel_cost(real_t p_travel_cost); - real_t get_travel_cost() const; - - void set_navigation_polygon(const Ref &p_navpoly); - Ref get_navigation_polygon() const; - - String get_configuration_warning() const; - - NavigationPolygonInstance(); - ~NavigationPolygonInstance(); -}; - #endif // NAVIGATION_POLYGON_H diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 58351834b7b3..c52e2abe292d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -34,8 +34,8 @@ #include "core/array.h" #include "core/resource.h" #include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_polygon.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/navigation_polygon.h" #include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" diff --git a/servers/navigation_2d_server.h b/servers/navigation_2d_server.h index 2ba14d27cc9c..5c75cc122553 100644 --- a/servers/navigation_2d_server.h +++ b/servers/navigation_2d_server.h @@ -33,7 +33,7 @@ #include "core/object.h" #include "core/rid.h" -#include "scene/2d/navigation_polygon.h" +#include "scene/resources/navigation_polygon.h" // This server exposes the 3D `NavigationServer` features in the 2D world. class Navigation2DServer : public Object {