1/**************************************************************************/
2/* navigation_region_3d.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 "navigation_region_3d.h"
32
33#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
34#include "servers/navigation_server_3d.h"
35
36void NavigationRegion3D::set_enabled(bool p_enabled) {
37 if (enabled == p_enabled) {
38 return;
39 }
40
41 enabled = p_enabled;
42
43 NavigationServer3D::get_singleton()->region_set_enabled(region, enabled);
44
45#ifdef DEBUG_ENABLED
46 if (debug_instance.is_valid()) {
47 if (!is_enabled()) {
48 if (debug_mesh.is_valid()) {
49 if (debug_mesh->get_surface_count() > 0) {
50 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
51 }
52 if (debug_mesh->get_surface_count() > 1) {
53 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
54 }
55 }
56 } else {
57 if (debug_mesh.is_valid()) {
58 if (debug_mesh->get_surface_count() > 0) {
59 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID());
60 }
61 if (debug_mesh->get_surface_count() > 1) {
62 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID());
63 }
64 }
65 }
66 }
67#endif // DEBUG_ENABLED
68
69 update_gizmos();
70}
71
72bool NavigationRegion3D::is_enabled() const {
73 return enabled;
74}
75
76void NavigationRegion3D::set_use_edge_connections(bool p_enabled) {
77 if (use_edge_connections == p_enabled) {
78 return;
79 }
80
81 use_edge_connections = p_enabled;
82
83 NavigationServer3D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections);
84}
85
86bool NavigationRegion3D::get_use_edge_connections() const {
87 return use_edge_connections;
88}
89
90void NavigationRegion3D::set_navigation_layers(uint32_t p_navigation_layers) {
91 if (navigation_layers == p_navigation_layers) {
92 return;
93 }
94
95 navigation_layers = p_navigation_layers;
96
97 NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
98}
99
100uint32_t NavigationRegion3D::get_navigation_layers() const {
101 return navigation_layers;
102}
103
104void NavigationRegion3D::set_navigation_layer_value(int p_layer_number, bool p_value) {
105 ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive.");
106 ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive.");
107
108 uint32_t _navigation_layers = get_navigation_layers();
109
110 if (p_value) {
111 _navigation_layers |= 1 << (p_layer_number - 1);
112 } else {
113 _navigation_layers &= ~(1 << (p_layer_number - 1));
114 }
115
116 set_navigation_layers(_navigation_layers);
117}
118
119bool NavigationRegion3D::get_navigation_layer_value(int p_layer_number) const {
120 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive.");
121 ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive.");
122
123 return get_navigation_layers() & (1 << (p_layer_number - 1));
124}
125
126void NavigationRegion3D::set_enter_cost(real_t p_enter_cost) {
127 ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive.");
128 if (Math::is_equal_approx(enter_cost, p_enter_cost)) {
129 return;
130 }
131
132 enter_cost = p_enter_cost;
133
134 NavigationServer3D::get_singleton()->region_set_enter_cost(region, enter_cost);
135}
136
137real_t NavigationRegion3D::get_enter_cost() const {
138 return enter_cost;
139}
140
141void NavigationRegion3D::set_travel_cost(real_t p_travel_cost) {
142 ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive.");
143 if (Math::is_equal_approx(travel_cost, p_travel_cost)) {
144 return;
145 }
146
147 travel_cost = p_travel_cost;
148
149 NavigationServer3D::get_singleton()->region_set_travel_cost(region, travel_cost);
150}
151
152real_t NavigationRegion3D::get_travel_cost() const {
153 return travel_cost;
154}
155
156RID NavigationRegion3D::get_region_rid() const {
157 return region;
158}
159
160void NavigationRegion3D::_notification(int p_what) {
161 switch (p_what) {
162 case NOTIFICATION_ENTER_TREE: {
163 _region_enter_navigation_map();
164 } break;
165
166 case NOTIFICATION_TRANSFORM_CHANGED: {
167 set_physics_process_internal(true);
168 } break;
169
170 case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
171 set_physics_process_internal(false);
172 _region_update_transform();
173 } break;
174
175 case NOTIFICATION_EXIT_TREE: {
176 _region_exit_navigation_map();
177 } break;
178 }
179}
180
181void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navigation_mesh) {
182 if (navigation_mesh.is_valid()) {
183 navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
184 }
185
186 navigation_mesh = p_navigation_mesh;
187
188 if (navigation_mesh.is_valid()) {
189 navigation_mesh->connect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
190 }
191
192 NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh);
193
194#ifdef DEBUG_ENABLED
195 if (is_inside_tree() && NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
196 if (navigation_mesh.is_valid()) {
197 _update_debug_mesh();
198 _update_debug_edge_connections_mesh();
199 } else {
200 if (debug_instance.is_valid()) {
201 RS::get_singleton()->instance_set_visible(debug_instance, false);
202 }
203 if (debug_edge_connections_instance.is_valid()) {
204 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
205 }
206 }
207 }
208#endif // DEBUG_ENABLED
209
210 emit_signal(SNAME("navigation_mesh_changed"));
211
212 update_gizmos();
213 update_configuration_warnings();
214}
215
216Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const {
217 return navigation_mesh;
218}
219
220void NavigationRegion3D::set_navigation_map(RID p_navigation_map) {
221 if (map_override == p_navigation_map) {
222 return;
223 }
224
225 map_override = p_navigation_map;
226
227 NavigationServer3D::get_singleton()->region_set_map(region, map_override);
228}
229
230RID NavigationRegion3D::get_navigation_map() const {
231 if (map_override.is_valid()) {
232 return map_override;
233 } else if (is_inside_tree()) {
234 return get_world_3d()->get_navigation_map();
235 }
236 return RID();
237}
238
239void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
240 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().");
241 ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
242
243 Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
244 source_geometry_data.instantiate();
245
246 NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
247
248 if (p_on_thread) {
249 NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
250 } else {
251 NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
252 }
253}
254
255void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) {
256 if (!Thread::is_main_thread()) {
257 call_deferred(SNAME("_bake_finished"), p_navigation_mesh);
258 return;
259 }
260
261 set_navigation_mesh(p_navigation_mesh);
262 emit_signal(SNAME("bake_finished"));
263}
264
265PackedStringArray NavigationRegion3D::get_configuration_warnings() const {
266 PackedStringArray warnings = Node::get_configuration_warnings();
267
268 if (is_visible_in_tree() && is_inside_tree()) {
269 if (!navigation_mesh.is_valid()) {
270 warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work."));
271 }
272 }
273
274 return warnings;
275}
276
277void NavigationRegion3D::_bind_methods() {
278 ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navigation_mesh"), &NavigationRegion3D::set_navigation_mesh);
279 ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationRegion3D::get_navigation_mesh);
280
281 ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled);
282 ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion3D::is_enabled);
283
284 ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationRegion3D::set_navigation_map);
285 ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationRegion3D::get_navigation_map);
286
287 ClassDB::bind_method(D_METHOD("set_use_edge_connections", "enabled"), &NavigationRegion3D::set_use_edge_connections);
288 ClassDB::bind_method(D_METHOD("get_use_edge_connections"), &NavigationRegion3D::get_use_edge_connections);
289
290 ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationRegion3D::set_navigation_layers);
291 ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationRegion3D::get_navigation_layers);
292
293 ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationRegion3D::set_navigation_layer_value);
294 ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationRegion3D::get_navigation_layer_value);
295
296 ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion3D::get_region_rid);
297
298 ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationRegion3D::set_enter_cost);
299 ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationRegion3D::get_enter_cost);
300
301 ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion3D::set_travel_cost);
302 ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion3D::get_travel_cost);
303
304 ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true));
305 ClassDB::bind_method(D_METHOD("_bake_finished", "navigation_mesh"), &NavigationRegion3D::_bake_finished);
306
307 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_mesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
308 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
309 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
310 ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
311 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
312 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
313
314 ADD_SIGNAL(MethodInfo("navigation_mesh_changed"));
315 ADD_SIGNAL(MethodInfo("bake_finished"));
316}
317
318#ifndef DISABLE_DEPRECATED
319// Compatibility with earlier 4.0 betas.
320bool NavigationRegion3D::_set(const StringName &p_name, const Variant &p_value) {
321 if (p_name == "navmesh") {
322 set_navigation_mesh(p_value);
323 return true;
324 }
325 return false;
326}
327
328bool NavigationRegion3D::_get(const StringName &p_name, Variant &r_ret) const {
329 if (p_name == "navmesh") {
330 r_ret = get_navigation_mesh();
331 return true;
332 }
333 return false;
334}
335#endif // DISABLE_DEPRECATED
336
337void NavigationRegion3D::_navigation_mesh_changed() {
338 update_gizmos();
339 update_configuration_warnings();
340
341#ifdef DEBUG_ENABLED
342 _update_debug_edge_connections_mesh();
343#endif // DEBUG_ENABLED
344}
345
346#ifdef DEBUG_ENABLED
347void NavigationRegion3D::_navigation_map_changed(RID p_map) {
348 if (is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) {
349 _update_debug_edge_connections_mesh();
350 }
351}
352#endif // DEBUG_ENABLED
353
354void NavigationRegion3D::_region_enter_navigation_map() {
355 if (!is_inside_tree()) {
356 return;
357 }
358
359 if (enabled) {
360 if (map_override.is_valid()) {
361 NavigationServer3D::get_singleton()->region_set_map(region, map_override);
362 } else {
363 NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
364 }
365 }
366
367 current_global_transform = get_global_transform();
368 NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
369
370#ifdef DEBUG_ENABLED
371 if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
372 _update_debug_mesh();
373 }
374#endif // DEBUG_ENABLED
375}
376
377void NavigationRegion3D::_region_exit_navigation_map() {
378 NavigationServer3D::get_singleton()->region_set_map(region, RID());
379#ifdef DEBUG_ENABLED
380 if (debug_instance.is_valid()) {
381 RS::get_singleton()->instance_set_visible(debug_instance, false);
382 }
383 if (debug_edge_connections_instance.is_valid()) {
384 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
385 }
386#endif // DEBUG_ENABLED
387}
388
389void NavigationRegion3D::_region_update_transform() {
390 if (!is_inside_tree()) {
391 return;
392 }
393
394 Transform3D new_global_transform = get_global_transform();
395 if (current_global_transform != new_global_transform) {
396 current_global_transform = new_global_transform;
397 NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
398#ifdef DEBUG_ENABLED
399 if (debug_instance.is_valid()) {
400 RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
401 }
402#endif // DEBUG_ENABLED
403 }
404}
405
406NavigationRegion3D::NavigationRegion3D() {
407 set_notify_transform(true);
408
409 region = NavigationServer3D::get_singleton()->region_create();
410 NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id());
411 NavigationServer3D::get_singleton()->region_set_enter_cost(region, get_enter_cost());
412 NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost());
413
414#ifdef DEBUG_ENABLED
415 NavigationServer3D::get_singleton()->connect(SNAME("map_changed"), callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
416 NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
417 NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
418#endif // DEBUG_ENABLED
419}
420
421NavigationRegion3D::~NavigationRegion3D() {
422 if (navigation_mesh.is_valid()) {
423 navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
424 }
425 ERR_FAIL_NULL(NavigationServer3D::get_singleton());
426 NavigationServer3D::get_singleton()->free(region);
427
428#ifdef DEBUG_ENABLED
429 NavigationServer3D::get_singleton()->disconnect(SNAME("map_changed"), callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
430 NavigationServer3D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
431 NavigationServer3D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
432
433 ERR_FAIL_NULL(RenderingServer::get_singleton());
434 if (debug_instance.is_valid()) {
435 RenderingServer::get_singleton()->free(debug_instance);
436 }
437 if (debug_mesh.is_valid()) {
438 RenderingServer::get_singleton()->free(debug_mesh->get_rid());
439 }
440 if (debug_edge_connections_instance.is_valid()) {
441 RenderingServer::get_singleton()->free(debug_edge_connections_instance);
442 }
443 if (debug_edge_connections_mesh.is_valid()) {
444 RenderingServer::get_singleton()->free(debug_edge_connections_mesh->get_rid());
445 }
446#endif // DEBUG_ENABLED
447}
448
449#ifdef DEBUG_ENABLED
450void NavigationRegion3D::_update_debug_mesh() {
451 if (Engine::get_singleton()->is_editor_hint()) {
452 // don't update inside Editor as node 3d gizmo takes care of this
453 // as collisions and selections for Editor Viewport need to be updated
454 return;
455 }
456
457 if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
458 if (debug_instance.is_valid()) {
459 RS::get_singleton()->instance_set_visible(debug_instance, false);
460 }
461 return;
462 }
463
464 if (!navigation_mesh.is_valid()) {
465 if (debug_instance.is_valid()) {
466 RS::get_singleton()->instance_set_visible(debug_instance, false);
467 }
468 return;
469 }
470
471 if (!debug_instance.is_valid()) {
472 debug_instance = RenderingServer::get_singleton()->instance_create();
473 }
474
475 if (!debug_mesh.is_valid()) {
476 debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
477 }
478
479 debug_mesh->clear_surfaces();
480
481 Vector<Vector3> vertices = navigation_mesh->get_vertices();
482 if (vertices.size() == 0) {
483 return;
484 }
485
486 int polygon_count = navigation_mesh->get_polygon_count();
487 if (polygon_count == 0) {
488 return;
489 }
490
491 bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
492 bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines();
493
494 int vertex_count = 0;
495 int line_count = 0;
496
497 for (int i = 0; i < polygon_count; i++) {
498 const Vector<int> &polygon = navigation_mesh->get_polygon(i);
499 int polygon_size = polygon.size();
500 if (polygon_size < 3) {
501 continue;
502 }
503 line_count += polygon_size * 2;
504 vertex_count += (polygon_size - 2) * 3;
505 }
506
507 Vector<Vector3> face_vertex_array;
508 face_vertex_array.resize(vertex_count);
509
510 Vector<Color> face_color_array;
511 if (enabled_geometry_face_random_color) {
512 face_color_array.resize(vertex_count);
513 }
514
515 Vector<Vector3> line_vertex_array;
516 if (enabled_edge_lines) {
517 line_vertex_array.resize(line_count);
518 }
519
520 Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
521
522 RandomPCG rand;
523 Color polygon_color = debug_navigation_geometry_face_color;
524
525 int face_vertex_index = 0;
526 int line_vertex_index = 0;
527
528 Vector3 *face_vertex_array_ptrw = face_vertex_array.ptrw();
529 Color *face_color_array_ptrw = face_color_array.ptrw();
530 Vector3 *line_vertex_array_ptrw = line_vertex_array.ptrw();
531
532 for (int polygon_index = 0; polygon_index < polygon_count; polygon_index++) {
533 const Vector<int> &polygon_indices = navigation_mesh->get_polygon(polygon_index);
534 int polygon_indices_size = polygon_indices.size();
535 if (polygon_indices_size < 3) {
536 continue;
537 }
538
539 if (enabled_geometry_face_random_color) {
540 // Generate the polygon color, slightly randomly modified from the settings one.
541 polygon_color.set_hsv(debug_navigation_geometry_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, debug_navigation_geometry_face_color.get_s(), debug_navigation_geometry_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
542 polygon_color.a = debug_navigation_geometry_face_color.a;
543 }
544
545 for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size - 2; polygon_indices_index++) {
546 face_vertex_array_ptrw[face_vertex_index] = vertices[polygon_indices[0]];
547 face_vertex_array_ptrw[face_vertex_index + 1] = vertices[polygon_indices[polygon_indices_index + 1]];
548 face_vertex_array_ptrw[face_vertex_index + 2] = vertices[polygon_indices[polygon_indices_index + 2]];
549 if (enabled_geometry_face_random_color) {
550 face_color_array_ptrw[face_vertex_index] = polygon_color;
551 face_color_array_ptrw[face_vertex_index + 1] = polygon_color;
552 face_color_array_ptrw[face_vertex_index + 2] = polygon_color;
553 }
554 face_vertex_index += 3;
555 }
556
557 if (enabled_edge_lines) {
558 for (int polygon_indices_index = 0; polygon_indices_index < polygon_indices_size; polygon_indices_index++) {
559 line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index]];
560 line_vertex_index += 1;
561 if (polygon_indices_index + 1 == polygon_indices_size) {
562 line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[0]];
563 line_vertex_index += 1;
564 } else {
565 line_vertex_array_ptrw[line_vertex_index] = vertices[polygon_indices[polygon_indices_index + 1]];
566 line_vertex_index += 1;
567 }
568 }
569 }
570 }
571
572 Ref<StandardMaterial3D> face_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_material();
573
574 Array face_mesh_array;
575 face_mesh_array.resize(Mesh::ARRAY_MAX);
576 face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
577 if (enabled_geometry_face_random_color) {
578 face_mesh_array[Mesh::ARRAY_COLOR] = face_color_array;
579 }
580 debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
581 debug_mesh->surface_set_material(0, face_material);
582
583 if (enabled_edge_lines) {
584 Ref<StandardMaterial3D> line_material = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_material();
585
586 Array line_mesh_array;
587 line_mesh_array.resize(Mesh::ARRAY_MAX);
588 line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
589 debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
590 debug_mesh->surface_set_material(1, line_material);
591 }
592
593 RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid());
594 if (is_inside_tree()) {
595 RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario());
596 RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
597 RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree());
598 }
599 if (!is_enabled()) {
600 if (debug_mesh.is_valid()) {
601 if (debug_mesh->get_surface_count() > 0) {
602 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_material()->get_rid());
603 }
604 if (debug_mesh->get_surface_count() > 1) {
605 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_material()->get_rid());
606 }
607 }
608 } else {
609 if (debug_mesh.is_valid()) {
610 if (debug_mesh->get_surface_count() > 0) {
611 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, RID());
612 }
613 if (debug_mesh->get_surface_count() > 1) {
614 RS::get_singleton()->instance_set_surface_override_material(debug_instance, 1, RID());
615 }
616 }
617 }
618}
619#endif // DEBUG_ENABLED
620
621#ifdef DEBUG_ENABLED
622void NavigationRegion3D::_update_debug_edge_connections_mesh() {
623 if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
624 if (debug_edge_connections_instance.is_valid()) {
625 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
626 }
627 return;
628 }
629
630 if (!is_inside_tree()) {
631 return;
632 }
633
634 if (!use_edge_connections || !NavigationServer3D::get_singleton()->map_get_use_edge_connections(get_world_3d()->get_navigation_map())) {
635 if (debug_edge_connections_instance.is_valid()) {
636 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
637 }
638 return;
639 }
640
641 if (!navigation_mesh.is_valid()) {
642 if (debug_edge_connections_instance.is_valid()) {
643 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
644 }
645 return;
646 }
647
648 if (!debug_edge_connections_instance.is_valid()) {
649 debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
650 }
651
652 if (!debug_edge_connections_mesh.is_valid()) {
653 debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
654 }
655
656 debug_edge_connections_mesh->clear_surfaces();
657
658 float edge_connection_margin = NavigationServer3D::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map());
659 float half_edge_connection_margin = edge_connection_margin * 0.5;
660 int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(region);
661
662 if (connections_count == 0) {
663 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
664 return;
665 }
666
667 Vector<Vector3> vertex_array;
668 vertex_array.resize(connections_count * 6);
669
670 for (int i = 0; i < connections_count; i++) {
671 Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i);
672 Vector3 connection_pathway_end = NavigationServer3D::get_singleton()->region_get_connection_pathway_end(region, i);
673
674 Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end);
675 Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start);
676
677 Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0));
678 Vector3 start_left_dir = -start_right_dir;
679
680 Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0));
681 Vector3 end_left_dir = -end_right_dir;
682
683 Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin);
684 Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin);
685 Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin);
686 Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin);
687
688 vertex_array.push_back(right_end_pos);
689 vertex_array.push_back(left_start_pos);
690 vertex_array.push_back(right_start_pos);
691
692 vertex_array.push_back(left_end_pos);
693 vertex_array.push_back(right_end_pos);
694 vertex_array.push_back(right_start_pos);
695 }
696
697 if (vertex_array.size() == 0) {
698 return;
699 }
700
701 Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connections_material();
702
703 Array mesh_array;
704 mesh_array.resize(Mesh::ARRAY_MAX);
705 mesh_array[Mesh::ARRAY_VERTEX] = vertex_array;
706
707 debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array);
708 debug_edge_connections_mesh->surface_set_material(0, edge_connections_material);
709
710 RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid());
711 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, is_visible_in_tree());
712 if (is_inside_tree()) {
713 RS::get_singleton()->instance_set_scenario(debug_edge_connections_instance, get_world_3d()->get_scenario());
714 }
715
716 bool enable_edge_connections = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections();
717 if (!enable_edge_connections) {
718 RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
719 }
720}
721#endif // DEBUG_ENABLED
722