Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/2d/navigation/navigation_obstacle_2d.cpp
9904 views
1
/**************************************************************************/
2
/* navigation_obstacle_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 "navigation_obstacle_2d.h"
32
33
#include "core/math/geometry_2d.h"
34
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
35
#include "scene/resources/2d/navigation_polygon.h"
36
#include "scene/resources/world_2d.h"
37
#include "servers/navigation_server_2d.h"
38
39
Callable NavigationObstacle2D::_navmesh_source_geometry_parsing_callback;
40
RID NavigationObstacle2D::_navmesh_source_geometry_parser;
41
42
void NavigationObstacle2D::_bind_methods() {
43
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
44
45
ClassDB::bind_method(D_METHOD("set_avoidance_enabled", "enabled"), &NavigationObstacle2D::set_avoidance_enabled);
46
ClassDB::bind_method(D_METHOD("get_avoidance_enabled"), &NavigationObstacle2D::get_avoidance_enabled);
47
48
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle2D::set_navigation_map);
49
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle2D::get_navigation_map);
50
51
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
52
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
53
54
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle2D::set_velocity);
55
ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle2D::get_velocity);
56
57
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle2D::set_vertices);
58
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle2D::get_vertices);
59
60
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle2D::set_avoidance_layers);
61
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle2D::get_avoidance_layers);
62
63
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle2D::set_avoidance_layer_value);
64
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle2D::get_avoidance_layer_value);
65
66
ClassDB::bind_method(D_METHOD("set_affect_navigation_mesh", "enabled"), &NavigationObstacle2D::set_affect_navigation_mesh);
67
ClassDB::bind_method(D_METHOD("get_affect_navigation_mesh"), &NavigationObstacle2D::get_affect_navigation_mesh);
68
69
ClassDB::bind_method(D_METHOD("set_carve_navigation_mesh", "enabled"), &NavigationObstacle2D::set_carve_navigation_mesh);
70
ClassDB::bind_method(D_METHOD("get_carve_navigation_mesh"), &NavigationObstacle2D::get_carve_navigation_mesh);
71
72
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
73
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
74
ADD_GROUP("NavigationMesh", "");
75
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "affect_navigation_mesh"), "set_affect_navigation_mesh", "get_affect_navigation_mesh");
76
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "carve_navigation_mesh"), "set_carve_navigation_mesh", "get_carve_navigation_mesh");
77
ADD_GROUP("Avoidance", "");
78
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
79
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");
80
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
81
}
82
83
void NavigationObstacle2D::_notification(int p_what) {
84
switch (p_what) {
85
case NOTIFICATION_POST_ENTER_TREE: {
86
if (map_override.is_valid()) {
87
_update_map(map_override);
88
} else if (is_inside_tree()) {
89
_update_map(get_world_2d()->get_navigation_map());
90
} else {
91
_update_map(RID());
92
}
93
previous_transform = get_global_transform();
94
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
95
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
96
_update_transform();
97
set_physics_process_internal(true);
98
#ifdef DEBUG_ENABLED
99
RS::get_singleton()->canvas_item_set_parent(debug_canvas_item, get_world_2d()->get_canvas());
100
#endif // DEBUG_ENABLED
101
} break;
102
103
case NOTIFICATION_EXIT_TREE: {
104
set_physics_process_internal(false);
105
_update_map(RID());
106
#ifdef DEBUG_ENABLED
107
RS::get_singleton()->canvas_item_set_parent(debug_canvas_item, RID());
108
#endif // DEBUG_ENABLED
109
} break;
110
111
case NOTIFICATION_SUSPENDED:
112
case NOTIFICATION_PAUSED: {
113
if (!can_process()) {
114
map_before_pause = map_current;
115
_update_map(RID());
116
} else if (can_process() && !(map_before_pause == RID())) {
117
_update_map(map_before_pause);
118
map_before_pause = RID();
119
}
120
NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process());
121
} break;
122
123
case NOTIFICATION_UNSUSPENDED: {
124
if (get_tree()->is_paused()) {
125
break;
126
}
127
[[fallthrough]];
128
}
129
130
case NOTIFICATION_UNPAUSED: {
131
if (!can_process()) {
132
map_before_pause = map_current;
133
_update_map(RID());
134
} else if (can_process() && !(map_before_pause == RID())) {
135
_update_map(map_before_pause);
136
map_before_pause = RID();
137
}
138
NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process());
139
} break;
140
141
case NOTIFICATION_VISIBILITY_CHANGED: {
142
#ifdef DEBUG_ENABLED
143
RS::get_singleton()->canvas_item_set_visible(debug_canvas_item, is_visible_in_tree());
144
#endif // DEBUG_ENABLED
145
} break;
146
147
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
148
if (is_inside_tree()) {
149
_update_transform();
150
151
if (velocity_submitted) {
152
velocity_submitted = false;
153
// only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
154
if (!previous_velocity.is_equal_approx(velocity)) {
155
NavigationServer2D::get_singleton()->obstacle_set_velocity(obstacle, velocity);
156
}
157
previous_velocity = velocity;
158
}
159
}
160
} break;
161
162
case NOTIFICATION_DRAW: {
163
#ifdef DEBUG_ENABLED
164
if (is_inside_tree()) {
165
bool is_debug_enabled = false;
166
if (Engine::get_singleton()->is_editor_hint()) {
167
is_debug_enabled = true;
168
} else if (NavigationServer2D::get_singleton()->get_debug_enabled() && NavigationServer2D::get_singleton()->get_debug_avoidance_enabled()) {
169
is_debug_enabled = true;
170
}
171
172
if (is_debug_enabled) {
173
RS::get_singleton()->canvas_item_clear(debug_canvas_item);
174
RS::get_singleton()->canvas_item_set_transform(debug_canvas_item, Transform2D());
175
_update_fake_agent_radius_debug();
176
_update_static_obstacle_debug();
177
}
178
}
179
#endif // DEBUG_ENABLED
180
} break;
181
}
182
}
183
184
NavigationObstacle2D::NavigationObstacle2D() {
185
obstacle = NavigationServer2D::get_singleton()->obstacle_create();
186
187
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, radius);
188
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
189
NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
190
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
191
192
#ifdef DEBUG_ENABLED
193
debug_canvas_item = RenderingServer::get_singleton()->canvas_item_create();
194
debug_mesh_rid = RenderingServer::get_singleton()->mesh_create();
195
#endif // DEBUG_ENABLED
196
}
197
198
NavigationObstacle2D::~NavigationObstacle2D() {
199
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
200
201
NavigationServer2D::get_singleton()->free(obstacle);
202
obstacle = RID();
203
204
#ifdef DEBUG_ENABLED
205
if (debug_mesh_rid.is_valid()) {
206
RenderingServer::get_singleton()->free(debug_mesh_rid);
207
debug_mesh_rid = RID();
208
}
209
if (debug_canvas_item.is_valid()) {
210
RenderingServer::get_singleton()->free(debug_canvas_item);
211
debug_canvas_item = RID();
212
}
213
#endif // DEBUG_ENABLED
214
}
215
216
void NavigationObstacle2D::set_vertices(const Vector<Vector2> &p_vertices) {
217
vertices = p_vertices;
218
219
vertices_are_clockwise = !Geometry2D::is_polygon_clockwise(vertices); // Geometry2D is inverted.
220
vertices_are_valid = !Geometry2D::triangulate_polygon(vertices).is_empty();
221
222
const Transform2D node_transform = is_inside_tree() ? get_global_transform() : Transform2D();
223
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, node_transform.xform(vertices));
224
#ifdef DEBUG_ENABLED
225
queue_redraw();
226
#endif // DEBUG_ENABLED
227
}
228
229
void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
230
if (map_override == p_navigation_map) {
231
return;
232
}
233
map_override = p_navigation_map;
234
_update_map(map_override);
235
}
236
237
RID NavigationObstacle2D::get_navigation_map() const {
238
if (map_override.is_valid()) {
239
return map_override;
240
} else if (is_inside_tree()) {
241
return get_world_2d()->get_navigation_map();
242
}
243
return RID();
244
}
245
246
void NavigationObstacle2D::set_radius(real_t p_radius) {
247
ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
248
if (Math::is_equal_approx(radius, p_radius)) {
249
return;
250
}
251
252
radius = p_radius;
253
254
const Vector2 safe_scale = (is_inside_tree() ? get_global_scale() : get_scale()).abs().maxf(0.001);
255
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, safe_scale[safe_scale.max_axis_index()] * radius);
256
#ifdef DEBUG_ENABLED
257
queue_redraw();
258
#endif // DEBUG_ENABLED
259
}
260
261
void NavigationObstacle2D::set_avoidance_layers(uint32_t p_layers) {
262
if (avoidance_layers == p_layers) {
263
return;
264
}
265
avoidance_layers = p_layers;
266
NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
267
}
268
269
uint32_t NavigationObstacle2D::get_avoidance_layers() const {
270
return avoidance_layers;
271
}
272
273
void NavigationObstacle2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
274
ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
275
ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
276
uint32_t avoidance_layers_new = get_avoidance_layers();
277
if (p_value) {
278
avoidance_layers_new |= 1 << (p_layer_number - 1);
279
} else {
280
avoidance_layers_new &= ~(1 << (p_layer_number - 1));
281
}
282
set_avoidance_layers(avoidance_layers_new);
283
}
284
285
bool NavigationObstacle2D::get_avoidance_layer_value(int p_layer_number) const {
286
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
287
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
288
return get_avoidance_layers() & (1 << (p_layer_number - 1));
289
}
290
291
void NavigationObstacle2D::set_avoidance_enabled(bool p_enabled) {
292
if (avoidance_enabled == p_enabled) {
293
return;
294
}
295
296
avoidance_enabled = p_enabled;
297
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
298
#ifdef DEBUG_ENABLED
299
queue_redraw();
300
#endif // DEBUG_ENABLED
301
}
302
303
bool NavigationObstacle2D::get_avoidance_enabled() const {
304
return avoidance_enabled;
305
}
306
307
void NavigationObstacle2D::set_velocity(const Vector2 p_velocity) {
308
velocity = p_velocity;
309
velocity_submitted = true;
310
}
311
312
void NavigationObstacle2D::set_affect_navigation_mesh(bool p_enabled) {
313
affect_navigation_mesh = p_enabled;
314
}
315
316
bool NavigationObstacle2D::get_affect_navigation_mesh() const {
317
return affect_navigation_mesh;
318
}
319
320
void NavigationObstacle2D::set_carve_navigation_mesh(bool p_enabled) {
321
carve_navigation_mesh = p_enabled;
322
}
323
324
bool NavigationObstacle2D::get_carve_navigation_mesh() const {
325
return carve_navigation_mesh;
326
}
327
328
PackedStringArray NavigationObstacle2D::get_configuration_warnings() const {
329
PackedStringArray warnings = Node2D::get_configuration_warnings();
330
331
const Vector2 global_scale = get_global_scale();
332
if (global_scale.x < 0.001 || global_scale.y < 0.001) {
333
warnings.push_back(RTR("NavigationObstacle2D does not support negative or zero scaling."));
334
}
335
336
if (radius > 0.0 && !get_global_transform().is_conformal()) {
337
warnings.push_back(RTR("The agent radius can only be scaled uniformly. The largest value along the two axes of the global scale will be used to scale the radius. This value may change in unexpected ways when the node is rotated."));
338
}
339
340
if (radius > 0.0 && get_global_skew() != 0.0) {
341
warnings.push_back(RTR("Skew has no effect on the agent radius."));
342
}
343
344
return warnings;
345
}
346
347
void NavigationObstacle2D::navmesh_parse_init() {
348
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
349
if (!_navmesh_source_geometry_parser.is_valid()) {
350
_navmesh_source_geometry_parsing_callback = callable_mp_static(&NavigationObstacle2D::navmesh_parse_source_geometry);
351
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
352
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
353
}
354
}
355
356
void NavigationObstacle2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
357
NavigationObstacle2D *obstacle = Object::cast_to<NavigationObstacle2D>(p_node);
358
359
if (obstacle == nullptr) {
360
return;
361
}
362
363
if (!obstacle->get_affect_navigation_mesh()) {
364
return;
365
}
366
367
const Vector2 safe_scale = obstacle->get_global_scale().abs().maxf(0.001);
368
const float obstacle_radius = obstacle->get_radius();
369
370
if (obstacle_radius > 0.0) {
371
// Radius defined obstacle should be uniformly scaled from obstacle basis max scale axis.
372
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
373
const Vector2 uniform_max_scale = Vector2(scaling_max_value, scaling_max_value);
374
const Transform2D obstacle_circle_transform = p_source_geometry_data->root_node_transform * Transform2D(obstacle->get_global_rotation(), uniform_max_scale, 0.0, obstacle->get_global_position());
375
376
Vector<Vector2> obstruction_circle_vertices;
377
378
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
379
// Without, navigation paths can go directly through the middle of the obstacle and conflict with the avoidance to get agents stuck.
380
// No place for excessive "round" detail here. Every additional edge adds a high cost for something that needs to be quick, not pretty.
381
static const int circle_points = 12;
382
383
obstruction_circle_vertices.resize(circle_points);
384
Vector2 *circle_vertices_ptrw = obstruction_circle_vertices.ptrw();
385
const real_t circle_point_step = Math::TAU / circle_points;
386
387
for (int i = 0; i < circle_points; i++) {
388
const float angle = i * circle_point_step;
389
circle_vertices_ptrw[i] = obstacle_circle_transform.xform(Vector2(Math::cos(angle) * obstacle_radius, Math::sin(angle) * obstacle_radius));
390
}
391
392
p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_carve_navigation_mesh());
393
}
394
395
// Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account.
396
const Transform2D node_xform = p_source_geometry_data->root_node_transform * obstacle->get_global_transform();
397
398
const Vector<Vector2> &obstacle_vertices = obstacle->get_vertices();
399
400
if (obstacle_vertices.is_empty()) {
401
return;
402
}
403
404
Vector<Vector2> obstruction_shape_vertices;
405
obstruction_shape_vertices.resize(obstacle_vertices.size());
406
407
const Vector2 *obstacle_vertices_ptr = obstacle_vertices.ptr();
408
Vector2 *obstruction_shape_vertices_ptrw = obstruction_shape_vertices.ptrw();
409
410
for (int i = 0; i < obstacle_vertices.size(); i++) {
411
obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]);
412
}
413
p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_carve_navigation_mesh());
414
}
415
416
void NavigationObstacle2D::_update_map(RID p_map) {
417
map_current = p_map;
418
NavigationServer2D::get_singleton()->obstacle_set_map(obstacle, p_map);
419
}
420
421
void NavigationObstacle2D::_update_position(const Vector2 p_position) {
422
NavigationServer2D::get_singleton()->obstacle_set_position(obstacle, p_position);
423
#ifdef DEBUG_ENABLED
424
queue_redraw();
425
#endif // DEBUG_ENABLED
426
}
427
428
void NavigationObstacle2D::_update_transform() {
429
_update_position(get_global_position());
430
// Prevent non-positive or non-uniform scaling of dynamic obstacle radius.
431
const Vector2 safe_scale = get_global_scale().abs().maxf(0.001);
432
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
433
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, scaling_max_value * radius);
434
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, get_global_transform().translated(-get_global_position()).xform(vertices));
435
#ifdef DEBUG_ENABLED
436
queue_redraw();
437
#endif // DEBUG_ENABLED
438
}
439
440
#ifdef DEBUG_ENABLED
441
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
442
if (radius > 0.0 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
443
Color debug_radius_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
444
// Prevent non-positive scaling.
445
const Vector2 safe_scale = get_global_scale().abs().maxf(0.001);
446
// Agent radius is a scalar value and does not support non-uniform scaling, choose the largest axis.
447
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
448
RS::get_singleton()->canvas_item_add_circle(debug_canvas_item, get_global_position(), scaling_max_value * radius, debug_radius_color);
449
}
450
}
451
#endif // DEBUG_ENABLED
452
453
#ifdef DEBUG_ENABLED
454
void NavigationObstacle2D::_update_static_obstacle_debug() {
455
if (get_vertices().size() < 3) {
456
return;
457
}
458
459
if (!NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
460
return;
461
}
462
463
RenderingServer *rs = RenderingServer::get_singleton();
464
465
rs->mesh_clear(debug_mesh_rid);
466
467
const int vertex_count = vertices.size();
468
469
Vector<Vector2> edge_vertex_array;
470
edge_vertex_array.resize(vertex_count * 4);
471
472
Vector2 *edge_vertex_array_ptrw = edge_vertex_array.ptrw();
473
474
int vertex_index = 0;
475
476
for (int i = 0; i < vertex_count; i++) {
477
Vector2 point = vertices[i];
478
Vector2 next_point = vertices[(i + 1) % vertex_count];
479
480
Vector2 direction = next_point.direction_to(point);
481
Vector2 arrow_dir = -direction.orthogonal();
482
Vector2 edge_middle = point + ((next_point - point) * 0.5);
483
484
edge_vertex_array_ptrw[vertex_index++] = edge_middle;
485
edge_vertex_array_ptrw[vertex_index++] = edge_middle + (arrow_dir * 10.0);
486
487
edge_vertex_array_ptrw[vertex_index++] = point;
488
edge_vertex_array_ptrw[vertex_index++] = next_point;
489
}
490
491
Color debug_static_obstacle_edge_color;
492
493
if (are_vertices_valid()) {
494
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
495
} else {
496
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
497
}
498
499
Vector<Color> line_color_array;
500
line_color_array.resize(edge_vertex_array.size());
501
line_color_array.fill(debug_static_obstacle_edge_color);
502
503
Array edge_mesh_array;
504
edge_mesh_array.resize(Mesh::ARRAY_MAX);
505
edge_mesh_array[Mesh::ARRAY_VERTEX] = edge_vertex_array;
506
edge_mesh_array[Mesh::ARRAY_COLOR] = line_color_array;
507
508
rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_LINES, edge_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
509
510
rs->canvas_item_add_mesh(debug_canvas_item, debug_mesh_rid, get_global_transform());
511
}
512
#endif // DEBUG_ENABLED
513
514