1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Math/BsAABox.h"
4#include "Math/BsRay.h"
5#include "Math/BsPlane.h"
6#include "Math/BsSphere.h"
7#include "Math/BsMath.h"
8
9namespace bs
10{
11 const AABox AABox::BOX_EMPTY = AABox(Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f));
12 const AABox AABox::UNIT_BOX = AABox(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f));
13 const AABox AABox::INF_BOX = AABox(
14 Vector3(
15 std::numeric_limits<float>::infinity(),
16 std::numeric_limits<float>::infinity(),
17 std::numeric_limits<float>::infinity()),
18 Vector3(
19 -std::numeric_limits<float>::infinity(),
20 -std::numeric_limits<float>::infinity(),
21 -std::numeric_limits<float>::infinity()));
22
23 const UINT32 AABox::CUBE_INDICES[36] =
24 {
25 // Near
26 NEAR_LEFT_BOTTOM, NEAR_LEFT_TOP, NEAR_RIGHT_TOP,
27 NEAR_LEFT_BOTTOM, NEAR_RIGHT_TOP, NEAR_RIGHT_BOTTOM,
28
29 // Far
30 FAR_RIGHT_BOTTOM, FAR_RIGHT_TOP, FAR_LEFT_TOP,
31 FAR_RIGHT_BOTTOM, FAR_LEFT_TOP, FAR_LEFT_BOTTOM,
32
33 // Left
34 FAR_LEFT_BOTTOM, FAR_LEFT_TOP, NEAR_LEFT_TOP,
35 FAR_LEFT_BOTTOM, NEAR_LEFT_TOP, NEAR_LEFT_BOTTOM,
36
37 // Right
38 NEAR_RIGHT_BOTTOM, NEAR_RIGHT_TOP, FAR_RIGHT_TOP,
39 NEAR_RIGHT_BOTTOM, FAR_RIGHT_TOP, FAR_RIGHT_BOTTOM,
40
41 // Top
42 FAR_LEFT_TOP, FAR_RIGHT_TOP, NEAR_RIGHT_TOP,
43 FAR_LEFT_TOP, NEAR_RIGHT_TOP, NEAR_LEFT_TOP,
44
45 // Bottom
46 NEAR_LEFT_BOTTOM, NEAR_RIGHT_BOTTOM, FAR_RIGHT_BOTTOM,
47 NEAR_LEFT_BOTTOM, FAR_RIGHT_BOTTOM, FAR_LEFT_BOTTOM
48 };
49
50 AABox::AABox()
51 {
52 // Default to a unit box
53 setMin(Vector3(-0.5f, -0.5f, -0.5f));
54 setMax(Vector3(0.5f, 0.5f, 0.5f));
55 }
56
57 AABox::AABox(const Vector3& min, const Vector3& max)
58 {
59 setExtents(min, max);
60 }
61
62 void AABox::setExtents(const Vector3& min, const Vector3& max)
63 {
64 mMinimum = min;
65 mMaximum = max;
66 }
67
68 void AABox::scale(const Vector3& s)
69 {
70 Vector3 center = getCenter();
71 Vector3 min = center + (mMinimum - center) * s;
72 Vector3 max = center + (mMaximum - center) * s;
73
74 setExtents(min, max);
75 }
76
77 Vector3 AABox::getCorner(Corner cornerToGet) const
78 {
79 switch(cornerToGet)
80 {
81 case FAR_LEFT_BOTTOM:
82 return mMinimum;
83 case FAR_LEFT_TOP:
84 return Vector3(mMinimum.x, mMaximum.y, mMinimum.z);
85 case FAR_RIGHT_TOP:
86 return Vector3(mMaximum.x, mMaximum.y, mMinimum.z);
87 case FAR_RIGHT_BOTTOM:
88 return Vector3(mMaximum.x, mMinimum.y, mMinimum.z);
89 case NEAR_RIGHT_BOTTOM:
90 return Vector3(mMaximum.x, mMinimum.y, mMaximum.z);
91 case NEAR_LEFT_BOTTOM:
92 return Vector3(mMinimum.x, mMinimum.y, mMaximum.z);
93 case NEAR_LEFT_TOP:
94 return Vector3(mMinimum.x, mMaximum.y, mMaximum.z);
95 case NEAR_RIGHT_TOP:
96 return mMaximum;
97 default:
98 return Vector3(BsZero);
99 }
100 }
101
102 void AABox::merge(const AABox& rhs)
103 {
104 Vector3 min = mMinimum;
105 Vector3 max = mMaximum;
106 max.max(rhs.mMaximum);
107 min.min(rhs.mMinimum);
108
109 setExtents(min, max);
110 }
111
112 void AABox::merge(const Vector3& point)
113 {
114 mMaximum.max(point);
115 mMinimum.min(point);
116 }
117
118 void AABox::transform(const Matrix4& matrix)
119 {
120 // Getting the old values so that we can use the existing merge method.
121 Vector3 oldMin = mMinimum;
122 Vector3 oldMax = mMaximum;
123
124 Vector3 currentCorner;
125 // We sequentially compute the corners in the following order :
126 // 0, 6, 5, 1, 2, 4, 7, 3
127 // This sequence allows us to only change one member at a time to get at all corners.
128
129 // For each one, we transform it using the matrix
130 // Which gives the resulting point and merge the resulting point.
131
132 // First corner
133 // min min min
134 currentCorner = oldMin;
135 merge(matrix.multiplyAffine(currentCorner));
136
137 // min,min,max
138 currentCorner.z = oldMax.z;
139 merge(matrix.multiplyAffine(currentCorner));
140
141 // min max max
142 currentCorner.y = oldMax.y;
143 merge(matrix.multiplyAffine(currentCorner));
144
145 // min max min
146 currentCorner.z = oldMin.z;
147 merge(matrix.multiplyAffine(currentCorner));
148
149 // max max min
150 currentCorner.x = oldMax.x;
151 merge(matrix.multiplyAffine(currentCorner));
152
153 // max max max
154 currentCorner.z = oldMax.z;
155 merge(matrix.multiplyAffine(currentCorner));
156
157 // max min max
158 currentCorner.y = oldMin.y;
159 merge(matrix.multiplyAffine(currentCorner));
160
161 // max min min
162 currentCorner.z = oldMin.z;
163 merge(matrix.multiplyAffine(currentCorner));
164 }
165
166 void AABox::transformAffine(const Matrix4& m)
167 {
168 Vector3 min = m.getTranslation();
169 Vector3 max = m.getTranslation();
170 for(UINT32 i = 0; i < 3; i++)
171 {
172 for(UINT32 j = 0; j < 3; j++)
173 {
174 float e = m[i][j] * mMinimum[j];
175 float f = m[i][j] * mMaximum[j];
176
177 if(e < f)
178 {
179 min[i] += e;
180 max[i] += f;
181 }
182 else
183 {
184 min[i] += f;
185 max[i] += e;
186 }
187 }
188
189 }
190
191 setExtents(min, max);
192 }
193
194 bool AABox::intersects(const AABox& b2) const
195 {
196 // Use up to 6 separating planes
197 if (mMaximum.x < b2.mMinimum.x)
198 return false;
199 if (mMaximum.y < b2.mMinimum.y)
200 return false;
201 if (mMaximum.z < b2.mMinimum.z)
202 return false;
203
204 if (mMinimum.x > b2.mMaximum.x)
205 return false;
206 if (mMinimum.y > b2.mMaximum.y)
207 return false;
208 if (mMinimum.z > b2.mMaximum.z)
209 return false;
210
211 // Otherwise, must be intersecting
212 return true;
213 }
214
215 bool AABox::intersects(const Sphere& sphere) const
216 {
217 // Use splitting planes
218 const Vector3& center = sphere.getCenter();
219 float radius = sphere.getRadius();
220 const Vector3& min = getMin();
221 const Vector3& max = getMax();
222
223 // Arvo's algorithm
224 float s, d = 0;
225 for (int i = 0; i < 3; ++i)
226 {
227 if (center[i] < min[i])
228 {
229 s = center[i] - min[i];
230 d += s * s;
231 }
232 else if(center[i] > max[i])
233 {
234 s = center[i] - max[i];
235 d += s * s;
236 }
237 }
238 return d <= radius * radius;
239 }
240
241 bool AABox::intersects(const Plane& p) const
242 {
243 return (p.getSide(*this) == Plane::BOTH_SIDE);
244 }
245
246 std::pair<bool, float> AABox::intersects(const Ray& ray) const
247 {
248 float lowt = 0.0f;
249 float t;
250 bool hit = false;
251 Vector3 hitpoint(BsZero);
252 const Vector3& min = getMin();
253 const Vector3& max = getMax();
254 const Vector3& rayorig = ray.getOrigin();
255 const Vector3& raydir = ray.getDirection();
256
257 // Check origin inside first
258 if ((rayorig.x > min.x && rayorig.y > min.y && rayorig.z > min.z) && (rayorig.x < max.x && rayorig.y < max.y && rayorig.z < max.z))
259 {
260 return std::pair<bool, float>(true, 0.0f);
261 }
262
263 // Check each face in turn, only check closest 3
264 // Min x
265 if (rayorig.x <= min.x && raydir.x > 0)
266 {
267 t = (min.x - rayorig.x) / raydir.x;
268 if (t >= 0)
269 {
270 // Substitute t back into ray and check bounds and dist
271 hitpoint = rayorig + raydir * t;
272 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
273 hitpoint.z >= min.z && hitpoint.z <= max.z &&
274 (!hit || t < lowt))
275 {
276 hit = true;
277 lowt = t;
278 }
279 }
280 }
281 // Max x
282 if (rayorig.x >= max.x && raydir.x < 0)
283 {
284 t = (max.x - rayorig.x) / raydir.x;
285 if (t >= 0)
286 {
287 // Substitute t back into ray and check bounds and dist
288 hitpoint = rayorig + raydir * t;
289 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
290 hitpoint.z >= min.z && hitpoint.z <= max.z &&
291 (!hit || t < lowt))
292 {
293 hit = true;
294 lowt = t;
295 }
296 }
297 }
298 // Min y
299 if (rayorig.y <= min.y && raydir.y > 0)
300 {
301 t = (min.y - rayorig.y) / raydir.y;
302 if (t >= 0)
303 {
304 // Substitute t back into ray and check bounds and dist
305 hitpoint = rayorig + raydir * t;
306 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
307 hitpoint.z >= min.z && hitpoint.z <= max.z &&
308 (!hit || t < lowt))
309 {
310 hit = true;
311 lowt = t;
312 }
313 }
314 }
315 // Max y
316 if (rayorig.y >= max.y && raydir.y < 0)
317 {
318 t = (max.y - rayorig.y) / raydir.y;
319 if (t >= 0)
320 {
321 // Substitute t back into ray and check bounds and dist
322 hitpoint = rayorig + raydir * t;
323 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
324 hitpoint.z >= min.z && hitpoint.z <= max.z &&
325 (!hit || t < lowt))
326 {
327 hit = true;
328 lowt = t;
329 }
330 }
331 }
332 // Min z
333 if (rayorig.z <= min.z && raydir.z > 0)
334 {
335 t = (min.z - rayorig.z) / raydir.z;
336 if (t >= 0)
337 {
338 // Substitute t back into ray and check bounds and dist
339 hitpoint = rayorig + raydir * t;
340 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
341 hitpoint.y >= min.y && hitpoint.y <= max.y &&
342 (!hit || t < lowt))
343 {
344 hit = true;
345 lowt = t;
346 }
347 }
348 }
349 // Max z
350 if (rayorig.z >= max.z && raydir.z < 0)
351 {
352 t = (max.z - rayorig.z) / raydir.z;
353 if (t >= 0)
354 {
355 // Substitute t back into ray and check bounds and dist
356 hitpoint = rayorig + raydir * t;
357 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
358 hitpoint.y >= min.y && hitpoint.y <= max.y &&
359 (!hit || t < lowt))
360 {
361 hit = true;
362 lowt = t;
363 }
364 }
365 }
366
367 return std::pair<bool, float>(hit, lowt);
368
369 }
370
371 bool AABox::intersects(const Ray& ray, float& d1, float& d2) const
372 {
373 const Vector3& min = getMin();
374 const Vector3& max = getMax();
375 const Vector3& rayorig = ray.getOrigin();
376 const Vector3& raydir = ray.getDirection();
377
378 Vector3 absDir;
379 absDir[0] = Math::abs(raydir[0]);
380 absDir[1] = Math::abs(raydir[1]);
381 absDir[2] = Math::abs(raydir[2]);
382
383 // Sort the axis, ensure check minimise floating error axis first
384 int imax = 0, imid = 1, imin = 2;
385 if (absDir[0] < absDir[2])
386 {
387 imax = 2;
388 imin = 0;
389 }
390 if (absDir[1] < absDir[imin])
391 {
392 imid = imin;
393 imin = 1;
394 }
395 else if (absDir[1] > absDir[imax])
396 {
397 imid = imax;
398 imax = 1;
399 }
400
401 float start = 0, end = Math::POS_INFINITY;
402
403#define _CALC_AXIS(i) \
404 do { \
405 float denom = 1 / raydir[i]; \
406 float newstart = (min[i] - rayorig[i]) * denom; \
407 float newend = (max[i] - rayorig[i]) * denom; \
408 if (newstart > newend) std::swap(newstart, newend); \
409 if (newstart > end || newend < start) return false; \
410 if (newstart > start) start = newstart; \
411 if (newend < end) end = newend; \
412 } while(0)
413
414 // Check each axis in turn
415
416 _CALC_AXIS(imax);
417
418 if (absDir[imid] < std::numeric_limits<float>::epsilon())
419 {
420 // Parallel with middle and minimise axis, check bounds only
421 if (rayorig[imid] < min[imid] || rayorig[imid] > max[imid] ||
422 rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
423 return false;
424 }
425 else
426 {
427 _CALC_AXIS(imid);
428
429 if (absDir[imin] < std::numeric_limits<float>::epsilon())
430 {
431 // Parallel with minimise axis, check bounds only
432 if (rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
433 return false;
434 }
435 else
436 {
437 _CALC_AXIS(imin);
438 }
439 }
440#undef _CALC_AXIS
441
442 d1 = start;
443 d2 = end;
444
445 return true;
446 }
447
448 Vector3 AABox::getCenter() const
449 {
450 return Vector3(
451 (mMaximum.x + mMinimum.x) * 0.5f,
452 (mMaximum.y + mMinimum.y) * 0.5f,
453 (mMaximum.z + mMinimum.z) * 0.5f);
454 }
455
456 Vector3 AABox::getSize() const
457 {
458 return mMaximum - mMinimum;
459 }
460
461 Vector3 AABox::getHalfSize() const
462 {
463 return (mMaximum - mMinimum) * 0.5;
464 }
465
466 float AABox::getRadius() const
467 {
468 return ((mMaximum - mMinimum) * 0.5).length();
469 }
470
471 float AABox::getVolume() const
472 {
473 Vector3 diff = mMaximum - mMinimum;
474 return diff.x * diff.y * diff.z;
475 }
476
477 bool AABox::contains(const Vector3& v) const
478 {
479 return mMinimum.x <= v.x && v.x <= mMaximum.x &&
480 mMinimum.y <= v.y && v.y <= mMaximum.y &&
481 mMinimum.z <= v.z && v.z <= mMaximum.z;
482 }
483
484 bool AABox::contains(const Vector3& v, float extra) const
485 {
486 return (mMinimum.x - extra) <= v.x && v.x <= (mMaximum.x + extra) &&
487 (mMinimum.y - extra) <= v.y && v.y <= (mMaximum.y + extra) &&
488 (mMinimum.z - extra) <= v.z && v.z <= (mMaximum.z + extra);
489 }
490
491 bool AABox::contains(const AABox& other) const
492 {
493 return this->mMinimum.x <= other.mMinimum.x &&
494 this->mMinimum.y <= other.mMinimum.y &&
495 this->mMinimum.z <= other.mMinimum.z &&
496 other.mMaximum.x <= this->mMaximum.x &&
497 other.mMaximum.y <= this->mMaximum.y &&
498 other.mMaximum.z <= this->mMaximum.z;
499 }
500
501 bool AABox::operator== (const AABox& rhs) const
502 {
503 return this->mMinimum == rhs.mMinimum &&
504 this->mMaximum == rhs.mMaximum;
505 }
506
507 bool AABox::operator!= (const AABox& rhs) const
508 {
509 return !(*this == rhs);
510 }
511}
512
513