Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/navigation_2d/nav_map_2d.cpp
10277 views
1
/**************************************************************************/
2
/* nav_map_2d.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "nav_map_2d.h"
32
33
#include "2d/nav_map_builder_2d.h"
34
#include "2d/nav_mesh_queries_2d.h"
35
#include "2d/nav_region_iteration_2d.h"
36
#include "nav_agent_2d.h"
37
#include "nav_link_2d.h"
38
#include "nav_obstacle_2d.h"
39
#include "nav_region_2d.h"
40
41
#include "core/config/project_settings.h"
42
#include "core/object/worker_thread_pool.h"
43
#include "servers/navigation_server_2d.h"
44
45
#include <Obstacle2d.h>
46
47
using namespace Nav2D;
48
49
#ifdef DEBUG_ENABLED
50
#define NAVMAP_ITERATION_ZERO_ERROR_MSG() \
51
ERR_PRINT_ONCE("NavigationServer navigation map query failed because it was made before first map synchronization.\n\
52
NavigationServer 'map_changed' signal can be used to receive update notifications.\n\
53
NavigationServer 'map_get_iteration_id()' can be used to check if a map has finished its newest iteration.");
54
#else
55
#define NAVMAP_ITERATION_ZERO_ERROR_MSG()
56
#endif // DEBUG_ENABLED
57
58
#define GET_MAP_ITERATION() \
59
iteration_slot_rwlock.read_lock(); \
60
NavMapIteration2D &map_iteration = iteration_slots[iteration_slot_index]; \
61
NavMapIterationRead2D iteration_read_lock(map_iteration); \
62
iteration_slot_rwlock.read_unlock();
63
64
#define GET_MAP_ITERATION_CONST() \
65
iteration_slot_rwlock.read_lock(); \
66
const NavMapIteration2D &map_iteration = iteration_slots[iteration_slot_index]; \
67
NavMapIterationRead2D iteration_read_lock(map_iteration); \
68
iteration_slot_rwlock.read_unlock();
69
70
void NavMap2D::set_cell_size(real_t p_cell_size) {
71
if (cell_size == p_cell_size) {
72
return;
73
}
74
cell_size = MAX(p_cell_size, NavigationDefaults2D::NAV_MESH_CELL_SIZE_MIN);
75
_update_merge_rasterizer_cell_dimensions();
76
map_settings_dirty = true;
77
}
78
79
void NavMap2D::set_merge_rasterizer_cell_scale(float p_value) {
80
if (merge_rasterizer_cell_scale == p_value) {
81
return;
82
}
83
merge_rasterizer_cell_scale = MAX(p_value, NavigationDefaults2D::NAV_MESH_CELL_SIZE_MIN);
84
_update_merge_rasterizer_cell_dimensions();
85
map_settings_dirty = true;
86
}
87
88
void NavMap2D::set_use_edge_connections(bool p_enabled) {
89
if (use_edge_connections == p_enabled) {
90
return;
91
}
92
use_edge_connections = p_enabled;
93
iteration_dirty = true;
94
}
95
96
void NavMap2D::set_edge_connection_margin(real_t p_edge_connection_margin) {
97
if (edge_connection_margin == p_edge_connection_margin) {
98
return;
99
}
100
edge_connection_margin = p_edge_connection_margin;
101
iteration_dirty = true;
102
}
103
104
void NavMap2D::set_link_connection_radius(real_t p_link_connection_radius) {
105
if (link_connection_radius == p_link_connection_radius) {
106
return;
107
}
108
link_connection_radius = p_link_connection_radius;
109
iteration_dirty = true;
110
}
111
112
const Vector2 &NavMap2D::get_merge_rasterizer_cell_size() const {
113
return merge_rasterizer_cell_size;
114
}
115
116
PointKey NavMap2D::get_point_key(const Vector2 &p_pos) const {
117
const int x = static_cast<int>(Math::floor(p_pos.x / merge_rasterizer_cell_size.x));
118
const int y = static_cast<int>(Math::floor(p_pos.y / merge_rasterizer_cell_size.y));
119
120
PointKey p;
121
p.key = 0;
122
p.x = x;
123
p.y = y;
124
return p;
125
}
126
127
void NavMap2D::query_path(NavMeshQueries2D::NavMeshPathQueryTask2D &p_query_task) {
128
if (iteration_id == 0) {
129
return;
130
}
131
132
GET_MAP_ITERATION();
133
134
map_iteration.path_query_slots_semaphore.wait();
135
136
map_iteration.path_query_slots_mutex.lock();
137
for (NavMeshQueries2D::PathQuerySlot &p_path_query_slot : map_iteration.path_query_slots) {
138
if (!p_path_query_slot.in_use) {
139
p_path_query_slot.in_use = true;
140
p_query_task.path_query_slot = &p_path_query_slot;
141
break;
142
}
143
}
144
map_iteration.path_query_slots_mutex.unlock();
145
146
if (p_query_task.path_query_slot == nullptr) {
147
map_iteration.path_query_slots_semaphore.post();
148
ERR_FAIL_NULL_MSG(p_query_task.path_query_slot, "No unused NavMap2D path query slot found! This should never happen :(.");
149
}
150
151
NavMeshQueries2D::query_task_map_iteration_get_path(p_query_task, map_iteration);
152
153
map_iteration.path_query_slots_mutex.lock();
154
uint32_t used_slot_index = p_query_task.path_query_slot->slot_index;
155
map_iteration.path_query_slots[used_slot_index].in_use = false;
156
p_query_task.path_query_slot = nullptr;
157
map_iteration.path_query_slots_mutex.unlock();
158
159
map_iteration.path_query_slots_semaphore.post();
160
}
161
162
Vector2 NavMap2D::get_closest_point(const Vector2 &p_point) const {
163
if (iteration_id == 0) {
164
NAVMAP_ITERATION_ZERO_ERROR_MSG();
165
return Vector2();
166
}
167
168
GET_MAP_ITERATION_CONST();
169
170
return NavMeshQueries2D::map_iteration_get_closest_point(map_iteration, p_point);
171
}
172
173
RID NavMap2D::get_closest_point_owner(const Vector2 &p_point) const {
174
if (iteration_id == 0) {
175
NAVMAP_ITERATION_ZERO_ERROR_MSG();
176
return RID();
177
}
178
179
GET_MAP_ITERATION_CONST();
180
181
return NavMeshQueries2D::map_iteration_get_closest_point_owner(map_iteration, p_point);
182
}
183
184
ClosestPointQueryResult NavMap2D::get_closest_point_info(const Vector2 &p_point) const {
185
GET_MAP_ITERATION_CONST();
186
187
return NavMeshQueries2D::map_iteration_get_closest_point_info(map_iteration, p_point);
188
}
189
190
void NavMap2D::add_region(NavRegion2D *p_region) {
191
DEV_ASSERT(!regions.has(p_region));
192
193
regions.push_back(p_region);
194
iteration_dirty = true;
195
}
196
197
void NavMap2D::remove_region(NavRegion2D *p_region) {
198
if (regions.erase_unordered(p_region)) {
199
iteration_dirty = true;
200
}
201
}
202
203
void NavMap2D::add_link(NavLink2D *p_link) {
204
DEV_ASSERT(!links.has(p_link));
205
206
links.push_back(p_link);
207
iteration_dirty = true;
208
}
209
210
void NavMap2D::remove_link(NavLink2D *p_link) {
211
if (links.erase_unordered(p_link)) {
212
iteration_dirty = true;
213
}
214
}
215
216
bool NavMap2D::has_agent(NavAgent2D *p_agent) const {
217
return agents.has(p_agent);
218
}
219
220
void NavMap2D::add_agent(NavAgent2D *p_agent) {
221
if (!has_agent(p_agent)) {
222
agents.push_back(p_agent);
223
agents_dirty = true;
224
}
225
}
226
227
void NavMap2D::remove_agent(NavAgent2D *p_agent) {
228
remove_agent_as_controlled(p_agent);
229
if (agents.erase_unordered(p_agent)) {
230
agents_dirty = true;
231
}
232
}
233
234
bool NavMap2D::has_obstacle(NavObstacle2D *p_obstacle) const {
235
return obstacles.has(p_obstacle);
236
}
237
238
void NavMap2D::add_obstacle(NavObstacle2D *p_obstacle) {
239
if (p_obstacle->get_paused()) {
240
// No point in adding a paused obstacle, it will add itself when unpaused again.
241
return;
242
}
243
244
if (!has_obstacle(p_obstacle)) {
245
obstacles.push_back(p_obstacle);
246
obstacles_dirty = true;
247
}
248
}
249
250
void NavMap2D::remove_obstacle(NavObstacle2D *p_obstacle) {
251
if (obstacles.erase_unordered(p_obstacle)) {
252
obstacles_dirty = true;
253
}
254
}
255
256
void NavMap2D::set_agent_as_controlled(NavAgent2D *p_agent) {
257
remove_agent_as_controlled(p_agent);
258
259
if (p_agent->get_paused()) {
260
// No point in adding a paused agent, it will add itself when unpaused again.
261
return;
262
}
263
264
int64_t agent_index = active_avoidance_agents.find(p_agent);
265
if (agent_index < 0) {
266
active_avoidance_agents.push_back(p_agent);
267
agents_dirty = true;
268
}
269
}
270
271
void NavMap2D::remove_agent_as_controlled(NavAgent2D *p_agent) {
272
if (active_avoidance_agents.erase_unordered(p_agent)) {
273
agents_dirty = true;
274
}
275
}
276
277
Vector2 NavMap2D::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const {
278
GET_MAP_ITERATION_CONST();
279
280
return NavMeshQueries2D::map_iteration_get_random_point(map_iteration, p_navigation_layers, p_uniformly);
281
}
282
283
void NavMap2D::_build_iteration() {
284
if (!iteration_dirty || iteration_building || iteration_ready) {
285
return;
286
}
287
288
// Get the next free iteration slot that should be potentially unused.
289
iteration_slot_rwlock.read_lock();
290
NavMapIteration2D &next_map_iteration = iteration_slots[(iteration_slot_index + 1) % 2];
291
// Check if the iteration slot is truly free or still used by an external thread.
292
bool iteration_is_free = next_map_iteration.users.get() == 0;
293
iteration_slot_rwlock.read_unlock();
294
295
if (!iteration_is_free) {
296
// A long running pathfinding thread or something is still reading
297
// from this older iteration and needs to finish first.
298
// Return and wait for the next sync cycle to check again.
299
return;
300
}
301
302
// Iteration slot is free and no longer used by anything, let's build.
303
304
iteration_dirty = false;
305
iteration_building = true;
306
iteration_ready = false;
307
308
// We don't need to hold any lock because at this point nothing else can touch it.
309
// All new queries are already forwarded to the other iteration slot.
310
311
iteration_build.reset();
312
313
iteration_build.merge_rasterizer_cell_size = get_merge_rasterizer_cell_size();
314
iteration_build.use_edge_connections = get_use_edge_connections();
315
iteration_build.edge_connection_margin = get_edge_connection_margin();
316
iteration_build.link_connection_radius = get_link_connection_radius();
317
318
next_map_iteration.clear();
319
320
next_map_iteration.region_iterations.resize(regions.size());
321
next_map_iteration.link_iterations.resize(links.size());
322
323
uint32_t region_id_count = 0;
324
uint32_t link_id_count = 0;
325
326
for (NavRegion2D *region : regions) {
327
const Ref<NavRegionIteration2D> region_iteration = region->get_iteration();
328
next_map_iteration.region_iterations[region_id_count++] = region_iteration;
329
next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;
330
}
331
for (NavLink2D *link : links) {
332
const Ref<NavLinkIteration2D> link_iteration = link->get_iteration();
333
next_map_iteration.link_iterations[link_id_count++] = link_iteration;
334
}
335
336
iteration_build.map_iteration = &next_map_iteration;
337
338
if (use_async_iterations) {
339
iteration_build_thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMap2D::_build_iteration_threaded, &iteration_build, true, SNAME("NavMapBuilder2D"));
340
} else {
341
NavMapBuilder2D::build_navmap_iteration(iteration_build);
342
343
iteration_building = false;
344
iteration_ready = true;
345
}
346
}
347
348
void NavMap2D::_build_iteration_threaded(void *p_arg) {
349
NavMapIterationBuild2D *_iteration_build = static_cast<NavMapIterationBuild2D *>(p_arg);
350
351
NavMapBuilder2D::build_navmap_iteration(*_iteration_build);
352
}
353
354
void NavMap2D::_sync_iteration() {
355
if (iteration_building || !iteration_ready) {
356
return;
357
}
358
359
performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;
360
performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;
361
362
iteration_id = iteration_id % UINT32_MAX + 1;
363
364
// Finally ping-pong switch the iteration slot.
365
iteration_slot_rwlock.write_lock();
366
uint32_t next_iteration_slot_index = (iteration_slot_index + 1) % 2;
367
iteration_slot_index = next_iteration_slot_index;
368
iteration_slot_rwlock.write_unlock();
369
370
iteration_ready = false;
371
}
372
373
void NavMap2D::sync() {
374
// Performance Monitor.
375
performance_data.pm_region_count = regions.size();
376
performance_data.pm_agent_count = agents.size();
377
performance_data.pm_link_count = links.size();
378
performance_data.pm_obstacle_count = obstacles.size();
379
380
_sync_async_tasks();
381
382
_sync_dirty_map_update_requests();
383
384
if (iteration_dirty && !iteration_building && !iteration_ready) {
385
_build_iteration();
386
}
387
if (use_async_iterations && iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
388
if (WorkerThreadPool::get_singleton()->is_task_completed(iteration_build_thread_task_id)) {
389
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
390
391
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
392
iteration_building = false;
393
iteration_ready = true;
394
}
395
}
396
if (iteration_ready) {
397
_sync_iteration();
398
399
NavigationServer2D::get_singleton()->emit_signal(SNAME("map_changed"), get_self());
400
}
401
402
map_settings_dirty = false;
403
404
_sync_avoidance();
405
406
performance_data.pm_polygon_count = 0;
407
performance_data.pm_edge_count = 0;
408
performance_data.pm_edge_merge_count = 0;
409
410
for (NavRegion2D *region : regions) {
411
performance_data.pm_polygon_count += region->get_pm_polygon_count();
412
performance_data.pm_edge_count += region->get_pm_edge_count();
413
performance_data.pm_edge_merge_count += region->get_pm_edge_merge_count();
414
}
415
}
416
417
void NavMap2D::_sync_avoidance() {
418
_sync_dirty_avoidance_update_requests();
419
420
if (obstacles_dirty || agents_dirty) {
421
_update_rvo_simulation();
422
}
423
424
obstacles_dirty = false;
425
agents_dirty = false;
426
}
427
428
void NavMap2D::_update_rvo_obstacles_tree() {
429
int obstacle_vertex_count = 0;
430
for (NavObstacle2D *obstacle : obstacles) {
431
obstacle_vertex_count += obstacle->get_vertices().size();
432
}
433
434
// Cleaning old obstacles.
435
for (size_t i = 0; i < rvo_simulation.obstacles_.size(); ++i) {
436
delete rvo_simulation.obstacles_[i];
437
}
438
rvo_simulation.obstacles_.clear();
439
440
// Cannot use LocalVector here as RVO library expects std::vector to build KdTree
441
std::vector<RVO2D::Obstacle2D *> &raw_obstacles = rvo_simulation.obstacles_;
442
raw_obstacles.reserve(obstacle_vertex_count);
443
444
// The following block is modified copy from RVO2D::AddObstacle()
445
// Obstacles are linked and depend on all other obstacles.
446
for (NavObstacle2D *obstacle : obstacles) {
447
if (!obstacle->is_avoidance_enabled()) {
448
continue;
449
}
450
const Vector2 &_obstacle_position = obstacle->get_position();
451
const Vector<Vector2> &_obstacle_vertices = obstacle->get_vertices();
452
453
if (_obstacle_vertices.size() < 2) {
454
continue;
455
}
456
457
std::vector<RVO2D::Vector2> rvo_vertices;
458
rvo_vertices.reserve(_obstacle_vertices.size());
459
460
uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();
461
462
for (const Vector2 &_obstacle_vertex : _obstacle_vertices) {
463
rvo_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.y + _obstacle_position.y));
464
}
465
466
const size_t obstacleNo = raw_obstacles.size();
467
468
for (size_t i = 0; i < rvo_vertices.size(); i++) {
469
RVO2D::Obstacle2D *rvo_obstacle = new RVO2D::Obstacle2D();
470
rvo_obstacle->point_ = rvo_vertices[i];
471
472
rvo_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;
473
474
if (i != 0) {
475
rvo_obstacle->prevObstacle_ = raw_obstacles.back();
476
rvo_obstacle->prevObstacle_->nextObstacle_ = rvo_obstacle;
477
}
478
479
if (i == rvo_vertices.size() - 1) {
480
rvo_obstacle->nextObstacle_ = raw_obstacles[obstacleNo];
481
rvo_obstacle->nextObstacle_->prevObstacle_ = rvo_obstacle;
482
}
483
484
rvo_obstacle->unitDir_ = normalize(rvo_vertices[(i == rvo_vertices.size() - 1 ? 0 : i + 1)] - rvo_vertices[i]);
485
486
if (rvo_vertices.size() == 2) {
487
rvo_obstacle->isConvex_ = true;
488
} else {
489
rvo_obstacle->isConvex_ = (leftOf(rvo_vertices[(i == 0 ? rvo_vertices.size() - 1 : i - 1)], rvo_vertices[i], rvo_vertices[(i == rvo_vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);
490
}
491
492
rvo_obstacle->id_ = raw_obstacles.size();
493
494
raw_obstacles.push_back(rvo_obstacle);
495
}
496
}
497
498
rvo_simulation.kdTree_->buildObstacleTree(raw_obstacles);
499
}
500
501
void NavMap2D::_update_rvo_agents_tree() {
502
// Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
503
std::vector<RVO2D::Agent2D *> raw_agents;
504
raw_agents.reserve(active_avoidance_agents.size());
505
for (NavAgent2D *agent : active_avoidance_agents) {
506
raw_agents.push_back(agent->get_rvo_agent());
507
}
508
rvo_simulation.kdTree_->buildAgentTree(raw_agents);
509
}
510
511
void NavMap2D::_update_rvo_simulation() {
512
if (obstacles_dirty) {
513
_update_rvo_obstacles_tree();
514
}
515
if (agents_dirty) {
516
_update_rvo_agents_tree();
517
}
518
}
519
520
void NavMap2D::compute_single_avoidance_step(uint32_t p_index, NavAgent2D **p_agent) {
521
(*(p_agent + p_index))->get_rvo_agent()->computeNeighbors(&rvo_simulation);
522
(*(p_agent + p_index))->get_rvo_agent()->computeNewVelocity(&rvo_simulation);
523
(*(p_agent + p_index))->get_rvo_agent()->update(&rvo_simulation);
524
(*(p_agent + p_index))->update();
525
}
526
527
void NavMap2D::step(double p_delta_time) {
528
rvo_simulation.setTimeStep(float(p_delta_time));
529
530
if (active_avoidance_agents.size() > 0) {
531
if (use_threads && avoidance_use_multiple_threads) {
532
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap2D::compute_single_avoidance_step, active_avoidance_agents.ptr(), active_avoidance_agents.size(), -1, true, SNAME("RVOAvoidanceAgents2D"));
533
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
534
} else {
535
for (NavAgent2D *agent : active_avoidance_agents) {
536
agent->get_rvo_agent()->computeNeighbors(&rvo_simulation);
537
agent->get_rvo_agent()->computeNewVelocity(&rvo_simulation);
538
agent->get_rvo_agent()->update(&rvo_simulation);
539
agent->update();
540
}
541
}
542
}
543
}
544
545
void NavMap2D::dispatch_callbacks() {
546
for (NavAgent2D *agent : active_avoidance_agents) {
547
agent->dispatch_avoidance_callback();
548
}
549
}
550
551
void NavMap2D::_update_merge_rasterizer_cell_dimensions() {
552
merge_rasterizer_cell_size.x = cell_size * merge_rasterizer_cell_scale;
553
merge_rasterizer_cell_size.y = cell_size * merge_rasterizer_cell_scale;
554
}
555
556
int NavMap2D::get_region_connections_count(NavRegion2D *p_region) const {
557
ERR_FAIL_NULL_V(p_region, 0);
558
559
GET_MAP_ITERATION_CONST();
560
561
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
562
if (found_id) {
563
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
564
if (found_connections) {
565
return found_connections->value.size();
566
}
567
}
568
569
return 0;
570
}
571
572
Vector2 NavMap2D::get_region_connection_pathway_start(NavRegion2D *p_region, int p_connection_id) const {
573
ERR_FAIL_NULL_V(p_region, Vector2());
574
575
GET_MAP_ITERATION_CONST();
576
577
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
578
if (found_id) {
579
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
580
if (found_connections) {
581
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
582
return found_connections->value[p_connection_id].pathway_start;
583
}
584
}
585
586
return Vector2();
587
}
588
589
Vector2 NavMap2D::get_region_connection_pathway_end(NavRegion2D *p_region, int p_connection_id) const {
590
ERR_FAIL_NULL_V(p_region, Vector2());
591
592
GET_MAP_ITERATION_CONST();
593
594
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
595
if (found_id) {
596
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
597
if (found_connections) {
598
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
599
return found_connections->value[p_connection_id].pathway_end;
600
}
601
}
602
603
return Vector2();
604
}
605
606
void NavMap2D::add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request) {
607
if (p_sync_request->in_list()) {
608
return;
609
}
610
RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
611
sync_dirty_requests.regions.list.add(p_sync_request);
612
}
613
614
void NavMap2D::add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
615
if (p_sync_request->in_list()) {
616
return;
617
}
618
RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
619
sync_dirty_requests.links.list.add(p_sync_request);
620
}
621
622
void NavMap2D::add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
623
if (p_sync_request->in_list()) {
624
return;
625
}
626
sync_dirty_requests.agents.list.add(p_sync_request);
627
}
628
629
void NavMap2D::add_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
630
if (p_sync_request->in_list()) {
631
return;
632
}
633
sync_dirty_requests.obstacles.list.add(p_sync_request);
634
}
635
636
void NavMap2D::remove_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request) {
637
if (!p_sync_request->in_list()) {
638
return;
639
}
640
RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
641
sync_dirty_requests.regions.list.remove(p_sync_request);
642
}
643
644
void NavMap2D::remove_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
645
if (!p_sync_request->in_list()) {
646
return;
647
}
648
RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
649
sync_dirty_requests.links.list.remove(p_sync_request);
650
}
651
652
void NavMap2D::remove_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
653
if (!p_sync_request->in_list()) {
654
return;
655
}
656
sync_dirty_requests.agents.list.remove(p_sync_request);
657
}
658
659
void NavMap2D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
660
if (!p_sync_request->in_list()) {
661
return;
662
}
663
sync_dirty_requests.obstacles.list.remove(p_sync_request);
664
}
665
666
void NavMap2D::_sync_dirty_map_update_requests() {
667
// If entire map settings changed make all regions dirty.
668
if (map_settings_dirty) {
669
for (NavRegion2D *region : regions) {
670
region->scratch_polygons();
671
}
672
iteration_dirty = true;
673
}
674
675
// Sync NavRegions.
676
RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);
677
for (SelfList<NavRegion2D> *element = sync_dirty_requests.regions.list.first(); element; element = element->next()) {
678
bool requires_map_update = element->self()->sync();
679
if (requires_map_update) {
680
iteration_dirty = true;
681
}
682
}
683
sync_dirty_requests.regions.list.clear();
684
685
// Sync NavLinks.
686
RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);
687
for (SelfList<NavLink2D> *element = sync_dirty_requests.links.list.first(); element; element = element->next()) {
688
bool requires_map_update = element->self()->sync();
689
if (requires_map_update) {
690
iteration_dirty = true;
691
}
692
}
693
sync_dirty_requests.links.list.clear();
694
}
695
696
void NavMap2D::_sync_dirty_avoidance_update_requests() {
697
// Sync NavAgents.
698
if (!agents_dirty) {
699
agents_dirty = sync_dirty_requests.agents.list.first();
700
}
701
for (SelfList<NavAgent2D> *element = sync_dirty_requests.agents.list.first(); element; element = element->next()) {
702
element->self()->sync();
703
}
704
sync_dirty_requests.agents.list.clear();
705
706
// Sync NavObstacles.
707
if (!obstacles_dirty) {
708
obstacles_dirty = sync_dirty_requests.obstacles.list.first();
709
}
710
for (SelfList<NavObstacle2D> *element = sync_dirty_requests.obstacles.list.first(); element; element = element->next()) {
711
element->self()->sync();
712
}
713
sync_dirty_requests.obstacles.list.clear();
714
}
715
716
void NavMap2D::add_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request) {
717
if (p_async_request->in_list()) {
718
return;
719
}
720
RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
721
async_dirty_requests.regions.list.add(p_async_request);
722
}
723
724
void NavMap2D::remove_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request) {
725
if (!p_async_request->in_list()) {
726
return;
727
}
728
RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
729
async_dirty_requests.regions.list.remove(p_async_request);
730
}
731
732
void NavMap2D::_sync_async_tasks() {
733
// Sync NavRegions that run async thread tasks.
734
RWLockWrite write_lock_regions(async_dirty_requests.regions.rwlock);
735
for (SelfList<NavRegion2D> *element = async_dirty_requests.regions.list.first(); element; element = element->next()) {
736
element->self()->sync_async_tasks();
737
}
738
}
739
740
void NavMap2D::set_use_async_iterations(bool p_enabled) {
741
if (use_async_iterations == p_enabled) {
742
return;
743
}
744
#ifdef THREADS_ENABLED
745
use_async_iterations = p_enabled;
746
#endif
747
}
748
749
bool NavMap2D::get_use_async_iterations() const {
750
return use_async_iterations;
751
}
752
753
NavMap2D::NavMap2D() {
754
avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");
755
avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");
756
757
path_query_slots_max = GLOBAL_GET("navigation/pathfinding/max_threads");
758
759
int processor_count = OS::get_singleton()->get_processor_count();
760
if (path_query_slots_max < 0) {
761
path_query_slots_max = processor_count;
762
}
763
if (processor_count < path_query_slots_max) {
764
path_query_slots_max = processor_count;
765
}
766
if (path_query_slots_max < 1) {
767
path_query_slots_max = 1;
768
}
769
770
iteration_slots.resize(2);
771
772
for (NavMapIteration2D &iteration_slot : iteration_slots) {
773
iteration_slot.path_query_slots.resize(path_query_slots_max);
774
for (uint32_t i = 0; i < iteration_slot.path_query_slots.size(); i++) {
775
iteration_slot.path_query_slots[i].slot_index = i;
776
}
777
iteration_slot.path_query_slots_semaphore.post(path_query_slots_max);
778
}
779
780
#ifdef THREADS_ENABLED
781
use_async_iterations = GLOBAL_GET("navigation/world/map_use_async_iterations");
782
#else
783
use_async_iterations = false;
784
#endif
785
}
786
787
NavMap2D::~NavMap2D() {
788
if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
789
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
790
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
791
}
792
793
RWLockWrite write_lock(iteration_slot_rwlock);
794
for (NavMapIteration2D &iteration_slot : iteration_slots) {
795
iteration_slot.clear();
796
}
797
}
798
799