Path: blob/master/modules/navigation_3d/nav_map_3d.cpp
11352 views
/**************************************************************************/1/* nav_map_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "nav_map_3d.h"3132#include "3d/nav_map_builder_3d.h"33#include "3d/nav_mesh_queries_3d.h"34#include "3d/nav_region_iteration_3d.h"35#include "nav_agent_3d.h"36#include "nav_link_3d.h"37#include "nav_obstacle_3d.h"38#include "nav_region_3d.h"3940#include "core/config/project_settings.h"41#include "core/object/worker_thread_pool.h"42#include "servers/navigation_3d/navigation_server_3d.h"4344#include <Obstacle2d.h>4546using namespace Nav3D;4748#ifdef DEBUG_ENABLED49#define NAVMAP_ITERATION_ZERO_ERROR_MSG() \50ERR_PRINT_ONCE("NavigationServer navigation map query failed because it was made before first map synchronization.\n\51NavigationServer 'map_changed' signal can be used to receive update notifications.\n\52NavigationServer 'map_get_iteration_id()' can be used to check if a map has finished its newest iteration.");53#else54#define NAVMAP_ITERATION_ZERO_ERROR_MSG()55#endif // DEBUG_ENABLED5657#define GET_MAP_ITERATION() \58iteration_slot_rwlock.read_lock(); \59NavMapIteration3D &map_iteration = iteration_slots[iteration_slot_index]; \60NavMapIterationRead3D iteration_read_lock(map_iteration); \61iteration_slot_rwlock.read_unlock();6263#define GET_MAP_ITERATION_CONST() \64iteration_slot_rwlock.read_lock(); \65const NavMapIteration3D &map_iteration = iteration_slots[iteration_slot_index]; \66NavMapIterationRead3D iteration_read_lock(map_iteration); \67iteration_slot_rwlock.read_unlock();6869void NavMap3D::set_up(Vector3 p_up) {70if (up == p_up) {71return;72}73up = p_up;74map_settings_dirty = true;75}7677void NavMap3D::set_cell_size(real_t p_cell_size) {78if (cell_size == p_cell_size) {79return;80}81cell_size = MAX(p_cell_size, NavigationDefaults3D::NAV_MESH_CELL_SIZE_MIN);82_update_merge_rasterizer_cell_dimensions();83map_settings_dirty = true;84}8586void NavMap3D::set_cell_height(real_t p_cell_height) {87if (cell_height == p_cell_height) {88return;89}90cell_height = MAX(p_cell_height, NavigationDefaults3D::NAV_MESH_CELL_SIZE_MIN);91_update_merge_rasterizer_cell_dimensions();92map_settings_dirty = true;93}9495void NavMap3D::set_merge_rasterizer_cell_scale(float p_value) {96if (merge_rasterizer_cell_scale == p_value) {97return;98}99merge_rasterizer_cell_scale = MAX(MIN(p_value, 0.1), NavigationDefaults3D::NAV_MESH_CELL_SIZE_MIN);100_update_merge_rasterizer_cell_dimensions();101map_settings_dirty = true;102}103104void NavMap3D::set_use_edge_connections(bool p_enabled) {105if (use_edge_connections == p_enabled) {106return;107}108use_edge_connections = p_enabled;109iteration_dirty = true;110}111112void NavMap3D::set_edge_connection_margin(real_t p_edge_connection_margin) {113if (edge_connection_margin == p_edge_connection_margin) {114return;115}116edge_connection_margin = p_edge_connection_margin;117iteration_dirty = true;118}119120void NavMap3D::set_link_connection_radius(real_t p_link_connection_radius) {121if (link_connection_radius == p_link_connection_radius) {122return;123}124link_connection_radius = p_link_connection_radius;125iteration_dirty = true;126}127128const Vector3 &NavMap3D::get_merge_rasterizer_cell_size() const {129return merge_rasterizer_cell_size;130}131132PointKey NavMap3D::get_point_key(const Vector3 &p_pos) const {133const int x = static_cast<int>(Math::floor(p_pos.x / merge_rasterizer_cell_size.x));134const int y = static_cast<int>(Math::floor(p_pos.y / merge_rasterizer_cell_size.y));135const int z = static_cast<int>(Math::floor(p_pos.z / merge_rasterizer_cell_size.z));136137PointKey p;138p.key = 0;139p.x = x;140p.y = y;141p.z = z;142return p;143}144145void NavMap3D::query_path(NavMeshQueries3D::NavMeshPathQueryTask3D &p_query_task) {146if (iteration_id == 0) {147return;148}149150GET_MAP_ITERATION();151152map_iteration.path_query_slots_semaphore.wait();153154map_iteration.path_query_slots_mutex.lock();155for (NavMeshQueries3D::PathQuerySlot &p_path_query_slot : map_iteration.path_query_slots) {156if (!p_path_query_slot.in_use) {157p_path_query_slot.in_use = true;158p_query_task.path_query_slot = &p_path_query_slot;159break;160}161}162map_iteration.path_query_slots_mutex.unlock();163164if (p_query_task.path_query_slot == nullptr) {165map_iteration.path_query_slots_semaphore.post();166ERR_FAIL_NULL_MSG(p_query_task.path_query_slot, "No unused NavMap3D path query slot found! This should never happen :(.");167}168169p_query_task.map_up = map_iteration.map_up;170171NavMeshQueries3D::query_task_map_iteration_get_path(p_query_task, map_iteration);172173map_iteration.path_query_slots_mutex.lock();174uint32_t used_slot_index = p_query_task.path_query_slot->slot_index;175map_iteration.path_query_slots[used_slot_index].in_use = false;176p_query_task.path_query_slot = nullptr;177map_iteration.path_query_slots_mutex.unlock();178179map_iteration.path_query_slots_semaphore.post();180}181182Vector3 NavMap3D::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {183if (iteration_id == 0) {184NAVMAP_ITERATION_ZERO_ERROR_MSG();185return Vector3();186}187188GET_MAP_ITERATION_CONST();189190return NavMeshQueries3D::map_iteration_get_closest_point_to_segment(map_iteration, p_from, p_to, p_use_collision);191}192193Vector3 NavMap3D::get_closest_point(const Vector3 &p_point) const {194if (iteration_id == 0) {195NAVMAP_ITERATION_ZERO_ERROR_MSG();196return Vector3();197}198199GET_MAP_ITERATION_CONST();200201return NavMeshQueries3D::map_iteration_get_closest_point(map_iteration, p_point);202}203204Vector3 NavMap3D::get_closest_point_normal(const Vector3 &p_point) const {205if (iteration_id == 0) {206NAVMAP_ITERATION_ZERO_ERROR_MSG();207return Vector3();208}209210GET_MAP_ITERATION_CONST();211212return NavMeshQueries3D::map_iteration_get_closest_point_normal(map_iteration, p_point);213}214215RID NavMap3D::get_closest_point_owner(const Vector3 &p_point) const {216if (iteration_id == 0) {217NAVMAP_ITERATION_ZERO_ERROR_MSG();218return RID();219}220221GET_MAP_ITERATION_CONST();222223return NavMeshQueries3D::map_iteration_get_closest_point_owner(map_iteration, p_point);224}225226ClosestPointQueryResult NavMap3D::get_closest_point_info(const Vector3 &p_point) const {227GET_MAP_ITERATION_CONST();228229return NavMeshQueries3D::map_iteration_get_closest_point_info(map_iteration, p_point);230}231232void NavMap3D::add_region(NavRegion3D *p_region) {233DEV_ASSERT(!regions.has(p_region));234235regions.push_back(p_region);236iteration_dirty = true;237}238239void NavMap3D::remove_region(NavRegion3D *p_region) {240if (regions.erase_unordered(p_region)) {241iteration_dirty = true;242}243}244245void NavMap3D::add_link(NavLink3D *p_link) {246DEV_ASSERT(!links.has(p_link));247248links.push_back(p_link);249iteration_dirty = true;250}251252void NavMap3D::remove_link(NavLink3D *p_link) {253if (links.erase_unordered(p_link)) {254iteration_dirty = true;255}256}257258bool NavMap3D::has_agent(NavAgent3D *agent) const {259return agents.has(agent);260}261262void NavMap3D::add_agent(NavAgent3D *agent) {263if (!has_agent(agent)) {264agents.push_back(agent);265agents_dirty = true;266}267}268269void NavMap3D::remove_agent(NavAgent3D *agent) {270remove_agent_as_controlled(agent);271if (agents.erase_unordered(agent)) {272agents_dirty = true;273}274}275276bool NavMap3D::has_obstacle(NavObstacle3D *obstacle) const {277return obstacles.has(obstacle);278}279280void NavMap3D::add_obstacle(NavObstacle3D *obstacle) {281if (obstacle->get_paused()) {282// No point in adding a paused obstacle, it will add itself when unpaused again.283return;284}285286if (!has_obstacle(obstacle)) {287obstacles.push_back(obstacle);288obstacles_dirty = true;289}290}291292void NavMap3D::remove_obstacle(NavObstacle3D *obstacle) {293if (obstacles.erase_unordered(obstacle)) {294obstacles_dirty = true;295}296}297298void NavMap3D::set_agent_as_controlled(NavAgent3D *agent) {299remove_agent_as_controlled(agent);300301if (agent->get_paused()) {302// No point in adding a paused agent, it will add itself when unpaused again.303return;304}305306if (agent->get_use_3d_avoidance()) {307int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);308if (agent_3d_index < 0) {309active_3d_avoidance_agents.push_back(agent);310agents_dirty = true;311}312} else {313int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);314if (agent_2d_index < 0) {315active_2d_avoidance_agents.push_back(agent);316agents_dirty = true;317}318}319}320321void NavMap3D::remove_agent_as_controlled(NavAgent3D *agent) {322if (active_3d_avoidance_agents.erase_unordered(agent)) {323agents_dirty = true;324}325if (active_2d_avoidance_agents.erase_unordered(agent)) {326agents_dirty = true;327}328}329330Vector3 NavMap3D::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const {331GET_MAP_ITERATION_CONST();332333return NavMeshQueries3D::map_iteration_get_random_point(map_iteration, p_navigation_layers, p_uniformly);334}335336void NavMap3D::_build_iteration() {337if (!iteration_dirty || iteration_building || iteration_ready) {338return;339}340341// Get the next free iteration slot that should be potentially unused.342iteration_slot_rwlock.read_lock();343NavMapIteration3D &next_map_iteration = iteration_slots[(iteration_slot_index + 1) % 2];344// Check if the iteration slot is truly free or still used by an external thread.345bool iteration_is_free = next_map_iteration.users.get() == 0;346iteration_slot_rwlock.read_unlock();347348if (!iteration_is_free) {349// A long running pathfinding thread or something is still reading350// from this older iteration and needs to finish first.351// Return and wait for the next sync cycle to check again.352return;353}354355// Iteration slot is free and no longer used by anything, let's build.356357iteration_dirty = false;358iteration_building = true;359iteration_ready = false;360361// We don't need to hold any lock because at this point nothing else can touch it.362// All new queries are already forwarded to the other iteration slot.363364iteration_build.reset();365366iteration_build.merge_rasterizer_cell_size = get_merge_rasterizer_cell_size();367iteration_build.use_edge_connections = get_use_edge_connections();368iteration_build.edge_connection_margin = get_edge_connection_margin();369iteration_build.link_connection_radius = get_link_connection_radius();370371next_map_iteration.clear();372373next_map_iteration.region_iterations.resize(regions.size());374next_map_iteration.link_iterations.resize(links.size());375376uint32_t region_id_count = 0;377uint32_t link_id_count = 0;378379for (NavRegion3D *region : regions) {380const Ref<NavRegionIteration3D> region_iteration = region->get_iteration();381next_map_iteration.region_iterations[region_id_count++] = region_iteration;382next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;383}384for (NavLink3D *link : links) {385const Ref<NavLinkIteration3D> link_iteration = link->get_iteration();386next_map_iteration.link_iterations[link_id_count++] = link_iteration;387}388389next_map_iteration.map_up = get_up();390391iteration_build.map_iteration = &next_map_iteration;392393if (use_async_iterations) {394iteration_build_thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMap3D::_build_iteration_threaded, &iteration_build, true, SNAME("NavMapBuilder3D"));395} else {396NavMapBuilder3D::build_navmap_iteration(iteration_build);397398iteration_building = false;399iteration_ready = true;400}401}402403void NavMap3D::_build_iteration_threaded(void *p_arg) {404NavMapIterationBuild3D *_iteration_build = static_cast<NavMapIterationBuild3D *>(p_arg);405406NavMapBuilder3D::build_navmap_iteration(*_iteration_build);407}408409void NavMap3D::_sync_iteration() {410if (iteration_building || !iteration_ready) {411return;412}413414performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;415performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;416417iteration_id = iteration_id % UINT32_MAX + 1;418419// Finally ping-pong switch the iteration slot.420iteration_slot_rwlock.write_lock();421uint32_t next_iteration_slot_index = (iteration_slot_index + 1) % 2;422iteration_slot_index = next_iteration_slot_index;423iteration_slot_rwlock.write_unlock();424425iteration_ready = false;426}427428void NavMap3D::sync() {429// Performance Monitor.430performance_data.pm_region_count = regions.size();431performance_data.pm_agent_count = agents.size();432performance_data.pm_link_count = links.size();433performance_data.pm_obstacle_count = obstacles.size();434435_sync_async_tasks();436437_sync_dirty_map_update_requests();438439if (iteration_dirty && !iteration_building && !iteration_ready) {440_build_iteration();441}442if (use_async_iterations && iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {443if (WorkerThreadPool::get_singleton()->is_task_completed(iteration_build_thread_task_id)) {444WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);445446iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;447iteration_building = false;448iteration_ready = true;449}450}451if (iteration_ready) {452_sync_iteration();453454NavigationServer3D::get_singleton()->emit_signal(SNAME("map_changed"), get_self());455}456457map_settings_dirty = false;458459_sync_avoidance();460461performance_data.pm_polygon_count = 0;462performance_data.pm_edge_count = 0;463performance_data.pm_edge_merge_count = 0;464465for (NavRegion3D *region : regions) {466performance_data.pm_polygon_count += region->get_pm_polygon_count();467performance_data.pm_edge_count += region->get_pm_edge_count();468performance_data.pm_edge_merge_count += region->get_pm_edge_merge_count();469}470}471472void NavMap3D::_sync_avoidance() {473_sync_dirty_avoidance_update_requests();474475if (obstacles_dirty || agents_dirty) {476_update_rvo_simulation();477}478479obstacles_dirty = false;480agents_dirty = false;481}482483void NavMap3D::_update_rvo_obstacles_tree_2d() {484int obstacle_vertex_count = 0;485for (NavObstacle3D *obstacle : obstacles) {486obstacle_vertex_count += obstacle->get_vertices().size();487}488489// Cleaning old obstacles.490for (size_t i = 0; i < rvo_simulation_2d.obstacles_.size(); ++i) {491delete rvo_simulation_2d.obstacles_[i];492}493rvo_simulation_2d.obstacles_.clear();494495// Cannot use LocalVector here as RVO library expects std::vector to build KdTree496std::vector<RVO2D::Obstacle2D *> &raw_obstacles = rvo_simulation_2d.obstacles_;497raw_obstacles.reserve(obstacle_vertex_count);498499// The following block is modified copy from RVO2D::AddObstacle()500// Obstacles are linked and depend on all other obstacles.501for (NavObstacle3D *obstacle : obstacles) {502if (!obstacle->is_avoidance_enabled()) {503continue;504}505const Vector3 &_obstacle_position = obstacle->get_position();506const Vector<Vector3> &_obstacle_vertices = obstacle->get_vertices();507508if (_obstacle_vertices.size() < 2) {509continue;510}511512std::vector<RVO2D::Vector2> rvo_2d_vertices;513rvo_2d_vertices.reserve(_obstacle_vertices.size());514515uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();516real_t _obstacle_height = obstacle->get_height();517518for (const Vector3 &_obstacle_vertex : _obstacle_vertices) {519#ifdef TOOLS_ENABLED520if (_obstacle_vertex.y != 0) {521WARN_PRINT_ONCE("Y coordinates of static obstacle vertices are ignored. Please use obstacle position Y to change elevation of obstacle.");522}523#endif524rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z));525}526527const size_t obstacleNo = raw_obstacles.size();528529for (size_t i = 0; i < rvo_2d_vertices.size(); i++) {530RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D();531rvo_2d_obstacle->point_ = rvo_2d_vertices[i];532rvo_2d_obstacle->height_ = _obstacle_height;533rvo_2d_obstacle->elevation_ = _obstacle_position.y;534535rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;536537if (i != 0) {538rvo_2d_obstacle->prevObstacle_ = raw_obstacles.back();539rvo_2d_obstacle->prevObstacle_->nextObstacle_ = rvo_2d_obstacle;540}541542if (i == rvo_2d_vertices.size() - 1) {543rvo_2d_obstacle->nextObstacle_ = raw_obstacles[obstacleNo];544rvo_2d_obstacle->nextObstacle_->prevObstacle_ = rvo_2d_obstacle;545}546547rvo_2d_obstacle->unitDir_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]);548549if (rvo_2d_vertices.size() == 2) {550rvo_2d_obstacle->isConvex_ = true;551} else {552rvo_2d_obstacle->isConvex_ = (leftOf(rvo_2d_vertices[(i == 0 ? rvo_2d_vertices.size() - 1 : i - 1)], rvo_2d_vertices[i], rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);553}554555rvo_2d_obstacle->id_ = raw_obstacles.size();556557raw_obstacles.push_back(rvo_2d_obstacle);558}559}560561rvo_simulation_2d.kdTree_->buildObstacleTree(raw_obstacles);562}563564void NavMap3D::_update_rvo_agents_tree_2d() {565// Cannot use LocalVector here as RVO library expects std::vector to build KdTree.566std::vector<RVO2D::Agent2D *> raw_agents;567raw_agents.reserve(active_2d_avoidance_agents.size());568for (NavAgent3D *agent : active_2d_avoidance_agents) {569raw_agents.push_back(agent->get_rvo_agent_2d());570}571rvo_simulation_2d.kdTree_->buildAgentTree(raw_agents);572}573574void NavMap3D::_update_rvo_agents_tree_3d() {575// Cannot use LocalVector here as RVO library expects std::vector to build KdTree.576std::vector<RVO3D::Agent3D *> raw_agents;577raw_agents.reserve(active_3d_avoidance_agents.size());578for (NavAgent3D *agent : active_3d_avoidance_agents) {579raw_agents.push_back(agent->get_rvo_agent_3d());580}581rvo_simulation_3d.kdTree_->buildAgentTree(raw_agents);582}583584void NavMap3D::_update_rvo_simulation() {585if (obstacles_dirty) {586_update_rvo_obstacles_tree_2d();587}588if (agents_dirty) {589_update_rvo_agents_tree_2d();590_update_rvo_agents_tree_3d();591}592}593594void NavMap3D::compute_single_avoidance_step_2d(uint32_t index, NavAgent3D **agent) {595(*(agent + index))->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);596(*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);597(*(agent + index))->get_rvo_agent_2d()->update(&rvo_simulation_2d);598(*(agent + index))->update();599}600601void NavMap3D::compute_single_avoidance_step_3d(uint32_t index, NavAgent3D **agent) {602(*(agent + index))->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);603(*(agent + index))->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);604(*(agent + index))->get_rvo_agent_3d()->update(&rvo_simulation_3d);605(*(agent + index))->update();606}607608void NavMap3D::step(double p_delta_time) {609rvo_simulation_2d.setTimeStep(float(p_delta_time));610rvo_simulation_3d.setTimeStep(float(p_delta_time));611612if (active_2d_avoidance_agents.size() > 0) {613if (use_threads && avoidance_use_multiple_threads) {614WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap3D::compute_single_avoidance_step_2d, active_2d_avoidance_agents.ptr(), active_2d_avoidance_agents.size(), -1, true, SNAME("RVOAvoidanceAgents2D"));615WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);616} else {617for (NavAgent3D *agent : active_2d_avoidance_agents) {618agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);619agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);620agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);621agent->update();622}623}624}625626if (active_3d_avoidance_agents.size() > 0) {627if (use_threads && avoidance_use_multiple_threads) {628WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap3D::compute_single_avoidance_step_3d, active_3d_avoidance_agents.ptr(), active_3d_avoidance_agents.size(), -1, true, SNAME("RVOAvoidanceAgents3D"));629WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);630} else {631for (NavAgent3D *agent : active_3d_avoidance_agents) {632agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);633agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);634agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);635agent->update();636}637}638}639}640641void NavMap3D::dispatch_callbacks() {642for (NavAgent3D *agent : active_2d_avoidance_agents) {643agent->dispatch_avoidance_callback();644}645646for (NavAgent3D *agent : active_3d_avoidance_agents) {647agent->dispatch_avoidance_callback();648}649}650651void NavMap3D::_update_merge_rasterizer_cell_dimensions() {652merge_rasterizer_cell_size.x = cell_size * merge_rasterizer_cell_scale;653merge_rasterizer_cell_size.y = cell_height * merge_rasterizer_cell_scale;654merge_rasterizer_cell_size.z = cell_size * merge_rasterizer_cell_scale;655}656657int NavMap3D::get_region_connections_count(NavRegion3D *p_region) const {658ERR_FAIL_NULL_V(p_region, 0);659660GET_MAP_ITERATION_CONST();661662HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);663if (found_id) {664HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());665if (found_connections) {666return found_connections->value.size();667}668}669670return 0;671}672673Vector3 NavMap3D::get_region_connection_pathway_start(NavRegion3D *p_region, int p_connection_id) const {674ERR_FAIL_NULL_V(p_region, Vector3());675676GET_MAP_ITERATION_CONST();677678HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);679if (found_id) {680HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());681if (found_connections) {682ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());683return found_connections->value[p_connection_id].pathway_start;684}685}686687return Vector3();688}689690Vector3 NavMap3D::get_region_connection_pathway_end(NavRegion3D *p_region, int p_connection_id) const {691ERR_FAIL_NULL_V(p_region, Vector3());692693GET_MAP_ITERATION_CONST();694695HashMap<NavRegion3D *, Ref<NavRegionIteration3D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);696if (found_id) {697HashMap<const NavBaseIteration3D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());698if (found_connections) {699ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());700return found_connections->value[p_connection_id].pathway_end;701}702}703704return Vector3();705}706707void NavMap3D::add_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request) {708if (p_sync_request->in_list()) {709return;710}711RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);712sync_dirty_requests.regions.list.add(p_sync_request);713}714715void NavMap3D::add_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {716if (p_sync_request->in_list()) {717return;718}719RWLockWrite write_lock(sync_dirty_requests.links.rwlock);720sync_dirty_requests.links.list.add(p_sync_request);721}722723void NavMap3D::add_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {724if (p_sync_request->in_list()) {725return;726}727sync_dirty_requests.agents.list.add(p_sync_request);728}729730void NavMap3D::add_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {731if (p_sync_request->in_list()) {732return;733}734sync_dirty_requests.obstacles.list.add(p_sync_request);735}736737void NavMap3D::remove_region_sync_dirty_request(SelfList<NavRegion3D> *p_sync_request) {738if (!p_sync_request->in_list()) {739return;740}741RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);742sync_dirty_requests.regions.list.remove(p_sync_request);743}744745void NavMap3D::remove_link_sync_dirty_request(SelfList<NavLink3D> *p_sync_request) {746if (!p_sync_request->in_list()) {747return;748}749RWLockWrite write_lock(sync_dirty_requests.links.rwlock);750sync_dirty_requests.links.list.remove(p_sync_request);751}752753void NavMap3D::remove_agent_sync_dirty_request(SelfList<NavAgent3D> *p_sync_request) {754if (!p_sync_request->in_list()) {755return;756}757sync_dirty_requests.agents.list.remove(p_sync_request);758}759760void NavMap3D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle3D> *p_sync_request) {761if (!p_sync_request->in_list()) {762return;763}764sync_dirty_requests.obstacles.list.remove(p_sync_request);765}766767void NavMap3D::_sync_dirty_map_update_requests() {768// If entire map settings changed make all regions dirty.769if (map_settings_dirty) {770for (NavRegion3D *region : regions) {771region->scratch_polygons();772}773iteration_dirty = true;774}775776// Sync NavRegions.777RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);778for (SelfList<NavRegion3D> *element = sync_dirty_requests.regions.list.first(); element; element = element->next()) {779bool requires_map_update = element->self()->sync();780if (requires_map_update) {781iteration_dirty = true;782}783}784sync_dirty_requests.regions.list.clear();785786// Sync NavLinks.787RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);788for (SelfList<NavLink3D> *element = sync_dirty_requests.links.list.first(); element; element = element->next()) {789bool requires_map_update = element->self()->sync();790if (requires_map_update) {791iteration_dirty = true;792}793}794sync_dirty_requests.links.list.clear();795}796797void NavMap3D::_sync_dirty_avoidance_update_requests() {798// Sync NavAgents.799if (!agents_dirty) {800agents_dirty = sync_dirty_requests.agents.list.first();801}802for (SelfList<NavAgent3D> *element = sync_dirty_requests.agents.list.first(); element; element = element->next()) {803element->self()->sync();804}805sync_dirty_requests.agents.list.clear();806807// Sync NavObstacles.808if (!obstacles_dirty) {809obstacles_dirty = sync_dirty_requests.obstacles.list.first();810}811for (SelfList<NavObstacle3D> *element = sync_dirty_requests.obstacles.list.first(); element; element = element->next()) {812element->self()->sync();813}814sync_dirty_requests.obstacles.list.clear();815}816817void NavMap3D::add_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request) {818if (p_async_request->in_list()) {819return;820}821RWLockWrite write_lock(async_dirty_requests.regions.rwlock);822async_dirty_requests.regions.list.add(p_async_request);823}824825void NavMap3D::remove_region_async_thread_join_request(SelfList<NavRegion3D> *p_async_request) {826if (!p_async_request->in_list()) {827return;828}829RWLockWrite write_lock(async_dirty_requests.regions.rwlock);830async_dirty_requests.regions.list.remove(p_async_request);831}832833void NavMap3D::_sync_async_tasks() {834// Sync NavRegions that run async thread tasks.835RWLockWrite write_lock_regions(async_dirty_requests.regions.rwlock);836for (SelfList<NavRegion3D> *element = async_dirty_requests.regions.list.first(); element; element = element->next()) {837element->self()->sync_async_tasks();838}839}840841void NavMap3D::set_use_async_iterations(bool p_enabled) {842if (use_async_iterations == p_enabled) {843return;844}845#ifdef THREADS_ENABLED846use_async_iterations = p_enabled;847#endif848}849850bool NavMap3D::get_use_async_iterations() const {851return use_async_iterations;852}853854NavMap3D::NavMap3D() {855avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");856avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");857858path_query_slots_max = GLOBAL_GET("navigation/pathfinding/max_threads");859860int processor_count = OS::get_singleton()->get_processor_count();861if (path_query_slots_max < 0) {862path_query_slots_max = processor_count;863}864if (processor_count < path_query_slots_max) {865path_query_slots_max = processor_count;866}867if (path_query_slots_max < 1) {868path_query_slots_max = 1;869}870871iteration_slots.resize(2);872873for (NavMapIteration3D &iteration_slot : iteration_slots) {874iteration_slot.path_query_slots.resize(path_query_slots_max);875for (uint32_t i = 0; i < iteration_slot.path_query_slots.size(); i++) {876iteration_slot.path_query_slots[i].slot_index = i;877}878iteration_slot.path_query_slots_semaphore.post(path_query_slots_max);879}880881#ifdef THREADS_ENABLED882use_async_iterations = GLOBAL_GET("navigation/world/map_use_async_iterations");883#else884use_async_iterations = false;885#endif886}887888NavMap3D::~NavMap3D() {889if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {890WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);891iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;892}893894RWLockWrite write_lock(iteration_slot_rwlock);895for (NavMapIteration3D &iteration_slot : iteration_slots) {896iteration_slot.clear();897}898}899900901