1/**************************************************************************/
2/* godot_navigation_server.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 "godot_navigation_server.h"
32
33#ifndef _3D_DISABLED
34#include "nav_mesh_generator_3d.h"
35#endif // _3D_DISABLED
36
37#include "core/os/mutex.h"
38
39using namespace NavigationUtilities;
40
41/// Creates a struct for each function and a function that once called creates
42/// an instance of that struct with the submitted parameters.
43/// Then, that struct is stored in an array; the `sync` function consume that array.
44
45#define COMMAND_1(F_NAME, T_0, D_0) \
46 struct MERGE(F_NAME, _command) : public SetCommand { \
47 T_0 d_0; \
48 MERGE(F_NAME, _command) \
49 (T_0 p_d_0) : \
50 d_0(p_d_0) {} \
51 virtual void exec(GodotNavigationServer *server) override { \
52 server->MERGE(_cmd_, F_NAME)(d_0); \
53 } \
54 }; \
55 void GodotNavigationServer::F_NAME(T_0 D_0) { \
56 auto cmd = memnew(MERGE(F_NAME, _command)( \
57 D_0)); \
58 add_command(cmd); \
59 } \
60 void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0)
61
62#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
63 struct MERGE(F_NAME, _command) : public SetCommand { \
64 T_0 d_0; \
65 T_1 d_1; \
66 MERGE(F_NAME, _command) \
67 ( \
68 T_0 p_d_0, \
69 T_1 p_d_1) : \
70 d_0(p_d_0), \
71 d_1(p_d_1) {} \
72 virtual void exec(GodotNavigationServer *server) override { \
73 server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
74 } \
75 }; \
76 void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) { \
77 auto cmd = memnew(MERGE(F_NAME, _command)( \
78 D_0, \
79 D_1)); \
80 add_command(cmd); \
81 } \
82 void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
83
84GodotNavigationServer::GodotNavigationServer() {}
85
86GodotNavigationServer::~GodotNavigationServer() {
87 flush_queries();
88}
89
90void GodotNavigationServer::add_command(SetCommand *command) {
91 MutexLock lock(commands_mutex);
92
93 commands.push_back(command);
94}
95
96TypedArray<RID> GodotNavigationServer::get_maps() const {
97 TypedArray<RID> all_map_rids;
98 List<RID> maps_owned;
99 map_owner.get_owned_list(&maps_owned);
100 if (maps_owned.size()) {
101 for (const RID &E : maps_owned) {
102 all_map_rids.push_back(E);
103 }
104 }
105 return all_map_rids;
106}
107
108RID GodotNavigationServer::map_create() {
109 MutexLock lock(operations_mutex);
110
111 RID rid = map_owner.make_rid();
112 NavMap *map = map_owner.get_or_null(rid);
113 map->set_self(rid);
114 return rid;
115}
116
117COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
118 NavMap *map = map_owner.get_or_null(p_map);
119 ERR_FAIL_COND(map == nullptr);
120
121 if (p_active) {
122 if (!map_is_active(p_map)) {
123 active_maps.push_back(map);
124 active_maps_update_id.push_back(map->get_map_update_id());
125 }
126 } else {
127 int map_index = active_maps.find(map);
128 ERR_FAIL_COND(map_index < 0);
129 active_maps.remove_at(map_index);
130 active_maps_update_id.remove_at(map_index);
131 }
132}
133
134bool GodotNavigationServer::map_is_active(RID p_map) const {
135 NavMap *map = map_owner.get_or_null(p_map);
136 ERR_FAIL_COND_V(map == nullptr, false);
137
138 return active_maps.find(map) >= 0;
139}
140
141COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) {
142 NavMap *map = map_owner.get_or_null(p_map);
143 ERR_FAIL_COND(map == nullptr);
144
145 map->set_up(p_up);
146}
147
148Vector3 GodotNavigationServer::map_get_up(RID p_map) const {
149 const NavMap *map = map_owner.get_or_null(p_map);
150 ERR_FAIL_COND_V(map == nullptr, Vector3());
151
152 return map->get_up();
153}
154
155COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) {
156 NavMap *map = map_owner.get_or_null(p_map);
157 ERR_FAIL_COND(map == nullptr);
158
159 map->set_cell_size(p_cell_size);
160}
161
162real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
163 const NavMap *map = map_owner.get_or_null(p_map);
164 ERR_FAIL_COND_V(map == nullptr, 0);
165
166 return map->get_cell_size();
167}
168
169COMMAND_2(map_set_cell_height, RID, p_map, real_t, p_cell_height) {
170 NavMap *map = map_owner.get_or_null(p_map);
171 ERR_FAIL_COND(map == nullptr);
172
173 map->set_cell_height(p_cell_height);
174}
175
176real_t GodotNavigationServer::map_get_cell_height(RID p_map) const {
177 const NavMap *map = map_owner.get_or_null(p_map);
178 ERR_FAIL_COND_V(map == nullptr, 0);
179
180 return map->get_cell_height();
181}
182
183COMMAND_2(map_set_use_edge_connections, RID, p_map, bool, p_enabled) {
184 NavMap *map = map_owner.get_or_null(p_map);
185 ERR_FAIL_COND(map == nullptr);
186
187 map->set_use_edge_connections(p_enabled);
188}
189
190bool GodotNavigationServer::map_get_use_edge_connections(RID p_map) const {
191 NavMap *map = map_owner.get_or_null(p_map);
192 ERR_FAIL_COND_V(map == nullptr, false);
193
194 return map->get_use_edge_connections();
195}
196
197COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
198 NavMap *map = map_owner.get_or_null(p_map);
199 ERR_FAIL_COND(map == nullptr);
200
201 map->set_edge_connection_margin(p_connection_margin);
202}
203
204real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
205 const NavMap *map = map_owner.get_or_null(p_map);
206 ERR_FAIL_COND_V(map == nullptr, 0);
207
208 return map->get_edge_connection_margin();
209}
210
211COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius) {
212 NavMap *map = map_owner.get_or_null(p_map);
213 ERR_FAIL_COND(map == nullptr);
214
215 map->set_link_connection_radius(p_connection_radius);
216}
217
218real_t GodotNavigationServer::map_get_link_connection_radius(RID p_map) const {
219 const NavMap *map = map_owner.get_or_null(p_map);
220 ERR_FAIL_COND_V(map == nullptr, 0);
221
222 return map->get_link_connection_radius();
223}
224
225Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
226 const NavMap *map = map_owner.get_or_null(p_map);
227 ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
228
229 return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers, nullptr, nullptr, nullptr);
230}
231
232Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
233 const NavMap *map = map_owner.get_or_null(p_map);
234 ERR_FAIL_COND_V(map == nullptr, Vector3());
235
236 return map->get_closest_point_to_segment(p_from, p_to, p_use_collision);
237}
238
239Vector3 GodotNavigationServer::map_get_closest_point(RID p_map, const Vector3 &p_point) const {
240 const NavMap *map = map_owner.get_or_null(p_map);
241 ERR_FAIL_COND_V(map == nullptr, Vector3());
242
243 return map->get_closest_point(p_point);
244}
245
246Vector3 GodotNavigationServer::map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const {
247 const NavMap *map = map_owner.get_or_null(p_map);
248 ERR_FAIL_COND_V(map == nullptr, Vector3());
249
250 return map->get_closest_point_normal(p_point);
251}
252
253RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const {
254 const NavMap *map = map_owner.get_or_null(p_map);
255 ERR_FAIL_COND_V(map == nullptr, RID());
256
257 return map->get_closest_point_owner(p_point);
258}
259
260TypedArray<RID> GodotNavigationServer::map_get_links(RID p_map) const {
261 TypedArray<RID> link_rids;
262 const NavMap *map = map_owner.get_or_null(p_map);
263 ERR_FAIL_COND_V(map == nullptr, link_rids);
264
265 const LocalVector<NavLink *> &links = map->get_links();
266 link_rids.resize(links.size());
267
268 for (uint32_t i = 0; i < links.size(); i++) {
269 link_rids[i] = links[i]->get_self();
270 }
271 return link_rids;
272}
273
274TypedArray<RID> GodotNavigationServer::map_get_regions(RID p_map) const {
275 TypedArray<RID> regions_rids;
276 const NavMap *map = map_owner.get_or_null(p_map);
277 ERR_FAIL_COND_V(map == nullptr, regions_rids);
278
279 const LocalVector<NavRegion *> &regions = map->get_regions();
280 regions_rids.resize(regions.size());
281
282 for (uint32_t i = 0; i < regions.size(); i++) {
283 regions_rids[i] = regions[i]->get_self();
284 }
285 return regions_rids;
286}
287
288TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const {
289 TypedArray<RID> agents_rids;
290 const NavMap *map = map_owner.get_or_null(p_map);
291 ERR_FAIL_COND_V(map == nullptr, agents_rids);
292
293 const LocalVector<NavAgent *> &agents = map->get_agents();
294 agents_rids.resize(agents.size());
295
296 for (uint32_t i = 0; i < agents.size(); i++) {
297 agents_rids[i] = agents[i]->get_self();
298 }
299 return agents_rids;
300}
301
302TypedArray<RID> GodotNavigationServer::map_get_obstacles(RID p_map) const {
303 TypedArray<RID> obstacles_rids;
304 const NavMap *map = map_owner.get_or_null(p_map);
305 ERR_FAIL_COND_V(map == nullptr, obstacles_rids);
306 const LocalVector<NavObstacle *> obstacles = map->get_obstacles();
307 obstacles_rids.resize(obstacles.size());
308 for (uint32_t i = 0; i < obstacles.size(); i++) {
309 obstacles_rids[i] = obstacles[i]->get_self();
310 }
311 return obstacles_rids;
312}
313
314RID GodotNavigationServer::region_get_map(RID p_region) const {
315 NavRegion *region = region_owner.get_or_null(p_region);
316 ERR_FAIL_COND_V(region == nullptr, RID());
317
318 if (region->get_map()) {
319 return region->get_map()->get_self();
320 }
321 return RID();
322}
323
324RID GodotNavigationServer::agent_get_map(RID p_agent) const {
325 NavAgent *agent = agent_owner.get_or_null(p_agent);
326 ERR_FAIL_COND_V(agent == nullptr, RID());
327
328 if (agent->get_map()) {
329 return agent->get_map()->get_self();
330 }
331 return RID();
332}
333
334RID GodotNavigationServer::region_create() {
335 MutexLock lock(operations_mutex);
336
337 RID rid = region_owner.make_rid();
338 NavRegion *reg = region_owner.get_or_null(rid);
339 reg->set_self(rid);
340 return rid;
341}
342
343COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
344 NavRegion *region = region_owner.get_or_null(p_region);
345 ERR_FAIL_COND(region == nullptr);
346
347 region->set_enabled(p_enabled);
348}
349
350bool GodotNavigationServer::region_get_enabled(RID p_region) const {
351 const NavRegion *region = region_owner.get_or_null(p_region);
352 ERR_FAIL_COND_V(region == nullptr, false);
353
354 return region->get_enabled();
355}
356
357COMMAND_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled) {
358 NavRegion *region = region_owner.get_or_null(p_region);
359 ERR_FAIL_COND(region == nullptr);
360
361 region->set_use_edge_connections(p_enabled);
362}
363
364bool GodotNavigationServer::region_get_use_edge_connections(RID p_region) const {
365 NavRegion *region = region_owner.get_or_null(p_region);
366 ERR_FAIL_COND_V(region == nullptr, false);
367
368 return region->get_use_edge_connections();
369}
370
371COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
372 NavRegion *region = region_owner.get_or_null(p_region);
373 ERR_FAIL_COND(region == nullptr);
374
375 NavMap *map = map_owner.get_or_null(p_map);
376
377 region->set_map(map);
378}
379
380COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
381 NavRegion *region = region_owner.get_or_null(p_region);
382 ERR_FAIL_COND(region == nullptr);
383
384 region->set_transform(p_transform);
385}
386
387COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) {
388 NavRegion *region = region_owner.get_or_null(p_region);
389 ERR_FAIL_COND(region == nullptr);
390 ERR_FAIL_COND(p_enter_cost < 0.0);
391
392 region->set_enter_cost(p_enter_cost);
393}
394
395real_t GodotNavigationServer::region_get_enter_cost(RID p_region) const {
396 NavRegion *region = region_owner.get_or_null(p_region);
397 ERR_FAIL_COND_V(region == nullptr, 0);
398
399 return region->get_enter_cost();
400}
401
402COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost) {
403 NavRegion *region = region_owner.get_or_null(p_region);
404 ERR_FAIL_COND(region == nullptr);
405 ERR_FAIL_COND(p_travel_cost < 0.0);
406
407 region->set_travel_cost(p_travel_cost);
408}
409
410real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
411 NavRegion *region = region_owner.get_or_null(p_region);
412 ERR_FAIL_COND_V(region == nullptr, 0);
413
414 return region->get_travel_cost();
415}
416
417COMMAND_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id) {
418 NavRegion *region = region_owner.get_or_null(p_region);
419 ERR_FAIL_COND(region == nullptr);
420
421 region->set_owner_id(p_owner_id);
422}
423
424ObjectID GodotNavigationServer::region_get_owner_id(RID p_region) const {
425 const NavRegion *region = region_owner.get_or_null(p_region);
426 ERR_FAIL_COND_V(region == nullptr, ObjectID());
427
428 return region->get_owner_id();
429}
430
431bool GodotNavigationServer::region_owns_point(RID p_region, const Vector3 &p_point) const {
432 const NavRegion *region = region_owner.get_or_null(p_region);
433 ERR_FAIL_COND_V(region == nullptr, false);
434
435 if (region->get_map()) {
436 RID closest_point_owner = map_get_closest_point_owner(region->get_map()->get_self(), p_point);
437 return closest_point_owner == region->get_self();
438 }
439 return false;
440}
441
442COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers) {
443 NavRegion *region = region_owner.get_or_null(p_region);
444 ERR_FAIL_COND(region == nullptr);
445
446 region->set_navigation_layers(p_navigation_layers);
447}
448
449uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const {
450 NavRegion *region = region_owner.get_or_null(p_region);
451 ERR_FAIL_COND_V(region == nullptr, 0);
452
453 return region->get_navigation_layers();
454}
455
456COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh) {
457 NavRegion *region = region_owner.get_or_null(p_region);
458 ERR_FAIL_COND(region == nullptr);
459
460 region->set_mesh(p_navigation_mesh);
461}
462
463#ifndef DISABLE_DEPRECATED
464void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) {
465 ERR_FAIL_COND(p_navigation_mesh.is_null());
466 ERR_FAIL_COND(p_root_node == nullptr);
467
468 WARN_PRINT_ONCE("NavigationServer3D::region_bake_navigation_mesh() is deprecated due to core threading changes. To upgrade existing code, first create a NavigationMeshSourceGeometryData3D resource. Use this resource with method parse_source_geometry_data() to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with method bake_from_source_geometry_data() to bake a navigation mesh..");
469
470#ifndef _3D_DISABLED
471 p_navigation_mesh->clear();
472 Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
473 source_geometry_data.instantiate();
474 parse_source_geometry_data(p_navigation_mesh, source_geometry_data, p_root_node);
475 bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data);
476#endif
477}
478#endif // DISABLE_DEPRECATED
479
480int GodotNavigationServer::region_get_connections_count(RID p_region) const {
481 NavRegion *region = region_owner.get_or_null(p_region);
482 ERR_FAIL_COND_V(!region, 0);
483
484 return region->get_connections_count();
485}
486
487Vector3 GodotNavigationServer::region_get_connection_pathway_start(RID p_region, int p_connection_id) const {
488 NavRegion *region = region_owner.get_or_null(p_region);
489 ERR_FAIL_COND_V(!region, Vector3());
490
491 return region->get_connection_pathway_start(p_connection_id);
492}
493
494Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, int p_connection_id) const {
495 NavRegion *region = region_owner.get_or_null(p_region);
496 ERR_FAIL_COND_V(!region, Vector3());
497
498 return region->get_connection_pathway_end(p_connection_id);
499}
500
501RID GodotNavigationServer::link_create() {
502 MutexLock lock(operations_mutex);
503
504 RID rid = link_owner.make_rid();
505 NavLink *link = link_owner.get_or_null(rid);
506 link->set_self(rid);
507 return rid;
508}
509
510COMMAND_2(link_set_map, RID, p_link, RID, p_map) {
511 NavLink *link = link_owner.get_or_null(p_link);
512 ERR_FAIL_COND(link == nullptr);
513
514 NavMap *map = map_owner.get_or_null(p_map);
515
516 link->set_map(map);
517}
518
519RID GodotNavigationServer::link_get_map(const RID p_link) const {
520 const NavLink *link = link_owner.get_or_null(p_link);
521 ERR_FAIL_COND_V(link == nullptr, RID());
522
523 if (link->get_map()) {
524 return link->get_map()->get_self();
525 }
526 return RID();
527}
528
529COMMAND_2(link_set_enabled, RID, p_link, bool, p_enabled) {
530 NavLink *link = link_owner.get_or_null(p_link);
531 ERR_FAIL_COND(link == nullptr);
532
533 link->set_enabled(p_enabled);
534}
535
536bool GodotNavigationServer::link_get_enabled(RID p_link) const {
537 const NavLink *link = link_owner.get_or_null(p_link);
538 ERR_FAIL_COND_V(link == nullptr, false);
539
540 return link->get_enabled();
541}
542
543COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional) {
544 NavLink *link = link_owner.get_or_null(p_link);
545 ERR_FAIL_COND(link == nullptr);
546
547 link->set_bidirectional(p_bidirectional);
548}
549
550bool GodotNavigationServer::link_is_bidirectional(RID p_link) const {
551 const NavLink *link = link_owner.get_or_null(p_link);
552 ERR_FAIL_COND_V(link == nullptr, false);
553
554 return link->is_bidirectional();
555}
556
557COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers) {
558 NavLink *link = link_owner.get_or_null(p_link);
559 ERR_FAIL_COND(link == nullptr);
560
561 link->set_navigation_layers(p_navigation_layers);
562}
563
564uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) const {
565 const NavLink *link = link_owner.get_or_null(p_link);
566 ERR_FAIL_COND_V(link == nullptr, 0);
567
568 return link->get_navigation_layers();
569}
570
571COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position) {
572 NavLink *link = link_owner.get_or_null(p_link);
573 ERR_FAIL_COND(link == nullptr);
574
575 link->set_start_position(p_position);
576}
577
578Vector3 GodotNavigationServer::link_get_start_position(RID p_link) const {
579 const NavLink *link = link_owner.get_or_null(p_link);
580 ERR_FAIL_COND_V(link == nullptr, Vector3());
581
582 return link->get_start_position();
583}
584
585COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position) {
586 NavLink *link = link_owner.get_or_null(p_link);
587 ERR_FAIL_COND(link == nullptr);
588
589 link->set_end_position(p_position);
590}
591
592Vector3 GodotNavigationServer::link_get_end_position(RID p_link) const {
593 const NavLink *link = link_owner.get_or_null(p_link);
594 ERR_FAIL_COND_V(link == nullptr, Vector3());
595
596 return link->get_end_position();
597}
598
599COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) {
600 NavLink *link = link_owner.get_or_null(p_link);
601 ERR_FAIL_COND(link == nullptr);
602
603 link->set_enter_cost(p_enter_cost);
604}
605
606real_t GodotNavigationServer::link_get_enter_cost(const RID p_link) const {
607 const NavLink *link = link_owner.get_or_null(p_link);
608 ERR_FAIL_COND_V(link == nullptr, 0);
609
610 return link->get_enter_cost();
611}
612
613COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost) {
614 NavLink *link = link_owner.get_or_null(p_link);
615 ERR_FAIL_COND(link == nullptr);
616
617 link->set_travel_cost(p_travel_cost);
618}
619
620real_t GodotNavigationServer::link_get_travel_cost(const RID p_link) const {
621 const NavLink *link = link_owner.get_or_null(p_link);
622 ERR_FAIL_COND_V(link == nullptr, 0);
623
624 return link->get_travel_cost();
625}
626
627COMMAND_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id) {
628 NavLink *link = link_owner.get_or_null(p_link);
629 ERR_FAIL_COND(link == nullptr);
630
631 link->set_owner_id(p_owner_id);
632}
633
634ObjectID GodotNavigationServer::link_get_owner_id(RID p_link) const {
635 const NavLink *link = link_owner.get_or_null(p_link);
636 ERR_FAIL_COND_V(link == nullptr, ObjectID());
637
638 return link->get_owner_id();
639}
640
641RID GodotNavigationServer::agent_create() {
642 MutexLock lock(operations_mutex);
643
644 RID rid = agent_owner.make_rid();
645 NavAgent *agent = agent_owner.get_or_null(rid);
646 agent->set_self(rid);
647 return rid;
648}
649
650COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled) {
651 NavAgent *agent = agent_owner.get_or_null(p_agent);
652 ERR_FAIL_COND(agent == nullptr);
653
654 agent->set_avoidance_enabled(p_enabled);
655}
656
657bool GodotNavigationServer::agent_get_avoidance_enabled(RID p_agent) const {
658 NavAgent *agent = agent_owner.get_or_null(p_agent);
659 ERR_FAIL_COND_V(agent == nullptr, false);
660
661 return agent->is_avoidance_enabled();
662}
663
664COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled) {
665 NavAgent *agent = agent_owner.get_or_null(p_agent);
666 ERR_FAIL_COND(agent == nullptr);
667
668 agent->set_use_3d_avoidance(p_enabled);
669}
670
671bool GodotNavigationServer::agent_get_use_3d_avoidance(RID p_agent) const {
672 NavAgent *agent = agent_owner.get_or_null(p_agent);
673 ERR_FAIL_COND_V(agent == nullptr, false);
674
675 return agent->get_use_3d_avoidance();
676}
677
678COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
679 NavAgent *agent = agent_owner.get_or_null(p_agent);
680 ERR_FAIL_COND(agent == nullptr);
681
682 NavMap *map = map_owner.get_or_null(p_map);
683
684 agent->set_map(map);
685}
686
687COMMAND_2(agent_set_paused, RID, p_agent, bool, p_paused) {
688 NavAgent *agent = agent_owner.get_or_null(p_agent);
689 ERR_FAIL_COND(agent == nullptr);
690
691 agent->set_paused(p_paused);
692}
693
694bool GodotNavigationServer::agent_get_paused(RID p_agent) const {
695 NavAgent *agent = agent_owner.get_or_null(p_agent);
696 ERR_FAIL_COND_V(agent == nullptr, false);
697
698 return agent->get_paused();
699}
700
701COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) {
702 NavAgent *agent = agent_owner.get_or_null(p_agent);
703 ERR_FAIL_COND(agent == nullptr);
704
705 agent->set_neighbor_distance(p_distance);
706}
707
708COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
709 NavAgent *agent = agent_owner.get_or_null(p_agent);
710 ERR_FAIL_COND(agent == nullptr);
711
712 agent->set_max_neighbors(p_count);
713}
714
715COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon) {
716 ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
717 NavAgent *agent = agent_owner.get_or_null(p_agent);
718 ERR_FAIL_COND(agent == nullptr);
719
720 agent->set_time_horizon_agents(p_time_horizon);
721}
722
723COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon) {
724 ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
725 NavAgent *agent = agent_owner.get_or_null(p_agent);
726 ERR_FAIL_COND(agent == nullptr);
727
728 agent->set_time_horizon_obstacles(p_time_horizon);
729}
730
731COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
732 ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
733 NavAgent *agent = agent_owner.get_or_null(p_agent);
734 ERR_FAIL_COND(agent == nullptr);
735
736 agent->set_radius(p_radius);
737}
738
739COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) {
740 ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
741 NavAgent *agent = agent_owner.get_or_null(p_agent);
742 ERR_FAIL_COND(agent == nullptr);
743
744 agent->set_height(p_height);
745}
746
747COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
748 ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
749 NavAgent *agent = agent_owner.get_or_null(p_agent);
750 ERR_FAIL_COND(agent == nullptr);
751
752 agent->set_max_speed(p_max_speed);
753}
754
755COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
756 NavAgent *agent = agent_owner.get_or_null(p_agent);
757 ERR_FAIL_COND(agent == nullptr);
758
759 agent->set_velocity(p_velocity);
760}
761
762COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity) {
763 NavAgent *agent = agent_owner.get_or_null(p_agent);
764 ERR_FAIL_COND(agent == nullptr);
765
766 agent->set_velocity_forced(p_velocity);
767}
768
769COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
770 NavAgent *agent = agent_owner.get_or_null(p_agent);
771 ERR_FAIL_COND(agent == nullptr);
772
773 agent->set_position(p_position);
774}
775
776bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
777 NavAgent *agent = agent_owner.get_or_null(p_agent);
778 ERR_FAIL_COND_V(agent == nullptr, false);
779
780 return agent->is_map_changed();
781}
782
783COMMAND_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback) {
784 NavAgent *agent = agent_owner.get_or_null(p_agent);
785 ERR_FAIL_COND(agent == nullptr);
786
787 agent->set_avoidance_callback(p_callback);
788
789 if (agent->get_map()) {
790 if (p_callback.is_valid()) {
791 agent->get_map()->set_agent_as_controlled(agent);
792 } else {
793 agent->get_map()->remove_agent_as_controlled(agent);
794 }
795 }
796}
797
798COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers) {
799 NavAgent *agent = agent_owner.get_or_null(p_agent);
800 ERR_FAIL_COND(agent == nullptr);
801 agent->set_avoidance_layers(p_layers);
802}
803
804COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask) {
805 NavAgent *agent = agent_owner.get_or_null(p_agent);
806 ERR_FAIL_COND(agent == nullptr);
807 agent->set_avoidance_mask(p_mask);
808}
809
810COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) {
811 ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
812 ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
813 NavAgent *agent = agent_owner.get_or_null(p_agent);
814 ERR_FAIL_COND(agent == nullptr);
815 agent->set_avoidance_priority(p_priority);
816}
817
818RID GodotNavigationServer::obstacle_create() {
819 MutexLock lock(operations_mutex);
820
821 RID rid = obstacle_owner.make_rid();
822 NavObstacle *obstacle = obstacle_owner.get_or_null(rid);
823 obstacle->set_self(rid);
824
825 RID agent_rid = agent_owner.make_rid();
826 NavAgent *agent = agent_owner.get_or_null(agent_rid);
827 agent->set_self(agent_rid);
828
829 obstacle->set_agent(agent);
830
831 return rid;
832}
833
834COMMAND_2(obstacle_set_avoidance_enabled, RID, p_obstacle, bool, p_enabled) {
835 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
836 ERR_FAIL_COND(obstacle == nullptr);
837
838 obstacle->set_avoidance_enabled(p_enabled);
839}
840
841bool GodotNavigationServer::obstacle_get_avoidance_enabled(RID p_obstacle) const {
842 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
843 ERR_FAIL_COND_V(obstacle == nullptr, false);
844
845 return obstacle->is_avoidance_enabled();
846}
847
848COMMAND_2(obstacle_set_use_3d_avoidance, RID, p_obstacle, bool, p_enabled) {
849 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
850 ERR_FAIL_COND(obstacle == nullptr);
851
852 obstacle->set_use_3d_avoidance(p_enabled);
853}
854
855bool GodotNavigationServer::obstacle_get_use_3d_avoidance(RID p_obstacle) const {
856 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
857 ERR_FAIL_COND_V(obstacle == nullptr, false);
858
859 return obstacle->get_use_3d_avoidance();
860}
861
862COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map) {
863 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
864 ERR_FAIL_COND(obstacle == nullptr);
865
866 NavMap *map = map_owner.get_or_null(p_map);
867
868 obstacle->set_map(map);
869}
870
871RID GodotNavigationServer::obstacle_get_map(RID p_obstacle) const {
872 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
873 ERR_FAIL_COND_V(obstacle == nullptr, RID());
874 if (obstacle->get_map()) {
875 return obstacle->get_map()->get_self();
876 }
877 return RID();
878}
879
880COMMAND_2(obstacle_set_paused, RID, p_obstacle, bool, p_paused) {
881 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
882 ERR_FAIL_COND(obstacle == nullptr);
883
884 obstacle->set_paused(p_paused);
885}
886
887bool GodotNavigationServer::obstacle_get_paused(RID p_obstacle) const {
888 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
889 ERR_FAIL_COND_V(obstacle == nullptr, false);
890
891 return obstacle->get_paused();
892}
893
894COMMAND_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius) {
895 ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
896 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
897 ERR_FAIL_COND(obstacle == nullptr);
898
899 obstacle->set_radius(p_radius);
900}
901
902COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height) {
903 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
904 ERR_FAIL_COND(obstacle == nullptr);
905 obstacle->set_height(p_height);
906}
907
908COMMAND_2(obstacle_set_velocity, RID, p_obstacle, Vector3, p_velocity) {
909 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
910 ERR_FAIL_COND(obstacle == nullptr);
911
912 obstacle->set_velocity(p_velocity);
913}
914
915COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position) {
916 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
917 ERR_FAIL_COND(obstacle == nullptr);
918 obstacle->set_position(p_position);
919}
920
921void GodotNavigationServer::obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) {
922 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
923 ERR_FAIL_COND(obstacle == nullptr);
924 obstacle->set_vertices(p_vertices);
925}
926
927COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
928 NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle);
929 ERR_FAIL_COND(obstacle == nullptr);
930 obstacle->set_avoidance_layers(p_layers);
931}
932
933void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
934#ifndef _3D_DISABLED
935 ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
936 ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
937 ERR_FAIL_COND_MSG(p_root_node == nullptr, "No parsing root node specified.");
938 ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree.");
939
940 ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
941 NavMeshGenerator3D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback);
942#endif // _3D_DISABLED
943}
944
945void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
946#ifndef _3D_DISABLED
947 ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
948 ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
949
950 ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
951 NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
952#endif // _3D_DISABLED
953}
954
955void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
956#ifndef _3D_DISABLED
957 ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
958 ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
959
960 ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
961 NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
962#endif // _3D_DISABLED
963}
964
965COMMAND_1(free, RID, p_object) {
966 if (map_owner.owns(p_object)) {
967 NavMap *map = map_owner.get_or_null(p_object);
968
969 // Removes any assigned region
970 for (NavRegion *region : map->get_regions()) {
971 map->remove_region(region);
972 region->set_map(nullptr);
973 }
974
975 // Removes any assigned links
976 for (NavLink *link : map->get_links()) {
977 map->remove_link(link);
978 link->set_map(nullptr);
979 }
980
981 // Remove any assigned agent
982 for (NavAgent *agent : map->get_agents()) {
983 map->remove_agent(agent);
984 agent->set_map(nullptr);
985 }
986
987 // Remove any assigned obstacles
988 for (NavObstacle *obstacle : map->get_obstacles()) {
989 map->remove_obstacle(obstacle);
990 obstacle->set_map(nullptr);
991 }
992
993 int map_index = active_maps.find(map);
994 if (map_index >= 0) {
995 active_maps.remove_at(map_index);
996 active_maps_update_id.remove_at(map_index);
997 }
998 map_owner.free(p_object);
999
1000 } else if (region_owner.owns(p_object)) {
1001 NavRegion *region = region_owner.get_or_null(p_object);
1002
1003 // Removes this region from the map if assigned
1004 if (region->get_map() != nullptr) {
1005 region->get_map()->remove_region(region);
1006 region->set_map(nullptr);
1007 }
1008
1009 region_owner.free(p_object);
1010
1011 } else if (link_owner.owns(p_object)) {
1012 NavLink *link = link_owner.get_or_null(p_object);
1013
1014 // Removes this link from the map if assigned
1015 if (link->get_map() != nullptr) {
1016 link->get_map()->remove_link(link);
1017 link->set_map(nullptr);
1018 }
1019
1020 link_owner.free(p_object);
1021
1022 } else if (agent_owner.owns(p_object)) {
1023 internal_free_agent(p_object);
1024
1025 } else if (obstacle_owner.owns(p_object)) {
1026 internal_free_obstacle(p_object);
1027
1028 } else {
1029 ERR_PRINT("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
1030 }
1031}
1032
1033void GodotNavigationServer::internal_free_agent(RID p_object) {
1034 NavAgent *agent = agent_owner.get_or_null(p_object);
1035 if (agent) {
1036 if (agent->get_map() != nullptr) {
1037 agent->get_map()->remove_agent(agent);
1038 agent->set_map(nullptr);
1039 }
1040 agent_owner.free(p_object);
1041 }
1042}
1043
1044void GodotNavigationServer::internal_free_obstacle(RID p_object) {
1045 NavObstacle *obstacle = obstacle_owner.get_or_null(p_object);
1046 if (obstacle) {
1047 NavAgent *obstacle_agent = obstacle->get_agent();
1048 if (obstacle_agent) {
1049 RID _agent_rid = obstacle_agent->get_self();
1050 internal_free_agent(_agent_rid);
1051 obstacle->set_agent(nullptr);
1052 }
1053 if (obstacle->get_map() != nullptr) {
1054 obstacle->get_map()->remove_obstacle(obstacle);
1055 obstacle->set_map(nullptr);
1056 }
1057 obstacle_owner.free(p_object);
1058 }
1059}
1060
1061void GodotNavigationServer::set_active(bool p_active) {
1062 MutexLock lock(operations_mutex);
1063
1064 active = p_active;
1065}
1066
1067void GodotNavigationServer::flush_queries() {
1068 // In c++ we can't be sure that this is performed in the main thread
1069 // even with mutable functions.
1070 MutexLock lock(commands_mutex);
1071 MutexLock lock2(operations_mutex);
1072
1073 for (SetCommand *command : commands) {
1074 command->exec(this);
1075 memdelete(command);
1076 }
1077 commands.clear();
1078}
1079
1080void GodotNavigationServer::map_force_update(RID p_map) {
1081 NavMap *map = map_owner.get_or_null(p_map);
1082 ERR_FAIL_COND(map == nullptr);
1083
1084 flush_queries();
1085
1086 map->sync();
1087}
1088
1089void GodotNavigationServer::process(real_t p_delta_time) {
1090 flush_queries();
1091
1092 if (!active) {
1093 return;
1094 }
1095
1096#ifndef _3D_DISABLED
1097 // Sync finished navmesh bakes before doing NavMap updates.
1098 if (navmesh_generator_3d) {
1099 navmesh_generator_3d->sync();
1100 // Finished bakes emit callbacks and users might have reacted to those.
1101 // Flush queue again so users do not have to wait for the next sync.
1102 flush_queries();
1103 }
1104#endif // _3D_DISABLED
1105
1106 int _new_pm_region_count = 0;
1107 int _new_pm_agent_count = 0;
1108 int _new_pm_link_count = 0;
1109 int _new_pm_polygon_count = 0;
1110 int _new_pm_edge_count = 0;
1111 int _new_pm_edge_merge_count = 0;
1112 int _new_pm_edge_connection_count = 0;
1113 int _new_pm_edge_free_count = 0;
1114
1115 // In c++ we can't be sure that this is performed in the main thread
1116 // even with mutable functions.
1117 MutexLock lock(operations_mutex);
1118 for (uint32_t i(0); i < active_maps.size(); i++) {
1119 active_maps[i]->sync();
1120 active_maps[i]->step(p_delta_time);
1121 active_maps[i]->dispatch_callbacks();
1122
1123 _new_pm_region_count += active_maps[i]->get_pm_region_count();
1124 _new_pm_agent_count += active_maps[i]->get_pm_agent_count();
1125 _new_pm_link_count += active_maps[i]->get_pm_link_count();
1126 _new_pm_polygon_count += active_maps[i]->get_pm_polygon_count();
1127 _new_pm_edge_count += active_maps[i]->get_pm_edge_count();
1128 _new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
1129 _new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
1130 _new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
1131
1132 // Emit a signal if a map changed.
1133 const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
1134 if (new_map_update_id != active_maps_update_id[i]) {
1135 emit_signal(SNAME("map_changed"), active_maps[i]->get_self());
1136 active_maps_update_id[i] = new_map_update_id;
1137 }
1138 }
1139
1140 pm_region_count = _new_pm_region_count;
1141 pm_agent_count = _new_pm_agent_count;
1142 pm_link_count = _new_pm_link_count;
1143 pm_polygon_count = _new_pm_polygon_count;
1144 pm_edge_count = _new_pm_edge_count;
1145 pm_edge_merge_count = _new_pm_edge_merge_count;
1146 pm_edge_connection_count = _new_pm_edge_connection_count;
1147 pm_edge_free_count = _new_pm_edge_free_count;
1148}
1149
1150void GodotNavigationServer::init() {
1151#ifndef _3D_DISABLED
1152 navmesh_generator_3d = memnew(NavMeshGenerator3D);
1153#endif // _3D_DISABLED
1154}
1155
1156void GodotNavigationServer::finish() {
1157 flush_queries();
1158#ifndef _3D_DISABLED
1159 if (navmesh_generator_3d) {
1160 navmesh_generator_3d->finish();
1161 memdelete(navmesh_generator_3d);
1162 navmesh_generator_3d = nullptr;
1163 }
1164#endif // _3D_DISABLED
1165}
1166
1167PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const {
1168 PathQueryResult r_query_result;
1169
1170 const NavMap *map = map_owner.get_or_null(p_parameters.map);
1171 ERR_FAIL_COND_V(map == nullptr, r_query_result);
1172
1173 // run the pathfinding
1174
1175 if (p_parameters.pathfinding_algorithm == PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) {
1176 // while postprocessing is still part of map.get_path() need to check and route it here for the correct "optimize" post-processing
1177 if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) {
1178 r_query_result.path = map->get_path(
1179 p_parameters.start_position,
1180 p_parameters.target_position,
1181 true,
1182 p_parameters.navigation_layers,
1183 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
1184 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
1185 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
1186 } else if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) {
1187 r_query_result.path = map->get_path(
1188 p_parameters.start_position,
1189 p_parameters.target_position,
1190 false,
1191 p_parameters.navigation_layers,
1192 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
1193 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
1194 p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
1195 }
1196 } else {
1197 return r_query_result;
1198 }
1199
1200 // add path postprocessing
1201
1202 // add path stats
1203
1204 return r_query_result;
1205}
1206
1207int GodotNavigationServer::get_process_info(ProcessInfo p_info) const {
1208 switch (p_info) {
1209 case INFO_ACTIVE_MAPS: {
1210 return active_maps.size();
1211 } break;
1212 case INFO_REGION_COUNT: {
1213 return pm_region_count;
1214 } break;
1215 case INFO_AGENT_COUNT: {
1216 return pm_agent_count;
1217 } break;
1218 case INFO_LINK_COUNT: {
1219 return pm_link_count;
1220 } break;
1221 case INFO_POLYGON_COUNT: {
1222 return pm_polygon_count;
1223 } break;
1224 case INFO_EDGE_COUNT: {
1225 return pm_edge_count;
1226 } break;
1227 case INFO_EDGE_MERGE_COUNT: {
1228 return pm_edge_merge_count;
1229 } break;
1230 case INFO_EDGE_CONNECTION_COUNT: {
1231 return pm_edge_connection_count;
1232 } break;
1233 case INFO_EDGE_FREE_COUNT: {
1234 return pm_edge_free_count;
1235 } break;
1236 }
1237
1238 return 0;
1239}
1240
1241#undef COMMAND_1
1242#undef COMMAND_2
1243