Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/navigation_3d/3d/nav_mesh_generator_3d.cpp
10278 views
1
/**************************************************************************/
2
/* nav_mesh_generator_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 "nav_mesh_generator_3d.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/os/thread.h"
35
#include "scene/3d/node_3d.h"
36
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
37
#include "scene/resources/navigation_mesh.h"
38
39
#include <Recast.h>
40
41
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
42
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
43
Mutex NavMeshGenerator3D::generator_task_mutex;
44
RWLock NavMeshGenerator3D::generator_parsers_rwlock;
45
bool NavMeshGenerator3D::use_threads = true;
46
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
47
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
48
HashMap<Ref<NavigationMesh>, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::baking_navmeshes;
49
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
50
LocalVector<NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
51
52
static const char *_navmesh_bake_state_msgs[(size_t)NavMeshGenerator3D::NavMeshBakeState::BAKE_STATE_MAX] = {
53
"",
54
"Setting up configuration...",
55
"Calculating grid size...",
56
"Creating heightfield...",
57
"Marking walkable triangles...",
58
"Constructing compact heightfield...", // step 5
59
"Eroding walkable area...",
60
"Sample partitioning...",
61
"Creating contours...",
62
"Creating polymesh...",
63
"Converting to native navigation mesh...", // step 10
64
"Baking cleanup...",
65
"Baking finished.",
66
};
67
68
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
69
return singleton;
70
}
71
72
NavMeshGenerator3D::NavMeshGenerator3D() {
73
ERR_FAIL_COND(singleton != nullptr);
74
singleton = this;
75
76
baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
77
baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
78
79
// Using threads might cause problems on certain exports or with the Editor on certain devices.
80
// This is the main switch to turn threaded navmesh baking off should the need arise.
81
use_threads = baking_use_multiple_threads;
82
}
83
84
NavMeshGenerator3D::~NavMeshGenerator3D() {
85
cleanup();
86
}
87
88
void NavMeshGenerator3D::sync() {
89
if (generator_tasks.is_empty()) {
90
return;
91
}
92
93
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
94
{
95
MutexLock generator_task_lock(generator_task_mutex);
96
97
LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
98
99
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
100
if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
101
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
102
finished_task_ids.push_back(E.key);
103
104
NavMeshGeneratorTask3D *generator_task = E.value;
105
DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
106
107
baking_navmeshes.erase(generator_task->navigation_mesh);
108
if (generator_task->callback.is_valid()) {
109
generator_emit_callback(generator_task->callback);
110
}
111
generator_task->navigation_mesh->emit_changed();
112
memdelete(generator_task);
113
}
114
}
115
116
for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
117
generator_tasks.erase(finished_task_id);
118
}
119
}
120
}
121
122
void NavMeshGenerator3D::cleanup() {
123
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
124
{
125
MutexLock generator_task_lock(generator_task_mutex);
126
127
baking_navmeshes.clear();
128
129
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
130
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
131
NavMeshGeneratorTask3D *generator_task = E.value;
132
memdelete(generator_task);
133
}
134
generator_tasks.clear();
135
136
generator_parsers_rwlock.write_lock();
137
generator_parsers.clear();
138
generator_parsers_rwlock.write_unlock();
139
}
140
}
141
142
void NavMeshGenerator3D::finish() {
143
cleanup();
144
}
145
146
void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
147
ERR_FAIL_COND(!Thread::is_main_thread());
148
ERR_FAIL_COND(p_navigation_mesh.is_null());
149
ERR_FAIL_NULL(p_root_node);
150
ERR_FAIL_COND(!p_root_node->is_inside_tree());
151
ERR_FAIL_COND(p_source_geometry_data.is_null());
152
153
generator_parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node);
154
155
if (p_callback.is_valid()) {
156
generator_emit_callback(p_callback);
157
}
158
}
159
160
void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
161
ERR_FAIL_COND(p_navigation_mesh.is_null());
162
ERR_FAIL_COND(p_source_geometry_data.is_null());
163
164
if (!p_source_geometry_data->has_data()) {
165
p_navigation_mesh->clear();
166
if (p_callback.is_valid()) {
167
generator_emit_callback(p_callback);
168
}
169
p_navigation_mesh->emit_changed();
170
return;
171
}
172
173
if (is_baking(p_navigation_mesh)) {
174
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
175
}
176
baking_navmesh_mutex.lock();
177
NavMeshGeneratorTask3D generator_task;
178
baking_navmeshes.insert(p_navigation_mesh, &generator_task);
179
baking_navmesh_mutex.unlock();
180
181
generator_task.navigation_mesh = p_navigation_mesh;
182
generator_task.source_geometry_data = p_source_geometry_data;
183
generator_task.status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
184
185
generator_bake_from_source_geometry_data(&generator_task);
186
187
baking_navmesh_mutex.lock();
188
baking_navmeshes.erase(p_navigation_mesh);
189
baking_navmesh_mutex.unlock();
190
191
if (p_callback.is_valid()) {
192
generator_emit_callback(p_callback);
193
}
194
195
p_navigation_mesh->emit_changed();
196
}
197
198
void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
199
ERR_FAIL_COND(p_navigation_mesh.is_null());
200
ERR_FAIL_COND(p_source_geometry_data.is_null());
201
202
if (!p_source_geometry_data->has_data()) {
203
p_navigation_mesh->clear();
204
if (p_callback.is_valid()) {
205
generator_emit_callback(p_callback);
206
}
207
p_navigation_mesh->emit_changed();
208
return;
209
}
210
211
if (!use_threads) {
212
bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
213
return;
214
}
215
216
if (is_baking(p_navigation_mesh)) {
217
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
218
return;
219
}
220
baking_navmesh_mutex.lock();
221
NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
222
baking_navmeshes.insert(p_navigation_mesh, generator_task);
223
baking_navmesh_mutex.unlock();
224
225
generator_task->navigation_mesh = p_navigation_mesh;
226
generator_task->source_geometry_data = p_source_geometry_data;
227
generator_task->callback = p_callback;
228
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
229
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
230
MutexLock generator_task_lock(generator_task_mutex);
231
generator_tasks.insert(generator_task->thread_task_id, generator_task);
232
}
233
234
bool NavMeshGenerator3D::is_baking(Ref<NavigationMesh> p_navigation_mesh) {
235
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
236
return baking_navmeshes.has(p_navigation_mesh);
237
}
238
239
String NavMeshGenerator3D::get_baking_state_msg(Ref<NavigationMesh> p_navigation_mesh) {
240
String bake_state_msg;
241
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
242
if (baking_navmeshes.has(p_navigation_mesh)) {
243
bake_state_msg = _navmesh_bake_state_msgs[baking_navmeshes[p_navigation_mesh]->bake_state];
244
} else {
245
bake_state_msg = _navmesh_bake_state_msgs[NavMeshBakeState::BAKE_STATE_NONE];
246
}
247
return bake_state_msg;
248
}
249
250
void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
251
NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
252
253
generator_bake_from_source_geometry_data(generator_task);
254
255
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
256
}
257
258
void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
259
generator_parsers_rwlock.read_lock();
260
for (const NavMeshGeometryParser3D *parser : generator_parsers) {
261
if (!parser->callback.is_valid()) {
262
continue;
263
}
264
parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node);
265
}
266
generator_parsers_rwlock.read_unlock();
267
268
if (p_recurse_children) {
269
for (int i = 0; i < p_node->get_child_count(); i++) {
270
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
271
}
272
}
273
}
274
275
void NavMeshGenerator3D::set_generator_parsers(LocalVector<NavMeshGeometryParser3D *> p_parsers) {
276
RWLockWrite write_lock(generator_parsers_rwlock);
277
generator_parsers = p_parsers;
278
}
279
280
void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) {
281
List<Node *> parse_nodes;
282
283
if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
284
parse_nodes.push_back(p_root_node);
285
} else {
286
p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes);
287
}
288
289
Transform3D root_node_transform = Transform3D();
290
if (Object::cast_to<Node3D>(p_root_node)) {
291
root_node_transform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse();
292
}
293
294
p_source_geometry_data->clear();
295
p_source_geometry_data->root_node_transform = root_node_transform;
296
297
bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
298
299
for (Node *parse_node : parse_nodes) {
300
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, parse_node, recurse_children);
301
}
302
}
303
304
void NavMeshGenerator3D::generator_bake_from_source_geometry_data(NavMeshGeneratorTask3D *p_generator_task) {
305
Ref<NavigationMesh> p_navigation_mesh = p_generator_task->navigation_mesh;
306
const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data = p_generator_task->source_geometry_data;
307
308
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
309
return;
310
}
311
312
Vector<float> source_geometry_vertices;
313
Vector<int> source_geometry_indices;
314
Vector<NavigationMeshSourceGeometryData3D::ProjectedObstruction> projected_obstructions;
315
316
p_source_geometry_data->get_data(
317
source_geometry_vertices,
318
source_geometry_indices,
319
projected_obstructions);
320
321
if (source_geometry_vertices.size() < 3 || source_geometry_indices.size() < 3) {
322
return;
323
}
324
325
rcHeightfield *hf = nullptr;
326
rcCompactHeightfield *chf = nullptr;
327
rcContourSet *cset = nullptr;
328
rcPolyMesh *poly_mesh = nullptr;
329
rcPolyMeshDetail *detail_mesh = nullptr;
330
rcContext ctx;
331
332
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONFIGURATION; // step #1
333
334
const float *verts = source_geometry_vertices.ptr();
335
const int nverts = source_geometry_vertices.size() / 3;
336
const int *tris = source_geometry_indices.ptr();
337
const int ntris = source_geometry_indices.size() / 3;
338
339
float bmin[3], bmax[3];
340
rcCalcBounds(verts, nverts, bmin, bmax);
341
342
rcConfig cfg;
343
memset(&cfg, 0, sizeof(cfg));
344
345
cfg.cs = p_navigation_mesh->get_cell_size();
346
cfg.ch = p_navigation_mesh->get_cell_height();
347
if (p_navigation_mesh->get_border_size() > 0.0) {
348
cfg.borderSize = (int)Math::ceil(p_navigation_mesh->get_border_size() / cfg.cs);
349
}
350
cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope();
351
cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch);
352
cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch);
353
cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs);
354
cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size());
355
cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error();
356
cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size());
357
cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size());
358
cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon();
359
cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
360
cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
361
362
if (p_navigation_mesh->get_border_size() > 0.0 && !Math::is_zero_approx(Math::fmod(p_navigation_mesh->get_border_size(), p_navigation_mesh->get_cell_size()))) {
363
WARN_PRINT("Property border_size is ceiled to cell_size voxel units and loses precision.");
364
}
365
if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
366
WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
367
}
368
if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) {
369
WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
370
}
371
if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) {
372
WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
373
}
374
if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) {
375
WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
376
}
377
if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) {
378
WARN_PRINT("Property region_min_size is converted to int and loses precision.");
379
}
380
if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) {
381
WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
382
}
383
if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) {
384
WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision.");
385
}
386
if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) {
387
WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
388
}
389
390
cfg.bmin[0] = bmin[0];
391
cfg.bmin[1] = bmin[1];
392
cfg.bmin[2] = bmin[2];
393
cfg.bmax[0] = bmax[0];
394
cfg.bmax[1] = bmax[1];
395
cfg.bmax[2] = bmax[2];
396
397
AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb();
398
if (baking_aabb.has_volume()) {
399
Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset();
400
cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x;
401
cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y;
402
cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z;
403
cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0];
404
cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1];
405
cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
406
}
407
408
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CALC_GRID_SIZE; // step #2
409
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
410
411
// ~30000000 seems to be around sweetspot where Editor baking breaks
412
if ((cfg.width * cfg.height) > 30000000 && GLOBAL_GET("navigation/baking/use_crash_prevention_checks")) {
413
ERR_FAIL_MSG("Baking interrupted."
414
"\nNavigationMesh baking process would likely crash the engine."
415
"\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings."
416
"\nIf baking does not crash the engine or fail, the resulting NavigationMesh will create serious pathfinding performance issues."
417
"\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry."
418
"\nIf you would like to try baking anyway, disable the 'navigation/baking/use_crash_prevention_checks' project setting.");
419
return;
420
}
421
422
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATE_HEIGHTFIELD; // step #3
423
hf = rcAllocHeightfield();
424
425
ERR_FAIL_NULL(hf);
426
ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
427
428
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_MARK_WALKABLE_TRIANGLES; // step #4
429
{
430
Vector<unsigned char> tri_areas;
431
tri_areas.resize(ntris);
432
433
ERR_FAIL_COND(tri_areas.is_empty());
434
435
memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char));
436
rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw());
437
438
ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb));
439
}
440
441
if (p_navigation_mesh->get_filter_low_hanging_obstacles()) {
442
rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf);
443
}
444
if (p_navigation_mesh->get_filter_ledge_spans()) {
445
rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf);
446
}
447
if (p_navigation_mesh->get_filter_walkable_low_height_spans()) {
448
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
449
}
450
451
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONSTRUCT_COMPACT_HEIGHTFIELD; // step #5
452
453
chf = rcAllocCompactHeightfield();
454
455
ERR_FAIL_NULL(chf);
456
ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf));
457
458
rcFreeHeightField(hf);
459
hf = nullptr;
460
461
// Add obstacles to the source geometry. Those will be affected by e.g. agent_radius.
462
if (!projected_obstructions.is_empty()) {
463
for (const NavigationMeshSourceGeometryData3D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
464
if (projected_obstruction.carve) {
465
continue;
466
}
467
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 3 != 0) {
468
continue;
469
}
470
471
const float *projected_obstruction_verts = projected_obstruction.vertices.ptr();
472
const int projected_obstruction_nverts = projected_obstruction.vertices.size() / 3;
473
474
rcMarkConvexPolyArea(&ctx, projected_obstruction_verts, projected_obstruction_nverts, projected_obstruction.elevation, projected_obstruction.elevation + projected_obstruction.height, RC_NULL_AREA, *chf);
475
}
476
}
477
478
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_ERODE_WALKABLE_AREA; // step #6
479
480
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
481
482
// Carve obstacles to the eroded geometry. Those will NOT be affected by e.g. agent_radius because that step is already done.
483
if (!projected_obstructions.is_empty()) {
484
for (const NavigationMeshSourceGeometryData3D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
485
if (!projected_obstruction.carve) {
486
continue;
487
}
488
if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 3 != 0) {
489
continue;
490
}
491
492
const float *projected_obstruction_verts = projected_obstruction.vertices.ptr();
493
const int projected_obstruction_nverts = projected_obstruction.vertices.size() / 3;
494
495
rcMarkConvexPolyArea(&ctx, projected_obstruction_verts, projected_obstruction_nverts, projected_obstruction.elevation, projected_obstruction.elevation + projected_obstruction.height, RC_NULL_AREA, *chf);
496
}
497
}
498
499
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_SAMPLE_PARTITIONING; // step #7
500
501
if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
502
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
503
ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea));
504
} else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) {
505
ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea));
506
} else {
507
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea));
508
}
509
510
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATING_CONTOURS; // step #8
511
512
cset = rcAllocContourSet();
513
514
ERR_FAIL_NULL(cset);
515
ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
516
517
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATING_POLYMESH; // step #9
518
519
poly_mesh = rcAllocPolyMesh();
520
ERR_FAIL_NULL(poly_mesh);
521
ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh));
522
523
detail_mesh = rcAllocPolyMeshDetail();
524
ERR_FAIL_NULL(detail_mesh);
525
ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh));
526
527
rcFreeCompactHeightfield(chf);
528
chf = nullptr;
529
rcFreeContourSet(cset);
530
cset = nullptr;
531
532
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONVERTING_NATIVE_NAVMESH; // step #10
533
534
Vector<Vector3> nav_vertices;
535
Vector<Vector<int>> nav_polygons;
536
537
HashMap<Vector3, int> recast_vertex_to_native_index;
538
LocalVector<int> recast_index_to_native_index;
539
recast_index_to_native_index.resize(detail_mesh->nverts);
540
541
for (int i = 0; i < detail_mesh->nverts; i++) {
542
const float *v = &detail_mesh->verts[i * 3];
543
const Vector3 vertex = Vector3(v[0], v[1], v[2]);
544
int *existing_index_ptr = recast_vertex_to_native_index.getptr(vertex);
545
if (!existing_index_ptr) {
546
int new_index = recast_vertex_to_native_index.size();
547
recast_index_to_native_index[i] = new_index;
548
recast_vertex_to_native_index[vertex] = new_index;
549
nav_vertices.push_back(vertex);
550
} else {
551
recast_index_to_native_index[i] = *existing_index_ptr;
552
}
553
}
554
555
for (int i = 0; i < detail_mesh->nmeshes; i++) {
556
const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4];
557
const unsigned int detail_mesh_bverts = detail_mesh_m[0];
558
const unsigned int detail_mesh_m_btris = detail_mesh_m[2];
559
const unsigned int detail_mesh_ntris = detail_mesh_m[3];
560
const unsigned char *detail_mesh_tris = &detail_mesh->tris[detail_mesh_m_btris * 4];
561
for (unsigned int j = 0; j < detail_mesh_ntris; j++) {
562
Vector<int> nav_indices;
563
nav_indices.resize(3);
564
// Polygon order in recast is opposite than godot's
565
int index1 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0]));
566
int index2 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2]));
567
int index3 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1]));
568
569
nav_indices.write[0] = recast_index_to_native_index[index1];
570
nav_indices.write[1] = recast_index_to_native_index[index2];
571
nav_indices.write[2] = recast_index_to_native_index[index3];
572
573
nav_polygons.push_back(nav_indices);
574
}
575
}
576
577
p_navigation_mesh->set_data(nav_vertices, nav_polygons);
578
579
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_CLEANUP; // step #11
580
581
rcFreePolyMesh(poly_mesh);
582
poly_mesh = nullptr;
583
rcFreePolyMeshDetail(detail_mesh);
584
detail_mesh = nullptr;
585
586
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_FINISHED; // step #12
587
}
588
589
bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
590
ERR_FAIL_COND_V(!p_callback.is_valid(), false);
591
592
Callable::CallError ce;
593
Variant result;
594
p_callback.callp(nullptr, 0, result, ce);
595
596
return ce.error == Callable::CallError::CALL_OK;
597
}
598
599