1 | /**************************************************************************/ |
2 | /* navigation_region_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_region_2d.h" |
32 | |
33 | #include "core/math/geometry_2d.h" |
34 | #include "scene/2d/navigation_obstacle_2d.h" |
35 | #include "scene/resources/world_2d.h" |
36 | #include "servers/navigation_server_2d.h" |
37 | |
38 | void NavigationRegion2D::set_enabled(bool p_enabled) { |
39 | if (enabled == p_enabled) { |
40 | return; |
41 | } |
42 | |
43 | enabled = p_enabled; |
44 | |
45 | NavigationServer2D::get_singleton()->region_set_enabled(region, enabled); |
46 | |
47 | #ifdef DEBUG_ENABLED |
48 | if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_navigation_enabled()) { |
49 | queue_redraw(); |
50 | } |
51 | #endif // DEBUG_ENABLED |
52 | } |
53 | |
54 | bool NavigationRegion2D::is_enabled() const { |
55 | return enabled; |
56 | } |
57 | |
58 | void NavigationRegion2D::set_use_edge_connections(bool p_enabled) { |
59 | if (use_edge_connections == p_enabled) { |
60 | return; |
61 | } |
62 | |
63 | use_edge_connections = p_enabled; |
64 | |
65 | NavigationServer2D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections); |
66 | } |
67 | |
68 | bool NavigationRegion2D::get_use_edge_connections() const { |
69 | return use_edge_connections; |
70 | } |
71 | |
72 | void NavigationRegion2D::set_navigation_layers(uint32_t p_navigation_layers) { |
73 | if (navigation_layers == p_navigation_layers) { |
74 | return; |
75 | } |
76 | |
77 | navigation_layers = p_navigation_layers; |
78 | |
79 | NavigationServer2D::get_singleton()->region_set_navigation_layers(region, navigation_layers); |
80 | } |
81 | |
82 | uint32_t NavigationRegion2D::get_navigation_layers() const { |
83 | return navigation_layers; |
84 | } |
85 | |
86 | void NavigationRegion2D::set_navigation_layer_value(int p_layer_number, bool p_value) { |
87 | ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive." ); |
88 | ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive." ); |
89 | |
90 | uint32_t _navigation_layers = get_navigation_layers(); |
91 | |
92 | if (p_value) { |
93 | _navigation_layers |= 1 << (p_layer_number - 1); |
94 | } else { |
95 | _navigation_layers &= ~(1 << (p_layer_number - 1)); |
96 | } |
97 | |
98 | set_navigation_layers(_navigation_layers); |
99 | } |
100 | |
101 | bool NavigationRegion2D::get_navigation_layer_value(int p_layer_number) const { |
102 | ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive." ); |
103 | ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive." ); |
104 | |
105 | return get_navigation_layers() & (1 << (p_layer_number - 1)); |
106 | } |
107 | |
108 | void NavigationRegion2D::set_enter_cost(real_t p_enter_cost) { |
109 | ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive." ); |
110 | if (Math::is_equal_approx(enter_cost, p_enter_cost)) { |
111 | return; |
112 | } |
113 | |
114 | enter_cost = p_enter_cost; |
115 | |
116 | NavigationServer2D::get_singleton()->region_set_enter_cost(region, enter_cost); |
117 | } |
118 | |
119 | real_t NavigationRegion2D::get_enter_cost() const { |
120 | return enter_cost; |
121 | } |
122 | |
123 | void NavigationRegion2D::set_travel_cost(real_t p_travel_cost) { |
124 | ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive." ); |
125 | if (Math::is_equal_approx(travel_cost, p_travel_cost)) { |
126 | return; |
127 | } |
128 | |
129 | travel_cost = p_travel_cost; |
130 | |
131 | NavigationServer2D::get_singleton()->region_set_travel_cost(region, travel_cost); |
132 | } |
133 | |
134 | real_t NavigationRegion2D::get_travel_cost() const { |
135 | return travel_cost; |
136 | } |
137 | |
138 | RID NavigationRegion2D::get_region_rid() const { |
139 | return region; |
140 | } |
141 | |
142 | #ifdef TOOLS_ENABLED |
143 | Rect2 NavigationRegion2D::_edit_get_rect() const { |
144 | return navigation_polygon.is_valid() ? navigation_polygon->_edit_get_rect() : Rect2(); |
145 | } |
146 | |
147 | bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { |
148 | return navigation_polygon.is_valid() ? navigation_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; |
149 | } |
150 | #endif |
151 | |
152 | void NavigationRegion2D::_notification(int p_what) { |
153 | switch (p_what) { |
154 | case NOTIFICATION_ENTER_TREE: { |
155 | _region_enter_navigation_map(); |
156 | } break; |
157 | |
158 | case NOTIFICATION_TRANSFORM_CHANGED: { |
159 | set_physics_process_internal(true); |
160 | } break; |
161 | |
162 | case NOTIFICATION_EXIT_TREE: { |
163 | _region_exit_navigation_map(); |
164 | } break; |
165 | |
166 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
167 | set_physics_process_internal(false); |
168 | } break; |
169 | |
170 | case NOTIFICATION_DRAW: { |
171 | #ifdef DEBUG_ENABLED |
172 | if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) && navigation_polygon.is_valid()) { |
173 | _update_debug_mesh(); |
174 | _update_debug_edge_connections_mesh(); |
175 | } |
176 | #endif // DEBUG_ENABLED |
177 | } break; |
178 | } |
179 | } |
180 | |
181 | void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon) { |
182 | if (p_navigation_polygon == navigation_polygon) { |
183 | return; |
184 | } |
185 | |
186 | if (navigation_polygon.is_valid()) { |
187 | navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); |
188 | } |
189 | |
190 | navigation_polygon = p_navigation_polygon; |
191 | NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon); |
192 | |
193 | if (navigation_polygon.is_valid()) { |
194 | navigation_polygon->connect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed)); |
195 | } |
196 | _navigation_polygon_changed(); |
197 | |
198 | update_configuration_warnings(); |
199 | } |
200 | |
201 | Ref<NavigationPolygon> NavigationRegion2D::get_navigation_polygon() const { |
202 | return navigation_polygon; |
203 | } |
204 | |
205 | void NavigationRegion2D::set_navigation_map(RID p_navigation_map) { |
206 | if (map_override == p_navigation_map) { |
207 | return; |
208 | } |
209 | |
210 | map_override = p_navigation_map; |
211 | |
212 | NavigationServer2D::get_singleton()->region_set_map(region, map_override); |
213 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
214 | if (constrain_avoidance_obstacles[i].is_valid()) { |
215 | NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override); |
216 | } |
217 | } |
218 | } |
219 | |
220 | RID NavigationRegion2D::get_navigation_map() const { |
221 | if (map_override.is_valid()) { |
222 | return map_override; |
223 | } else if (is_inside_tree()) { |
224 | return get_world_2d()->get_navigation_map(); |
225 | } |
226 | return RID(); |
227 | } |
228 | |
229 | void NavigationRegion2D::_navigation_polygon_changed() { |
230 | if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { |
231 | queue_redraw(); |
232 | } |
233 | if (navigation_polygon.is_valid()) { |
234 | NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon); |
235 | } |
236 | _update_avoidance_constrain(); |
237 | } |
238 | |
239 | #ifdef DEBUG_ENABLED |
240 | void NavigationRegion2D::_navigation_map_changed(RID p_map) { |
241 | if (is_inside_tree() && get_world_2d()->get_navigation_map() == p_map) { |
242 | queue_redraw(); |
243 | } |
244 | } |
245 | #endif // DEBUG_ENABLED |
246 | |
247 | PackedStringArray NavigationRegion2D::get_configuration_warnings() const { |
248 | PackedStringArray warnings = Node2D::get_configuration_warnings(); |
249 | |
250 | if (is_visible_in_tree() && is_inside_tree()) { |
251 | if (!navigation_polygon.is_valid()) { |
252 | warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon." )); |
253 | } |
254 | } |
255 | |
256 | return warnings; |
257 | } |
258 | |
259 | void NavigationRegion2D::_bind_methods() { |
260 | ClassDB::bind_method(D_METHOD("set_navigation_polygon" , "navigation_polygon" ), &NavigationRegion2D::set_navigation_polygon); |
261 | ClassDB::bind_method(D_METHOD("get_navigation_polygon" ), &NavigationRegion2D::get_navigation_polygon); |
262 | |
263 | ClassDB::bind_method(D_METHOD("set_enabled" , "enabled" ), &NavigationRegion2D::set_enabled); |
264 | ClassDB::bind_method(D_METHOD("is_enabled" ), &NavigationRegion2D::is_enabled); |
265 | |
266 | ClassDB::bind_method(D_METHOD("set_navigation_map" , "navigation_map" ), &NavigationRegion2D::set_navigation_map); |
267 | ClassDB::bind_method(D_METHOD("get_navigation_map" ), &NavigationRegion2D::get_navigation_map); |
268 | |
269 | ClassDB::bind_method(D_METHOD("set_use_edge_connections" , "enabled" ), &NavigationRegion2D::set_use_edge_connections); |
270 | ClassDB::bind_method(D_METHOD("get_use_edge_connections" ), &NavigationRegion2D::get_use_edge_connections); |
271 | |
272 | ClassDB::bind_method(D_METHOD("set_navigation_layers" , "navigation_layers" ), &NavigationRegion2D::set_navigation_layers); |
273 | ClassDB::bind_method(D_METHOD("get_navigation_layers" ), &NavigationRegion2D::get_navigation_layers); |
274 | |
275 | ClassDB::bind_method(D_METHOD("set_navigation_layer_value" , "layer_number" , "value" ), &NavigationRegion2D::set_navigation_layer_value); |
276 | ClassDB::bind_method(D_METHOD("get_navigation_layer_value" , "layer_number" ), &NavigationRegion2D::get_navigation_layer_value); |
277 | |
278 | ClassDB::bind_method(D_METHOD("set_constrain_avoidance" , "enabled" ), &NavigationRegion2D::set_constrain_avoidance); |
279 | ClassDB::bind_method(D_METHOD("get_constrain_avoidance" ), &NavigationRegion2D::get_constrain_avoidance); |
280 | ClassDB::bind_method(D_METHOD("set_avoidance_layers" , "layers" ), &NavigationRegion2D::set_avoidance_layers); |
281 | ClassDB::bind_method(D_METHOD("get_avoidance_layers" ), &NavigationRegion2D::get_avoidance_layers); |
282 | ClassDB::bind_method(D_METHOD("set_avoidance_layer_value" , "layer_number" , "value" ), &NavigationRegion2D::set_avoidance_layer_value); |
283 | ClassDB::bind_method(D_METHOD("get_avoidance_layer_value" , "layer_number" ), &NavigationRegion2D::get_avoidance_layer_value); |
284 | |
285 | ClassDB::bind_method(D_METHOD("get_region_rid" ), &NavigationRegion2D::get_region_rid); |
286 | |
287 | ClassDB::bind_method(D_METHOD("set_enter_cost" , "enter_cost" ), &NavigationRegion2D::set_enter_cost); |
288 | ClassDB::bind_method(D_METHOD("get_enter_cost" ), &NavigationRegion2D::get_enter_cost); |
289 | |
290 | ClassDB::bind_method(D_METHOD("set_travel_cost" , "travel_cost" ), &NavigationRegion2D::set_travel_cost); |
291 | ClassDB::bind_method(D_METHOD("get_travel_cost" ), &NavigationRegion2D::get_travel_cost); |
292 | |
293 | ClassDB::bind_method(D_METHOD("_navigation_polygon_changed" ), &NavigationRegion2D::_navigation_polygon_changed); |
294 | |
295 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon" , PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon" ), "set_navigation_polygon" , "get_navigation_polygon" ); |
296 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled" ), "set_enabled" , "is_enabled" ); |
297 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections" ), "set_use_edge_connections" , "get_use_edge_connections" ); |
298 | ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers" , PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers" , "get_navigation_layers" ); |
299 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost" ), "set_enter_cost" , "get_enter_cost" ); |
300 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost" ), "set_travel_cost" , "get_travel_cost" ); |
301 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance" ), "set_constrain_avoidance" , "get_constrain_avoidance" ); |
302 | ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers" , PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers" , "get_avoidance_layers" ); |
303 | } |
304 | |
305 | #ifndef DISABLE_DEPRECATED |
306 | // Compatibility with earlier 4.0 betas. |
307 | bool NavigationRegion2D::_set(const StringName &p_name, const Variant &p_value) { |
308 | if (p_name == "navpoly" ) { |
309 | set_navigation_polygon(p_value); |
310 | return true; |
311 | } |
312 | return false; |
313 | } |
314 | |
315 | bool NavigationRegion2D::_get(const StringName &p_name, Variant &r_ret) const { |
316 | if (p_name == "navpoly" ) { |
317 | r_ret = get_navigation_polygon(); |
318 | return true; |
319 | } |
320 | return false; |
321 | } |
322 | #endif // DISABLE_DEPRECATED |
323 | |
324 | NavigationRegion2D::NavigationRegion2D() { |
325 | set_notify_transform(true); |
326 | set_hide_clip_children(true); |
327 | |
328 | region = NavigationServer2D::get_singleton()->region_create(); |
329 | NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); |
330 | NavigationServer2D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); |
331 | NavigationServer2D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); |
332 | |
333 | #ifdef DEBUG_ENABLED |
334 | NavigationServer2D::get_singleton()->connect(SNAME("map_changed" ), callable_mp(this, &NavigationRegion2D::_navigation_map_changed)); |
335 | NavigationServer2D::get_singleton()->connect(SNAME("navigation_debug_changed" ), callable_mp(this, &NavigationRegion2D::_navigation_map_changed)); |
336 | #endif // DEBUG_ENABLED |
337 | } |
338 | |
339 | NavigationRegion2D::~NavigationRegion2D() { |
340 | ERR_FAIL_NULL(NavigationServer2D::get_singleton()); |
341 | NavigationServer2D::get_singleton()->free(region); |
342 | |
343 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
344 | if (constrain_avoidance_obstacles[i].is_valid()) { |
345 | NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]); |
346 | } |
347 | } |
348 | constrain_avoidance_obstacles.clear(); |
349 | |
350 | #ifdef DEBUG_ENABLED |
351 | NavigationServer2D::get_singleton()->disconnect(SNAME("map_changed" ), callable_mp(this, &NavigationRegion2D::_navigation_map_changed)); |
352 | NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed" ), callable_mp(this, &NavigationRegion2D::_navigation_map_changed)); |
353 | #endif // DEBUG_ENABLED |
354 | } |
355 | |
356 | void NavigationRegion2D::_update_avoidance_constrain() { |
357 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
358 | if (constrain_avoidance_obstacles[i].is_valid()) { |
359 | NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]); |
360 | constrain_avoidance_obstacles[i] = RID(); |
361 | } |
362 | } |
363 | constrain_avoidance_obstacles.clear(); |
364 | |
365 | if (!constrain_avoidance) { |
366 | return; |
367 | } |
368 | |
369 | if (get_navigation_polygon() == nullptr) { |
370 | return; |
371 | } |
372 | |
373 | Ref<NavigationPolygon> _navpoly = get_navigation_polygon(); |
374 | int _outline_count = _navpoly->get_outline_count(); |
375 | if (_outline_count == 0) { |
376 | return; |
377 | } |
378 | |
379 | for (int outline_index(0); outline_index < _outline_count; outline_index++) { |
380 | const Vector<Vector2> &_outline = _navpoly->get_outline(outline_index); |
381 | |
382 | const int outline_size = _outline.size(); |
383 | if (outline_size < 3) { |
384 | ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's" ); |
385 | continue; |
386 | } |
387 | |
388 | RID obstacle_rid = NavigationServer2D::get_singleton()->obstacle_create(); |
389 | constrain_avoidance_obstacles.push_back(obstacle_rid); |
390 | |
391 | Vector<Vector2> new_obstacle_outline; |
392 | |
393 | if (outline_index == 0) { |
394 | for (int i(0); i < outline_size; i++) { |
395 | new_obstacle_outline.push_back(_outline[outline_size - i - 1]); |
396 | } |
397 | ERR_FAIL_COND_MSG(Geometry2D::is_polygon_clockwise(_outline), "Outer most outline needs to be clockwise to push avoidance agent inside" ); |
398 | } else { |
399 | for (int i(0); i < outline_size; i++) { |
400 | new_obstacle_outline.push_back(_outline[i]); |
401 | } |
402 | } |
403 | new_obstacle_outline.resize(outline_size); |
404 | |
405 | NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline); |
406 | NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers); |
407 | if (is_inside_tree()) { |
408 | if (map_override.is_valid()) { |
409 | NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, map_override); |
410 | } else { |
411 | NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map()); |
412 | } |
413 | NavigationServer2D::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position()); |
414 | } |
415 | } |
416 | constrain_avoidance_obstacles.resize(_outline_count); |
417 | } |
418 | |
419 | void NavigationRegion2D::set_constrain_avoidance(bool p_enabled) { |
420 | constrain_avoidance = p_enabled; |
421 | _update_avoidance_constrain(); |
422 | notify_property_list_changed(); |
423 | } |
424 | |
425 | bool NavigationRegion2D::get_constrain_avoidance() const { |
426 | return constrain_avoidance; |
427 | } |
428 | |
429 | void NavigationRegion2D::_validate_property(PropertyInfo &p_property) const { |
430 | if (p_property.name == "avoidance_layers" ) { |
431 | if (!constrain_avoidance) { |
432 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
433 | } |
434 | } |
435 | } |
436 | |
437 | void NavigationRegion2D::set_avoidance_layers(uint32_t p_layers) { |
438 | avoidance_layers = p_layers; |
439 | if (constrain_avoidance_obstacles.size() > 0) { |
440 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
441 | NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers); |
442 | } |
443 | } |
444 | } |
445 | |
446 | uint32_t NavigationRegion2D::get_avoidance_layers() const { |
447 | return avoidance_layers; |
448 | } |
449 | |
450 | void NavigationRegion2D::set_avoidance_layer_value(int p_layer_number, bool p_value) { |
451 | ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive." ); |
452 | ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive." ); |
453 | uint32_t avoidance_layers_new = get_avoidance_layers(); |
454 | if (p_value) { |
455 | avoidance_layers_new |= 1 << (p_layer_number - 1); |
456 | } else { |
457 | avoidance_layers_new &= ~(1 << (p_layer_number - 1)); |
458 | } |
459 | set_avoidance_layers(avoidance_layers_new); |
460 | } |
461 | |
462 | bool NavigationRegion2D::get_avoidance_layer_value(int p_layer_number) const { |
463 | ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive." ); |
464 | ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive." ); |
465 | return get_avoidance_layers() & (1 << (p_layer_number - 1)); |
466 | } |
467 | |
468 | void NavigationRegion2D::_region_enter_navigation_map() { |
469 | if (!is_inside_tree()) { |
470 | return; |
471 | } |
472 | |
473 | if (enabled) { |
474 | if (map_override.is_valid()) { |
475 | NavigationServer2D::get_singleton()->region_set_map(region, map_override); |
476 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
477 | if (constrain_avoidance_obstacles[i].is_valid()) { |
478 | NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override); |
479 | } |
480 | } |
481 | } else { |
482 | NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); |
483 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
484 | if (constrain_avoidance_obstacles[i].is_valid()) { |
485 | NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], get_world_2d()->get_navigation_map()); |
486 | } |
487 | } |
488 | } |
489 | } |
490 | |
491 | current_global_transform = get_global_transform(); |
492 | NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform); |
493 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
494 | if (constrain_avoidance_obstacles[i].is_valid()) { |
495 | NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position()); |
496 | } |
497 | } |
498 | |
499 | queue_redraw(); |
500 | } |
501 | |
502 | void NavigationRegion2D::_region_exit_navigation_map() { |
503 | NavigationServer2D::get_singleton()->region_set_map(region, RID()); |
504 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
505 | if (constrain_avoidance_obstacles[i].is_valid()) { |
506 | NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID()); |
507 | } |
508 | } |
509 | } |
510 | |
511 | void NavigationRegion2D::_region_update_transform() { |
512 | if (!is_inside_tree()) { |
513 | return; |
514 | } |
515 | |
516 | Transform2D new_global_transform = get_global_transform(); |
517 | if (current_global_transform != new_global_transform) { |
518 | current_global_transform = new_global_transform; |
519 | NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform); |
520 | for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) { |
521 | if (constrain_avoidance_obstacles[i].is_valid()) { |
522 | NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position()); |
523 | } |
524 | } |
525 | } |
526 | |
527 | queue_redraw(); |
528 | } |
529 | |
530 | #ifdef DEBUG_ENABLED |
531 | void NavigationRegion2D::_update_debug_mesh() { |
532 | Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices(); |
533 | if (navigation_polygon_vertices.size() < 3) { |
534 | return; |
535 | } |
536 | |
537 | const NavigationServer2D *ns2d = NavigationServer2D::get_singleton(); |
538 | |
539 | bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color(); |
540 | bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines(); |
541 | |
542 | Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color(); |
543 | Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color(); |
544 | |
545 | if (!enabled) { |
546 | debug_face_color = ns2d->get_debug_navigation_geometry_face_disabled_color(); |
547 | debug_edge_color = ns2d->get_debug_navigation_geometry_edge_disabled_color(); |
548 | } |
549 | |
550 | RandomPCG rand; |
551 | |
552 | for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { |
553 | // An array of vertices for this polygon. |
554 | Vector<int> polygon = navigation_polygon->get_polygon(i); |
555 | Vector<Vector2> debug_polygon_vertices; |
556 | debug_polygon_vertices.resize(polygon.size()); |
557 | for (int j = 0; j < polygon.size(); j++) { |
558 | ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); |
559 | debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]]; |
560 | } |
561 | |
562 | // Generate the polygon color, slightly randomly modified from the settings one. |
563 | Color random_variation_color = debug_face_color; |
564 | if (enabled_geometry_face_random_color) { |
565 | random_variation_color.set_hsv( |
566 | debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, |
567 | debug_face_color.get_s(), |
568 | debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); |
569 | } |
570 | random_variation_color.a = debug_face_color.a; |
571 | |
572 | Vector<Color> debug_face_colors; |
573 | debug_face_colors.push_back(random_variation_color); |
574 | RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_polygon_vertices, debug_face_colors); |
575 | |
576 | if (enabled_edge_lines) { |
577 | Vector<Color> debug_edge_colors; |
578 | debug_edge_colors.push_back(debug_edge_color); |
579 | debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline. |
580 | RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors); |
581 | } |
582 | } |
583 | } |
584 | #endif // DEBUG_ENABLED |
585 | |
586 | #ifdef DEBUG_ENABLED |
587 | void NavigationRegion2D::_update_debug_edge_connections_mesh() { |
588 | const NavigationServer2D *ns2d = NavigationServer2D::get_singleton(); |
589 | bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map()); |
590 | |
591 | if (enable_edge_connections) { |
592 | Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color(); |
593 | // Draw the region edge connections. |
594 | Transform2D xform = get_global_transform(); |
595 | real_t radius = ns2d->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0; |
596 | for (int i = 0; i < ns2d->region_get_connections_count(region); i++) { |
597 | // Two main points |
598 | Vector2 a = ns2d->region_get_connection_pathway_start(region, i); |
599 | a = xform.affine_inverse().xform(a); |
600 | Vector2 b = ns2d->region_get_connection_pathway_end(region, i); |
601 | b = xform.affine_inverse().xform(b); |
602 | draw_line(a, b, debug_edge_connection_color); |
603 | |
604 | // Draw a circle to illustrate the margins. |
605 | real_t angle = a.angle_to_point(b); |
606 | draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, debug_edge_connection_color); |
607 | draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, debug_edge_connection_color); |
608 | } |
609 | } |
610 | } |
611 | #endif // DEBUG_ENABLED |
612 | |