Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/math/aabb.cpp
9903 views
1
/**************************************************************************/
2
/* aabb.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 "aabb.h"
32
33
#include "core/string/ustring.h"
34
#include "core/variant/variant.h"
35
36
real_t AABB::get_volume() const {
37
return size.x * size.y * size.z;
38
}
39
40
void AABB::merge_with(const AABB &p_aabb) {
41
#ifdef MATH_CHECKS
42
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
43
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
44
}
45
#endif
46
Vector3 beg_1, beg_2;
47
Vector3 end_1, end_2;
48
Vector3 min, max;
49
50
beg_1 = position;
51
beg_2 = p_aabb.position;
52
end_1 = size + beg_1;
53
end_2 = p_aabb.size + beg_2;
54
55
min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x;
56
min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y;
57
min.z = (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z;
58
59
max.x = (end_1.x > end_2.x) ? end_1.x : end_2.x;
60
max.y = (end_1.y > end_2.y) ? end_1.y : end_2.y;
61
max.z = (end_1.z > end_2.z) ? end_1.z : end_2.z;
62
63
position = min;
64
size = max - min;
65
}
66
67
bool AABB::is_equal_approx(const AABB &p_aabb) const {
68
return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
69
}
70
71
bool AABB::is_same(const AABB &p_aabb) const {
72
return position.is_same(p_aabb.position) && size.is_same(p_aabb.size);
73
}
74
75
bool AABB::is_finite() const {
76
return position.is_finite() && size.is_finite();
77
}
78
79
AABB AABB::intersection(const AABB &p_aabb) const {
80
#ifdef MATH_CHECKS
81
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
82
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
83
}
84
#endif
85
Vector3 src_min = position;
86
Vector3 src_max = position + size;
87
Vector3 dst_min = p_aabb.position;
88
Vector3 dst_max = p_aabb.position + p_aabb.size;
89
90
Vector3 min, max;
91
92
if (src_min.x > dst_max.x || src_max.x < dst_min.x) {
93
return AABB();
94
} else {
95
min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
96
max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
97
}
98
99
if (src_min.y > dst_max.y || src_max.y < dst_min.y) {
100
return AABB();
101
} else {
102
min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
103
max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
104
}
105
106
if (src_min.z > dst_max.z || src_max.z < dst_min.z) {
107
return AABB();
108
} else {
109
min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
110
max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
111
}
112
113
return AABB(min, max - min);
114
}
115
116
// Note that this routine returns the BACKTRACKED (i.e. behind the ray origin)
117
// intersection point + normal if INSIDE the AABB.
118
// The caller can therefore decide when INSIDE whether to use the
119
// backtracked intersection, or use p_from as the intersection, and
120
// carry on progressing without e.g. reflecting against the normal.
121
bool AABB::find_intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, bool &r_inside, Vector3 *r_intersection_point, Vector3 *r_normal) const {
122
#ifdef MATH_CHECKS
123
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) {
124
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
125
}
126
#endif
127
Vector3 end = position + size;
128
real_t tmin = -1e20;
129
real_t tmax = 1e20;
130
int axis = 0;
131
132
// Make sure r_inside is always initialized,
133
// to prevent reading uninitialized data in the client code.
134
r_inside = false;
135
136
for (int i = 0; i < 3; i++) {
137
if (p_dir[i] == 0) {
138
if ((p_from[i] < position[i]) || (p_from[i] > end[i])) {
139
return false;
140
}
141
} else { // ray not parallel to planes in this direction
142
real_t t1 = (position[i] - p_from[i]) / p_dir[i];
143
real_t t2 = (end[i] - p_from[i]) / p_dir[i];
144
145
if (t1 > t2) {
146
SWAP(t1, t2);
147
}
148
if (t1 >= tmin) {
149
tmin = t1;
150
axis = i;
151
}
152
if (t2 < tmax) {
153
if (t2 < 0) {
154
return false;
155
}
156
tmax = t2;
157
}
158
if (tmin > tmax) {
159
return false;
160
}
161
}
162
}
163
164
// Did the ray start from inside the box?
165
// In which case the intersection returned is the point of entry
166
// (behind the ray start) or the calling routine can use the ray origin as intersection point.
167
r_inside = tmin < 0;
168
169
if (r_intersection_point) {
170
*r_intersection_point = p_from + p_dir * tmin;
171
172
// Prevent float error by making sure the point is exactly
173
// on the AABB border on the relevant axis.
174
r_intersection_point->coord[axis] = (p_dir[axis] >= 0) ? position.coord[axis] : end.coord[axis];
175
}
176
if (r_normal) {
177
*r_normal = Vector3();
178
(*r_normal)[axis] = (p_dir[axis] >= 0) ? -1 : 1;
179
}
180
181
return true;
182
}
183
184
bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_intersection_point, Vector3 *r_normal) const {
185
#ifdef MATH_CHECKS
186
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) {
187
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
188
}
189
#endif
190
real_t min = 0, max = 1;
191
int axis = 0;
192
real_t sign = 0;
193
194
for (int i = 0; i < 3; i++) {
195
real_t seg_from = p_from[i];
196
real_t seg_to = p_to[i];
197
real_t box_begin = position[i];
198
real_t box_end = box_begin + size[i];
199
real_t cmin, cmax;
200
real_t csign;
201
202
if (seg_from < seg_to) {
203
if (seg_from > box_end || seg_to < box_begin) {
204
return false;
205
}
206
real_t length = seg_to - seg_from;
207
cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0;
208
cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1;
209
csign = -1.0;
210
211
} else {
212
if (seg_to > box_end || seg_from < box_begin) {
213
return false;
214
}
215
real_t length = seg_to - seg_from;
216
cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0;
217
cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1;
218
csign = 1.0;
219
}
220
221
if (cmin > min) {
222
min = cmin;
223
axis = i;
224
sign = csign;
225
}
226
if (cmax < max) {
227
max = cmax;
228
}
229
if (max < min) {
230
return false;
231
}
232
}
233
234
Vector3 rel = p_to - p_from;
235
236
if (r_normal) {
237
Vector3 normal;
238
normal[axis] = sign;
239
*r_normal = normal;
240
}
241
242
if (r_intersection_point) {
243
*r_intersection_point = p_from + rel * min;
244
}
245
246
return true;
247
}
248
249
bool AABB::intersects_plane(const Plane &p_plane) const {
250
Vector3 points[8] = {
251
Vector3(position.x, position.y, position.z),
252
Vector3(position.x, position.y, position.z + size.z),
253
Vector3(position.x, position.y + size.y, position.z),
254
Vector3(position.x, position.y + size.y, position.z + size.z),
255
Vector3(position.x + size.x, position.y, position.z),
256
Vector3(position.x + size.x, position.y, position.z + size.z),
257
Vector3(position.x + size.x, position.y + size.y, position.z),
258
Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
259
};
260
261
bool over = false;
262
bool under = false;
263
264
for (int i = 0; i < 8; i++) {
265
if (p_plane.distance_to(points[i]) > 0) {
266
over = true;
267
} else {
268
under = true;
269
}
270
}
271
272
return under && over;
273
}
274
275
Vector3 AABB::get_longest_axis() const {
276
Vector3 axis(1, 0, 0);
277
real_t max_size = size.x;
278
279
if (size.y > max_size) {
280
axis = Vector3(0, 1, 0);
281
max_size = size.y;
282
}
283
284
if (size.z > max_size) {
285
axis = Vector3(0, 0, 1);
286
}
287
288
return axis;
289
}
290
291
int AABB::get_longest_axis_index() const {
292
int axis = 0;
293
real_t max_size = size.x;
294
295
if (size.y > max_size) {
296
axis = 1;
297
max_size = size.y;
298
}
299
300
if (size.z > max_size) {
301
axis = 2;
302
}
303
304
return axis;
305
}
306
307
Vector3 AABB::get_shortest_axis() const {
308
Vector3 axis(1, 0, 0);
309
real_t min_size = size.x;
310
311
if (size.y < min_size) {
312
axis = Vector3(0, 1, 0);
313
min_size = size.y;
314
}
315
316
if (size.z < min_size) {
317
axis = Vector3(0, 0, 1);
318
}
319
320
return axis;
321
}
322
323
int AABB::get_shortest_axis_index() const {
324
int axis = 0;
325
real_t min_size = size.x;
326
327
if (size.y < min_size) {
328
axis = 1;
329
min_size = size.y;
330
}
331
332
if (size.z < min_size) {
333
axis = 2;
334
}
335
336
return axis;
337
}
338
339
AABB AABB::merge(const AABB &p_with) const {
340
AABB aabb = *this;
341
aabb.merge_with(p_with);
342
return aabb;
343
}
344
345
AABB AABB::expand(const Vector3 &p_vector) const {
346
AABB aabb = *this;
347
aabb.expand_to(p_vector);
348
return aabb;
349
}
350
351
AABB AABB::grow(real_t p_by) const {
352
AABB aabb = *this;
353
aabb.grow_by(p_by);
354
return aabb;
355
}
356
357
void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const {
358
ERR_FAIL_INDEX(p_edge, 12);
359
switch (p_edge) {
360
case 0: {
361
r_from = Vector3(position.x + size.x, position.y, position.z);
362
r_to = Vector3(position.x, position.y, position.z);
363
} break;
364
case 1: {
365
r_from = Vector3(position.x + size.x, position.y, position.z + size.z);
366
r_to = Vector3(position.x + size.x, position.y, position.z);
367
} break;
368
case 2: {
369
r_from = Vector3(position.x, position.y, position.z + size.z);
370
r_to = Vector3(position.x + size.x, position.y, position.z + size.z);
371
372
} break;
373
case 3: {
374
r_from = Vector3(position.x, position.y, position.z);
375
r_to = Vector3(position.x, position.y, position.z + size.z);
376
377
} break;
378
case 4: {
379
r_from = Vector3(position.x, position.y + size.y, position.z);
380
r_to = Vector3(position.x + size.x, position.y + size.y, position.z);
381
} break;
382
case 5: {
383
r_from = Vector3(position.x + size.x, position.y + size.y, position.z);
384
r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
385
} break;
386
case 6: {
387
r_from = Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
388
r_to = Vector3(position.x, position.y + size.y, position.z + size.z);
389
390
} break;
391
case 7: {
392
r_from = Vector3(position.x, position.y + size.y, position.z + size.z);
393
r_to = Vector3(position.x, position.y + size.y, position.z);
394
395
} break;
396
case 8: {
397
r_from = Vector3(position.x, position.y, position.z + size.z);
398
r_to = Vector3(position.x, position.y + size.y, position.z + size.z);
399
400
} break;
401
case 9: {
402
r_from = Vector3(position.x, position.y, position.z);
403
r_to = Vector3(position.x, position.y + size.y, position.z);
404
405
} break;
406
case 10: {
407
r_from = Vector3(position.x + size.x, position.y, position.z);
408
r_to = Vector3(position.x + size.x, position.y + size.y, position.z);
409
410
} break;
411
case 11: {
412
r_from = Vector3(position.x + size.x, position.y, position.z + size.z);
413
r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
414
415
} break;
416
}
417
}
418
419
Variant AABB::intersects_segment_bind(const Vector3 &p_from, const Vector3 &p_to) const {
420
Vector3 inters;
421
if (intersects_segment(p_from, p_to, &inters)) {
422
return inters;
423
}
424
return Variant();
425
}
426
427
Variant AABB::intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const {
428
Vector3 inters;
429
bool inside = false;
430
431
if (find_intersects_ray(p_from, p_dir, inside, &inters)) {
432
// When inside the intersection point may be BEHIND the ray,
433
// so for general use we return the ray origin.
434
if (inside) {
435
return p_from;
436
}
437
438
return inters;
439
}
440
return Variant();
441
}
442
443
AABB::operator String() const {
444
return "[P: " + String(position) + ", S: " + String(size) + "]";
445
}
446
447