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 | |
9 | namespace 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 ) 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 | |