Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/servers/test_navigation_server_3d.cpp
23449 views
1
/**************************************************************************/
2
/* test_navigation_server_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 "tests/test_macros.h"
32
33
TEST_FORCE_LINK(test_navigation_server_3d)
34
35
#include "modules/modules_enabled.gen.h" // For navigation 3D.
36
37
#ifdef MODULE_NAVIGATION_3D_ENABLED
38
39
#include "scene/3d/mesh_instance_3d.h"
40
#include "scene/main/window.h"
41
#include "scene/resources/3d/primitive_meshes.h"
42
#include "servers/navigation_3d/navigation_server_3d.h"
43
#include "tests/signal_watcher.h"
44
45
namespace TestNavigationServer3D {
46
47
// TODO: Find a more generic way to create `Callable` mocks.
48
class CallableMock : public Object {
49
GDCLASS(CallableMock, Object);
50
51
public:
52
void function1(Variant arg0) {
53
function1_calls++;
54
function1_latest_arg0 = arg0;
55
}
56
57
unsigned function1_calls{ 0 };
58
Variant function1_latest_arg0;
59
};
60
61
TEST_SUITE("[Navigation3D]") {
62
TEST_CASE("[NavigationServer3D] Server should be empty when initialized") {
63
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
64
CHECK_EQ(navigation_server->get_maps().size(), 0);
65
66
SUBCASE("'ProcessInfo' should report all counters empty as well") {
67
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS), 0);
68
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 0);
69
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 0);
70
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 0);
71
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_POLYGON_COUNT), 0);
72
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_EDGE_COUNT), 0);
73
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_EDGE_MERGE_COUNT), 0);
74
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT), 0);
75
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT), 0);
76
}
77
}
78
79
TEST_CASE("[NavigationServer3D] Server should manage agent properly") {
80
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
81
82
RID agent = navigation_server->agent_create();
83
CHECK(agent.is_valid());
84
85
SUBCASE("'ProcessInfo' should not report dangling agent") {
86
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 0);
87
}
88
89
SUBCASE("Setters/getters should work") {
90
bool initial_use_3d_avoidance = navigation_server->agent_get_use_3d_avoidance(agent);
91
navigation_server->agent_set_use_3d_avoidance(agent, !initial_use_3d_avoidance);
92
navigation_server->physics_process(0.0); // Give server some cycles to commit.
93
94
CHECK_EQ(navigation_server->agent_get_use_3d_avoidance(agent), !initial_use_3d_avoidance);
95
// TODO: Add remaining setters/getters once the missing getters are added.
96
}
97
98
SUBCASE("'ProcessInfo' should report agent with active map") {
99
RID map = navigation_server->map_create();
100
CHECK(map.is_valid());
101
navigation_server->map_set_active(map, true);
102
navigation_server->agent_set_map(agent, map);
103
navigation_server->physics_process(0.0); // Give server some cycles to commit.
104
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 1);
105
navigation_server->agent_set_map(agent, RID());
106
navigation_server->free_rid(map);
107
navigation_server->physics_process(0.0); // Give server some cycles to commit.
108
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 0);
109
}
110
111
navigation_server->free_rid(agent);
112
}
113
114
TEST_CASE("[NavigationServer3D] Server should manage map properly") {
115
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
116
117
RID map;
118
CHECK_FALSE(map.is_valid());
119
120
SUBCASE("Queries against invalid map should return empty or invalid values") {
121
ERR_PRINT_OFF;
122
CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
123
CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
124
CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
125
CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true), Vector3());
126
CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false), Vector3());
127
CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true).size(), 0);
128
CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false).size(), 0);
129
130
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
131
query_parameters->set_map(map);
132
query_parameters->set_start_position(Vector3(7, 7, 7));
133
query_parameters->set_target_position(Vector3(8, 8, 8));
134
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
135
navigation_server->query_path(query_parameters, query_result);
136
CHECK_EQ(query_result->get_path().size(), 0);
137
CHECK_EQ(query_result->get_path_types().size(), 0);
138
CHECK_EQ(query_result->get_path_rids().size(), 0);
139
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
140
ERR_PRINT_ON;
141
}
142
143
map = navigation_server->map_create();
144
CHECK(map.is_valid());
145
CHECK_EQ(navigation_server->get_maps().size(), 1);
146
147
SUBCASE("'ProcessInfo' should not report inactive map") {
148
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS), 0);
149
}
150
151
SUBCASE("Setters/getters should work") {
152
navigation_server->map_set_cell_size(map, 0.55);
153
navigation_server->map_set_edge_connection_margin(map, 0.66);
154
navigation_server->map_set_link_connection_radius(map, 0.77);
155
navigation_server->map_set_up(map, Vector3(1, 0, 0));
156
bool initial_use_edge_connections = navigation_server->map_get_use_edge_connections(map);
157
navigation_server->map_set_use_edge_connections(map, !initial_use_edge_connections);
158
navigation_server->physics_process(0.0); // Give server some cycles to commit.
159
160
CHECK_EQ(navigation_server->map_get_cell_size(map), doctest::Approx(0.55));
161
CHECK_EQ(navigation_server->map_get_edge_connection_margin(map), doctest::Approx(0.66));
162
CHECK_EQ(navigation_server->map_get_link_connection_radius(map), doctest::Approx(0.77));
163
CHECK_EQ(navigation_server->map_get_up(map), Vector3(1, 0, 0));
164
CHECK_EQ(navigation_server->map_get_use_edge_connections(map), !initial_use_edge_connections);
165
}
166
167
SUBCASE("'ProcessInfo' should report map iff active") {
168
navigation_server->map_set_active(map, true);
169
navigation_server->physics_process(0.0); // Give server some cycles to commit.
170
CHECK(navigation_server->map_is_active(map));
171
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS), 1);
172
navigation_server->map_set_active(map, false);
173
navigation_server->physics_process(0.0); // Give server some cycles to commit.
174
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_ACTIVE_MAPS), 0);
175
}
176
177
SUBCASE("Number of agents should be reported properly") {
178
RID agent = navigation_server->agent_create();
179
CHECK(agent.is_valid());
180
navigation_server->agent_set_map(agent, map);
181
navigation_server->physics_process(0.0); // Give server some cycles to commit.
182
CHECK_EQ(navigation_server->map_get_agents(map).size(), 1);
183
navigation_server->free_rid(agent);
184
navigation_server->physics_process(0.0); // Give server some cycles to commit.
185
CHECK_EQ(navigation_server->map_get_agents(map).size(), 0);
186
}
187
188
SUBCASE("Number of links should be reported properly") {
189
RID link = navigation_server->link_create();
190
CHECK(link.is_valid());
191
navigation_server->link_set_map(link, map);
192
navigation_server->physics_process(0.0); // Give server some cycles to commit.
193
CHECK_EQ(navigation_server->map_get_links(map).size(), 1);
194
navigation_server->free_rid(link);
195
navigation_server->physics_process(0.0); // Give server some cycles to commit.
196
CHECK_EQ(navigation_server->map_get_links(map).size(), 0);
197
}
198
199
SUBCASE("Number of obstacles should be reported properly") {
200
RID obstacle = navigation_server->obstacle_create();
201
CHECK(obstacle.is_valid());
202
navigation_server->obstacle_set_map(obstacle, map);
203
navigation_server->physics_process(0.0); // Give server some cycles to commit.
204
CHECK_EQ(navigation_server->map_get_obstacles(map).size(), 1);
205
navigation_server->free_rid(obstacle);
206
navigation_server->physics_process(0.0); // Give server some cycles to commit.
207
CHECK_EQ(navigation_server->map_get_obstacles(map).size(), 0);
208
}
209
210
SUBCASE("Number of regions should be reported properly") {
211
RID region = navigation_server->region_create();
212
CHECK(region.is_valid());
213
navigation_server->region_set_map(region, map);
214
navigation_server->physics_process(0.0); // Give server some cycles to commit.
215
CHECK_EQ(navigation_server->map_get_regions(map).size(), 1);
216
navigation_server->free_rid(region);
217
navigation_server->physics_process(0.0); // Give server some cycles to commit.
218
CHECK_EQ(navigation_server->map_get_regions(map).size(), 0);
219
}
220
221
SUBCASE("Queries against empty map should return empty or invalid values") {
222
navigation_server->map_set_active(map, true);
223
navigation_server->physics_process(0.0); // Give server some cycles to commit.
224
225
ERR_PRINT_OFF;
226
CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
227
CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
228
CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
229
CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true), Vector3());
230
CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false), Vector3());
231
CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true).size(), 0);
232
CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false).size(), 0);
233
234
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
235
query_parameters->set_map(map);
236
query_parameters->set_start_position(Vector3(7, 7, 7));
237
query_parameters->set_target_position(Vector3(8, 8, 8));
238
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
239
navigation_server->query_path(query_parameters, query_result);
240
CHECK_EQ(query_result->get_path().size(), 0);
241
CHECK_EQ(query_result->get_path_types().size(), 0);
242
CHECK_EQ(query_result->get_path_rids().size(), 0);
243
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
244
ERR_PRINT_ON;
245
246
navigation_server->map_set_active(map, false);
247
navigation_server->physics_process(0.0); // Give server some cycles to commit.
248
}
249
250
navigation_server->free_rid(map);
251
navigation_server->physics_process(0.0); // Give server some cycles to actually remove map.
252
CHECK_EQ(navigation_server->get_maps().size(), 0);
253
}
254
255
TEST_CASE("[NavigationServer3D] Server should manage link properly") {
256
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
257
258
RID link = navigation_server->link_create();
259
CHECK(link.is_valid());
260
261
SUBCASE("'ProcessInfo' should not report dangling link") {
262
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 0);
263
}
264
265
SUBCASE("Setters/getters should work") {
266
bool initial_bidirectional = navigation_server->link_is_bidirectional(link);
267
navigation_server->link_set_bidirectional(link, !initial_bidirectional);
268
navigation_server->link_set_end_position(link, Vector3(7, 7, 7));
269
navigation_server->link_set_enter_cost(link, 0.55);
270
navigation_server->link_set_navigation_layers(link, 6);
271
navigation_server->link_set_owner_id(link, ObjectID((int64_t)7));
272
navigation_server->link_set_start_position(link, Vector3(8, 8, 8));
273
navigation_server->link_set_travel_cost(link, 0.66);
274
navigation_server->physics_process(0.0); // Give server some cycles to commit.
275
276
CHECK_EQ(navigation_server->link_is_bidirectional(link), !initial_bidirectional);
277
CHECK_EQ(navigation_server->link_get_end_position(link), Vector3(7, 7, 7));
278
CHECK_EQ(navigation_server->link_get_enter_cost(link), doctest::Approx(0.55));
279
CHECK_EQ(navigation_server->link_get_navigation_layers(link), 6);
280
CHECK_EQ(navigation_server->link_get_owner_id(link), ObjectID((int64_t)7));
281
CHECK_EQ(navigation_server->link_get_start_position(link), Vector3(8, 8, 8));
282
CHECK_EQ(navigation_server->link_get_travel_cost(link), doctest::Approx(0.66));
283
}
284
285
SUBCASE("'ProcessInfo' should report link with active map") {
286
RID map = navigation_server->map_create();
287
CHECK(map.is_valid());
288
navigation_server->map_set_active(map, true);
289
navigation_server->link_set_map(link, map);
290
navigation_server->physics_process(0.0); // Give server some cycles to commit.
291
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 1);
292
navigation_server->link_set_map(link, RID());
293
navigation_server->free_rid(map);
294
navigation_server->physics_process(0.0); // Give server some cycles to commit.
295
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 0);
296
}
297
298
navigation_server->free_rid(link);
299
}
300
301
TEST_CASE("[NavigationServer3D] Server should manage obstacles properly") {
302
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
303
304
RID obstacle = navigation_server->obstacle_create();
305
CHECK(obstacle.is_valid());
306
307
// TODO: Add tests for setters/getters once getters are added.
308
309
navigation_server->free_rid(obstacle);
310
}
311
312
TEST_CASE("[NavigationServer3D] Server should manage regions properly") {
313
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
314
315
RID region = navigation_server->region_create();
316
CHECK(region.is_valid());
317
318
SUBCASE("'ProcessInfo' should not report dangling region") {
319
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 0);
320
}
321
322
SUBCASE("Setters/getters should work") {
323
bool initial_use_edge_connections = navigation_server->region_get_use_edge_connections(region);
324
navigation_server->region_set_enter_cost(region, 0.55);
325
navigation_server->region_set_navigation_layers(region, 5);
326
navigation_server->region_set_owner_id(region, ObjectID((int64_t)7));
327
navigation_server->region_set_travel_cost(region, 0.66);
328
navigation_server->region_set_use_edge_connections(region, !initial_use_edge_connections);
329
navigation_server->physics_process(0.0); // Give server some cycles to commit.
330
331
CHECK_EQ(navigation_server->region_get_enter_cost(region), doctest::Approx(0.55));
332
CHECK_EQ(navigation_server->region_get_navigation_layers(region), 5);
333
CHECK_EQ(navigation_server->region_get_owner_id(region), ObjectID((int64_t)7));
334
CHECK_EQ(navigation_server->region_get_travel_cost(region), doctest::Approx(0.66));
335
CHECK_EQ(navigation_server->region_get_use_edge_connections(region), !initial_use_edge_connections);
336
}
337
338
SUBCASE("'ProcessInfo' should report region with active map") {
339
RID map = navigation_server->map_create();
340
CHECK(map.is_valid());
341
navigation_server->map_set_active(map, true);
342
navigation_server->region_set_map(region, map);
343
navigation_server->physics_process(0.0); // Give server some cycles to commit.
344
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 1);
345
navigation_server->region_set_map(region, RID());
346
navigation_server->free_rid(map);
347
navigation_server->physics_process(0.0); // Give server some cycles to commit.
348
CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 0);
349
}
350
351
SUBCASE("Queries against empty region should return empty or invalid values") {
352
ERR_PRINT_OFF;
353
CHECK_EQ(navigation_server->region_get_connections_count(region), 0);
354
CHECK_EQ(navigation_server->region_get_connection_pathway_end(region, 55), Vector3());
355
CHECK_EQ(navigation_server->region_get_connection_pathway_start(region, 55), Vector3());
356
ERR_PRINT_ON;
357
}
358
359
navigation_server->free_rid(region);
360
}
361
362
// This test case does not check precise values on purpose - to not be too sensitivte.
363
TEST_CASE("[NavigationServer3D] Server should move agent properly") {
364
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
365
366
RID map = navigation_server->map_create();
367
RID agent = navigation_server->agent_create();
368
369
navigation_server->map_set_active(map, true);
370
navigation_server->agent_set_map(agent, map);
371
navigation_server->agent_set_avoidance_enabled(agent, true);
372
navigation_server->agent_set_velocity(agent, Vector3(1, 0, 1));
373
CallableMock agent_avoidance_callback_mock;
374
navigation_server->agent_set_avoidance_callback(agent, callable_mp(&agent_avoidance_callback_mock, &CallableMock::function1));
375
CHECK_EQ(agent_avoidance_callback_mock.function1_calls, 0);
376
navigation_server->physics_process(0.0); // Give server some cycles to commit.
377
CHECK_EQ(agent_avoidance_callback_mock.function1_calls, 1);
378
CHECK_NE(agent_avoidance_callback_mock.function1_latest_arg0, Vector3(0, 0, 0));
379
380
navigation_server->free_rid(agent);
381
navigation_server->free_rid(map);
382
}
383
384
// This test case does not check precise values on purpose - to not be too sensitivte.
385
TEST_CASE("[NavigationServer3D] Server should make agents avoid each other when avoidance enabled") {
386
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
387
388
RID map = navigation_server->map_create();
389
RID agent_1 = navigation_server->agent_create();
390
RID agent_2 = navigation_server->agent_create();
391
392
navigation_server->map_set_active(map, true);
393
394
navigation_server->agent_set_map(agent_1, map);
395
navigation_server->agent_set_avoidance_enabled(agent_1, true);
396
navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
397
navigation_server->agent_set_radius(agent_1, 1);
398
navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
399
CallableMock agent_1_avoidance_callback_mock;
400
navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
401
402
navigation_server->agent_set_map(agent_2, map);
403
navigation_server->agent_set_avoidance_enabled(agent_2, true);
404
navigation_server->agent_set_position(agent_2, Vector3(2.5, 0, 0.5));
405
navigation_server->agent_set_radius(agent_2, 1);
406
navigation_server->agent_set_velocity(agent_2, Vector3(-1, 0, 0));
407
CallableMock agent_2_avoidance_callback_mock;
408
navigation_server->agent_set_avoidance_callback(agent_2, callable_mp(&agent_2_avoidance_callback_mock, &CallableMock::function1));
409
410
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
411
CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 0);
412
navigation_server->physics_process(0.0); // Give server some cycles to commit.
413
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
414
CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 1);
415
Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
416
Vector3 agent_2_safe_velocity = agent_2_avoidance_callback_mock.function1_latest_arg0;
417
CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "agent 1 should move a bit along desired velocity (+X)");
418
CHECK_MESSAGE(agent_2_safe_velocity.x < 0, "agent 2 should move a bit along desired velocity (-X)");
419
CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "agent 1 should move a bit to the side so that it avoids agent 2");
420
CHECK_MESSAGE(agent_2_safe_velocity.z > 0, "agent 2 should move a bit to the side so that it avoids agent 1");
421
422
navigation_server->free_rid(agent_2);
423
navigation_server->free_rid(agent_1);
424
navigation_server->free_rid(map);
425
}
426
427
TEST_CASE("[NavigationServer3D] Server should make agents avoid dynamic obstacles when avoidance enabled") {
428
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
429
430
RID map = navigation_server->map_create();
431
RID agent_1 = navigation_server->agent_create();
432
RID obstacle_1 = navigation_server->obstacle_create();
433
434
navigation_server->map_set_active(map, true);
435
436
navigation_server->agent_set_map(agent_1, map);
437
navigation_server->agent_set_avoidance_enabled(agent_1, true);
438
navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
439
navigation_server->agent_set_radius(agent_1, 1);
440
navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
441
CallableMock agent_1_avoidance_callback_mock;
442
navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
443
444
navigation_server->obstacle_set_map(obstacle_1, map);
445
navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true);
446
navigation_server->obstacle_set_position(obstacle_1, Vector3(2.5, 0, 0.5));
447
navigation_server->obstacle_set_radius(obstacle_1, 1);
448
449
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
450
navigation_server->physics_process(0.0); // Give server some cycles to commit.
451
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
452
Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
453
CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X).");
454
CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle.");
455
456
navigation_server->free_rid(obstacle_1);
457
navigation_server->free_rid(agent_1);
458
navigation_server->free_rid(map);
459
navigation_server->physics_process(0.0); // Give server some cycles to commit.
460
}
461
462
TEST_CASE("[NavigationServer3D] Server should make agents avoid static obstacles when avoidance enabled") {
463
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
464
465
RID map = navigation_server->map_create();
466
RID agent_1 = navigation_server->agent_create();
467
RID agent_2 = navigation_server->agent_create();
468
RID obstacle_1 = navigation_server->obstacle_create();
469
470
navigation_server->map_set_active(map, true);
471
472
navigation_server->agent_set_map(agent_1, map);
473
navigation_server->agent_set_avoidance_enabled(agent_1, true);
474
navigation_server->agent_set_radius(agent_1, 1.6); // Have hit the obstacle already.
475
navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
476
CallableMock agent_1_avoidance_callback_mock;
477
navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
478
479
navigation_server->agent_set_map(agent_2, map);
480
navigation_server->agent_set_avoidance_enabled(agent_2, true);
481
navigation_server->agent_set_radius(agent_2, 1.4); // Haven't hit the obstacle yet.
482
navigation_server->agent_set_velocity(agent_2, Vector3(1, 0, 0));
483
CallableMock agent_2_avoidance_callback_mock;
484
navigation_server->agent_set_avoidance_callback(agent_2, callable_mp(&agent_2_avoidance_callback_mock, &CallableMock::function1));
485
486
navigation_server->obstacle_set_map(obstacle_1, map);
487
navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true);
488
PackedVector3Array obstacle_1_vertices;
489
490
SUBCASE("Static obstacles should work on ground level") {
491
navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
492
navigation_server->agent_set_position(agent_2, Vector3(0, 0, 5));
493
obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5));
494
obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5));
495
}
496
497
SUBCASE("Static obstacles should work when elevated") {
498
navigation_server->agent_set_position(agent_1, Vector3(0, 5, 0));
499
navigation_server->agent_set_position(agent_2, Vector3(0, 5, 5));
500
obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5));
501
obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5));
502
navigation_server->obstacle_set_position(obstacle_1, Vector3(0, 5, 0));
503
}
504
505
navigation_server->obstacle_set_vertices(obstacle_1, obstacle_1_vertices);
506
507
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
508
CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 0);
509
navigation_server->physics_process(0.0); // Give server some cycles to commit.
510
CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
511
CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 1);
512
Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
513
Vector3 agent_2_safe_velocity = agent_2_avoidance_callback_mock.function1_latest_arg0;
514
CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X).");
515
CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle.");
516
CHECK_MESSAGE(agent_2_safe_velocity.x > 0, "Agent 2 should move a bit along desired velocity (+X).");
517
CHECK_MESSAGE(agent_2_safe_velocity.z == 0, "Agent 2 should not move to the side.");
518
519
navigation_server->free_rid(obstacle_1);
520
navigation_server->free_rid(agent_2);
521
navigation_server->free_rid(agent_1);
522
navigation_server->free_rid(map);
523
navigation_server->physics_process(0.0); // Give server some cycles to commit.
524
}
525
526
#ifndef DISABLE_DEPRECATED
527
// This test case uses only public APIs on purpose - other test cases use simplified baking.
528
// FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed.
529
TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") {
530
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
531
532
// Prepare scene tree with simple mesh to serve as an input geometry.
533
Node3D *node_3d = memnew(Node3D);
534
SceneTree::get_singleton()->get_root()->add_child(node_3d);
535
Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
536
plane_mesh->set_size(Size2(10.0, 10.0));
537
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
538
mesh_instance->set_mesh(plane_mesh);
539
node_3d->add_child(mesh_instance);
540
541
// Prepare anything necessary to bake navigation mesh.
542
RID map = navigation_server->map_create();
543
RID region = navigation_server->region_create();
544
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
545
navigation_server->map_set_use_async_iterations(map, false);
546
navigation_server->map_set_active(map, true);
547
navigation_server->region_set_use_async_iterations(region, false);
548
navigation_server->region_set_map(region, map);
549
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
550
navigation_server->physics_process(0.0); // Give server some cycles to commit.
551
552
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
553
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
554
555
ERR_PRINT_OFF;
556
navigation_server->region_bake_navigation_mesh(navigation_mesh, node_3d);
557
ERR_PRINT_ON;
558
// FIXME: The above line should trigger the update (line below) under the hood.
559
navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update.
560
CHECK_EQ(navigation_mesh->get_polygon_count(), 2);
561
CHECK_EQ(navigation_mesh->get_vertices().size(), 4);
562
563
SUBCASE("Map should emit signal and take newly baked navigation mesh into account") {
564
SIGNAL_WATCH(navigation_server, "map_changed");
565
SIGNAL_CHECK_FALSE("map_changed");
566
navigation_server->physics_process(0.0); // Give server some cycles to commit.
567
SIGNAL_CHECK("map_changed", { { map } });
568
SIGNAL_UNWATCH(navigation_server, "map_changed");
569
CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
570
}
571
572
navigation_server->free_rid(region);
573
navigation_server->free_rid(map);
574
navigation_server->physics_process(0.0); // Give server some cycles to commit.
575
memdelete(mesh_instance);
576
memdelete(node_3d);
577
}
578
#endif // DISABLE_DEPRECATED
579
580
TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to parse geometry") {
581
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
582
583
// Prepare scene tree with simple mesh to serve as an input geometry.
584
Node3D *node_3d = memnew(Node3D);
585
SceneTree::get_singleton()->get_root()->add_child(node_3d);
586
Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
587
plane_mesh->set_size(Size2(10.0, 10.0));
588
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
589
mesh_instance->set_mesh(plane_mesh);
590
node_3d->add_child(mesh_instance);
591
592
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
593
Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
594
CHECK_EQ(source_geometry->get_vertices().size(), 0);
595
CHECK_EQ(source_geometry->get_indices().size(), 0);
596
597
navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, mesh_instance);
598
CHECK_EQ(source_geometry->get_vertices().size(), 12);
599
CHECK_EQ(source_geometry->get_indices().size(), 6);
600
601
SUBCASE("By default, parsing should remove any data that was parsed before") {
602
navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, mesh_instance);
603
CHECK_EQ(source_geometry->get_vertices().size(), 12);
604
CHECK_EQ(source_geometry->get_indices().size(), 6);
605
}
606
607
SUBCASE("Parsed geometry should be extendable with other geometry") {
608
source_geometry->merge(source_geometry); // Merging with itself.
609
const Vector<float> vertices = source_geometry->get_vertices();
610
const Vector<int> indices = source_geometry->get_indices();
611
REQUIRE_EQ(vertices.size(), 24);
612
REQUIRE_EQ(indices.size(), 12);
613
// Check if first newly added vertex is the same as first vertex.
614
CHECK_EQ(vertices[0], vertices[12]);
615
CHECK_EQ(vertices[1], vertices[13]);
616
CHECK_EQ(vertices[2], vertices[14]);
617
// Check if first newly added index is the same as first index.
618
CHECK_EQ(indices[0] + 4, indices[6]);
619
}
620
621
memdelete(mesh_instance);
622
memdelete(node_3d);
623
}
624
625
// This test case uses only public APIs on purpose - other test cases use simplified baking.
626
TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") {
627
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
628
629
// Prepare scene tree with simple mesh to serve as an input geometry.
630
Node3D *node_3d = memnew(Node3D);
631
SceneTree::get_singleton()->get_root()->add_child(node_3d);
632
Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
633
plane_mesh->set_size(Size2(10.0, 10.0));
634
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
635
mesh_instance->set_mesh(plane_mesh);
636
node_3d->add_child(mesh_instance);
637
638
// Prepare anything necessary to bake navigation mesh.
639
RID map = navigation_server->map_create();
640
RID region = navigation_server->region_create();
641
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
642
navigation_server->map_set_use_async_iterations(map, false);
643
navigation_server->map_set_active(map, true);
644
navigation_server->region_set_use_async_iterations(region, false);
645
navigation_server->region_set_map(region, map);
646
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
647
navigation_server->physics_process(0.0); // Give server some cycles to commit.
648
649
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
650
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
651
652
Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
653
navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, node_3d);
654
navigation_server->bake_from_source_geometry_data(navigation_mesh, source_geometry, Callable());
655
// FIXME: The above line should trigger the update (line below) under the hood.
656
navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update.
657
CHECK_EQ(navigation_mesh->get_polygon_count(), 2);
658
CHECK_EQ(navigation_mesh->get_vertices().size(), 4);
659
660
SUBCASE("Map should emit signal and take newly baked navigation mesh into account") {
661
SIGNAL_WATCH(navigation_server, "map_changed");
662
SIGNAL_CHECK_FALSE("map_changed");
663
navigation_server->physics_process(0.0); // Give server some cycles to commit.
664
SIGNAL_CHECK("map_changed", { { map } });
665
SIGNAL_UNWATCH(navigation_server, "map_changed");
666
CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
667
}
668
669
navigation_server->free_rid(region);
670
navigation_server->free_rid(map);
671
navigation_server->physics_process(0.0); // Give server some cycles to commit.
672
memdelete(mesh_instance);
673
memdelete(node_3d);
674
}
675
676
// This test case does not check precise values on purpose - to not be too sensitivte.
677
TEST_CASE("[NavigationServer3D] Server should respond to queries against valid map properly") {
678
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
679
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
680
Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
681
682
Array arr;
683
arr.resize(RSE::ARRAY_MAX);
684
BoxMesh::create_mesh_array(arr, Vector3(10.0, 0.001, 10.0));
685
source_geometry->add_mesh_array(arr, Transform3D());
686
navigation_server->bake_from_source_geometry_data(navigation_mesh, source_geometry, Callable());
687
CHECK_NE(navigation_mesh->get_polygon_count(), 0);
688
CHECK_NE(navigation_mesh->get_vertices().size(), 0);
689
690
RID map = navigation_server->map_create();
691
RID region = navigation_server->region_create();
692
navigation_server->map_set_active(map, true);
693
navigation_server->map_set_use_async_iterations(map, false);
694
navigation_server->region_set_use_async_iterations(region, false);
695
navigation_server->region_set_map(region, map);
696
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
697
navigation_server->physics_process(0.0); // Give server some cycles to commit.
698
699
SUBCASE("Simple queries should return non-default values") {
700
CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
701
CHECK_NE(navigation_server->map_get_closest_point_normal(map, Vector3(0, 0, 0)), Vector3());
702
CHECK(navigation_server->map_get_closest_point_owner(map, Vector3(0, 0, 0)).is_valid());
703
CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), false), Vector3());
704
CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), true), Vector3());
705
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), true).size(), 0);
706
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), false).size(), 0);
707
}
708
709
SUBCASE("'map_get_closest_point_to_segment' with 'use_collision' should return default if segment doesn't intersect map") {
710
CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(1, 2, 1), Vector3(1, 1, 1), true), Vector3());
711
}
712
713
SUBCASE("Elaborate query with 'CORRIDORFUNNEL' post-processing should yield non-empty result") {
714
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
715
query_parameters->set_map(map);
716
query_parameters->set_start_position(Vector3(0, 0, 0));
717
query_parameters->set_target_position(Vector3(10, 0, 10));
718
query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_CORRIDORFUNNEL);
719
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
720
navigation_server->query_path(query_parameters, query_result);
721
CHECK_NE(query_result->get_path().size(), 0);
722
CHECK_NE(query_result->get_path_types().size(), 0);
723
CHECK_NE(query_result->get_path_rids().size(), 0);
724
CHECK_NE(query_result->get_path_owner_ids().size(), 0);
725
}
726
727
SUBCASE("Elaborate query with 'EDGECENTERED' post-processing should yield non-empty result") {
728
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
729
query_parameters->set_map(map);
730
query_parameters->set_start_position(Vector3(10, 0, 10));
731
query_parameters->set_target_position(Vector3(0, 0, 0));
732
query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_EDGECENTERED);
733
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
734
navigation_server->query_path(query_parameters, query_result);
735
CHECK_NE(query_result->get_path().size(), 0);
736
CHECK_NE(query_result->get_path_types().size(), 0);
737
CHECK_NE(query_result->get_path_rids().size(), 0);
738
CHECK_NE(query_result->get_path_owner_ids().size(), 0);
739
}
740
741
SUBCASE("Elaborate query with non-matching navigation layer mask should yield empty result") {
742
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
743
query_parameters->set_map(map);
744
query_parameters->set_start_position(Vector3(10, 0, 10));
745
query_parameters->set_target_position(Vector3(0, 0, 0));
746
query_parameters->set_navigation_layers(2);
747
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
748
navigation_server->query_path(query_parameters, query_result);
749
CHECK_EQ(query_result->get_path().size(), 0);
750
CHECK_EQ(query_result->get_path_types().size(), 0);
751
CHECK_EQ(query_result->get_path_rids().size(), 0);
752
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
753
}
754
755
SUBCASE("Elaborate query without metadata flags should yield path only") {
756
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
757
query_parameters->set_map(map);
758
query_parameters->set_start_position(Vector3(10, 0, 10));
759
query_parameters->set_target_position(Vector3(0, 0, 0));
760
query_parameters->set_metadata_flags(0);
761
Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
762
navigation_server->query_path(query_parameters, query_result);
763
CHECK_NE(query_result->get_path().size(), 0);
764
CHECK_EQ(query_result->get_path_types().size(), 0);
765
CHECK_EQ(query_result->get_path_rids().size(), 0);
766
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
767
}
768
769
SUBCASE("Elaborate query with excluded region should yield empty path") {
770
Ref<NavigationPathQueryParameters3D> query_parameters;
771
query_parameters.instantiate();
772
query_parameters->set_map(map);
773
query_parameters->set_start_position(Vector3(10, 0, 10));
774
query_parameters->set_target_position(Vector3(0, 0, 0));
775
query_parameters->set_excluded_regions({ region });
776
Ref<NavigationPathQueryResult3D> query_result;
777
query_result.instantiate();
778
navigation_server->query_path(query_parameters, query_result);
779
CHECK_EQ(query_result->get_path().size(), 0);
780
}
781
782
SUBCASE("Elaborate query with included region should yield path") {
783
Ref<NavigationPathQueryParameters3D> query_parameters;
784
query_parameters.instantiate();
785
query_parameters->set_map(map);
786
query_parameters->set_start_position(Vector3(10, 0, 10));
787
query_parameters->set_target_position(Vector3(0, 0, 0));
788
query_parameters->set_included_regions({ region });
789
Ref<NavigationPathQueryResult3D> query_result;
790
query_result.instantiate();
791
navigation_server->query_path(query_parameters, query_result);
792
CHECK_NE(query_result->get_path().size(), 0);
793
}
794
795
SUBCASE("Elaborate query with excluded and included region should yield empty path") {
796
Ref<NavigationPathQueryParameters3D> query_parameters;
797
query_parameters.instantiate();
798
query_parameters->set_map(map);
799
query_parameters->set_start_position(Vector3(10, 0, 10));
800
query_parameters->set_target_position(Vector3(0, 0, 0));
801
query_parameters->set_excluded_regions({ region });
802
query_parameters->set_included_regions({ region });
803
Ref<NavigationPathQueryResult3D> query_result;
804
query_result.instantiate();
805
navigation_server->query_path(query_parameters, query_result);
806
CHECK_EQ(query_result->get_path().size(), 0);
807
}
808
809
navigation_server->free_rid(region);
810
navigation_server->free_rid(map);
811
navigation_server->physics_process(0.0); // Give server some cycles to commit.
812
}
813
814
// FIXME: The race condition mentioned below is actually a problem and fails on CI (GH-90613).
815
/*
816
TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") {
817
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
818
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
819
Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
820
821
Array arr;
822
arr.resize(RSE::ARRAY_MAX);
823
BoxMesh::create_mesh_array(arr, Vector3(10.0, 0.001, 10.0));
824
source_geometry->add_mesh_array(arr, Transform3D());
825
826
// Race condition is present below, but baking should take many orders of magnitude
827
// longer than basic checks on the main thread, so it's fine.
828
navigation_server->bake_from_source_geometry_data_async(navigation_mesh, source_geometry, Callable());
829
CHECK(navigation_server->is_baking_navigation_mesh(navigation_mesh));
830
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
831
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
832
}
833
*/
834
835
TEST_CASE("[NavigationServer3D] Server should simplify path properly") {
836
real_t simplify_epsilon = 0.2;
837
Vector<Vector3> source_path;
838
source_path.resize(7);
839
source_path.write[0] = Vector3(0.0, 0.0, 0.0);
840
source_path.write[1] = Vector3(0.0, 0.0, 1.0); // This point needs to go.
841
source_path.write[2] = Vector3(0.0, 0.0, 2.0); // This point needs to go.
842
source_path.write[3] = Vector3(0.0, 0.0, 2.0);
843
source_path.write[4] = Vector3(2.0, 1.0, 3.0);
844
source_path.write[5] = Vector3(2.0, 1.5, 4.0); // This point needs to go.
845
source_path.write[6] = Vector3(2.0, 2.0, 5.0);
846
Vector<Vector3> simplified_path = NavigationServer3D::get_singleton()->simplify_path(source_path, simplify_epsilon);
847
CHECK_EQ(simplified_path.size(), 4);
848
}
849
}
850
851
} // namespace TestNavigationServer3D
852
853
#endif // MODULE_NAVIGATION_3D_ENABLED
854
855