2020-01-10 12:22:34 +01:00
/**************************************************************************/
2025-02-26 11:48:13 +01:00
/* nav_region_2d.cpp */
2020-01-10 12:22:34 +01:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
2020-02-11 14:01:43 +01:00
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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. */
/**************************************************************************/
2025-02-26 11:48:13 +01:00
# include "nav_region_2d.h"
2020-01-10 12:22:34 +01:00
2025-02-26 11:48:13 +01:00
# include "nav_map_2d.h"
# include "triangle2.h"
2020-01-10 12:22:34 +01:00
2025-02-26 11:48:13 +01:00
# include "2d/nav_map_builder_2d.h"
# include "2d/nav_mesh_queries_2d.h"
# include "2d/nav_region_iteration_2d.h"
2024-08-30 15:52:29 +02:00
2025-05-12 16:59:53 -07:00
using namespace Nav2D ;
2025-02-26 11:42:07 +01:00
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_map ( NavMap2D * p_map ) {
2023-04-05 02:15:25 +02:00
if ( map = = p_map ) {
return ;
}
2023-06-25 05:28:21 +02:00
2024-11-24 18:30:19 +01:00
cancel_sync_request ( ) ;
2023-06-25 05:28:21 +02:00
if ( map ) {
map - > remove_region ( this ) ;
}
2020-01-10 12:22:34 +01:00
map = p_map ;
polygons_dirty = true ;
2023-06-25 05:28:21 +02:00
if ( map ) {
map - > add_region ( this ) ;
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2021-03-15 12:45:28 +01:00
}
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_enabled ( bool p_enabled ) {
2023-07-06 23:01:19 +02:00
if ( enabled = = p_enabled ) {
return ;
}
enabled = p_enabled ;
// TODO: This should not require a full rebuild as the region has not really changed.
polygons_dirty = true ;
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2023-07-06 23:01:19 +02:00
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_use_edge_connections ( bool p_enabled ) {
2023-04-01 01:49:43 +02:00
if ( use_edge_connections ! = p_enabled ) {
use_edge_connections = p_enabled ;
polygons_dirty = true ;
}
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2023-04-01 01:49:43 +02:00
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_transform ( const Transform2D & p_transform ) {
2023-04-05 02:15:25 +02:00
if ( transform = = p_transform ) {
return ;
}
2020-01-10 12:22:34 +01:00
transform = p_transform ;
polygons_dirty = true ;
2024-06-20 15:26:57 +02:00
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_navigation_polygon ( Ref < NavigationPolygon > p_navigation_polygon ) {
2024-06-20 15:26:57 +02:00
# ifdef DEBUG_ENABLED
2025-02-26 11:48:13 +01:00
if ( map & & p_navigation_polygon . is_valid ( ) & & ! Math : : is_equal_approx ( double ( map - > get_cell_size ( ) ) , double ( p_navigation_polygon - > get_cell_size ( ) ) ) ) {
ERR_PRINT_ONCE ( vformat ( " Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings. " , double ( p_navigation_polygon - > get_cell_size ( ) ) , double ( map - > get_cell_size ( ) ) ) ) ;
2024-06-20 15:26:57 +02:00
}
# endif // DEBUG_ENABLED
RWLockWrite write_lock ( navmesh_rwlock ) ;
pending_navmesh_vertices . clear ( ) ;
pending_navmesh_polygons . clear ( ) ;
2025-02-26 11:48:13 +01:00
if ( p_navigation_polygon . is_valid ( ) ) {
p_navigation_polygon - > get_data ( pending_navmesh_vertices , pending_navmesh_polygons ) ;
2024-06-20 15:26:57 +02:00
}
2020-01-10 12:22:34 +01:00
polygons_dirty = true ;
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:48:13 +01:00
ClosestPointQueryResult NavRegion2D : : get_closest_point_info ( const Vector2 & p_point ) const {
2024-08-25 22:59:13 +02:00
RWLockRead read_lock ( region_rwlock ) ;
2025-02-26 11:48:13 +01:00
return NavMeshQueries2D : : polygons_get_closest_point_info ( get_polygons ( ) , p_point ) ;
2024-08-25 22:59:13 +02:00
}
2025-02-26 11:48:13 +01:00
Vector2 NavRegion2D : : get_random_point ( uint32_t p_navigation_layers , bool p_uniformly ) const {
2024-08-25 22:59:13 +02:00
RWLockRead read_lock ( region_rwlock ) ;
2023-10-23 18:16:05 +02:00
if ( ! get_enabled ( ) ) {
2025-02-26 11:48:13 +01:00
return Vector2 ( ) ;
2023-10-23 18:16:05 +02:00
}
2025-02-26 11:48:13 +01:00
return NavMeshQueries2D : : polygons_get_random_point ( get_polygons ( ) , p_navigation_layers , p_uniformly ) ;
2023-10-23 18:16:05 +02:00
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_navigation_layers ( uint32_t p_navigation_layers ) {
2025-02-12 10:03:26 +01:00
if ( navigation_layers = = p_navigation_layers ) {
return ;
}
navigation_layers = p_navigation_layers ;
region_dirty = true ;
request_sync ( ) ;
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_enter_cost ( real_t p_enter_cost ) {
2025-02-12 10:03:26 +01:00
real_t new_enter_cost = MAX ( p_enter_cost , 0.0 ) ;
if ( enter_cost = = new_enter_cost ) {
return ;
}
enter_cost = new_enter_cost ;
region_dirty = true ;
request_sync ( ) ;
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_travel_cost ( real_t p_travel_cost ) {
2025-02-12 10:03:26 +01:00
real_t new_travel_cost = MAX ( p_travel_cost , 0.0 ) ;
if ( travel_cost = = new_travel_cost ) {
return ;
}
travel_cost = new_travel_cost ;
region_dirty = true ;
request_sync ( ) ;
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : set_owner_id ( ObjectID p_owner_id ) {
2025-02-12 10:03:26 +01:00
if ( owner_id = = p_owner_id ) {
return ;
}
owner_id = p_owner_id ;
region_dirty = true ;
request_sync ( ) ;
}
2025-02-26 11:48:13 +01:00
bool NavRegion2D : : sync ( ) {
2024-08-25 22:59:13 +02:00
RWLockWrite write_lock ( region_rwlock ) ;
2025-02-12 10:03:26 +01:00
bool something_changed = region_dirty | | polygons_dirty ;
region_dirty = false ;
2020-01-10 12:22:34 +01:00
update_polygons ( ) ;
2025-04-15 19:17:20 +02:00
if ( something_changed ) {
iteration_id = iteration_id % UINT32_MAX + 1 ;
}
2020-01-10 12:22:34 +01:00
return something_changed ;
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : update_polygons ( ) {
2020-01-10 12:22:34 +01:00
if ( ! polygons_dirty ) {
return ;
}
2024-12-15 20:31:13 +01:00
navmesh_polygons . clear ( ) ;
2023-10-23 18:16:05 +02:00
surface_area = 0.0 ;
2025-02-26 11:48:13 +01:00
bounds = Rect2 ( ) ;
2020-01-10 12:22:34 +01:00
polygons_dirty = false ;
2020-04-02 01:20:12 +02:00
if ( map = = nullptr ) {
2020-01-10 12:22:34 +01:00
return ;
}
2024-06-20 15:26:57 +02:00
RWLockRead read_lock ( navmesh_rwlock ) ;
2023-06-13 13:36:05 +02:00
2024-06-20 15:26:57 +02:00
if ( pending_navmesh_vertices . is_empty ( ) | | pending_navmesh_polygons . is_empty ( ) ) {
return ;
2023-06-13 10:14:20 +02:00
}
2024-06-20 15:26:57 +02:00
int len = pending_navmesh_vertices . size ( ) ;
2020-05-14 16:41:43 +02:00
if ( len = = 0 ) {
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
2025-02-26 11:48:13 +01:00
const Vector2 * vertices_r = pending_navmesh_vertices . ptr ( ) ;
2020-01-10 12:22:34 +01:00
2024-12-15 20:31:13 +01:00
navmesh_polygons . resize ( pending_navmesh_polygons . size ( ) ) ;
2020-01-10 12:22:34 +01:00
2023-10-23 18:16:05 +02:00
real_t _new_region_surface_area = 0.0 ;
2025-02-26 11:48:13 +01:00
Rect2 _new_bounds ;
2023-10-23 18:16:05 +02:00
2024-12-15 20:31:13 +01:00
bool first_vertex = true ;
2023-10-23 18:16:05 +02:00
int navigation_mesh_polygon_index = 0 ;
2024-12-15 20:31:13 +01:00
2025-02-26 11:42:07 +01:00
for ( Polygon & polygon : navmesh_polygons ) {
2023-10-23 18:16:05 +02:00
polygon . surface_area = 0.0 ;
2020-01-10 12:22:34 +01:00
2024-06-20 15:26:57 +02:00
Vector < int > navigation_mesh_polygon = pending_navmesh_polygons [ navigation_mesh_polygon_index ] ;
2023-10-23 18:16:05 +02:00
navigation_mesh_polygon_index + = 1 ;
int navigation_mesh_polygon_size = navigation_mesh_polygon . size ( ) ;
if ( navigation_mesh_polygon_size < 3 ) {
continue ;
}
const int * indices = navigation_mesh_polygon . ptr ( ) ;
2020-01-10 12:22:34 +01:00
bool valid ( true ) ;
2025-04-10 01:03:58 +02:00
polygon . vertices . resize ( navigation_mesh_polygon_size ) ;
2023-10-23 18:16:05 +02:00
polygon . edges . resize ( navigation_mesh_polygon_size ) ;
real_t _new_polygon_surface_area = 0.0 ;
for ( int j ( 2 ) ; j < navigation_mesh_polygon_size ; j + + ) {
2025-02-26 11:48:13 +01:00
const Triangle2 triangle = Triangle2 (
2023-10-23 18:16:05 +02:00
transform . xform ( vertices_r [ indices [ 0 ] ] ) ,
transform . xform ( vertices_r [ indices [ j - 1 ] ] ) ,
transform . xform ( vertices_r [ indices [ j ] ] ) ) ;
2025-02-26 11:48:13 +01:00
_new_polygon_surface_area + = triangle . get_area ( ) ;
2023-10-23 18:16:05 +02:00
}
polygon . surface_area = _new_polygon_surface_area ;
_new_region_surface_area + = _new_polygon_surface_area ;
for ( int j ( 0 ) ; j < navigation_mesh_polygon_size ; j + + ) {
2020-01-10 12:22:34 +01:00
int idx = indices [ j ] ;
if ( idx < 0 | | idx > = len ) {
valid = false ;
break ;
}
2025-02-26 11:48:13 +01:00
Vector2 point_position = transform . xform ( vertices_r [ idx ] ) ;
2025-04-10 01:03:58 +02:00
polygon . vertices [ j ] = point_position ;
2024-12-15 20:31:13 +01:00
if ( first_vertex ) {
first_vertex = false ;
_new_bounds . position = point_position ;
} else {
_new_bounds . expand_to ( point_position ) ;
}
2020-01-10 12:22:34 +01:00
}
if ( ! valid ) {
2025-02-26 11:48:13 +01:00
ERR_BREAK_MSG ( ! valid , " The navigation polygon set in this region is not valid! " ) ;
2020-01-10 12:22:34 +01:00
}
}
2023-10-23 18:16:05 +02:00
surface_area = _new_region_surface_area ;
2024-12-15 20:31:13 +01:00
bounds = _new_bounds ;
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : get_iteration_update ( NavRegionIteration2D & r_iteration ) {
2024-12-15 20:31:13 +01:00
r_iteration . navigation_layers = get_navigation_layers ( ) ;
r_iteration . enter_cost = get_enter_cost ( ) ;
r_iteration . travel_cost = get_travel_cost ( ) ;
r_iteration . owner_object_id = get_owner_id ( ) ;
r_iteration . owner_type = get_type ( ) ;
2024-12-26 04:50:33 +01:00
r_iteration . owner_rid = get_self ( ) ;
2024-12-15 20:31:13 +01:00
2024-12-26 04:50:33 +01:00
r_iteration . enabled = get_enabled ( ) ;
r_iteration . transform = get_transform ( ) ;
r_iteration . owner_use_edge_connections = get_use_edge_connections ( ) ;
2024-12-15 20:31:13 +01:00
r_iteration . bounds = get_bounds ( ) ;
2024-12-26 04:50:33 +01:00
r_iteration . surface_area = get_surface_area ( ) ;
2024-12-15 20:31:13 +01:00
2024-12-26 04:50:33 +01:00
r_iteration . navmesh_polygons . clear ( ) ;
2024-12-15 20:31:13 +01:00
r_iteration . navmesh_polygons . resize ( navmesh_polygons . size ( ) ) ;
for ( uint32_t i = 0 ; i < navmesh_polygons . size ( ) ; i + + ) {
2025-02-26 11:42:07 +01:00
Polygon & navmesh_polygon = navmesh_polygons [ i ] ;
2024-12-26 04:50:33 +01:00
navmesh_polygon . owner = & r_iteration ;
r_iteration . navmesh_polygons [ i ] = navmesh_polygon ;
2024-12-15 20:31:13 +01:00
}
2020-01-10 12:22:34 +01:00
}
2024-11-24 18:30:19 +01:00
2025-02-26 11:48:13 +01:00
void NavRegion2D : : request_sync ( ) {
2024-11-24 18:30:19 +01:00
if ( map & & ! sync_dirty_request_list_element . in_list ( ) ) {
map - > add_region_sync_dirty_request ( & sync_dirty_request_list_element ) ;
}
}
2025-02-26 11:48:13 +01:00
void NavRegion2D : : cancel_sync_request ( ) {
2024-11-24 18:30:19 +01:00
if ( map & & sync_dirty_request_list_element . in_list ( ) ) {
map - > remove_region_sync_dirty_request ( & sync_dirty_request_list_element ) ;
}
}
2025-02-26 11:48:13 +01:00
NavRegion2D : : NavRegion2D ( ) :
2024-11-24 18:30:19 +01:00
sync_dirty_request_list_element ( this ) {
type = NavigationUtilities : : PathSegmentType : : PATH_SEGMENT_TYPE_REGION ;
}
2025-02-26 11:48:13 +01:00
NavRegion2D : : ~ NavRegion2D ( ) {
2024-11-24 18:30:19 +01:00
cancel_sync_request ( ) ;
}