Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/navigation_3d/3d/nav_region_builder_3d.cpp
11353 views
1
/**************************************************************************/
2
/* nav_region_builder_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_region_builder_3d.h"
32
33
#include "../nav_map_3d.h"
34
#include "../nav_region_3d.h"
35
#include "nav_region_iteration_3d.h"
36
37
#include "core/config/project_settings.h"
38
39
using namespace Nav3D;
40
41
void NavRegionBuilder3D::build_iteration(NavRegionIterationBuild3D &r_build) {
42
PerformanceData &performance_data = r_build.performance_data;
43
44
performance_data.pm_polygon_count = 0;
45
performance_data.pm_edge_count = 0;
46
performance_data.pm_edge_merge_count = 0;
47
performance_data.pm_edge_connection_count = 0;
48
performance_data.pm_edge_free_count = 0;
49
50
_build_step_process_navmesh_data(r_build);
51
52
_build_step_find_edge_connection_pairs(r_build);
53
54
_build_step_merge_edge_connection_pairs(r_build);
55
56
_build_update_iteration(r_build);
57
}
58
59
void NavRegionBuilder3D::_build_step_process_navmesh_data(NavRegionIterationBuild3D &r_build) {
60
Vector<Vector3> _navmesh_vertices = r_build.navmesh_data.vertices;
61
Vector<Vector<int>> _navmesh_polygons = r_build.navmesh_data.polygons;
62
63
if (_navmesh_vertices.is_empty() || _navmesh_polygons.is_empty()) {
64
return;
65
}
66
67
PerformanceData &performance_data = r_build.performance_data;
68
Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
69
70
const Transform3D &region_transform = region_iteration->transform;
71
LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
72
73
const int vertex_count = _navmesh_vertices.size();
74
75
const Vector3 *vertices_ptr = _navmesh_vertices.ptr();
76
const Vector<int> *polygons_ptr = _navmesh_polygons.ptr();
77
78
navmesh_polygons.resize(_navmesh_polygons.size());
79
80
real_t _new_region_surface_area = 0.0;
81
AABB _new_region_bounds;
82
83
bool first_vertex = true;
84
85
for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
86
Polygon &polygon = navmesh_polygons[i];
87
polygon.id = i;
88
polygon.owner = region_iteration.ptr();
89
polygon.surface_area = 0.0;
90
91
Vector<int> polygon_indices = polygons_ptr[i];
92
93
int polygon_size = polygon_indices.size();
94
if (polygon_size < 3) {
95
continue;
96
}
97
98
const int *indices_ptr = polygon_indices.ptr();
99
100
bool polygon_valid = true;
101
102
polygon.vertices.resize(polygon_size);
103
104
{
105
real_t _new_polygon_surface_area = 0.0;
106
107
for (int j(2); j < polygon_size; j++) {
108
const Face3 face = Face3(
109
region_transform.xform(vertices_ptr[indices_ptr[0]]),
110
region_transform.xform(vertices_ptr[indices_ptr[j - 1]]),
111
region_transform.xform(vertices_ptr[indices_ptr[j]]));
112
113
_new_polygon_surface_area += face.get_area();
114
}
115
116
polygon.surface_area = _new_polygon_surface_area;
117
_new_region_surface_area += _new_polygon_surface_area;
118
}
119
120
for (int j(0); j < polygon_size; j++) {
121
int vertex_index = indices_ptr[j];
122
if (vertex_index < 0 || vertex_index >= vertex_count) {
123
polygon_valid = false;
124
break;
125
}
126
127
const Vector3 point_position = region_transform.xform(vertices_ptr[vertex_index]);
128
polygon.vertices[j] = point_position;
129
130
if (first_vertex) {
131
first_vertex = false;
132
_new_region_bounds.position = point_position;
133
} else {
134
_new_region_bounds.expand_to(point_position);
135
}
136
}
137
138
if (!polygon_valid) {
139
polygon.surface_area = 0.0;
140
polygon.vertices.clear();
141
ERR_FAIL_COND_MSG(!polygon_valid, "Corrupted navigation mesh set on region. The indices of a polygon are out of range.");
142
}
143
}
144
145
region_iteration->surface_area = _new_region_surface_area;
146
region_iteration->bounds = _new_region_bounds;
147
148
performance_data.pm_polygon_count = navmesh_polygons.size();
149
}
150
151
Nav3D::PointKey NavRegionBuilder3D::get_point_key(const Vector3 &p_pos, const Vector3 &p_cell_size) {
152
const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));
153
const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));
154
const int z = static_cast<int>(Math::floor(p_pos.z / p_cell_size.z));
155
156
PointKey p;
157
p.key = 0;
158
p.x = x;
159
p.y = y;
160
p.z = z;
161
return p;
162
}
163
164
Nav3D::EdgeKey NavRegionBuilder3D::get_edge_key(const Vector3 &p_vertex1, const Vector3 &p_vertex2, const Vector3 &p_cell_size) {
165
EdgeKey ek(get_point_key(p_vertex1, p_cell_size), get_point_key(p_vertex2, p_cell_size));
166
return ek;
167
}
168
169
void NavRegionBuilder3D::_build_step_find_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {
170
PerformanceData &performance_data = r_build.performance_data;
171
172
const Vector3 &map_cell_size = r_build.map_cell_size;
173
Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
174
LocalVector<Nav3D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
175
176
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
177
connection_pairs_map.clear();
178
179
region_iteration->internal_connections.clear();
180
region_iteration->internal_connections.resize(navmesh_polygons.size());
181
182
region_iteration->external_edges.clear();
183
184
int free_edges_count = 0;
185
int edge_merge_error_count = 0;
186
187
for (Polygon &poly : region_iteration->navmesh_polygons) {
188
for (uint32_t p = 0; p < poly.vertices.size(); p++) {
189
const int next_point = (p + 1) % poly.vertices.size();
190
const EdgeKey ek = get_edge_key(poly.vertices[p], poly.vertices[next_point], map_cell_size);
191
192
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
193
if (!pair_it) {
194
pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
195
performance_data.pm_edge_count += 1;
196
++free_edges_count;
197
}
198
EdgeConnectionPair &pair = pair_it->value;
199
if (pair.size < 2) {
200
// Add the polygon/edge tuple to this key.
201
Connection new_connection;
202
new_connection.polygon = &poly;
203
new_connection.edge = p;
204
new_connection.pathway_start = poly.vertices[p];
205
new_connection.pathway_end = poly.vertices[next_point];
206
207
pair.connections[pair.size] = new_connection;
208
++pair.size;
209
if (pair.size == 2) {
210
--free_edges_count;
211
}
212
213
} else {
214
// The edge is already connected with another edge, skip.
215
edge_merge_error_count++;
216
}
217
}
218
}
219
220
if (edge_merge_error_count > 0 && GLOBAL_GET_CACHED(bool, "navigation/3d/warnings/navmesh_edge_merge_errors")) {
221
WARN_PRINT("Navigation region synchronization had " + itos(edge_merge_error_count) + " edge error(s).\nMore than 2 edges tried to occupy the same map rasterization space.\nThis causes a logical error in the navigation mesh geometry and is commonly caused by overlap or too densely placed edges.\nConsider baking with a higher 'cell_size', greater geometry margin, and less detailed bake objects to cause fewer edges.\nConsider lowering the 'navigation/3d/merge_rasterizer_cell_scale' in the project settings.\nThis warning can be toggled under 'navigation/3d/warnings/navmesh_edge_merge_errors' in the project settings.");
222
}
223
224
performance_data.pm_edge_free_count = free_edges_count;
225
}
226
227
void NavRegionBuilder3D::_build_step_merge_edge_connection_pairs(NavRegionIterationBuild3D &r_build) {
228
PerformanceData &performance_data = r_build.performance_data;
229
230
Ref<NavRegionIteration3D> region_iteration = r_build.region_iteration;
231
232
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
233
234
for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
235
const EdgeConnectionPair &pair = pair_it.value;
236
if (pair.size == 2) {
237
// Connect edge that are shared in different polygons.
238
const Connection &c1 = pair.connections[0];
239
const Connection &c2 = pair.connections[1];
240
region_iteration->internal_connections[c1.polygon->id].push_back(c2);
241
region_iteration->internal_connections[c2.polygon->id].push_back(c1);
242
performance_data.pm_edge_merge_count += 1;
243
244
} else {
245
ERR_FAIL_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
246
247
const Connection &connection = pair.connections[0];
248
249
ConnectableEdge ce;
250
ce.ek = pair_it.key;
251
ce.polygon_index = connection.polygon->id;
252
ce.edge = connection.edge;
253
ce.pathway_start = connection.pathway_start;
254
ce.pathway_end = connection.pathway_end;
255
256
region_iteration->external_edges.push_back(ce);
257
}
258
}
259
}
260
261
void NavRegionBuilder3D::_build_update_iteration(NavRegionIterationBuild3D &r_build) {
262
ERR_FAIL_NULL(r_build.region);
263
// Stub. End of the build.
264
}
265
266