jaulib v1.3.8
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
aabbox3f.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef JAU_MATH_GEOM_AABBOX3F_HPP_
12#define JAU_MATH_GEOM_AABBOX3F_HPP_
13
14#include <jau/basic_types.hpp>
15#include <jau/debug.hpp>
16#include <jau/functional.hpp>
17#include <jau/math/vec3f.hpp>
18#include <jau/math/mat4f.hpp>
19
20namespace jau::math::geom {
21
22
23 /** \addtogroup Math
24 *
25 * @{
26 */
27
28 /**
29 * Axis Aligned Bounding Box. Defined by two 3D coordinates (low and high)
30 * The low being the the lower left corner of the box, and the high being the upper
31 * right corner of the box.
32 *
33 * A few references for collision detection, intersections:
34 * - Brian Smits: [Efficiency Issues for Ray Tracing.](http://www.cs.utah.edu/~bes/papers/fastRT/) Journal of Graphics Tools (1998).
35 * - Amy Williams. et al.: [An Efficient and Robust Ray-Box Intersection Algorithm.](http://www.cs.utah.edu/~awilliam/box/) Journal of Graphics Tools (2005).
36 * - Tavian Barnes: [Fast, Branchless Ray/Bounding Box Intersections](https://tavianator.com/2015/ray_box_nan.html)
37 * - http://www.codercorner.com/RayAABB.cpp (Updated October 2001)
38 * - http://www.realtimerendering.com/intersections.html
39 * - http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c
40 * - http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter0.htm
41 * - http://realtimecollisiondetection.net/files/levine_swept_sat.txt
42 */
43 class AABBox3f {
44 private:
45 /** bottom left (low) */
46 Point3f m_lo;
47 /** top right (high) */
48 Point3f m_hi;
49 /** center */
50 Point3f m_center;
51
52 public:
53 /**
54 * Create an Axis Aligned bounding box (aabbox3f)
55 * where the low and and high MAX float Values.
56 */
57 AABBox3f() noexcept {
58 reset();
59 }
60
61 /**
62 * Create an aabbox3f with given bl (low) and tr (high)
63 */
64 AABBox3f(const Point3f& bl_, const Point3f& tr_) noexcept
65 : m_lo( bl_ ), m_hi( tr_ ) {
66 }
67
68 constexpr AABBox3f(const AABBox3f& o) noexcept = default;
69 constexpr AABBox3f(AABBox3f&& o) noexcept = default;
70 AABBox3f& operator=(const AABBox3f&) noexcept = default;
71 AABBox3f& operator=(AABBox3f&&) noexcept = default;
72
73 private:
74 void setHigh(const float hx, const float hy, const float hz) noexcept {
75 m_hi.set(hx, hy, hz);
76 }
77
78 void setLow(const float lx, const float ly, const float lz) noexcept {
79 m_lo.set(lx, ly, lz);
80 }
81
82 void computeCenter() noexcept {
83 ( ( m_center = m_hi ) += m_lo ) *= 0.5f;
84 }
85
86 public:
87 /**
88 * Reset this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit.
89 * @return this aabbox3f for chaining
90 */
91 AABBox3f& reset() noexcept {
92 setLow(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
93 setHigh(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
94 m_center.set(0, 0, 0);
95 return *this;
96 }
97
98 /** Returns the maximum right-top-near (xyz) coordinate */
99 const Point3f& high() const noexcept { return m_hi; }
100
101 /** Returns the minimum left-bottom-far (xyz) coordinate */
102 const Point3f& low() const noexcept { return m_lo; }
103
104 /** Returns computed center of this aabbox3f of low() and high(). */
105 const Point3f& center() const noexcept { return m_center; }
106
107 /**
108 * Get the size of this aabbox3f where the size is represented by the
109 * length of the vector between low and high.
110 * @return a float representing the size of the aabbox3f
111 */
112 float size() const noexcept { return m_lo.dist(m_hi); }
113
114 float width() const noexcept { return m_hi.x - m_lo.x; }
115
116 float height() const noexcept { return m_hi.y - m_lo.y; }
117
118 float depth() const noexcept { return m_hi.z - m_lo.z; }
119
120 /** Returns the volume, i.e. width * height * depth */
121 float volume() const noexcept { return width() * height() * depth(); }
122
123 /** Return true if {@link #getVolume()} is {@link FloatUtil#isZero(float)}, considering epsilon. */
124 bool hasZeroVolume() const noexcept { return jau::is_zero( volume() ); }
125
126 /** Returns the assumed 2D area, i.e. width * height while assuming low and high lies on same plane. */
127 float area2D() const noexcept { return width() * height(); }
128
129 /** Return true if {@link #get2DArea()} is {@link FloatUtil#isZero(float)}, considering epsilon. */
130 bool hasZeroArea2D() const noexcept { return jau::is_zero( area2D() ); }
131
132 /**
133 * Set size of the aabbox3f specifying the coordinates
134 * of the low and high.
135 *
136 * @param low min xyz-coordinates
137 * @param high max xyz-coordinates
138 * @return this aabbox3f for chaining
139 */
140 AABBox3f& setSize(const float low[], const float high[]) noexcept {
141 return setSize(low[0],low[1],low[2], high[0],high[1],high[2]);
142 }
143
144 /**
145 * Set size of the aabbox3f specifying the coordinates
146 * of the low and high.
147 *
148 * @param lx min x-coordinate
149 * @param ly min y-coordnate
150 * @param lz min z-coordinate
151 * @param hx max x-coordinate
152 * @param hy max y-coordinate
153 * @param hz max z-coordinate
154 * @return this aabbox3f for chaining
155 */
156 AABBox3f& setSize(const float lx, const float ly, const float lz,
157 const float hx, const float hy, const float hz) noexcept {
158 m_lo.set(lx, ly, lz);
159 m_hi.set(hx, hy, hz);
160 computeCenter();
161 return *this;
162 }
163
164 /**
165 * Set size of the aabbox3f specifying the coordinates
166 * of the low and high.
167 *
168 * @param low min xyz-coordinates
169 * @param high max xyz-coordinates
170 * @return this aabbox3f for chaining
171 */
172 AABBox3f& setSize(const Vec3f& low, const Vec3f& high) noexcept {
173 m_lo = low;
174 m_hi = high;
175 computeCenter();
176 return *this;
177 }
178
179 /**
180 * Resize width of this aabbox3f with explicit left- and right delta values
181 * @param deltaLeft positive value will expand width, otherwise shrink width
182 * @param deltaRight positive value will expand width, otherwise shrink width
183 * @return this aabbox3f for chaining
184 */
185 AABBox3f& resizeWidth(const float deltaLeft, const float deltaRight) noexcept {
186 bool mod = false;
187 if( !jau::is_zero(deltaLeft) ) {
188 m_lo.x -= deltaLeft;
189 mod = true;
190 }
191 if( !jau::is_zero(deltaRight) ) {
192 m_hi.x += deltaRight;
193 mod = true;
194 }
195 if( mod ) {
196 computeCenter();
197 }
198 return *this;
199 }
200
201 /**
202 * Resize height of this aabbox3f with explicit bottom- and top delta values
203 * @param deltaBottom positive value will expand height, otherwise shrink height
204 * @param deltaTop positive value will expand height, otherwise shrink height
205 * @return this aabbox3f for chaining
206 */
207 AABBox3f& resizeHeight(const float deltaBottom, const float deltaTop) noexcept {
208 bool mod = false;
209 if( !jau::is_zero(deltaBottom) ) {
210 m_lo.y -= deltaBottom;
211 mod = true;
212 }
213 if( !jau::is_zero(deltaTop) ) {
214 m_lo.y += deltaTop;
215 mod = true;
216 }
217 if( mod ) {
218 computeCenter();
219 }
220 return *this;
221 }
222
223 /**
224 * Resize the aabbox3f to encapsulate another AABox
225 * @param newBox aabbox3f to be encapsulated in
226 * @return this aabbox3f for chaining
227 */
228 AABBox3f& resize(const AABBox3f& o) noexcept {
229 /** test bl (low) */
230 if (o.m_lo.x < m_lo.x) {
231 m_lo.x = o.m_lo.x;
232 }
233 if (o.m_lo.y < m_lo.y) {
234 m_lo.y = o.m_lo.y;
235 }
236 if (o.m_lo.z < m_lo.z) {
237 m_lo.z = o.m_lo.z;
238 }
239
240 /** test tr (high) */
241 if (o.m_hi.x > m_hi.x) {
242 m_hi.x = o.m_hi.x;
243 }
244 if (o.m_hi.y > m_hi.y) {
245 m_hi.y = o.m_hi.y;
246 }
247 if (o.m_hi.z > m_hi.z) {
248 m_hi.z = o.m_hi.z;
249 }
250 computeCenter();
251 return *this;
252 }
253
254 /**
255 * General purpose Vec3f transform function
256 */
258
259 /**
260 * Resize the aabbox3f to encapsulate another AABox, which will be <i>transformed</i> on the fly first.
261 * @param newBox aabbox3f to be encapsulated in
262 * @param transform the transform function, applied on <i>newBox</i> on the fly
263 * @param tmpV3 temporary storage
264 * @return this aabbox3f for chaining
265 */
267 /** test low */
268 {
269 const jau::math::Vec3f newBL = transform(newBox.low());
270 if (newBL.x < m_lo.x) {
271 m_lo.x = newBL.x;
272 }
273 if (newBL.y < m_lo.y) {
274 m_lo.y = newBL.y;
275 }
276 if (newBL.z < m_lo.z) {
277 m_lo.z = newBL.z;
278 }
279 }
280
281 /** test high */
282 {
283 const jau::math::Vec3f newTR = transform(newBox.high());
284 if (newTR.x > m_hi.x) {
285 m_hi.x = newTR.x;
286 }
287 if (newTR.y > m_hi.y) {
288 m_hi.y = newTR.y;
289 }
290 if (newTR.z > m_hi.z) {
291 m_hi.z = newTR.z;
292 }
293 }
294 computeCenter();
295 return *this;
296 }
297
298 /**
299 * Resize the aabbox3f to encapsulate the passed
300 * xyz-coordinates.
301 * @param x x-axis coordinate value
302 * @param y y-axis coordinate value
303 * @param z z-axis coordinate value
304 * @return this aabbox3f for chaining
305 */
306 AABBox3f& resize(const float x, const float y, const float z) noexcept {
307 /** test low */
308 if (x < m_lo.x) {
309 m_lo.x = x;
310 }
311 if (y < m_lo.y) {
312 m_lo.y = y;
313 }
314 if (z < m_lo.z) {
315 m_lo.z = z;
316 }
317
318 /** test high */
319 if (x > m_hi.x) {
320 m_hi.x = x;
321 }
322 if (y > m_hi.y) {
323 m_hi.y = y;
324 }
325 if (z > m_hi.z) {
326 m_hi.z = z;
327 }
328 computeCenter();
329 return *this;
330 }
331
332 /**
333 * Resize the aabbox3f to encapsulate the passed
334 * xyz-coordinates.
335 * @param xyz xyz-axis coordinate values
336 * @return this aabbox3f for chaining
337 */
338 AABBox3f& resize(const float xyz[]) noexcept {
339 return resize(xyz[0], xyz[1], xyz[2]);
340 }
341
342 /**
343 * Resize the aabbox3f to encapsulate the passed
344 * xyz-coordinates.
345 * @param xyz xyz-axis coordinate values
346 * @return this aabbox3f for chaining
347 */
348 AABBox3f& resize(const Point3f& p) noexcept {
349 return resize(p.x, p.y, p.z);
350 }
351
352 /**
353 * Check if the 2D point is bounded/contained by this aabbox3f
354 * @return true if {x, y} belongs to {low, high}
355 */
356 bool contains(const float x, const float y) const noexcept {
357 return !( x<m_lo.x || x>m_hi.x ||
358 y<m_lo.y || y>m_hi.y );
359 }
360
361 /**
362 * Check if the 2D point is bounded/contained by this aabbox3f
363 * @return true if p belongs to {low, high}
364 */
365 bool contains(const Point2f& p) const noexcept { return contains(p.x, p.y); }
366
367 /**
368 * Check if the 3D point is bounded/contained by this aabbox3f
369 * @return true if {x, y, z} belongs to {low, high}
370 */
371 bool contains(const float x, const float y, const float z) const noexcept {
372 return m_lo.x<=x && x<=m_hi.x &&
373 m_lo.y<=y && y<=m_hi.y &&
374 m_lo.z<=z && z<=m_hi.z;
375 }
376
377 /**
378 * Check if the 3D point is bounded/contained by this aabbox3f
379 * @return true if p belongs to (low.x, high.x) and y belong to (low.y, high.y)
380 */
381 bool contains(const Point3f& p) const noexcept { return contains(p.x, p.y, p.z); }
382
383 /** Returns whether this aabbox3f intersects (partially contains) given aabbox3f. */
384 bool intersects(const AABBox3f& o) const noexcept {
385 /**
386 * Traditional boolean equation leads to multiple branches,
387 * using max/min approach allowing for branch-less optimizations.
388 *
389 return !( m_hi.x < o.m_lo.x ||
390 m_hi.y < o.m_lo.y ||
391 m_hi.z < o.m_lo.z ||
392 m_lo.x > o.m_hi.x ||
393 m_lo.y > o.m_hi.y ||
394 m_lo.z > o.m_hi.z );
395 */
396 const Point3f lo = max(m_lo, o.m_lo);
397 const Point3f hi = min(m_hi, o.m_hi);
398 return lo.x <= hi.x && lo.y <= hi.y && lo.z <= hi.z;
399 }
400
401 /** Returns whether this aabbox3f fully contains given aabbox3f. */
402 bool contains(const AABBox3f& o) const noexcept {
403 return m_hi.x >= o.m_hi.x &&
404 m_hi.y >= o.m_hi.y &&
405 m_hi.z >= o.m_hi.z &&
406 m_lo.x <= o.m_lo.x &&
407 m_lo.y <= o.m_lo.y &&
408 m_lo.z <= o.m_lo.z;
409 }
410
411 /**
412 * Check if there is a common region between this AABBox and the passed
413 * 2D region irrespective of z range
414 * @param x lower left x-coord
415 * @param y lower left y-coord
416 * @param w width
417 * @param h hight
418 * @return true if this AABBox might have a common region with this 2D region
419 */
420 bool intersects2DRegion(const float x, const float y, const float w, const float h) const noexcept {
421 if (w <= 0 || h <= 0) {
422 return false;
423 }
424 const float _w = width();
425 const float _h = height();
426 if (_w <= 0 || _h <= 0) {
427 return false;
428 }
429 const float x0 = m_lo.x;
430 const float y0 = m_lo.y;
431 return (x >= x0 &&
432 y >= y0 &&
433 x + w <= x0 + _w &&
434 y + h <= y0 + _h);
435 }
436
437 /**
438 * Check if {@link Ray} intersects this bounding box.
439 *
440 * Versions uses the SAT[1], testing 6 axes with branching.
441 *
442 * Original code for OBBs from MAGIC.
443 * Rewritten for AABBs and reorganized for early exits[2].
444 *
445 * - [1] SAT = Separating Axis Theorem
446 * - [2] http://www.codercorner.com/RayAABB.cpp
447 *
448 * @param ray
449 * @return true if ray intersects with this box, otherwise false
450 */
451 bool intersectsRay0(const Ray3f ray) const noexcept {
452 // diff[XYZ] -> ray.orig - center
453 // ext[XYZ] -> extend high - center
454
455 const Vec3f diff = ray.orig - m_center;
456 const Vec3f ext = m_hi - m_center;
457 if( std::abs(diff.x) > ext.x && diff.x * ray.dir.x >= 0.0f ) return false;
458 if( std::abs(diff.y) > ext.y && diff.y * ray.dir.y >= 0.0f ) return false;
459 if( std::abs(diff.z) > ext.z && diff.z * ray.dir.z >= 0.0f ) return false;
460
461 const Vec3f absDir = jau::math::abs(ray.dir);
462 float f = ray.dir.y * diff.z - ray.dir.z * diff.y;
463 if( std::abs(f) > ext.y * absDir.z + ext.z * absDir.y ) return false;
464
465 f = ray.dir.z * diff.x - ray.dir.x * diff.z;
466 if( std::abs(f) > ext.x * absDir.z + ext.z * absDir.x ) return false;
467
468 f = ray.dir.x * diff.y - ray.dir.y * diff.x;
469 if( std::abs(f) > ext.x * absDir.y + ext.y * absDir.x ) return false;
470 return true;
471 }
472
473 /**
474 * Check if {@link Ray} intersects this bounding box.
475 *
476 * Fast, Branchless Ray/Bounding Box Intersections[3]
477 *
478 * This variant of intersectsRay() is a bit slower but handles NaNs more consistently.
479 *
480 * The idea to eliminate branches by relying on IEEE 754 floating point properties
481 * goes back to Brian Smits[1], and the implementation was fleshed out by Amy Williams. et al.[2].
482 *
483 * - [1] Brian Smits: [Efficiency Issues for Ray Tracing.](http://www.cs.utah.edu/~bes/papers/fastRT/) Journal of Graphics Tools (1998).
484 * - [2] Amy Williams. et al.: [An Efficient and Robust Ray-Box Intersection Algorithm.](http://www.cs.utah.edu/~awilliam/box/) Journal of Graphics Tools (2005).
485 * - [3] Tavian Barnes: [Fast, Branchless Ray/Bounding Box Intersections](https://tavianator.com/2015/ray_box_nan.html)
486 * - [4] SAT = Separating Axis Theorem
487 * - [5] http://www.codercorner.com/RayAABB.cpp
488 *
489 * @param ray
490 * @return true if ray intersects with this box, otherwise false
491 */
492 bool intersectsRay1(const Ray3f& r) const noexcept {
493 const Vec3f dir_inv = 1.0f / r.dir;
494 float t1 = (m_lo.x - r.orig.x)*dir_inv.x;
495 float t2 = (m_hi.x - r.orig.x)*dir_inv.x;
496
497 float tmin = std::min(t1, t2);
498 float tmax = std::max(t1, t2);
499
500 t1 = (m_lo.y - r.orig.y)*dir_inv.y;
501 t2 = (m_hi.y - r.orig.y)*dir_inv.y;
502 tmin = std::max(tmin, std::min(std::min(t1, t2), tmax));
503 tmax = std::min(tmax, std::max(std::max(t1, t2), tmin));
504
505 t1 = (m_lo.z - r.orig.z)*dir_inv.z;
506 t2 = (m_hi.z - r.orig.z)*dir_inv.z;
507 tmin = std::max(tmin, std::min(std::min(t1, t2), tmax));
508 tmax = std::min(tmax, std::max(std::max(t1, t2), tmin));
509
510 return tmax > std::max(tmin, 0.0f);
511 }
512
513 /**
514 * Check if {@link Ray} intersects this bounding box.
515 *
516 * Fast, Branchless Ray/Bounding Box Intersections[3]
517 *
518 * This variant of intersectsRay0() is a faster and doesn't handles NaNs perfectly.
519 * However, it may only cause false positives, which will be checked later.
520 *
521 * The idea to eliminate branches by relying on IEEE 754 floating point properties
522 * goes back to Brian Smits[1], and the implementation was fleshed out by Amy Williams. et al.[2].
523 *
524 * - [1] Brian Smits: [Efficiency Issues for Ray Tracing.](http://www.cs.utah.edu/~bes/papers/fastRT/) Journal of Graphics Tools (1998).
525 * - [2] Amy Williams. et al.: [An Efficient and Robust Ray-Box Intersection Algorithm.](http://www.cs.utah.edu/~awilliam/box/) Journal of Graphics Tools (2005).
526 * - [3] Tavian Barnes: [Fast, Branchless Ray/Bounding Box Intersections](https://tavianator.com/2015/ray_box_nan.html)
527 * - [4] SAT = Separating Axis Theorem
528 * - [5] http://www.codercorner.com/RayAABB.cpp
529 *
530 * @param ray
531 * @return true if ray intersects with this box, otherwise false
532 */
533 bool intersectsRay(const Ray3f& r) const noexcept {
534 const Vec3f dir_inv = 1.0f / r.dir;
535 float t1 = (m_lo.x - r.orig.x)*dir_inv.x;
536 float t2 = (m_hi.x - r.orig.x)*dir_inv.x;
537
538 float tmin = std::min(t1, t2);
539 float tmax = std::max(t1, t2);
540
541 t1 = (m_lo.y - r.orig.y)*dir_inv.y;
542 t2 = (m_hi.y - r.orig.y)*dir_inv.y;
543 tmin = std::max(tmin, std::min(t1, t2));
544 tmax = std::min(tmax, std::max(t1, t2));
545
546 t1 = (m_lo.z - r.orig.z)*dir_inv.z;
547 t2 = (m_hi.z - r.orig.z)*dir_inv.z;
548 tmin = std::max(tmin, std::min(t1, t2));
549 tmax = std::min(tmax, std::max(t1, t2));
550
551 return tmax > std::max(tmin, 0.0f);
552 }
553
554 /**
555 * Return intersection of a {@link Ray} with this bounding box,
556 * or false if none exist.
557 * - >Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 [2]
558 * - Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
559 * - Epsilon value added by Klaus Hartmann.
560 *
561 * Method is based on the requirements:
562 * - the integer representation of 0.0f is 0x00000000
563 * - the sign bit of the float is the most significant one
564 *
565 * Report bugs: p.terdiman@codercorner.com (original author)
566 *
567 * - [1] http://www.codercorner.com/RayAABB.cpp (Updated October 2001)
568 * - [2] http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c
569 *
570 * @param result vec3
571 * @param ray
572 * @param epsilon
573 * @param assumeIntersection if true, method assumes an intersection, i.e. by pre-checking via {@link #intersectsRay(Ray)}.
574 * In this case method will not validate a possible non-intersection and just computes
575 * coordinates.
576 * @return true with having intersection coordinates stored in result, or false if none exists
577 */
578 bool getRayIntersection(Vec3f& result, const Ray3f& ray, const float epsilon,
579 const bool assumeIntersection) const noexcept {
580 float maxT[] = { -1.0f, -1.0f, -1.0f };
581
582 const Vec3f& origin = ray.orig;
583 const Vec3f& dir = ray.dir;
584
585 bool inside = true;
586
587 // Find candidate planes, unrolled
588 {
589 if(origin.x < m_lo.x) {
590 result.x = m_lo.x;
591 inside = false;
592
593 // Calculate T distances to candidate planes
594 if( 0 != jau::bit_value_raw(dir.x) ) {
595 maxT[0] = (m_lo.x - origin.x) / dir.x;
596 }
597 } else if(origin.x > m_hi.x) {
598 result.x = m_hi.x;
599 inside = false;
600
601 // Calculate T distances to candidate planes
602 if( 0 != jau::bit_value_raw(dir.x) ) {
603 maxT[0] = (m_hi.x - origin.x) / dir.x;
604 }
605 }
606 }
607 {
608 if(origin.y < m_lo.y) {
609 result.y = m_lo.y;
610 inside = false;
611
612 // Calculate T distances to candidate planes
613 if( 0 != jau::bit_value_raw(dir.y) ) {
614 maxT[1] = (m_lo.y - origin.y) / dir.y;
615 }
616 } else if(origin.y > m_hi.y) {
617 result.y = m_hi.y;
618 inside = false;
619
620 // Calculate T distances to candidate planes
621 if( 0 != jau::bit_value_raw(dir.y) ) {
622 maxT[1] = (m_hi.y - origin.y) / dir.y;
623 }
624 }
625 }
626 {
627 if(origin.z < m_lo.z) {
628 result.z = m_lo.z;
629 inside = false;
630
631 // Calculate T distances to candidate planes
632 if( 0 != jau::bit_value_raw(dir.z) ) {
633 maxT[2] = (m_lo.z - origin.z) / dir.z;
634 }
635 } else if(origin.z > m_hi.z) {
636 result.z = m_hi.z;
637 inside = false;
638
639 // Calculate T distances to candidate planes
640 if( 0 != jau::bit_value_raw(dir.z) ) {
641 maxT[2] = (m_hi.z - origin.z) / dir.z;
642 }
643 }
644 }
645
646 // Ray origin inside bounding box
647 if(inside) {
648 result = origin;
649 return true;
650 }
651 // Get largest of the maxT's for final choice of intersection
652 // - this version without FPU compares
653 // - but branch prediction might suffer
654 // - a bit faster on my Celeron, duno how it behaves on something like a P4
655 // (Updated October 2001)
656 int whichPlane;
658 // T[0]<0
660 // T[0]<0 T[1]<0
662 // T[0]<0 T[1]<0 T[2]<0
663 return false;
664 } else {
665 whichPlane = 2;
666 }
667 } else if( jau::bit_value_raw(maxT[2]) & jau::float_iec559_sign_bit ) {
668 // T[0]<0 T[1]>0 T[2]<0
669 whichPlane = 1;
670 } else {
671 // T[0]<0 T[1]>0 T[2]>0
672 if( jau::bit_value_raw(maxT[2]) > jau::bit_value_raw(maxT[1]) ) {
673 whichPlane = 2;
674 } else {
675 whichPlane = 1;
676 }
677 }
678 } else {
679 // T[0]>0
681 // T[0]>0 T[1]<0
683 // T[0]>0 T[1]<0 T[2]<0
684 whichPlane = 0;
685 } else {
686 // T[0]>0 T[1]<0 T[2]>0
687 if( jau::bit_value_raw(maxT[2]) > jau::bit_value_raw(maxT[0]) ) {
688 whichPlane = 2;
689 } else {
690 whichPlane = 0;
691 }
692 }
693 } else if( jau::bit_value_raw(maxT[2]) & jau::float_iec559_sign_bit ) {
694 // T[0]>0 T[1]>0 T[2]<0
695 if( jau::bit_value_raw(maxT[1]) > jau::bit_value_raw(maxT[0]) ) {
696 whichPlane = 1;
697 } else {
698 whichPlane = 0;
699 }
700 } else {
701 // T[0]>0 T[1]>0 T[2]>0
702 whichPlane = 0;
703 if( jau::bit_value_raw(maxT[1]) > jau::bit_value_raw(maxT[whichPlane]) ) whichPlane = 1;
704 if( jau::bit_value_raw(maxT[2]) > jau::bit_value_raw(maxT[whichPlane]) ) whichPlane = 2;
705 }
706 }
707
708 // Old code below:
709 /*
710 // Get largest of the maxT's for final choice of intersection
711 int whichPlane = 0;
712 if(maxT[1] > maxT[whichPlane]) { whichPlane = 1; }
713 if(maxT[2] > maxT[whichPlane]) { whichPlane = 2; }
714
715 // Check final candidate actually inside box
716 if(jau::bit_value_raw(maxT[WhichPlane])&jau::float_iec559_sign_bit) return false;
717 */
718
719 if( !assumeIntersection ) {
720 switch( whichPlane ) {
721 case 0:
722 result.y = origin.y + maxT[whichPlane] * dir.y;
723 if(result.y < m_lo.y - epsilon || result.y > m_hi.y + epsilon) { return false; }
724 result.z = origin.z + maxT[whichPlane] * dir.z;
725 if(result.z < m_lo.z - epsilon || result.z > m_hi.z + epsilon) { return false; }
726 break;
727 case 1:
728 result.x = origin.x + maxT[whichPlane] * dir.x;
729 if(result.x < m_lo.x - epsilon || result.x > m_hi.x + epsilon) { return false; }
730 result.z = origin.z + maxT[whichPlane] * dir.z;
731 if(result.z < m_lo.z - epsilon || result.z > m_hi.z + epsilon) { return false; }
732 break;
733 case 2:
734 result.x = origin.x + maxT[whichPlane] * dir.x;
735 if(result.x < m_lo.x - epsilon || result.x > m_hi.x + epsilon) { return false; }
736 result.y = origin.y + maxT[whichPlane] * dir.y;
737 if(result.y < m_lo.y - epsilon || result.y > m_hi.y + epsilon) { return false; }
738 break;
739 default:
740 ERR_PRINT("Internal Error", E_FILE_LINE);
741 return false;
742 }
743 } else {
744 switch( whichPlane ) {
745 case 0:
746 result.y = origin.y + maxT[whichPlane] * dir.y;
747 result.z = origin.z + maxT[whichPlane] * dir.z;
748 break;
749 case 1:
750 result.x = origin.x + maxT[whichPlane] * dir.x;
751 result.z = origin.z + maxT[whichPlane] * dir.z;
752 break;
753 case 2:
754 result.x = origin.x + maxT[whichPlane] * dir.x;
755 result.y = origin.y + maxT[whichPlane] * dir.y;
756 break;
757 default:
758 ERR_PRINT("Internal Error", E_FILE_LINE);
759 return false;
760 }
761 }
762 return true; // ray hits box
763 }
764
765 /**
766 * Transform this box using the given Mat4f into {@code out}
767 * @param mat transformation Mat4f
768 * @param out the resulting AABBox3f
769 * @return the resulting AABBox3f for chaining
770 */
771 AABBox3f& transform(const Mat4f& mat, AABBox3f& out) const noexcept {
772 Vec3f tmp;
773 out.reset();
774 out.resize( mat.mulVec3(m_lo, tmp) );
775 out.resize( mat.mulVec3(m_hi, tmp) );
776 return out;
777 }
778
779 /**
780 * Assume this bounding box as being in object space and
781 * compute the window bounding box.
782 * <p>
783 * If <code>useCenterZ</code> is <code>true</code>,
784 * only 4 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords}
785 * operations are made on points [1..4] using {@link #getCenter()}'s z-value.
786 * Otherwise 8 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords}
787 * operation on all 8 points are performed.
788 * </p>
789 * <pre>
790 * .z() ------ [4]
791 * | |
792 * | |
793 * .y() ------ [3]
794 * </pre>
795 * @param mat4PMv [projection] x [modelview] matrix, i.e. P x Mv
796 * @param viewport viewport rectangle
797 * @param useCenterZ
798 * @param vec3Tmp0 3 component vector for temp storage
799 * @param vec4Tmp1 4 component vector for temp storage
800 * @param vec4Tmp2 4 component vector for temp storage
801 * @return
802 */
803 AABBox3f& mapToWindow(AABBox3f& result, const Mat4f& mat4PMv, const Recti& viewport, bool useCenterZ) const noexcept {
804 Vec3f tmp, winPos;
805 {
806 float objZ = useCenterZ ? m_center.z : m_lo.z;
807 result.reset();
808
809 Mat4f::mapObjToWin(tmp.set(m_lo.x, m_lo.y, objZ), mat4PMv, viewport, winPos);
810 result.resize(winPos);
811
812 Mat4f::mapObjToWin(tmp.set(m_lo.x, m_hi.y, objZ), mat4PMv, viewport, winPos);
813 result.resize(winPos);
814
815 Mat4f::mapObjToWin(tmp.set(m_hi.x, m_hi.y, objZ), mat4PMv, viewport, winPos);
816 result.resize(winPos);
817
818 Mat4f::mapObjToWin(tmp.set(m_hi.x, m_lo.y, objZ), mat4PMv, viewport, winPos);
819 result.resize(winPos);
820 }
821
822 if( !useCenterZ ) {
823 const float objZ = m_hi.z;
824
825 Mat4f::mapObjToWin(tmp.set(m_lo.x, m_lo.y, objZ), mat4PMv, viewport, winPos);
826 result.resize(winPos);
827
828 Mat4f::mapObjToWin(tmp.set(m_lo.x, m_hi.y, objZ), mat4PMv, viewport, winPos);
829 result.resize(winPos);
830
831 Mat4f::mapObjToWin(tmp.set(m_hi.x, m_hi.y, objZ), mat4PMv, viewport, winPos);
832 result.resize(winPos);
833
834 Mat4f::mapObjToWin(tmp.set(m_hi.x, m_lo.y, objZ), mat4PMv, viewport, winPos);
835 result.resize(winPos);
836 }
837 return result;
838 }
839
840 std::string toString() const noexcept {
841 return "aabb[bl " + m_lo.toString() +
842 ", tr " + m_hi.toString() +
843 "]"; }
844 };
845
846 /**@}*/
847
848} // namespace jau::math::geom
849
850#endif /* JAU_MATH_GEOM_AABBOX3F_HPP_ */
#define E_FILE_LINE
Class template jau::function is a general-purpose static-polymorphic function wrapper.
static bool mapObjToWin(const Vec3 &obj, const Matrix4 &mPMv, const Recti &viewport, Vec3 &winPos) noexcept
Definition mat4f.hpp:1462
constexpr Vector3F & set(const Vec2f &o, const value_type z_) noexcept
TODO constexpr bool operator<=>(const vec3f_t& rhs ) const noexcept { return ... }...
Definition vec3f.hpp:138
value_type x
Definition vec3f.hpp:69
value_type y
Definition vec3f.hpp:70
value_type z
Definition vec3f.hpp:71
bool contains(const float x, const float y) const noexcept
Check if the 2D point is bounded/contained by this aabbox3f.
Definition aabbox3f.hpp:356
AABBox3f & resizeWidth(const float deltaLeft, const float deltaRight) noexcept
Resize width of this aabbox3f with explicit left- and right delta values.
Definition aabbox3f.hpp:185
AABBox3f & setSize(const Vec3f &low, const Vec3f &high) noexcept
Set size of the aabbox3f specifying the coordinates of the low and high.
Definition aabbox3f.hpp:172
AABBox3f & resizeHeight(const float deltaBottom, const float deltaTop) noexcept
Resize height of this aabbox3f with explicit bottom- and top delta values.
Definition aabbox3f.hpp:207
AABBox3f & operator=(const AABBox3f &) noexcept=default
float depth() const noexcept
Definition aabbox3f.hpp:118
float width() const noexcept
Definition aabbox3f.hpp:114
bool intersectsRay0(const Ray3f ray) const noexcept
Check if Ray intersects this bounding box.
Definition aabbox3f.hpp:451
float size() const noexcept
Get the size of this aabbox3f where the size is represented by the length of the vector between low a...
Definition aabbox3f.hpp:112
AABBox3f & setSize(const float low[], const float high[]) noexcept
Set size of the aabbox3f specifying the coordinates of the low and high.
Definition aabbox3f.hpp:140
float height() const noexcept
Definition aabbox3f.hpp:116
AABBox3f & resize(const AABBox3f &newBox, transform_vec3f_func &transform) noexcept
Resize the aabbox3f to encapsulate another AABox, which will be transformed on the fly first.
Definition aabbox3f.hpp:266
bool contains(const Point2f &p) const noexcept
Check if the 2D point is bounded/contained by this aabbox3f.
Definition aabbox3f.hpp:365
std::string toString() const noexcept
Definition aabbox3f.hpp:840
bool contains(const Point3f &p) const noexcept
Check if the 3D point is bounded/contained by this aabbox3f.
Definition aabbox3f.hpp:381
AABBox3f(const Point3f &bl_, const Point3f &tr_) noexcept
Create an aabbox3f with given bl (low) and tr (high)
Definition aabbox3f.hpp:64
bool hasZeroArea2D() const noexcept
Return true if get2DArea() is FloatUtil#isZero(float), considering epsilon.
Definition aabbox3f.hpp:130
float volume() const noexcept
Returns the volume, i.e.
Definition aabbox3f.hpp:121
AABBox3f & reset() noexcept
Reset this box to the inverse low/high, allowing the next resize(float, float, float) command to hit.
Definition aabbox3f.hpp:91
bool intersects2DRegion(const float x, const float y, const float w, const float h) const noexcept
Check if there is a common region between this AABBox and the passed 2D region irrespective of z rang...
Definition aabbox3f.hpp:420
float area2D() const noexcept
Returns the assumed 2D area, i.e.
Definition aabbox3f.hpp:127
constexpr AABBox3f(AABBox3f &&o) noexcept=default
bool getRayIntersection(Vec3f &result, const Ray3f &ray, const float epsilon, const bool assumeIntersection) const noexcept
Return intersection of a Ray with this bounding box, or false if none exist.
Definition aabbox3f.hpp:578
AABBox3f & resize(const float xyz[]) noexcept
Resize the aabbox3f to encapsulate the passed xyz-coordinates.
Definition aabbox3f.hpp:338
AABBox3f & transform(const Mat4f &mat, AABBox3f &out) const noexcept
Transform this box using the given Mat4f into out @endiliteral.
Definition aabbox3f.hpp:771
AABBox3f() noexcept
Create an Axis Aligned bounding box (aabbox3f) where the low and and high MAX float Values.
Definition aabbox3f.hpp:57
bool intersectsRay1(const Ray3f &r) const noexcept
Check if Ray intersects this bounding box.
Definition aabbox3f.hpp:492
const Point3f & high() const noexcept
Returns the maximum right-top-near (xyz) coordinate.
Definition aabbox3f.hpp:99
AABBox3f & setSize(const float lx, const float ly, const float lz, const float hx, const float hy, const float hz) noexcept
Set size of the aabbox3f specifying the coordinates of the low and high.
Definition aabbox3f.hpp:156
bool hasZeroVolume() const noexcept
Return true if getVolume() is FloatUtil#isZero(float), considering epsilon.
Definition aabbox3f.hpp:124
jau::function< jau::math::Vec3f(const jau::math::Vec3f &)> transform_vec3f_func
General purpose Vec3f transform function.
Definition aabbox3f.hpp:257
bool intersects(const AABBox3f &o) const noexcept
Returns whether this aabbox3f intersects (partially contains) given aabbox3f.
Definition aabbox3f.hpp:384
const Point3f & low() const noexcept
Returns the minimum left-bottom-far (xyz) coordinate.
Definition aabbox3f.hpp:102
constexpr AABBox3f(const AABBox3f &o) noexcept=default
AABBox3f & mapToWindow(AABBox3f &result, const Mat4f &mat4PMv, const Recti &viewport, bool useCenterZ) const noexcept
Assume this bounding box as being in object space and compute the window bounding box.
Definition aabbox3f.hpp:803
const Point3f & center() const noexcept
Returns computed center of this aabbox3f of low() and high().
Definition aabbox3f.hpp:105
AABBox3f & resize(const float x, const float y, const float z) noexcept
Resize the aabbox3f to encapsulate the passed xyz-coordinates.
Definition aabbox3f.hpp:306
bool contains(const float x, const float y, const float z) const noexcept
Check if the 3D point is bounded/contained by this aabbox3f.
Definition aabbox3f.hpp:371
AABBox3f & resize(const AABBox3f &o) noexcept
Resize the aabbox3f to encapsulate another AABox.
Definition aabbox3f.hpp:228
AABBox3f & resize(const Point3f &p) noexcept
Resize the aabbox3f to encapsulate the passed xyz-coordinates.
Definition aabbox3f.hpp:348
bool intersectsRay(const Ray3f &r) const noexcept
Check if Ray intersects this bounding box.
Definition aabbox3f.hpp:533
bool contains(const AABBox3f &o) const noexcept
Returns whether this aabbox3f fully contains given aabbox3f.
Definition aabbox3f.hpp:402
AABBox3f & operator=(AABBox3f &&) noexcept=default
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:112
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
jau::uint_bytes_t< sizeof(T)> bit_value_raw(const T a) noexcept
Returns the unsigned integer representation according to IEEE 754 (IEC 559) floating-point bit layout...
constexpr uint32_t const float_iec559_sign_bit
Signed bit 31 of IEEE 754 (IEC 559) single float-point bit layout, i.e.
Matrix4< float > Mat4f
Definition mat4f.hpp:1928
constexpr Vector2F< T > max(const Vector2F< T > &lhs, const Vector2F< T > &rhs) noexcept
Definition vec2f.hpp:398
constexpr Vector2F< T > min(const Vector2F< T > &lhs, const Vector2F< T > &rhs) noexcept
Definition vec2f.hpp:392
Ray3F< float > Ray3f
Definition vec3f.hpp:499
RectI< int > Recti
Definition recti.hpp:139
Point2F< float > Point2f
Definition vec2f.hpp:431
Vector3F< float > Vec3f
Definition vec3f.hpp:433
constexpr Vector2F< T > abs(const Vector2F< T > &lhs) noexcept
Definition vec2f.hpp:404
Point3F< float > Point3f
Definition vec3f.hpp:448