2020-01-10 12:22:34 +01:00
/*************************************************************************/
2020-02-11 14:01:43 +01:00
/* nav_map.cpp */
2020-01-10 12:22:34 +01:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-01-10 12:22:34 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "nav_map.h"
2022-07-23 19:12:41 +02:00
# include "core/object/worker_thread_pool.h"
2022-01-31 00:39:52 +01:00
# include "nav_link.h"
2020-01-10 12:22:34 +01:00
# include "nav_region.h"
# include "rvo_agent.h"
# include <algorithm>
2021-03-15 12:45:28 +01:00
# define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
2020-01-10 12:22:34 +01:00
void NavMap : : set_up ( Vector3 p_up ) {
up = p_up ;
regenerate_polygons = true ;
}
void NavMap : : set_cell_size ( float p_cell_size ) {
cell_size = p_cell_size ;
regenerate_polygons = true ;
}
void NavMap : : set_edge_connection_margin ( float p_edge_connection_margin ) {
edge_connection_margin = p_edge_connection_margin ;
regenerate_links = true ;
}
2022-01-31 00:39:52 +01:00
void NavMap : : set_link_connection_radius ( float p_link_connection_radius ) {
link_connection_radius = p_link_connection_radius ;
regenerate_links = true ;
}
2020-01-10 12:22:34 +01:00
gd : : PointKey NavMap : : get_point_key ( const Vector3 & p_pos ) const {
const int x = int ( Math : : floor ( p_pos . x / cell_size ) ) ;
const int y = int ( Math : : floor ( p_pos . y / cell_size ) ) ;
const int z = int ( Math : : floor ( p_pos . z / cell_size ) ) ;
gd : : PointKey p ;
p . key = 0 ;
p . x = x ;
p . y = y ;
p . z = z ;
return p ;
}
2022-06-14 23:34:43 +02:00
Vector < Vector3 > NavMap : : get_path ( Vector3 p_origin , Vector3 p_destination , bool p_optimize , uint32_t p_navigation_layers ) const {
2021-03-15 12:45:28 +01:00
// Find the start poly and the end poly on this map.
2020-04-02 01:20:12 +02:00
const gd : : Polygon * begin_poly = nullptr ;
const gd : : Polygon * end_poly = nullptr ;
2020-01-10 12:22:34 +01:00
Vector3 begin_point ;
Vector3 end_point ;
float begin_d = 1e20 ;
float end_d = 1e20 ;
// Find the initial poly and the end poly on this map.
for ( size_t i ( 0 ) ; i < polygons . size ( ) ; i + + ) {
const gd : : Polygon & p = polygons [ i ] ;
2021-03-08 20:56:33 +01:00
// Only consider the polygon if it in a region with compatible layers.
2022-06-14 23:34:43 +02:00
if ( ( p_navigation_layers & p . owner - > get_navigation_layers ( ) ) = = 0 ) {
2021-03-08 20:56:33 +01:00
continue ;
}
2022-02-13 16:07:01 +01:00
// For each face check the distance between the origin/destination
for ( size_t point_id = 2 ; point_id < p . points . size ( ) ; point_id + + ) {
const Face3 face ( p . points [ 0 ] . pos , p . points [ point_id - 1 ] . pos , p . points [ point_id ] . pos ) ;
2021-03-15 12:45:28 +01:00
Vector3 point = face . get_closest_point_to ( p_origin ) ;
float distance_to_point = point . distance_to ( p_origin ) ;
if ( distance_to_point < begin_d ) {
begin_d = distance_to_point ;
2020-01-10 12:22:34 +01:00
begin_poly = & p ;
2021-03-15 12:45:28 +01:00
begin_point = point ;
2020-01-10 12:22:34 +01:00
}
2021-03-15 12:45:28 +01:00
point = face . get_closest_point_to ( p_destination ) ;
distance_to_point = point . distance_to ( p_destination ) ;
if ( distance_to_point < end_d ) {
end_d = distance_to_point ;
2020-01-10 12:22:34 +01:00
end_poly = & p ;
2021-03-15 12:45:28 +01:00
end_point = point ;
2020-01-10 12:22:34 +01:00
}
}
}
2021-05-20 12:07:26 +02:00
// Check for trivial cases
2020-01-10 12:22:34 +01:00
if ( ! begin_poly | | ! end_poly ) {
return Vector < Vector3 > ( ) ;
}
if ( begin_poly = = end_poly ) {
Vector < Vector3 > path ;
path . resize ( 2 ) ;
path . write [ 0 ] = begin_point ;
path . write [ 1 ] = end_point ;
return path ;
}
2021-03-15 12:45:28 +01:00
// List of all reachable navigation polys.
2022-07-28 19:24:14 +02:00
LocalVector < gd : : NavigationPoly > navigation_polys ;
2020-01-10 12:22:34 +01:00
navigation_polys . reserve ( polygons . size ( ) * 0.75 ) ;
2021-03-15 12:45:28 +01:00
// Add the start polygon to the reachable navigation polygons.
gd : : NavigationPoly begin_navigation_poly = gd : : NavigationPoly ( begin_poly ) ;
begin_navigation_poly . self_id = 0 ;
begin_navigation_poly . entry = begin_point ;
begin_navigation_poly . back_navigation_edge_pathway_start = begin_point ;
begin_navigation_poly . back_navigation_edge_pathway_end = begin_point ;
navigation_polys . push_back ( begin_navigation_poly ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
// List of polygon IDs to visit.
List < uint32_t > to_visit ;
to_visit . push_back ( 0 ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
// This is an implementation of the A* algorithm.
int least_cost_id = 0 ;
2022-09-27 12:51:41 +02:00
int prev_least_cost_id = - 1 ;
2021-03-15 12:45:28 +01:00
bool found_route = false ;
2020-01-10 12:22:34 +01:00
2020-04-02 01:20:12 +02:00
const gd : : Polygon * reachable_end = nullptr ;
2020-01-10 12:22:34 +01:00
float reachable_d = 1e30 ;
bool is_reachable = true ;
2021-03-15 12:45:28 +01:00
while ( true ) {
// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
2022-04-22 18:23:49 +02:00
for ( size_t i = 0 ; i < navigation_polys [ least_cost_id ] . poly - > edges . size ( ) ; i + + ) {
2022-09-27 12:51:41 +02:00
const gd : : Edge & edge = navigation_polys [ least_cost_id ] . poly - > edges [ i ] ;
2021-03-15 12:45:28 +01:00
// Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
for ( int connection_index = 0 ; connection_index < edge . connections . size ( ) ; connection_index + + ) {
const gd : : Edge : : Connection & connection = edge . connections [ connection_index ] ;
2021-03-08 20:56:33 +01:00
2021-03-15 12:45:28 +01:00
// Only consider the connection to another polygon if this polygon is in a region with compatible layers.
2022-06-14 23:34:43 +02:00
if ( ( p_navigation_layers & connection . polygon - > owner - > get_navigation_layers ( ) ) = = 0 ) {
2020-01-10 12:22:34 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
2022-09-27 12:51:41 +02:00
const gd : : NavigationPoly & least_cost_poly = navigation_polys [ least_cost_id ] ;
2022-01-31 00:39:52 +01:00
float poly_enter_cost = 0.0 ;
2022-09-27 12:51:41 +02:00
float poly_travel_cost = least_cost_poly . poly - > owner - > get_travel_cost ( ) ;
2022-06-06 05:24:11 +02:00
2022-09-27 12:51:41 +02:00
if ( prev_least_cost_id ! = - 1 & & ( navigation_polys [ prev_least_cost_id ] . poly - > owner - > get_self ( ) ! = least_cost_poly . poly - > owner - > get_self ( ) ) ) {
poly_enter_cost = least_cost_poly . poly - > owner - > get_enter_cost ( ) ;
2022-06-06 05:24:11 +02:00
}
2022-09-27 12:51:41 +02:00
prev_least_cost_id = least_cost_id ;
2022-06-06 05:24:11 +02:00
2021-03-15 12:45:28 +01:00
Vector3 pathway [ 2 ] = { connection . pathway_start , connection . pathway_end } ;
2022-09-27 12:51:41 +02:00
const Vector3 new_entry = Geometry3D : : get_closest_point_to_segment ( least_cost_poly . entry , pathway ) ;
const float new_distance = ( least_cost_poly . entry . distance_to ( new_entry ) * poly_travel_cost ) + poly_enter_cost + least_cost_poly . traveled_distance ;
2020-01-10 12:22:34 +01:00
2022-07-28 19:24:14 +02:00
int64_t already_visited_polygon_index = navigation_polys . find ( gd : : NavigationPoly ( connection . polygon ) ) ;
2020-01-10 12:22:34 +01:00
2022-07-28 19:24:14 +02:00
if ( already_visited_polygon_index ! = - 1 ) {
2021-03-15 12:45:28 +01:00
// Polygon already visited, check if we can reduce the travel cost.
2022-07-28 19:24:14 +02:00
gd : : NavigationPoly & avp = navigation_polys [ already_visited_polygon_index ] ;
if ( new_distance < avp . traveled_distance ) {
avp . back_navigation_poly_id = least_cost_id ;
avp . back_navigation_edge = connection . edge ;
avp . back_navigation_edge_pathway_start = connection . pathway_start ;
avp . back_navigation_edge_pathway_end = connection . pathway_end ;
avp . traveled_distance = new_distance ;
avp . entry = new_entry ;
2020-01-10 12:22:34 +01:00
}
} else {
2021-03-15 12:45:28 +01:00
// Add the neighbour polygon to the reachable ones.
gd : : NavigationPoly new_navigation_poly = gd : : NavigationPoly ( connection . polygon ) ;
new_navigation_poly . self_id = navigation_polys . size ( ) ;
new_navigation_poly . back_navigation_poly_id = least_cost_id ;
new_navigation_poly . back_navigation_edge = connection . edge ;
new_navigation_poly . back_navigation_edge_pathway_start = connection . pathway_start ;
new_navigation_poly . back_navigation_edge_pathway_end = connection . pathway_end ;
new_navigation_poly . traveled_distance = new_distance ;
new_navigation_poly . entry = new_entry ;
navigation_polys . push_back ( new_navigation_poly ) ;
// Add the neighbour polygon to the polygons to visit.
to_visit . push_back ( navigation_polys . size ( ) - 1 ) ;
2020-01-10 12:22:34 +01:00
}
}
}
2021-03-15 12:45:28 +01:00
// Removes the least cost polygon from the list of polygons to visit so we can advance.
to_visit . erase ( least_cost_id ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
// When the list of polygons to visit is empty at this point it means the End Polygon is not reachable
if ( to_visit . size ( ) = = 0 ) {
// Thus use the further reachable polygon
2020-01-10 12:22:34 +01:00
ERR_BREAK_MSG ( is_reachable = = false , " It's not expect to not find the most reachable polygons " ) ;
is_reachable = false ;
2020-04-02 01:20:12 +02:00
if ( reachable_end = = nullptr ) {
2020-01-10 12:22:34 +01:00
// The path is not found and there is not a way out.
break ;
}
// Set as end point the furthest reachable point.
end_poly = reachable_end ;
end_d = 1e20 ;
for ( size_t point_id = 2 ; point_id < end_poly - > points . size ( ) ; point_id + + ) {
2022-02-13 16:07:01 +01:00
Face3 f ( end_poly - > points [ 0 ] . pos , end_poly - > points [ point_id - 1 ] . pos , end_poly - > points [ point_id ] . pos ) ;
2020-01-10 12:22:34 +01:00
Vector3 spoint = f . get_closest_point_to ( p_destination ) ;
float dpoint = spoint . distance_to ( p_destination ) ;
if ( dpoint < end_d ) {
end_point = spoint ;
end_d = dpoint ;
}
}
// Reset open and navigation_polys
gd : : NavigationPoly np = navigation_polys [ 0 ] ;
navigation_polys . clear ( ) ;
navigation_polys . push_back ( np ) ;
2021-03-15 12:45:28 +01:00
to_visit . clear ( ) ;
to_visit . push_back ( 0 ) ;
2022-04-14 17:13:53 +02:00
least_cost_id = 0 ;
2020-01-10 12:22:34 +01:00
2020-04-02 01:20:12 +02:00
reachable_end = nullptr ;
2020-01-10 12:22:34 +01:00
continue ;
}
2021-03-15 12:45:28 +01:00
// Find the polygon with the minimum cost from the list of polygons to visit.
2020-01-10 12:22:34 +01:00
least_cost_id = - 1 ;
float least_cost = 1e30 ;
2021-03-15 12:45:28 +01:00
for ( List < uint32_t > : : Element * element = to_visit . front ( ) ; element ! = nullptr ; element = element - > next ( ) ) {
2020-01-10 12:22:34 +01:00
gd : : NavigationPoly * np = & navigation_polys [ element - > get ( ) ] ;
float cost = np - > traveled_distance ;
2022-06-06 05:24:11 +02:00
cost + = ( np - > entry . distance_to ( end_point ) * np - > poly - > owner - > get_travel_cost ( ) ) ;
2020-01-10 12:22:34 +01:00
if ( cost < least_cost ) {
least_cost_id = np - > self_id ;
least_cost = cost ;
}
}
2022-04-14 17:13:53 +02:00
ERR_BREAK ( least_cost_id = = - 1 ) ;
2020-01-10 12:22:34 +01:00
// Stores the further reachable end polygon, in case our goal is not reachable.
if ( is_reachable ) {
2022-06-06 05:24:11 +02:00
float d = navigation_polys [ least_cost_id ] . entry . distance_to ( p_destination ) * navigation_polys [ least_cost_id ] . poly - > owner - > get_travel_cost ( ) ;
2020-01-10 12:22:34 +01:00
if ( reachable_d > d ) {
reachable_d = d ;
reachable_end = navigation_polys [ least_cost_id ] . poly ;
}
}
// Check if we reached the end
if ( navigation_polys [ least_cost_id ] . poly = = end_poly ) {
found_route = true ;
break ;
}
}
2021-03-15 12:45:28 +01:00
// If we did not find a route, return an empty path.
if ( ! found_route ) {
return Vector < Vector3 > ( ) ;
}
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
Vector < Vector3 > path ;
// Optimize the path.
if ( p_optimize ) {
// Set the apex poly/point to the end point
gd : : NavigationPoly * apex_poly = & navigation_polys [ least_cost_id ] ;
Vector3 apex_point = end_point ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
gd : : NavigationPoly * left_poly = apex_poly ;
Vector3 left_portal = apex_point ;
gd : : NavigationPoly * right_poly = apex_poly ;
Vector3 right_portal = apex_point ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
gd : : NavigationPoly * p = apex_poly ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
path . push_back ( end_point ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
while ( p ) {
// Set left and right points of the pathway between polygons.
Vector3 left = p - > back_navigation_edge_pathway_start ;
Vector3 right = p - > back_navigation_edge_pathway_end ;
if ( THREE_POINTS_CROSS_PRODUCT ( apex_point , left , right ) . dot ( up ) < 0 ) {
SWAP ( left , right ) ;
}
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
bool skip = false ;
if ( THREE_POINTS_CROSS_PRODUCT ( apex_point , left_portal , left ) . dot ( up ) > = 0 ) {
//process
if ( left_portal = = apex_point | | THREE_POINTS_CROSS_PRODUCT ( apex_point , left , right_portal ) . dot ( up ) > 0 ) {
left_poly = p ;
left_portal = left ;
} else {
clip_path ( navigation_polys , path , apex_poly , right_portal , right_poly ) ;
apex_point = right_portal ;
p = right_poly ;
left_poly = p ;
apex_poly = p ;
left_portal = apex_point ;
right_portal = apex_point ;
path . push_back ( apex_point ) ;
skip = true ;
2020-01-10 12:22:34 +01:00
}
2021-03-15 12:45:28 +01:00
}
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
if ( ! skip & & THREE_POINTS_CROSS_PRODUCT ( apex_point , right_portal , right ) . dot ( up ) < = 0 ) {
//process
if ( right_portal = = apex_point | | THREE_POINTS_CROSS_PRODUCT ( apex_point , right , left_portal ) . dot ( up ) < 0 ) {
right_poly = p ;
right_portal = right ;
2020-05-14 16:41:43 +02:00
} else {
2021-03-15 12:45:28 +01:00
clip_path ( navigation_polys , path , apex_poly , left_portal , left_poly ) ;
apex_point = left_portal ;
p = left_poly ;
right_poly = p ;
apex_poly = p ;
right_portal = apex_point ;
left_portal = apex_point ;
path . push_back ( apex_point ) ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
}
2021-03-15 12:45:28 +01:00
// Go to the previous polygon.
if ( p - > back_navigation_poly_id ! = - 1 ) {
p = & navigation_polys [ p - > back_navigation_poly_id ] ;
} else {
// The end
p = nullptr ;
2020-05-14 16:41:43 +02:00
}
2021-03-15 12:45:28 +01:00
}
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
// If the last point is not the begin point, add it to the list.
if ( path [ path . size ( ) - 1 ] ! = begin_point ) {
path . push_back ( begin_point ) ;
}
2020-01-10 12:22:34 +01:00
2021-03-14 08:21:32 +01:00
path . reverse ( ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
} else {
path . push_back ( end_point ) ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
// Add mid points
int np_id = least_cost_id ;
2022-04-05 23:24:03 +02:00
while ( np_id ! = - 1 & & navigation_polys [ np_id ] . back_navigation_poly_id ! = - 1 ) {
2022-01-31 00:39:52 +01:00
if ( navigation_polys [ np_id ] . back_navigation_edge ! = - 1 ) {
int prev = navigation_polys [ np_id ] . back_navigation_edge ;
int prev_n = ( navigation_polys [ np_id ] . back_navigation_edge + 1 ) % navigation_polys [ np_id ] . poly - > points . size ( ) ;
Vector3 point = ( navigation_polys [ np_id ] . poly - > points [ prev ] . pos + navigation_polys [ np_id ] . poly - > points [ prev_n ] . pos ) * 0.5 ;
path . push_back ( point ) ;
} else {
path . push_back ( navigation_polys [ np_id ] . entry ) ;
}
2021-03-15 12:45:28 +01:00
np_id = navigation_polys [ np_id ] . back_navigation_poly_id ;
2020-01-10 12:22:34 +01:00
}
2022-04-05 23:24:03 +02:00
path . push_back ( begin_point ) ;
2021-03-14 08:21:32 +01:00
path . reverse ( ) ;
2020-01-10 12:22:34 +01:00
}
2021-03-15 12:45:28 +01:00
return path ;
2020-01-10 12:22:34 +01:00
}
2020-02-18 17:08:34 +01:00
Vector3 NavMap : : get_closest_point_to_segment ( const Vector3 & p_from , const Vector3 & p_to , const bool p_use_collision ) const {
bool use_collision = p_use_collision ;
Vector3 closest_point ;
real_t closest_point_d = 1e20 ;
for ( size_t i ( 0 ) ; i < polygons . size ( ) ; i + + ) {
const gd : : Polygon & p = polygons [ i ] ;
2022-02-13 16:07:01 +01:00
// For each face check the distance to the segment
2020-02-18 17:08:34 +01:00
for ( size_t point_id = 2 ; point_id < p . points . size ( ) ; point_id + = 1 ) {
2022-02-13 16:07:01 +01:00
const Face3 f ( p . points [ 0 ] . pos , p . points [ point_id - 1 ] . pos , p . points [ point_id ] . pos ) ;
2020-02-18 17:08:34 +01:00
Vector3 inters ;
if ( f . intersects_segment ( p_from , p_to , & inters ) ) {
const real_t d = closest_point_d = p_from . distance_to ( inters ) ;
if ( use_collision = = false ) {
closest_point = inters ;
use_collision = true ;
closest_point_d = d ;
} else if ( closest_point_d > d ) {
closest_point = inters ;
closest_point_d = d ;
}
}
}
if ( use_collision = = false ) {
for ( size_t point_id = 0 ; point_id < p . points . size ( ) ; point_id + = 1 ) {
Vector3 a , b ;
2020-05-25 19:20:45 +02:00
Geometry3D : : get_closest_points_between_segments (
2020-02-18 17:08:34 +01:00
p_from ,
p_to ,
p . points [ point_id ] . pos ,
p . points [ ( point_id + 1 ) % p . points . size ( ) ] . pos ,
a ,
b ) ;
const real_t d = a . distance_to ( b ) ;
if ( d < closest_point_d ) {
closest_point_d = d ;
closest_point = b ;
}
}
}
}
return closest_point ;
}
Vector3 NavMap : : get_closest_point ( const Vector3 & p_point ) const {
2022-02-13 16:07:01 +01:00
gd : : ClosestPointQueryResult cp = get_closest_point_info ( p_point ) ;
return cp . point ;
2020-02-18 17:08:34 +01:00
}
Vector3 NavMap : : get_closest_point_normal ( const Vector3 & p_point ) const {
2022-02-13 16:07:01 +01:00
gd : : ClosestPointQueryResult cp = get_closest_point_info ( p_point ) ;
return cp . normal ;
2020-02-18 17:08:34 +01:00
}
RID NavMap : : get_closest_point_owner ( const Vector3 & p_point ) const {
2022-02-13 16:07:01 +01:00
gd : : ClosestPointQueryResult cp = get_closest_point_info ( p_point ) ;
return cp . owner ;
}
2020-02-18 17:08:34 +01:00
2022-02-13 16:07:01 +01:00
gd : : ClosestPointQueryResult NavMap : : get_closest_point_info ( const Vector3 & p_point ) const {
gd : : ClosestPointQueryResult result ;
real_t closest_point_ds = 1e20 ;
2020-02-18 17:08:34 +01:00
for ( size_t i ( 0 ) ; i < polygons . size ( ) ; i + + ) {
const gd : : Polygon & p = polygons [ i ] ;
2022-02-13 16:07:01 +01:00
// For each face check the distance to the point
2020-02-18 17:08:34 +01:00
for ( size_t point_id = 2 ; point_id < p . points . size ( ) ; point_id + = 1 ) {
2022-02-13 16:07:01 +01:00
const Face3 f ( p . points [ 0 ] . pos , p . points [ point_id - 1 ] . pos , p . points [ point_id ] . pos ) ;
2020-02-18 17:08:34 +01:00
const Vector3 inters = f . get_closest_point_to ( p_point ) ;
2022-02-13 16:07:01 +01:00
const real_t ds = inters . distance_squared_to ( p_point ) ;
if ( ds < closest_point_ds ) {
result . point = inters ;
result . normal = f . get_plane ( ) . normal ;
result . owner = p . owner - > get_self ( ) ;
closest_point_ds = ds ;
2020-02-18 17:08:34 +01:00
}
}
}
2022-02-13 16:07:01 +01:00
return result ;
2020-02-18 17:08:34 +01:00
}
2020-01-10 12:22:34 +01:00
void NavMap : : add_region ( NavRegion * p_region ) {
regions . push_back ( p_region ) ;
regenerate_links = true ;
}
void NavMap : : remove_region ( NavRegion * p_region ) {
2022-07-28 19:24:14 +02:00
int64_t region_index = regions . find ( p_region ) ;
if ( region_index ! = - 1 ) {
regions . remove_at_unordered ( region_index ) ;
2020-02-24 18:09:14 +01:00
regenerate_links = true ;
}
2020-01-10 12:22:34 +01:00
}
2022-01-31 00:39:52 +01:00
void NavMap : : add_link ( NavLink * p_link ) {
links . push_back ( p_link ) ;
regenerate_links = true ;
}
void NavMap : : remove_link ( NavLink * p_link ) {
int64_t link_index = links . find ( p_link ) ;
if ( link_index ! = - 1 ) {
links . remove_at_unordered ( link_index ) ;
regenerate_links = true ;
}
}
2020-01-10 12:22:34 +01:00
bool NavMap : : has_agent ( RvoAgent * agent ) const {
2022-07-28 19:24:14 +02:00
return ( agents . find ( agent ) ! = - 1 ) ;
2020-01-10 12:22:34 +01:00
}
void NavMap : : add_agent ( RvoAgent * agent ) {
if ( ! has_agent ( agent ) ) {
agents . push_back ( agent ) ;
agents_dirty = true ;
}
}
void NavMap : : remove_agent ( RvoAgent * agent ) {
remove_agent_as_controlled ( agent ) ;
2022-07-28 19:24:14 +02:00
int64_t agent_index = agents . find ( agent ) ;
if ( agent_index ! = - 1 ) {
agents . remove_at_unordered ( agent_index ) ;
2020-01-10 12:22:34 +01:00
agents_dirty = true ;
}
}
void NavMap : : set_agent_as_controlled ( RvoAgent * agent ) {
2022-07-28 19:24:14 +02:00
const bool exist = ( controlled_agents . find ( agent ) ! = - 1 ) ;
2020-01-10 12:22:34 +01:00
if ( ! exist ) {
ERR_FAIL_COND ( ! has_agent ( agent ) ) ;
controlled_agents . push_back ( agent ) ;
}
}
void NavMap : : remove_agent_as_controlled ( RvoAgent * agent ) {
2022-07-28 19:24:14 +02:00
int64_t active_avoidance_agent_index = controlled_agents . find ( agent ) ;
if ( active_avoidance_agent_index ! = - 1 ) {
controlled_agents . remove_at_unordered ( active_avoidance_agent_index ) ;
agents_dirty = true ;
2020-01-10 12:22:34 +01:00
}
}
void NavMap : : sync ( ) {
2021-03-15 12:45:28 +01:00
// Check if we need to update the links.
2020-01-10 12:22:34 +01:00
if ( regenerate_polygons ) {
2022-07-28 19:24:14 +02:00
for ( uint32_t r = 0 ; r < regions . size ( ) ; r + + ) {
2020-01-10 12:22:34 +01:00
regions [ r ] - > scratch_polygons ( ) ;
}
regenerate_links = true ;
}
2022-07-28 19:24:14 +02:00
for ( uint32_t r = 0 ; r < regions . size ( ) ; r + + ) {
2020-01-10 12:22:34 +01:00
if ( regions [ r ] - > sync ( ) ) {
regenerate_links = true ;
}
}
2022-01-31 00:39:52 +01:00
for ( uint32_t l = 0 ; l < links . size ( ) ; l + + ) {
if ( links [ l ] - > check_dirty ( ) ) {
regenerate_links = true ;
}
}
2020-01-10 12:22:34 +01:00
if ( regenerate_links ) {
2021-03-15 12:45:28 +01:00
// Remove regions connections.
2022-07-28 19:24:14 +02:00
for ( uint32_t r = 0 ; r < regions . size ( ) ; r + + ) {
2021-03-15 12:45:28 +01:00
regions [ r ] - > get_connections ( ) . clear ( ) ;
}
// Resize the polygon count.
2020-01-10 12:22:34 +01:00
int count = 0 ;
2022-07-28 19:24:14 +02:00
for ( uint32_t r = 0 ; r < regions . size ( ) ; r + + ) {
2020-01-10 12:22:34 +01:00
count + = regions [ r ] - > get_polygons ( ) . size ( ) ;
}
polygons . resize ( count ) ;
2021-03-15 12:45:28 +01:00
// Copy all region polygons in the map.
count = 0 ;
2022-07-28 19:24:14 +02:00
for ( uint32_t r = 0 ; r < regions . size ( ) ; r + + ) {
const LocalVector < gd : : Polygon > & polygons_source = regions [ r ] - > get_polygons ( ) ;
for ( uint32_t n = 0 ; n < polygons_source . size ( ) ; n + + ) {
polygons [ count + n ] = polygons_source [ n ] ;
}
2020-01-10 12:22:34 +01:00
count + = regions [ r ] - > get_polygons ( ) . size ( ) ;
}
2021-03-15 12:45:28 +01:00
// Group all edges per key.
2022-05-13 15:04:37 +02:00
HashMap < gd : : EdgeKey , Vector < gd : : Edge : : Connection > , gd : : EdgeKey > connections ;
2022-07-28 19:24:14 +02:00
for ( uint32_t poly_id = 0 ; poly_id < polygons . size ( ) ; poly_id + + ) {
2020-01-10 12:22:34 +01:00
gd : : Polygon & poly ( polygons [ poly_id ] ) ;
2022-07-28 19:24:14 +02:00
for ( uint32_t p = 0 ; p < poly . points . size ( ) ; p + + ) {
2020-01-10 12:22:34 +01:00
int next_point = ( p + 1 ) % poly . points . size ( ) ;
gd : : EdgeKey ek ( poly . points [ p ] . key , poly . points [ next_point ] . key ) ;
2022-05-13 15:04:37 +02:00
HashMap < gd : : EdgeKey , Vector < gd : : Edge : : Connection > , gd : : EdgeKey > : : Iterator connection = connections . find ( ek ) ;
2020-01-10 12:22:34 +01:00
if ( ! connection ) {
2021-03-15 12:45:28 +01:00
connections [ ek ] = Vector < gd : : Edge : : Connection > ( ) ;
}
if ( connections [ ek ] . size ( ) < = 1 ) {
// Add the polygon/edge tuple to this key.
gd : : Edge : : Connection new_connection ;
new_connection . polygon = & poly ;
new_connection . edge = p ;
new_connection . pathway_start = poly . points [ p ] . pos ;
new_connection . pathway_end = poly . points [ next_point ] . pos ;
connections [ ek ] . push_back ( new_connection ) ;
2020-01-10 12:22:34 +01:00
} else {
// The edge is already connected with another edge, skip.
2021-11-24 20:21:33 +01:00
ERR_PRINT_ONCE ( " Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problems. " ) ;
2020-01-10 12:22:34 +01:00
}
}
}
2021-03-15 12:45:28 +01:00
Vector < gd : : Edge : : Connection > free_edges ;
2021-08-09 22:13:42 +02:00
for ( KeyValue < gd : : EdgeKey , Vector < gd : : Edge : : Connection > > & E : connections ) {
if ( E . value . size ( ) = = 2 ) {
2021-03-15 12:45:28 +01:00
// Connect edge that are shared in different polygons.
2021-08-09 22:13:42 +02:00
gd : : Edge : : Connection & c1 = E . value . write [ 0 ] ;
gd : : Edge : : Connection & c2 = E . value . write [ 1 ] ;
2021-03-15 12:45:28 +01:00
c1 . polygon - > edges [ c1 . edge ] . connections . push_back ( c2 ) ;
c2 . polygon - > edges [ c2 . edge ] . connections . push_back ( c1 ) ;
// Note: The pathway_start/end are full for those connection and do not need to be modified.
} else {
2021-08-09 22:13:42 +02:00
CRASH_COND_MSG ( E . value . size ( ) ! = 1 , vformat ( " Number of connection != 1. Found: %d " , E . value . size ( ) ) ) ;
free_edges . push_back ( E . value [ 0 ] ) ;
2020-01-10 12:22:34 +01:00
}
}
// Find the compatible near edges.
//
// Note:
// Considering that the edges must be compatible (for obvious reasons)
// to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during
// connection, integration and path finding.
2021-03-15 12:45:28 +01:00
for ( int i = 0 ; i < free_edges . size ( ) ; i + + ) {
const gd : : Edge : : Connection & free_edge = free_edges [ i ] ;
Vector3 edge_p1 = free_edge . polygon - > points [ free_edge . edge ] . pos ;
Vector3 edge_p2 = free_edge . polygon - > points [ ( free_edge . edge + 1 ) % free_edge . polygon - > points . size ( ) ] . pos ;
for ( int j = 0 ; j < free_edges . size ( ) ; j + + ) {
const gd : : Edge : : Connection & other_edge = free_edges [ j ] ;
if ( i = = j | | free_edge . polygon - > owner = = other_edge . polygon - > owner ) {
continue ;
}
Vector3 other_edge_p1 = other_edge . polygon - > points [ other_edge . edge ] . pos ;
Vector3 other_edge_p2 = other_edge . polygon - > points [ ( other_edge . edge + 1 ) % other_edge . polygon - > points . size ( ) ] . pos ;
// Compute the projection of the opposite edge on the current one
Vector3 edge_vector = edge_p2 - edge_p1 ;
float projected_p1_ratio = edge_vector . dot ( other_edge_p1 - edge_p1 ) / ( edge_vector . length_squared ( ) ) ;
float projected_p2_ratio = edge_vector . dot ( other_edge_p2 - edge_p1 ) / ( edge_vector . length_squared ( ) ) ;
if ( ( projected_p1_ratio < 0.0 & & projected_p2_ratio < 0.0 ) | | ( projected_p1_ratio > 1.0 & & projected_p2_ratio > 1.0 ) ) {
2020-01-10 12:22:34 +01:00
continue ;
}
2021-03-15 12:45:28 +01:00
// Check if the two edges are close to each other enough and compute a pathway between the two regions.
Vector3 self1 = edge_vector * CLAMP ( projected_p1_ratio , 0.0 , 1.0 ) + edge_p1 ;
Vector3 other1 ;
if ( projected_p1_ratio > = 0.0 & & projected_p1_ratio < = 1.0 ) {
other1 = other_edge_p1 ;
} else {
other1 = other_edge_p1 . lerp ( other_edge_p2 , ( 1.0 - projected_p1_ratio ) / ( projected_p2_ratio - projected_p1_ratio ) ) ;
2020-01-10 12:22:34 +01:00
}
2021-09-29 05:51:34 +02:00
if ( other1 . distance_to ( self1 ) > edge_connection_margin ) {
2021-03-15 12:45:28 +01:00
continue ;
}
Vector3 self2 = edge_vector * CLAMP ( projected_p2_ratio , 0.0 , 1.0 ) + edge_p1 ;
Vector3 other2 ;
if ( projected_p2_ratio > = 0.0 & & projected_p2_ratio < = 1.0 ) {
other2 = other_edge_p2 ;
} else {
other2 = other_edge_p1 . lerp ( other_edge_p2 , ( 0.0 - projected_p1_ratio ) / ( projected_p2_ratio - projected_p1_ratio ) ) ;
}
2021-09-29 05:51:34 +02:00
if ( other2 . distance_to ( self2 ) > edge_connection_margin ) {
2021-03-15 12:45:28 +01:00
continue ;
}
// The edges can now be connected.
gd : : Edge : : Connection new_connection = other_edge ;
new_connection . pathway_start = ( self1 + other1 ) / 2.0 ;
new_connection . pathway_end = ( self2 + other2 ) / 2.0 ;
free_edge . polygon - > edges [ free_edge . edge ] . connections . push_back ( new_connection ) ;
// Add the connection to the region_connection map.
2022-01-31 00:39:52 +01:00
( ( NavRegion * ) free_edge . polygon - > owner ) - > get_connections ( ) . push_back ( new_connection ) ;
}
}
uint32_t link_poly_idx = 0 ;
link_polygons . resize ( links . size ( ) ) ;
// Search for polygons within range of a nav link.
for ( uint32_t l = 0 ; l < links . size ( ) ; l + + ) {
const NavLink * link = links [ l ] ;
const Vector3 start = link - > get_start_location ( ) ;
const Vector3 end = link - > get_end_location ( ) ;
gd : : Polygon * closest_start_polygon = nullptr ;
real_t closest_start_distance = link_connection_radius ;
Vector3 closest_start_point ;
gd : : Polygon * closest_end_polygon = nullptr ;
real_t closest_end_distance = link_connection_radius ;
Vector3 closest_end_point ;
// Create link to any polygons within the search radius of the start point.
for ( uint32_t start_index = 0 ; start_index < polygons . size ( ) ; start_index + + ) {
gd : : Polygon & start_poly = polygons [ start_index ] ;
// For each face check the distance to the start
for ( uint32_t start_point_id = 2 ; start_point_id < start_poly . points . size ( ) ; start_point_id + = 1 ) {
const Face3 start_face ( start_poly . points [ 0 ] . pos , start_poly . points [ start_point_id - 1 ] . pos , start_poly . points [ start_point_id ] . pos ) ;
const Vector3 start_point = start_face . get_closest_point_to ( start ) ;
const real_t start_distance = start_point . distance_to ( start ) ;
// Pick the polygon that is within our radius and is closer than anything we've seen yet.
if ( start_distance < = link_connection_radius & & start_distance < closest_start_distance ) {
closest_start_distance = start_distance ;
closest_start_point = start_point ;
closest_start_polygon = & start_poly ;
}
}
}
// Find any polygons within the search radius of the end point.
for ( uint32_t end_index = 0 ; end_index < polygons . size ( ) ; end_index + + ) {
gd : : Polygon & end_poly = polygons [ end_index ] ;
// For each face check the distance to the end
for ( uint32_t end_point_id = 2 ; end_point_id < end_poly . points . size ( ) ; end_point_id + = 1 ) {
const Face3 end_face ( end_poly . points [ 0 ] . pos , end_poly . points [ end_point_id - 1 ] . pos , end_poly . points [ end_point_id ] . pos ) ;
const Vector3 end_point = end_face . get_closest_point_to ( end ) ;
const real_t end_distance = end_point . distance_to ( end ) ;
// Pick the polygon that is within our radius and is closer than anything we've seen yet.
if ( end_distance < = link_connection_radius & & end_distance < closest_end_distance ) {
closest_end_distance = end_distance ;
closest_end_point = end_point ;
closest_end_polygon = & end_poly ;
}
}
}
// If we have both a start and end point, then create a synthetic polygon to route through.
if ( closest_start_polygon & & closest_end_polygon ) {
gd : : Polygon & new_polygon = link_polygons [ link_poly_idx + + ] ;
new_polygon . owner = link ;
new_polygon . edges . clear ( ) ;
new_polygon . edges . resize ( 4 ) ;
new_polygon . points . clear ( ) ;
new_polygon . points . reserve ( 4 ) ;
// Build a set of vertices that create a thin polygon going from the start to the end point.
new_polygon . points . push_back ( { closest_start_point , get_point_key ( closest_start_point ) } ) ;
new_polygon . points . push_back ( { closest_start_point , get_point_key ( closest_start_point ) } ) ;
new_polygon . points . push_back ( { closest_end_point , get_point_key ( closest_end_point ) } ) ;
new_polygon . points . push_back ( { closest_end_point , get_point_key ( closest_end_point ) } ) ;
Vector3 center ;
for ( int p = 0 ; p < 4 ; + + p ) {
center + = new_polygon . points [ p ] . pos ;
}
new_polygon . center = center / real_t ( new_polygon . points . size ( ) ) ;
new_polygon . clockwise = true ;
// Setup connections to go forward in the link.
{
gd : : Edge : : Connection entry_connection ;
entry_connection . polygon = & new_polygon ;
entry_connection . edge = - 1 ;
entry_connection . pathway_start = new_polygon . points [ 0 ] . pos ;
entry_connection . pathway_end = new_polygon . points [ 1 ] . pos ;
closest_start_polygon - > edges [ 0 ] . connections . push_back ( entry_connection ) ;
gd : : Edge : : Connection exit_connection ;
exit_connection . polygon = closest_end_polygon ;
exit_connection . edge = - 1 ;
exit_connection . pathway_start = new_polygon . points [ 2 ] . pos ;
exit_connection . pathway_end = new_polygon . points [ 3 ] . pos ;
new_polygon . edges [ 2 ] . connections . push_back ( exit_connection ) ;
}
// If the link is bi-directional, create connections from the end to the start.
if ( link - > is_bidirectional ( ) ) {
gd : : Edge : : Connection entry_connection ;
entry_connection . polygon = & new_polygon ;
entry_connection . edge = - 1 ;
entry_connection . pathway_start = new_polygon . points [ 2 ] . pos ;
entry_connection . pathway_end = new_polygon . points [ 3 ] . pos ;
closest_end_polygon - > edges [ 0 ] . connections . push_back ( entry_connection ) ;
gd : : Edge : : Connection exit_connection ;
exit_connection . polygon = closest_start_polygon ;
exit_connection . edge = - 1 ;
exit_connection . pathway_start = new_polygon . points [ 0 ] . pos ;
exit_connection . pathway_end = new_polygon . points [ 1 ] . pos ;
new_polygon . edges [ 0 ] . connections . push_back ( exit_connection ) ;
}
2020-01-10 12:22:34 +01:00
}
}
2021-03-15 12:45:28 +01:00
// Update the update ID.
2020-12-08 19:58:49 +01:00
map_update_id = ( map_update_id + 1 ) % 9999999 ;
2020-01-10 12:22:34 +01:00
}
2021-03-15 12:45:28 +01:00
// Update agents tree.
2020-01-10 12:22:34 +01:00
if ( agents_dirty ) {
2022-07-28 19:24:14 +02:00
// cannot use LocalVector here as RVO library expects std::vector to build KdTree
2020-01-10 12:22:34 +01:00
std : : vector < RVO : : Agent * > raw_agents ;
raw_agents . reserve ( agents . size ( ) ) ;
2020-05-14 16:41:43 +02:00
for ( size_t i ( 0 ) ; i < agents . size ( ) ; i + + ) {
2020-01-10 12:22:34 +01:00
raw_agents . push_back ( agents [ i ] - > get_agent ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
rvo . buildAgentTree ( raw_agents ) ;
}
regenerate_polygons = false ;
regenerate_links = false ;
agents_dirty = false ;
}
void NavMap : : compute_single_step ( uint32_t index , RvoAgent * * agent ) {
( * ( agent + index ) ) - > get_agent ( ) - > computeNeighbors ( & rvo ) ;
( * ( agent + index ) ) - > get_agent ( ) - > computeNewVelocity ( deltatime ) ;
}
void NavMap : : step ( real_t p_deltatime ) {
deltatime = p_deltatime ;
if ( controlled_agents . size ( ) > 0 ) {
2022-07-28 19:24:14 +02:00
WorkerThreadPool : : GroupID group_task = WorkerThreadPool : : get_singleton ( ) - > add_template_group_task ( this , & NavMap : : compute_single_step , controlled_agents . ptr ( ) , controlled_agents . size ( ) , - 1 , true , SNAME ( " NavigationMapAgents " ) ) ;
2022-07-23 19:12:41 +02:00
WorkerThreadPool : : get_singleton ( ) - > wait_for_group_task_completion ( group_task ) ;
2020-01-10 12:22:34 +01:00
}
}
void NavMap : : dispatch_callbacks ( ) {
for ( int i ( 0 ) ; i < static_cast < int > ( controlled_agents . size ( ) ) ; i + + ) {
controlled_agents [ i ] - > dispatch_callback ( ) ;
}
}
2022-07-28 19:24:14 +02:00
void NavMap : : clip_path ( const LocalVector < gd : : NavigationPoly > & p_navigation_polys , Vector < Vector3 > & path , const gd : : NavigationPoly * from_poly , const Vector3 & p_to_point , const gd : : NavigationPoly * p_to_poly ) const {
2020-01-10 12:22:34 +01:00
Vector3 from = path [ path . size ( ) - 1 ] ;
2021-07-16 19:19:55 +02:00
if ( from . is_equal_approx ( p_to_point ) ) {
2020-01-10 12:22:34 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
Plane cut_plane ;
cut_plane . normal = ( from - p_to_point ) . cross ( up ) ;
2020-05-14 16:41:43 +02:00
if ( cut_plane . normal = = Vector3 ( ) ) {
2020-01-10 12:22:34 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
cut_plane . normal . normalize ( ) ;
2020-05-10 16:47:11 +02:00
cut_plane . d = cut_plane . normal . dot ( from ) ;
2020-01-10 12:22:34 +01:00
while ( from_poly ! = p_to_poly ) {
2021-03-15 12:45:28 +01:00
Vector3 pathway_start = from_poly - > back_navigation_edge_pathway_start ;
Vector3 pathway_end = from_poly - > back_navigation_edge_pathway_end ;
2020-01-10 12:22:34 +01:00
2021-03-15 12:45:28 +01:00
ERR_FAIL_COND ( from_poly - > back_navigation_poly_id = = - 1 ) ;
from_poly = & p_navigation_polys [ from_poly - > back_navigation_poly_id ] ;
2020-01-10 12:22:34 +01:00
2021-07-16 19:19:55 +02:00
if ( ! pathway_start . is_equal_approx ( pathway_end ) ) {
2020-01-10 12:22:34 +01:00
Vector3 inters ;
2021-03-15 12:45:28 +01:00
if ( cut_plane . intersects_segment ( pathway_start , pathway_end , & inters ) ) {
2021-07-16 19:19:55 +02:00
if ( ! inters . is_equal_approx ( p_to_point ) & & ! inters . is_equal_approx ( path [ path . size ( ) - 1 ] ) ) {
2020-01-10 12:22:34 +01:00
path . push_back ( inters ) ;
}
}
}
}
}
2022-04-18 21:57:23 +02:00
NavMap : : NavMap ( ) {
}
NavMap : : ~ NavMap ( ) {
}