Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/math/dynamic_bvh.h
9905 views
1
/**************************************************************************/
2
/* dynamic_bvh.h */
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
#pragma once
32
33
#include "core/math/aabb.h"
34
#include "core/templates/list.h"
35
#include "core/templates/local_vector.h"
36
#include "core/templates/paged_allocator.h"
37
#include "core/typedefs.h"
38
39
// Based on bullet Dbvh
40
41
/*
42
Bullet Continuous Collision Detection and Physics Library
43
Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org
44
45
This software is provided 'as-is', without any express or implied warranty.
46
In no event will the authors be held liable for any damages arising from the use of this software.
47
Permission is granted to anyone to use this software for any purpose,
48
including commercial applications, and to alter it and redistribute it freely,
49
subject to the following restrictions:
50
51
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
52
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
53
3. This notice may not be removed or altered from any source distribution.
54
*/
55
56
///DynamicBVH implementation by Nathanael Presson
57
// The DynamicBVH class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree).
58
59
class DynamicBVH {
60
struct Node;
61
62
public:
63
struct ID {
64
Node *node = nullptr;
65
66
public:
67
_FORCE_INLINE_ bool is_valid() const { return node != nullptr; }
68
};
69
70
private:
71
struct Volume {
72
Vector3 min, max;
73
74
_FORCE_INLINE_ Vector3 get_center() const { return ((min + max) / 2); }
75
_FORCE_INLINE_ Vector3 get_length() const { return (max - min); }
76
77
_FORCE_INLINE_ bool contains(const Volume &a) const {
78
return ((min.x <= a.min.x) &&
79
(min.y <= a.min.y) &&
80
(min.z <= a.min.z) &&
81
(max.x >= a.max.x) &&
82
(max.y >= a.max.y) &&
83
(max.z >= a.max.z));
84
}
85
86
_FORCE_INLINE_ Volume merge(const Volume &b) const {
87
Volume r;
88
for (int i = 0; i < 3; ++i) {
89
if (min[i] < b.min[i]) {
90
r.min[i] = min[i];
91
} else {
92
r.min[i] = b.min[i];
93
}
94
if (max[i] > b.max[i]) {
95
r.max[i] = max[i];
96
} else {
97
r.max[i] = b.max[i];
98
}
99
}
100
return r;
101
}
102
103
_FORCE_INLINE_ real_t get_size() const {
104
const Vector3 edges = get_length();
105
return (edges.x * edges.y * edges.z +
106
edges.x + edges.y + edges.z);
107
}
108
109
_FORCE_INLINE_ bool is_not_equal_to(const Volume &b) const {
110
return ((min.x != b.min.x) ||
111
(min.y != b.min.y) ||
112
(min.z != b.min.z) ||
113
(max.x != b.max.x) ||
114
(max.y != b.max.y) ||
115
(max.z != b.max.z));
116
}
117
118
_FORCE_INLINE_ real_t get_proximity_to(const Volume &b) const {
119
const Vector3 d = (min + max) - (b.min + b.max);
120
return (Math::abs(d.x) + Math::abs(d.y) + Math::abs(d.z));
121
}
122
123
_FORCE_INLINE_ int select_by_proximity(const Volume &a, const Volume &b) const {
124
return (get_proximity_to(a) < get_proximity_to(b) ? 0 : 1);
125
}
126
127
//
128
_FORCE_INLINE_ bool intersects(const Volume &b) const {
129
return ((min.x <= b.max.x) &&
130
(max.x >= b.min.x) &&
131
(min.y <= b.max.y) &&
132
(max.y >= b.min.y) &&
133
(min.z <= b.max.z) &&
134
(max.z >= b.min.z));
135
}
136
137
_FORCE_INLINE_ bool intersects_convex(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const {
138
Vector3 half_extents = (max - min) * 0.5;
139
Vector3 ofs = min + half_extents;
140
141
for (int i = 0; i < p_plane_count; i++) {
142
const Plane &p = p_planes[i];
143
Vector3 point(
144
(p.normal.x > 0) ? -half_extents.x : half_extents.x,
145
(p.normal.y > 0) ? -half_extents.y : half_extents.y,
146
(p.normal.z > 0) ? -half_extents.z : half_extents.z);
147
point += ofs;
148
if (p.is_point_over(point)) {
149
return false;
150
}
151
}
152
153
// Make sure all points in the shape aren't fully separated from the AABB on
154
// each axis.
155
int bad_point_counts_positive[3] = { 0 };
156
int bad_point_counts_negative[3] = { 0 };
157
158
for (int k = 0; k < 3; k++) {
159
for (int i = 0; i < p_point_count; i++) {
160
if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) {
161
bad_point_counts_positive[k]++;
162
}
163
if (p_points[i].coord[k] < ofs.coord[k] - half_extents.coord[k]) {
164
bad_point_counts_negative[k]++;
165
}
166
}
167
168
if (bad_point_counts_negative[k] == p_point_count) {
169
return false;
170
}
171
if (bad_point_counts_positive[k] == p_point_count) {
172
return false;
173
}
174
}
175
176
return true;
177
}
178
};
179
180
struct Node {
181
Volume volume;
182
Node *parent = nullptr;
183
union {
184
Node *children[2];
185
void *data;
186
};
187
188
_FORCE_INLINE_ bool is_leaf() const { return children[1] == nullptr; }
189
_FORCE_INLINE_ bool is_internal() const { return (!is_leaf()); }
190
191
_FORCE_INLINE_ int get_index_in_parent() const {
192
ERR_FAIL_NULL_V(parent, 0);
193
return (parent->children[1] == this) ? 1 : 0;
194
}
195
void get_max_depth(int depth, int &maxdepth) {
196
if (is_internal()) {
197
children[0]->get_max_depth(depth + 1, maxdepth);
198
children[1]->get_max_depth(depth + 1, maxdepth);
199
} else {
200
maxdepth = MAX(maxdepth, depth);
201
}
202
}
203
204
//
205
int count_leaves() const {
206
if (is_internal()) {
207
return children[0]->count_leaves() + children[1]->count_leaves();
208
} else {
209
return (1);
210
}
211
}
212
213
bool is_left_of_axis(const Vector3 &org, const Vector3 &axis) const {
214
return axis.dot(volume.get_center() - org) <= 0;
215
}
216
217
Node() {
218
children[0] = nullptr;
219
children[1] = nullptr;
220
}
221
};
222
223
PagedAllocator<Node> node_allocator;
224
// Fields
225
Node *bvh_root = nullptr;
226
int lkhd = -1;
227
int total_leaves = 0;
228
uint32_t opath = 0;
229
uint32_t index = 0;
230
231
enum {
232
ALLOCA_STACK_SIZE = 128
233
};
234
235
_FORCE_INLINE_ void _delete_node(Node *p_node);
236
void _recurse_delete_node(Node *p_node);
237
_FORCE_INLINE_ Node *_create_node(Node *p_parent, void *p_data);
238
_FORCE_INLINE_ DynamicBVH::Node *_create_node_with_volume(Node *p_parent, const Volume &p_volume, void *p_data);
239
_FORCE_INLINE_ void _insert_leaf(Node *p_root, Node *p_leaf);
240
_FORCE_INLINE_ Node *_remove_leaf(Node *leaf);
241
void _fetch_leaves(Node *p_root, LocalVector<Node *> &r_leaves, int p_depth = -1);
242
static int _split(Node **leaves, int p_count, const Vector3 &p_org, const Vector3 &p_axis);
243
static Volume _bounds(Node **leaves, int p_count);
244
void _bottom_up(Node **leaves, int p_count);
245
Node *_top_down(Node **leaves, int p_count, int p_bu_threshold);
246
Node *_node_sort(Node *n, Node *&r);
247
248
_FORCE_INLINE_ void _update(Node *leaf, int lookahead = -1);
249
250
void _extract_leaves(Node *p_node, List<ID> *r_elements);
251
252
_FORCE_INLINE_ bool _ray_aabb(const Vector3 &rayFrom, const Vector3 &rayInvDirection, const unsigned int raySign[3], const Vector3 bounds[2], real_t &tmin, real_t lambda_min, real_t lambda_max) {
253
real_t tmax, tymin, tymax, tzmin, tzmax;
254
tmin = (bounds[raySign[0]].x - rayFrom.x) * rayInvDirection.x;
255
tmax = (bounds[1 - raySign[0]].x - rayFrom.x) * rayInvDirection.x;
256
tymin = (bounds[raySign[1]].y - rayFrom.y) * rayInvDirection.y;
257
tymax = (bounds[1 - raySign[1]].y - rayFrom.y) * rayInvDirection.y;
258
259
if ((tmin > tymax) || (tymin > tmax)) {
260
return false;
261
}
262
263
if (tymin > tmin) {
264
tmin = tymin;
265
}
266
267
if (tymax < tmax) {
268
tmax = tymax;
269
}
270
271
tzmin = (bounds[raySign[2]].z - rayFrom.z) * rayInvDirection.z;
272
tzmax = (bounds[1 - raySign[2]].z - rayFrom.z) * rayInvDirection.z;
273
274
if ((tmin > tzmax) || (tzmin > tmax)) {
275
return false;
276
}
277
if (tzmin > tmin) {
278
tmin = tzmin;
279
}
280
if (tzmax < tmax) {
281
tmax = tzmax;
282
}
283
return ((tmin < lambda_max) && (tmax > lambda_min));
284
}
285
286
public:
287
// Methods
288
void clear();
289
bool is_empty() const { return (nullptr == bvh_root); }
290
void optimize_bottom_up();
291
void optimize_top_down(int bu_threshold = 128);
292
void optimize_incremental(int passes);
293
ID insert(const AABB &p_box, void *p_userdata);
294
bool update(const ID &p_id, const AABB &p_box);
295
void remove(const ID &p_id);
296
void get_elements(List<ID> *r_elements);
297
298
int get_leaf_count() const;
299
int get_max_depth() const;
300
301
/* Discouraged, but works as a reference on how it must be used */
302
struct DefaultQueryResult {
303
virtual bool operator()(void *p_data) = 0; //return true whether you want to continue the query
304
virtual ~DefaultQueryResult() {}
305
};
306
307
template <typename QueryResult>
308
_FORCE_INLINE_ void aabb_query(const AABB &p_aabb, QueryResult &r_result);
309
template <typename QueryResult>
310
_FORCE_INLINE_ void convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result);
311
template <typename QueryResult>
312
_FORCE_INLINE_ void ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result);
313
314
void set_index(uint32_t p_index);
315
uint32_t get_index() const;
316
317
~DynamicBVH();
318
};
319
320
template <typename QueryResult>
321
void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) {
322
if (!bvh_root) {
323
return;
324
}
325
326
Volume volume;
327
volume.min = p_box.position;
328
volume.max = p_box.position + p_box.size;
329
330
const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
331
const Node **stack = alloca_stack;
332
stack[0] = bvh_root;
333
int32_t depth = 1;
334
int32_t threshold = ALLOCA_STACK_SIZE - 2;
335
336
LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time.
337
338
do {
339
depth--;
340
const Node *n = stack[depth];
341
if (n->volume.intersects(volume)) {
342
if (n->is_internal()) {
343
if (depth > threshold) {
344
if (aux_stack.is_empty()) {
345
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
346
memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
347
alloca_stack = nullptr;
348
} else {
349
aux_stack.resize(aux_stack.size() * 2);
350
}
351
stack = aux_stack.ptr();
352
threshold = aux_stack.size() - 2;
353
}
354
stack[depth++] = n->children[0];
355
stack[depth++] = n->children[1];
356
} else {
357
if (r_result(n->data)) {
358
return;
359
}
360
}
361
}
362
} while (depth > 0);
363
}
364
365
template <typename QueryResult>
366
void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result) {
367
if (!bvh_root) {
368
return;
369
}
370
371
//generate a volume anyway to improve pre-testing
372
Volume volume;
373
for (int i = 0; i < p_point_count; i++) {
374
if (i == 0) {
375
volume.min = p_points[0];
376
volume.max = p_points[0];
377
} else {
378
volume.min = volume.min.min(p_points[i]);
379
volume.max = volume.max.max(p_points[i]);
380
}
381
}
382
383
const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
384
const Node **stack = alloca_stack;
385
stack[0] = bvh_root;
386
int32_t depth = 1;
387
int32_t threshold = ALLOCA_STACK_SIZE - 2;
388
389
LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time.
390
391
do {
392
depth--;
393
const Node *n = stack[depth];
394
if (n->volume.intersects(volume) && n->volume.intersects_convex(p_planes, p_plane_count, p_points, p_point_count)) {
395
if (n->is_internal()) {
396
if (depth > threshold) {
397
if (aux_stack.is_empty()) {
398
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
399
memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
400
alloca_stack = nullptr;
401
} else {
402
aux_stack.resize(aux_stack.size() * 2);
403
}
404
stack = aux_stack.ptr();
405
threshold = aux_stack.size() - 2;
406
}
407
stack[depth++] = n->children[0];
408
stack[depth++] = n->children[1];
409
} else {
410
if (r_result(n->data)) {
411
return;
412
}
413
}
414
}
415
} while (depth > 0);
416
}
417
template <typename QueryResult>
418
void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result) {
419
if (!bvh_root) {
420
return;
421
}
422
423
Vector3 ray_dir = (p_to - p_from);
424
ray_dir.normalize();
425
426
///what about division by zero? --> just set rayDirection[i] to INF/B3_LARGE_FLOAT
427
Vector3 inv_dir;
428
inv_dir[0] = ray_dir[0] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[0];
429
inv_dir[1] = ray_dir[1] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[1];
430
inv_dir[2] = ray_dir[2] == real_t(0.0) ? real_t(1e20) : real_t(1.0) / ray_dir[2];
431
unsigned int signs[3] = { inv_dir[0] < 0.0, inv_dir[1] < 0.0, inv_dir[2] < 0.0 };
432
433
real_t lambda_max = ray_dir.dot(p_to - p_from);
434
435
Vector3 bounds[2];
436
437
const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
438
const Node **stack = alloca_stack;
439
stack[0] = bvh_root;
440
int32_t depth = 1;
441
int32_t threshold = ALLOCA_STACK_SIZE - 2;
442
443
LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time.
444
445
do {
446
depth--;
447
const Node *node = stack[depth];
448
bounds[0] = node->volume.min;
449
bounds[1] = node->volume.max;
450
real_t tmin = 1.f, lambda_min = 0.f;
451
unsigned int result1 = false;
452
result1 = _ray_aabb(p_from, inv_dir, signs, bounds, tmin, lambda_min, lambda_max);
453
if (result1) {
454
if (node->is_internal()) {
455
if (depth > threshold) {
456
if (aux_stack.is_empty()) {
457
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
458
memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
459
alloca_stack = nullptr;
460
} else {
461
aux_stack.resize(aux_stack.size() * 2);
462
}
463
stack = aux_stack.ptr();
464
threshold = aux_stack.size() - 2;
465
}
466
stack[depth++] = node->children[0];
467
stack[depth++] = node->children[1];
468
} else {
469
if (r_result(node->data)) {
470
return;
471
}
472
}
473
}
474
} while (depth > 0);
475
}
476
477