Library of Assembled Shared Sources
qbvh_tree.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2024-2025 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45/** @class lass::spat::QbvhTree
46 * @brief an 4-ary AABB tree
47 * @author Bram de Greve [BdG]
48 *
49 * This BVH is much like an AABB tree, but with a branching factor of 4. It is
50 * constructed in similar fashion, by recursively splitting the objects into
51 * 2 sets along an axis, but then the sets are split again, totalling 4 sets per
52 * node.
53 *
54 * This tree, when used with SAHSplitHeuristics, should be the fastest for ray
55 * intersection tests. For other operations, it's generally faster than AabbTree
56 * if AVX is available. But as always, YMMV.
57 *
58 * The QbvhTree does NOT own the objects. You must keep them yourself!
59 *
60 * @note H. Dammertz, J. Hanika, and A. Keller. Shallow bounding volume hierarchies
61 * for fast SIMD ray tracing of incoherent rays. In Proceedings of the Nineteenth
62 * Eurographics conference on Rendering 2008 (EGSR '08). Eurographics Association,
63 * Goslar, DEU, 1225–1233. https://doi.org/10.1111/j.1467-8659.2008.01261.x
64 *
65 * @note T. Barnes. Fast, Branchless Ray/Bounding Box Intersections, Part 3: Boundaries
66 * https://tavianator.com/2022/ray_box_boundary.html
67 *
68 */
69
70#pragma once
71
72#include "spat_common.h"
74#include "split_heuristics.h"
75
76#if LASS_HAVE_AVX
77#include <immintrin.h>
78#include <xmmintrin.h>
79#endif
80
81namespace lass
82{
83namespace spat
84{
85namespace impl::qbvh
86{
87
88template <typename T, size_t D>
89struct Point
90{
91 T x[D];
92};
93
94template <typename T, size_t D>
95struct Ray
96{
97 T support[D];
98 T invDir[D];
99 int sign[D];
100};
101template <typename T, size_t D>
102struct Aabb
103{
104 T corners[D][2];
105};
106
107template <typename T, size_t D>
108struct QAabb
109{
110 T corners[4][D][2];
111 void set(size_t k, const Aabb<T, D>& aabb);
112};
113
114#if LASS_HAVE_AVX
115
116template <size_t D>
117struct alignas(16) QAabb<float, D>
118{
119 __m128 corners[D][2];
120 void set(size_t k, const Aabb<float, D>& aabb);
121};
122
123template <size_t D>
124struct alignas(32) QAabb<double, D>
125{
126 __m256d corners[D][2];
127 void set(size_t k, const Aabb<double, D>& aabb);
128};
129
130#endif
131
132}
133
134
135template
136<
137 typename ObjectType,
138 typename ObjectTraits = DefaultObjectTraits<ObjectType>,
139 typename SplitHeuristics = DefaultSplitHeuristics
140>
141class QbvhTree: public SplitHeuristics
142{
143public:
144
145 using TSelf = QbvhTree<ObjectType, ObjectTraits, SplitHeuristics>;
146
147 using TObject = ObjectType;
148 using TObjectTraits = ObjectTraits;
149 using TSplitHeuristics = SplitHeuristics;
150
151 using TObjectIterator = typename TObjectTraits::TObjectIterator;
152 using TObjectReference = typename TObjectTraits::TObjectReference;
153 using TAabb = typename TObjectTraits::TAabb;
154 using TRay = typename TObjectTraits::TRay;
155 using TPoint = typename TObjectTraits::TPoint;
156 using TVector = typename TObjectTraits::TVector;
157 using TValue = typename TObjectTraits::TValue;
158 using TParam = typename TObjectTraits::TParam;
159 using TReference = typename TObjectTraits::TReference;
160 using TConstReference = typename TObjectTraits::TConstReference;
161 using TInfo = typename TObjectTraits::TInfo;
162
163 using TNumTraits = num::NumTraits<TValue>;
164
165 using TObjectIterators = std::vector<TObjectIterator>;
166
167 class Neighbour
168 {
169 public:
170 Neighbour() {}
171 Neighbour(TObjectIterator object, TValue squaredDistance):
172 object_(object), squaredDistance_(squaredDistance) {}
173 TObjectIterator object() const { return object_; }
174 TValue squaredDistance() const { return squaredDistance_; }
175 TObjectIterator operator->() const { return object_; }
176 TObjectReference operator*() const { return TObjectTraits::object(object_); }
177 bool operator<(const Neighbour& other) const { return squaredDistance_ < other.squaredDistance_; }
178 private:
179 TObjectIterator object_;
180 TValue squaredDistance_;
181 };
182
183 constexpr static size_t dimension = TObjectTraits::dimension;
184 constexpr static size_t defaultMaxObjectsPerLeaf = 1;
185 constexpr static size_t defaultMaxDepth = 20;
186
187 QbvhTree(TSplitHeuristics heuristics = TSplitHeuristics(defaultMaxObjectsPerLeaf, defaultMaxDepth));
188 QbvhTree(TObjectIterator first, TObjectIterator last, TSplitHeuristics heuristics = TSplitHeuristics(defaultMaxObjectsPerLeaf, defaultMaxDepth));
189 QbvhTree(TSelf&& other) noexcept;
190
191 TSelf& operator=(TSelf&& other) noexcept;
192
193 void reset();
194 void reset(TObjectIterator first, TObjectIterator last);
195
196 const TAabb aabb() const;
197
198 bool contains(const TPoint& point, const TInfo* info = 0) const;
199
200 template <typename OutputIterator>
201 OutputIterator find(const TPoint& point, OutputIterator result, const TInfo* info = 0) const;
202 template <typename OutputIterator>
203 OutputIterator find(const TAabb& box, OutputIterator result, const TInfo* info = 0) const;
204 template <typename OutputIterator>
205 OutputIterator find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info = 0) const;
206
207 TObjectIterator intersect(const TRay& ray, TReference t, TParam tMin = 0, const TInfo* info = 0) const;
208 bool intersects(const TRay& ray, TParam tMin = 0, TParam tMax = std::numeric_limits<TValue>::infinity(), const TInfo* info = 0) const;
209
210 const Neighbour nearestNeighbour(const TPoint& point, const TInfo* info = 0) const;
211 template <typename RandomIterator>
212 RandomIterator rangeSearch(const TPoint& center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo* info = 0) const;
213
214 bool isEmpty() const;
215 const TObjectIterator end() const;
216 void swap(TSelf& other);
217
218private:
219
220 constexpr static size_t stackSize_ = 128;
221
222 using TIndex = unsigned;
223 using TAxis = int;
224
225 struct Input
226 {
227 TAabb aabb;
228 TObjectIterator object;
229 Input(const TAabb& aabb, TObjectIterator object): aabb(aabb), object(object) {}
230 };
231 using TInputs = std::vector<Input>;
232 using TInputIterator = typename TInputs::iterator;
233
234 class Child
235 {
236 public:
237 constexpr static size_t countBits = 4; ///< number of bits for object count
238 constexpr static TIndex maxCount = 1 << countBits; ///< max number of objects per node
239 constexpr static TIndex maxObject = static_cast<TIndex>(std::numeric_limits<int>::max()) >> countBits; ///< max object index
240 constexpr static TIndex maxNode = static_cast<TIndex>(std::numeric_limits<int>::max()) - 1; ///< max node index
241
242 /** Constructs an empty node */
243 Child(): index_(0) {}
244
245 /** Constructs an internal node */
246 explicit Child(TIndex node):
247 index_(static_cast<int>(node + 1))
248 {
249 LASS_ASSERT(index_ > 0);
250 }
251
252 /** Constructs a leaf node with the given range of objects. */
253 Child(TIndex first, TIndex count):
254 index_( -static_cast<int>((first << countBits) | (count - 1)) - 1 )
255 {
256 LASS_ASSERT(index_ < 0);
257 LASS_ASSERT((count > 0 && count <= maxCount) && (first + count -1 <= maxObject));
258 }
259
260 bool isEmpty() const { return !index_; }
261 bool isInternal() const { return index_ > 0; } ///< true if child is internal node
262 bool isLeaf() const { return index_ < 0; } ///< true if child contains objects
263 TIndex node() const { LASS_ASSERT(index_ > 0); return static_cast<TIndex>(index_ - 1); } ///< index of internal child node
264 TIndex first() const { LASS_ASSERT(index_ < 0); return static_cast<TIndex>(-(index_ + 1)) >> countBits; } ///< index of first object in leaf node
265 TIndex count() const { LASS_ASSERT(index_ < 0); return (static_cast<TIndex>(-(index_ + 1)) & (maxCount - 1)) + 1; } /// < number of objects in leaf node
266
267 private:
268 int index_;
269 };
270
271 using TPoint_ = impl::qbvh::Point<TValue, dimension>;
272 using TRay_ = impl::qbvh::Ray<TValue, dimension>;
273 using TAabb_ = impl::qbvh::Aabb<TValue, dimension>;
274 using TQAabb_ = impl::qbvh::QAabb<TValue, dimension>;
275
276 struct alignas(128) Node
277 {
278 TQAabb_ bounds;
279 Child children[4];
280 TAxis axis[3];
281 int usedMask; ///< bit mask of non-empty children
282 };
283
284 using TNodes = std::vector<Node>;
285
286 using TSplitInfo = SplitInfo<TObjectTraits>;
287
288 Child balance(TInputIterator first, TInputIterator last, TAabb& bounds);
289 TSplitInfo forceSplit(const TAabb& bounds);
290
291 bool doContains(Child root, const TPoint& point, const TInfo* info) const;
292
293 template <typename OutputIterator>
294 OutputIterator doFind(Child root, const TPoint& point, OutputIterator result, const TInfo* info) const;
295 template <typename OutputIterator>
296 OutputIterator doFind(Child root, const TAabb& box, OutputIterator result, const TInfo* info) const;
297 template <typename OutputIterator>
298 OutputIterator doFind(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const;
299
300 TObjectIterator doIntersect(Child root, const TRay& ray, const TVector& invDir, TReference t, TParam tMin, const TInfo* info) const;
301 bool doIntersects(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, const TInfo* info) const;
302
303 void doNearestNeighbour(Child root, const TPoint& point, const TInfo* info, Neighbour& best) const;
304 template <typename RandomIterator>
305 RandomIterator doRangeSearch(Child root, const TPoint& center, TReference squaredRadius, size_t maxCount, RandomIterator first, RandomIterator last, const TInfo* info) const;
306
307 bool volumeIntersect(const TAabb& box, const TRay& ray, const TVector& invDir, TReference t, TParam tMin) const;
308 bool volumeIntersects(const TAabb& box, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax) const;
309
310 static TPoint_ makePoint(const TPoint& point);
311 static TRay_ makeRay(const TRay& ray);
312 static TAabb_ makeAabb(const TAabb& aabb);
313
314 TAabb aabb_;
315 TNodes nodes_;
316 TObjectIterators objects_;
317 std::unique_ptr<TObjectIterator> end_;
318 Child root_;
319};
320
321} // namespace spat
322} // namespace lass
323
324#include "qbvh_tree.inl"
TObjectIterator intersect(const TRay &ray, TReference t, TParam tMin=0, const TInfo *info=0) const
Find the first object that is intersected by ray, so that t >= tMin and t is minimized.
bool isEmpty() const
Returns true if there are no objects in the tree.
RandomIterator rangeSearch(const TPoint &center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo *info=0) const
Find all objects that are within maxRadius from center, up to maxCount.
bool contains(const TPoint &point, const TInfo *info=0) const
Check whether there's any object in the tree that contains point.
OutputIterator find(const TRay &ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo *info=0) const
Find all objects that have an intersection with ray in the interval [tMin, tMax].
const TAabb aabb() const
Return the total bounding box of all objecs in the tree.
void reset()
Reset the tree to an empty one.
void swap(TSelf &other)
Swap the contents of this tree with another.
OutputIterator find(const TPoint &point, OutputIterator result, const TInfo *info=0) const
Find all objects that contain point.
bool intersects(const TRay &ray, TParam tMin=0, TParam tMax=std::numeric_limits< TValue >::infinity(), const TInfo *info=0) const
Check whether there's any object in the tree that is intersected by ray in the interval [tMin,...
const Neighbour nearestNeighbour(const TPoint &point, const TInfo *info=0) const
Find the object that is closest to point.
const TObjectIterator end() const
Returns an iterator not pointing to any object, used to indicate when no object is found.
OutputIterator find(const TAabb &box, OutputIterator result, const TInfo *info=0) const
Find all objects that intersect with box.
void reset(TObjectIterator first, TObjectIterator last)
Reset the tree to a new one with objects in the range [first, last)
spatial subdivisions, quadtrees, octrees, meshes in 2D and 3D, triangulators, ...
Definition aabb8_tree.h:80
Library for Assembled Shared Sources.
Definition config.h:53
default traits for objects to be stored in spatial subdivision trees