1 | // Copyright (C) 2008-2012 NVIDIA Corporation. |
2 | // Copyright (C) 2019 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
4 | |
5 | #ifndef QSSGBOUNDS3_H |
6 | #define QSSGBOUNDS3_H |
7 | |
8 | // |
9 | // W A R N I N G |
10 | // ------------- |
11 | // |
12 | // This file is not part of the Qt API. It exists purely as an |
13 | // implementation detail. This header file may change from version to |
14 | // version without notice, or even be removed. |
15 | // |
16 | // We mean it. |
17 | // |
18 | |
19 | #include "qssgutils_p.h" |
20 | #include <QtQuick3DUtils/private/qtquick3dutilsglobal_p.h> |
21 | |
22 | #include <QVector3D> |
23 | #include <QMatrix3x3> |
24 | #include <QMatrix4x4> |
25 | |
26 | #include <limits> |
27 | #include <qnumeric.h> |
28 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | using QSSGBoxPoints = std::array<QVector3D, 8>; |
32 | |
33 | /** |
34 | \brief Class representing 3D range or axis aligned bounding box. |
35 | |
36 | Stored as minimum and maximum extent corners. Alternate representation |
37 | would be center and dimensions. |
38 | May be empty or nonempty. If not empty, minimum <= maximum has to hold. |
39 | */ |
40 | class Q_QUICK3DUTILS_EXPORT QSSGBounds3 |
41 | { |
42 | public: |
43 | /** |
44 | \brief Default constructor, using empty bounds. |
45 | */ |
46 | Q_DECL_CONSTEXPR Q_ALWAYS_INLINE QSSGBounds3(); |
47 | |
48 | /** |
49 | \brief Construct uninitialized. |
50 | */ |
51 | Q_ALWAYS_INLINE QSSGBounds3(Qt::Initialization); |
52 | |
53 | /** |
54 | \brief Construct from two bounding points |
55 | */ |
56 | Q_DECL_CONSTEXPR Q_ALWAYS_INLINE QSSGBounds3(const QVector3D &minimum, const QVector3D &maximum); |
57 | |
58 | /** |
59 | \brief returns the AABB containing v0 and v1. |
60 | \param v0 first point included in the AABB. |
61 | \param v1 second point included in the AABB. |
62 | */ |
63 | static Q_ALWAYS_INLINE QSSGBounds3 boundsOfPoints(const QVector3D &v0, const QVector3D &v1); |
64 | |
65 | /** |
66 | \brief returns the AABB from center and extents vectors. |
67 | \param center Center vector |
68 | \param extent Extents vector |
69 | */ |
70 | static Q_ALWAYS_INLINE QSSGBounds3 centerExtents(const QVector3D ¢er, const QVector3D &extent); |
71 | |
72 | /** |
73 | \brief Construct from center, extent, and (not necessarily orthogonal) basis |
74 | */ |
75 | static Q_ALWAYS_INLINE QSSGBounds3 basisExtent(const QVector3D ¢er, const QMatrix3x3 &basis, const QVector3D &extent); |
76 | |
77 | /** |
78 | \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). |
79 | \param[in] matrix Transform to apply, can contain scaling as well |
80 | \param[in] bounds The bounds to transform. |
81 | */ |
82 | static QSSGBounds3 transform(const QMatrix3x3 &matrix, const QSSGBounds3 &bounds); |
83 | |
84 | /** |
85 | \brief Sets empty to true |
86 | */ |
87 | Q_ALWAYS_INLINE void setEmpty(); |
88 | |
89 | /** |
90 | \brief Sets infinite bounds |
91 | */ |
92 | Q_ALWAYS_INLINE void setInfinite(); |
93 | |
94 | /** |
95 | \brief expands the volume to include v |
96 | \param v Point to expand to. |
97 | */ |
98 | void include(const QVector3D &v); |
99 | |
100 | /** |
101 | \brief expands the volume to include b. |
102 | \param b Bounds to perform union with. |
103 | */ |
104 | void include(const QSSGBounds3 &b); |
105 | |
106 | Q_ALWAYS_INLINE bool isEmpty() const; |
107 | |
108 | /** |
109 | \brief indicates whether the intersection of this and b is empty or not. |
110 | \param b Bounds to test for intersection. |
111 | */ |
112 | Q_ALWAYS_INLINE bool intersects(const QSSGBounds3 &b) const; |
113 | |
114 | /** |
115 | \brief computes the 1D-intersection between two AABBs, on a given axis. |
116 | \param a the other AABB |
117 | \param axis the axis (0, 1, 2) |
118 | */ |
119 | Q_ALWAYS_INLINE bool intersects1D(const QSSGBounds3 &a, quint32 axis) const; |
120 | |
121 | /** |
122 | \brief indicates if these bounds contain v. |
123 | \param v Point to test against bounds. |
124 | */ |
125 | Q_ALWAYS_INLINE bool contains(const QVector3D &v) const; |
126 | |
127 | /** |
128 | \brief checks a box is inside another box. |
129 | \param box the other AABB |
130 | */ |
131 | Q_ALWAYS_INLINE bool isInside(const QSSGBounds3 &box) const; |
132 | |
133 | /** |
134 | \brief returns the center of this axis aligned box. |
135 | */ |
136 | Q_ALWAYS_INLINE QVector3D center() const; |
137 | |
138 | /** |
139 | \brief get component of the box's center along a given axis |
140 | */ |
141 | Q_ALWAYS_INLINE float center(quint32 axis) const; |
142 | |
143 | /** |
144 | \brief get component of the box's extents along a given axis |
145 | */ |
146 | Q_ALWAYS_INLINE float extents(quint32 axis) const; |
147 | |
148 | /** |
149 | \brief returns the dimensions (width/height/depth) of this axis aligned box. |
150 | */ |
151 | Q_ALWAYS_INLINE QVector3D dimensions() const; |
152 | |
153 | /** |
154 | \brief returns the extents, which are half of the width/height/depth. |
155 | */ |
156 | Q_ALWAYS_INLINE QVector3D extents() const; |
157 | |
158 | /** |
159 | \brief scales the AABB. |
160 | \param scale Factor to scale AABB by. |
161 | */ |
162 | Q_ALWAYS_INLINE void scale(float scale); |
163 | |
164 | /** |
165 | fattens the AABB in all 3 dimensions by the given distance. |
166 | */ |
167 | Q_ALWAYS_INLINE void fatten(double distance); |
168 | |
169 | /** |
170 | checks that the AABB values are not NaN |
171 | */ |
172 | |
173 | bool isFinite() const; |
174 | |
175 | /** |
176 | Use when the bounds is already verified to be non-empty!!! |
177 | */ |
178 | Q_ALWAYS_INLINE QSSGBoxPoints toQSSGBoxPointsNoEmptyCheck() const; |
179 | /** |
180 | Verifies that the bounds is non-empty. |
181 | */ |
182 | Q_ALWAYS_INLINE QSSGBoxPoints toQSSGBoxPoints() const; |
183 | |
184 | void transform(const QMatrix4x4 &inMatrix); |
185 | |
186 | /** |
187 | Returns the support point in a given direction |
188 | */ |
189 | QVector3D getSupport(const QVector3D &direction) const; |
190 | |
191 | QVector3D minimum; |
192 | QVector3D maximum; |
193 | }; |
194 | |
195 | Q_DECL_CONSTEXPR Q_ALWAYS_INLINE QSSGBounds3::QSSGBounds3() |
196 | : minimum(QVector3D(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max())) |
197 | , maximum(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max()) |
198 | { |
199 | } |
200 | |
201 | Q_ALWAYS_INLINE QSSGBounds3::QSSGBounds3(Qt::Initialization) |
202 | : minimum(Qt::Uninitialized), maximum(Qt::Uninitialized) { } |
203 | |
204 | Q_DECL_CONSTEXPR Q_ALWAYS_INLINE QSSGBounds3::QSSGBounds3(const QVector3D &_minimum, const QVector3D &_maximum) |
205 | : minimum(_minimum), maximum(_maximum) |
206 | { |
207 | } |
208 | |
209 | Q_ALWAYS_INLINE QSSGBounds3 QSSGBounds3::centerExtents(const QVector3D ¢er, const QVector3D &extent) |
210 | { |
211 | return QSSGBounds3(center - extent, center + extent); |
212 | } |
213 | |
214 | Q_ALWAYS_INLINE void QSSGBounds3::setEmpty() |
215 | { |
216 | constexpr float maxFloat = std::numeric_limits<float>::max(); |
217 | minimum = QVector3D(maxFloat, maxFloat, maxFloat); |
218 | maximum = QVector3D(-maxFloat, -maxFloat, -maxFloat); |
219 | } |
220 | |
221 | Q_ALWAYS_INLINE void QSSGBounds3::setInfinite() |
222 | { |
223 | constexpr float maxFloat = std::numeric_limits<float>::max(); |
224 | minimum = QVector3D(-maxFloat, -maxFloat, -maxFloat); |
225 | maximum = QVector3D(maxFloat, maxFloat, maxFloat); |
226 | } |
227 | |
228 | Q_ALWAYS_INLINE bool QSSGBounds3::isEmpty() const |
229 | { |
230 | Q_ASSERT(isFinite()); |
231 | // Consistency condition for (Min, Max) boxes: minimum < maximum |
232 | return minimum.x() > maximum.x() || minimum.y() > maximum.y() || minimum.z() > maximum.z(); |
233 | } |
234 | |
235 | Q_ALWAYS_INLINE bool QSSGBounds3::intersects(const QSSGBounds3 &b) const |
236 | { |
237 | Q_ASSERT(isFinite() && b.isFinite()); |
238 | return !(b.minimum.x() > maximum.x() || minimum.x() > b.maximum.x() || b.minimum.y() > maximum.y() |
239 | || minimum.y() > b.maximum.y() || b.minimum.z() > maximum.z() || minimum.z() > b.maximum.z()); |
240 | } |
241 | |
242 | Q_ALWAYS_INLINE bool QSSGBounds3::intersects1D(const QSSGBounds3 &a, quint32 axis) const |
243 | { |
244 | Q_ASSERT(isFinite() && a.isFinite()); |
245 | return maximum[int(axis)] >= a.minimum[axis] && a.maximum[axis] >= minimum[axis]; |
246 | } |
247 | |
248 | Q_ALWAYS_INLINE bool QSSGBounds3::contains(const QVector3D &v) const |
249 | { |
250 | Q_ASSERT(isFinite()); |
251 | |
252 | return !(v.x() < minimum.x() || v.x() > maximum.x() || v.y() < minimum.y() || v.y() > maximum.y() |
253 | || v.z() < minimum.z() || v.z() > maximum.z()); |
254 | } |
255 | |
256 | Q_ALWAYS_INLINE bool QSSGBounds3::isInside(const QSSGBounds3 &box) const |
257 | { |
258 | Q_ASSERT(isFinite() && box.isFinite()); |
259 | if (box.minimum.x() > minimum.x()) |
260 | return false; |
261 | if (box.minimum.y() > minimum.y()) |
262 | return false; |
263 | if (box.minimum.z() > minimum.z()) |
264 | return false; |
265 | if (box.maximum.x() < maximum.x()) |
266 | return false; |
267 | if (box.maximum.y() < maximum.y()) |
268 | return false; |
269 | if (box.maximum.z() < maximum.z()) |
270 | return false; |
271 | return true; |
272 | } |
273 | |
274 | Q_ALWAYS_INLINE QVector3D QSSGBounds3::center() const |
275 | { |
276 | Q_ASSERT(isFinite()); |
277 | return (minimum + maximum) * double(0.5); |
278 | } |
279 | |
280 | Q_ALWAYS_INLINE float QSSGBounds3::center(quint32 axis) const |
281 | { |
282 | Q_ASSERT(isFinite()); |
283 | return (minimum[int(axis)] + maximum[int(axis)]) * 0.5f; |
284 | } |
285 | |
286 | Q_ALWAYS_INLINE float QSSGBounds3::extents(quint32 axis) const |
287 | { |
288 | Q_ASSERT(isFinite()); |
289 | return (maximum[int(axis)] - minimum[int(axis)]) * 0.5f; |
290 | } |
291 | |
292 | Q_ALWAYS_INLINE QVector3D QSSGBounds3::dimensions() const |
293 | { |
294 | Q_ASSERT(isFinite()); |
295 | return maximum - minimum; |
296 | } |
297 | |
298 | Q_ALWAYS_INLINE QVector3D QSSGBounds3::extents() const |
299 | { |
300 | Q_ASSERT(isFinite()); |
301 | return dimensions() * double(0.5); |
302 | } |
303 | |
304 | Q_ALWAYS_INLINE void QSSGBounds3::scale(float scale) |
305 | { |
306 | Q_ASSERT(isFinite()); |
307 | *this = centerExtents(center: center(), extent: extents() * scale); |
308 | } |
309 | |
310 | Q_ALWAYS_INLINE void QSSGBounds3::fatten(double distance) |
311 | { |
312 | Q_ASSERT(isFinite()); |
313 | minimum -= QVector3D(float(distance), float(distance), float(distance)); |
314 | maximum += QVector3D(float(distance), float(distance), float(distance)); |
315 | } |
316 | |
317 | Q_ALWAYS_INLINE QSSGBoxPoints QSSGBounds3::toQSSGBoxPointsNoEmptyCheck() const |
318 | { |
319 | return { // Min corner of box |
320 | QVector3D(minimum[0], minimum[1], minimum[2]), |
321 | QVector3D(maximum[0], minimum[1], minimum[2]), |
322 | QVector3D(minimum[0], maximum[1], minimum[2]), |
323 | QVector3D(minimum[0], minimum[1], maximum[2]), |
324 | // Max corner of box |
325 | QVector3D(maximum[0], maximum[1], maximum[2]), |
326 | QVector3D(minimum[0], maximum[1], maximum[2]), |
327 | QVector3D(maximum[0], minimum[1], maximum[2]), |
328 | QVector3D(maximum[0], maximum[1], minimum[2]) |
329 | }; |
330 | } |
331 | |
332 | Q_ALWAYS_INLINE QSSGBoxPoints QSSGBounds3::toQSSGBoxPoints() const |
333 | { |
334 | if (isEmpty()) |
335 | return { QVector3D(0, 0, 0), QVector3D(0, 0, 0), QVector3D(0, 0, 0), QVector3D(0, 0, 0), |
336 | QVector3D(0, 0, 0), QVector3D(0, 0, 0), QVector3D(0, 0, 0), QVector3D(0, 0, 0) }; |
337 | return toQSSGBoxPointsNoEmptyCheck(); |
338 | } |
339 | |
340 | QT_END_NAMESPACE |
341 | |
342 | #endif // QSSGBOUNDS3_H |
343 | |