2020-01-10 12:22:34 +01:00
/**************************************************************************/
2025-02-26 11:42:07 +01:00
/* nav_region_3d.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:42:07 +01:00
# include "nav_region_3d.h"
2020-01-10 12:22:34 +01:00
2025-02-26 11:42:07 +01:00
# include "nav_map_3d.h"
2020-01-10 12:22:34 +01:00
2024-08-30 15:52:29 +02:00
# include "3d/nav_mesh_queries_3d.h"
2025-05-19 00:54:35 +02:00
# include "3d/nav_region_builder_3d.h"
2024-12-15 20:31:13 +01:00
# include "3d/nav_region_iteration_3d.h"
2025-05-19 00:54:35 +02:00
# include "core/config/project_settings.h"
2024-08-30 15:52:29 +02:00
2025-03-12 12:47:08 -05:00
using namespace Nav3D ;
2025-02-26 11:42:07 +01:00
void NavRegion3D : : set_map ( NavMap3D * p_map ) {
2023-04-05 02:15:25 +02:00
if ( map = = p_map ) {
return ;
}
2023-06-25 05:28:21 +02:00
2025-05-19 00:54:35 +02:00
cancel_async_thread_join ( ) ;
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 ;
2025-05-19 00:54:35 +02:00
iteration_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 ( ) ;
2025-05-19 00:54:35 +02:00
if ( iteration_build_thread_task_id ! = WorkerThreadPool : : INVALID_TASK_ID ) {
request_async_thread_join ( ) ;
}
2021-03-15 12:45:28 +01:00
}
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : set_enabled ( bool p_enabled ) {
2023-07-06 23:01:19 +02:00
if ( enabled = = p_enabled ) {
return ;
}
enabled = p_enabled ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2023-07-06 23:01:19 +02:00
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2023-04-01 01:49:43 +02:00
}
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2023-04-01 01:49:43 +02:00
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : set_transform ( Transform3D 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2024-06-20 15:26:57 +02:00
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2024-06-20 15:26:57 +02:00
# ifdef DEBUG_ENABLED
if ( map & & Math : : rad_to_deg ( map - > get_up ( ) . angle_to ( transform . basis . get_column ( 1 ) ) ) > = 90.0f ) {
ERR_PRINT_ONCE ( " Attempted to update a navigation region transform rotated 90 degrees or more away from the current navigation map UP orientation. " ) ;
}
# endif // DEBUG_ENABLED
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : set_navigation_mesh ( Ref < NavigationMesh > p_navigation_mesh ) {
2024-06-20 15:26:57 +02:00
# ifdef DEBUG_ENABLED
2024-06-21 11:15:12 +02:00
if ( map & & p_navigation_mesh . is_valid ( ) & & ! Math : : is_equal_approx ( double ( map - > get_cell_size ( ) ) , double ( p_navigation_mesh - > get_cell_size ( ) ) ) ) {
2024-06-20 15:26:57 +02:00
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_mesh - > get_cell_size ( ) ) , double ( map - > get_cell_size ( ) ) ) ) ;
}
2024-06-21 11:15:12 +02:00
if ( map & & p_navigation_mesh . is_valid ( ) & & ! Math : : is_equal_approx ( double ( map - > get_cell_height ( ) ) , double ( p_navigation_mesh - > get_cell_height ( ) ) ) ) {
2024-06-20 15:26:57 +02:00
ERR_PRINT_ONCE ( vformat ( " Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings. " , double ( p_navigation_mesh - > get_cell_height ( ) ) , double ( map - > get_cell_height ( ) ) ) ) ;
}
# endif // DEBUG_ENABLED
2025-05-19 00:54:35 +02:00
navmesh = p_navigation_mesh ;
2024-06-20 15:26:57 +02:00
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2024-11-24 18:30:19 +01:00
request_sync ( ) ;
2020-01-10 12:22:34 +01:00
}
2025-02-26 11:42:07 +01:00
Vector3 NavRegion3D : : get_closest_point_to_segment ( const Vector3 & p_from , const Vector3 & p_to , bool p_use_collision ) const {
2024-08-25 22:59:13 +02:00
RWLockRead read_lock ( region_rwlock ) ;
return NavMeshQueries3D : : polygons_get_closest_point_to_segment (
get_polygons ( ) , p_from , p_to , p_use_collision ) ;
}
2025-02-26 11:42:07 +01:00
ClosestPointQueryResult NavRegion3D : : get_closest_point_info ( const Vector3 & p_point ) const {
2024-08-25 22:59:13 +02:00
RWLockRead read_lock ( region_rwlock ) ;
return NavMeshQueries3D : : polygons_get_closest_point_info ( get_polygons ( ) , p_point ) ;
}
2025-02-26 11:42:07 +01:00
Vector3 NavRegion3D : : 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 ( ) ) {
return Vector3 ( ) ;
}
2024-08-30 15:52:29 +02:00
return NavMeshQueries3D : : polygons_get_random_point ( get_polygons ( ) , p_navigation_layers , p_uniformly ) ;
2023-10-23 18:16:05 +02:00
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2025-02-12 10:03:26 +01:00
request_sync ( ) ;
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2025-02-12 10:03:26 +01:00
request_sync ( ) ;
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2025-02-12 10:03:26 +01:00
request_sync ( ) ;
}
2025-02-26 11:42:07 +01:00
void NavRegion3D : : 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 ;
2025-05-19 00:54:35 +02:00
iteration_dirty = true ;
2025-02-12 10:03:26 +01:00
request_sync ( ) ;
}
2025-05-19 00:54:35 +02:00
void NavRegion3D : : scratch_polygons ( ) {
iteration_dirty = true ;
2024-08-25 22:59:13 +02:00
2025-05-19 00:54:35 +02:00
request_sync ( ) ;
}
real_t NavRegion3D : : get_surface_area ( ) const {
RWLockRead read_lock ( iteration_rwlock ) ;
return iteration - > get_surface_area ( ) ;
}
2025-02-12 10:03:26 +01:00
2025-05-19 00:54:35 +02:00
AABB NavRegion3D : : get_bounds ( ) const {
RWLockRead read_lock ( iteration_rwlock ) ;
return iteration - > get_bounds ( ) ;
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
LocalVector < Nav3D : : Polygon > const & NavRegion3D : : get_polygons ( ) const {
RWLockRead read_lock ( iteration_rwlock ) ;
return iteration - > get_navmesh_polygons ( ) ;
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
bool NavRegion3D : : sync ( ) {
bool requires_map_update = false ;
if ( ! map ) {
return requires_map_update ;
}
if ( iteration_dirty & & ! iteration_building & & ! iteration_ready ) {
_build_iteration ( ) ;
2025-04-15 19:17:20 +02:00
}
2025-05-19 00:54:35 +02:00
if ( iteration_ready ) {
_sync_iteration ( ) ;
requires_map_update = true ;
}
return requires_map_update ;
2020-01-10 12:22:34 +01:00
}
2025-05-19 00:54:35 +02:00
void NavRegion3D : : sync_async_tasks ( ) {
if ( iteration_build_thread_task_id ! = WorkerThreadPool : : INVALID_TASK_ID ) {
if ( WorkerThreadPool : : get_singleton ( ) - > is_task_completed ( iteration_build_thread_task_id ) ) {
WorkerThreadPool : : get_singleton ( ) - > wait_for_task_completion ( iteration_build_thread_task_id ) ;
iteration_build_thread_task_id = WorkerThreadPool : : INVALID_TASK_ID ;
iteration_building = false ;
iteration_ready = true ;
request_sync ( ) ;
}
2020-01-10 12:22:34 +01:00
}
2025-05-19 00:54:35 +02:00
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
void NavRegion3D : : _build_iteration ( ) {
if ( ! iteration_dirty | | iteration_building | | iteration_ready ) {
2020-01-10 12:22:34 +01:00
return ;
}
2025-05-19 00:54:35 +02:00
iteration_dirty = false ;
iteration_building = true ;
iteration_ready = false ;
2023-06-13 13:36:05 +02:00
2025-05-19 00:54:35 +02:00
iteration_build . reset ( ) ;
2023-06-13 10:14:20 +02:00
2025-05-19 00:54:35 +02:00
if ( navmesh . is_valid ( ) ) {
navmesh - > get_data ( iteration_build . navmesh_data . vertices , iteration_build . navmesh_data . polygons ) ;
2020-05-14 16:41:43 +02:00
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
iteration_build . map_cell_size = map - > get_merge_rasterizer_cell_size ( ) ;
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
Ref < NavRegionIteration3D > new_iteration ;
new_iteration . instantiate ( ) ;
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
new_iteration - > navigation_layers = get_navigation_layers ( ) ;
new_iteration - > enter_cost = get_enter_cost ( ) ;
new_iteration - > travel_cost = get_travel_cost ( ) ;
new_iteration - > owner_object_id = get_owner_id ( ) ;
new_iteration - > owner_type = get_type ( ) ;
new_iteration - > owner_rid = get_self ( ) ;
new_iteration - > enabled = get_enabled ( ) ;
new_iteration - > transform = get_transform ( ) ;
new_iteration - > owner_use_edge_connections = get_use_edge_connections ( ) ;
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
iteration_build . region_iteration = new_iteration ;
2024-12-15 20:31:13 +01:00
2025-05-19 00:54:35 +02:00
if ( use_async_iterations ) {
iteration_build_thread_task_id = WorkerThreadPool : : get_singleton ( ) - > add_native_task ( & NavRegion3D : : _build_iteration_threaded , & iteration_build , true , SNAME ( " NavRegionBuilder3D " ) ) ;
request_async_thread_join ( ) ;
} else {
NavRegionBuilder3D : : build_iteration ( iteration_build ) ;
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
iteration_building = false ;
iteration_ready = true ;
}
}
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
void NavRegion3D : : _build_iteration_threaded ( void * p_arg ) {
NavRegionIterationBuild3D * _iteration_build = static_cast < NavRegionIterationBuild3D * > ( p_arg ) ;
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
NavRegionBuilder3D : : build_iteration ( * _iteration_build ) ;
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
void NavRegion3D : : _sync_iteration ( ) {
if ( iteration_building | | ! iteration_ready ) {
return ;
}
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
performance_data . pm_polygon_count = iteration_build . performance_data . pm_polygon_count ;
performance_data . pm_edge_count = iteration_build . performance_data . pm_edge_count ;
performance_data . pm_edge_merge_count = iteration_build . performance_data . pm_edge_merge_count ;
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
RWLockWrite write_lock ( iteration_rwlock ) ;
ERR_FAIL_COND ( iteration . is_null ( ) ) ;
iteration = Ref < NavRegionIteration3D > ( ) ;
DEV_ASSERT ( iteration . is_null ( ) ) ;
iteration = iteration_build . region_iteration ;
iteration_build . region_iteration = Ref < NavRegionIteration3D > ( ) ;
DEV_ASSERT ( iteration_build . region_iteration . is_null ( ) ) ;
iteration_id = iteration_id % UINT32_MAX + 1 ;
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
iteration_ready = false ;
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
cancel_async_thread_join ( ) ;
}
2020-01-10 12:22:34 +01:00
2025-05-19 00:54:35 +02:00
Ref < NavRegionIteration3D > NavRegion3D : : get_iteration ( ) {
RWLockRead read_lock ( iteration_rwlock ) ;
return iteration ;
}
2023-10-23 18:16:05 +02:00
2025-05-19 00:54:35 +02:00
void NavRegion3D : : request_async_thread_join ( ) {
DEV_ASSERT ( map ) ;
if ( map & & ! async_list_element . in_list ( ) ) {
map - > add_region_async_thread_join_request ( & async_list_element ) ;
}
2024-12-15 20:31:13 +01:00
}
2025-05-19 00:54:35 +02:00
void NavRegion3D : : cancel_async_thread_join ( ) {
if ( map & & async_list_element . in_list ( ) ) {
map - > remove_region_async_thread_join_request ( & async_list_element ) ;
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:42:07 +01:00
void NavRegion3D : : 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:42:07 +01:00
void NavRegion3D : : 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-05-19 00:54:35 +02:00
void NavRegion3D : : set_use_async_iterations ( bool p_enabled ) {
if ( use_async_iterations = = p_enabled ) {
return ;
}
# ifdef THREADS_ENABLED
use_async_iterations = p_enabled ;
# endif
}
bool NavRegion3D : : get_use_async_iterations ( ) const {
return use_async_iterations ;
}
2025-02-26 11:42:07 +01:00
NavRegion3D : : NavRegion3D ( ) :
2025-05-19 00:54:35 +02:00
sync_dirty_request_list_element ( this ) , async_list_element ( this ) {
2024-11-24 18:30:19 +01:00
type = NavigationUtilities : : PathSegmentType : : PATH_SEGMENT_TYPE_REGION ;
2025-05-19 00:54:35 +02:00
iteration_build . region = this ;
iteration . instantiate ( ) ;
# ifdef THREADS_ENABLED
use_async_iterations = GLOBAL_GET ( " navigation/world/region_use_async_iterations " ) ;
# else
use_async_iterations = false ;
# endif
2024-11-24 18:30:19 +01:00
}
2025-02-26 11:42:07 +01:00
NavRegion3D : : ~ NavRegion3D ( ) {
2025-05-19 00:54:35 +02:00
cancel_async_thread_join ( ) ;
2024-11-24 18:30:19 +01:00
cancel_sync_request ( ) ;
2025-05-19 00:54:35 +02:00
if ( iteration_build_thread_task_id ! = WorkerThreadPool : : INVALID_TASK_ID ) {
WorkerThreadPool : : get_singleton ( ) - > wait_for_task_completion ( iteration_build_thread_task_id ) ;
iteration_build_thread_task_id = WorkerThreadPool : : INVALID_TASK_ID ;
}
iteration_build . region = nullptr ;
iteration_build . region_iteration = Ref < NavRegionIteration3D > ( ) ;
iteration = Ref < NavRegionIteration3D > ( ) ;
2024-11-24 18:30:19 +01:00
}