Library of Assembled Shared Sources
aabb8_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 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::Aabb8Tree
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 Aabb8Tree 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
66#pragma once
67
68#include "spat_common.h"
70#include "split_heuristics.h"
71
72#if LASS_HAVE_AVX
73#include <immintrin.h>
74#include <xmmintrin.h>
75#endif
76
77namespace lass
78{
79namespace spat
80{
81namespace impl::aabb8
82{
83
84template <typename T, size_t D>
85struct Point
86{
87 T x[D];
88};
89
90template <typename T, size_t D>
91struct Ray
92{
93 T support[D];
94 T invDir[D];
95 bool sign[D];
96};
97template <typename T, size_t D>
98struct Aabb
99{
100 T corners[D][2];
101};
102
103template <typename T, size_t D>
104struct Aabb8
105{
106 T corners[8][D][2];
107 void set(size_t k, const Aabb<T, D>& aabb);
108};
109
110#if LASS_HAVE_AVX
111
112template <size_t D>
113struct alignas(32) Aabb8<float, D>
114{
115 __m256 corners[D][2];
116 void set(size_t k, const Aabb<float, D>& aabb);
117};
118
119template <size_t D>
120struct alignas(32) Aabb8<double, D>
121{
122 __m256d corners[D][2][2];
123 void set(size_t k, const Aabb<double, D>& aabb);
124};
125
126#endif
127
128}
129
130
131template
132<
133 typename ObjectType,
134 typename ObjectTraits = DefaultObjectTraits<ObjectType>,
135 typename SplitHeuristics = DefaultSplitHeuristics
136>
137class Aabb8Tree: public SplitHeuristics
138{
139public:
140
141 using TSelf = Aabb8Tree<ObjectType, ObjectTraits, SplitHeuristics>;
142
143 using TObject = ObjectType;
144 using TObjectTraits = ObjectTraits;
145 using TSplitHeuristics = SplitHeuristics;
146
147 using TObjectIterator = typename TObjectTraits::TObjectIterator;
148 using TObjectReference = typename TObjectTraits::TObjectReference;
149 using TAabb = typename TObjectTraits::TAabb;
150 using TRay = typename TObjectTraits::TRay;
151 using TPoint = typename TObjectTraits::TPoint;
152 using TVector = typename TObjectTraits::TVector;
153 using TValue = typename TObjectTraits::TValue;
154 using TParam = typename TObjectTraits::TParam;
155 using TReference = typename TObjectTraits::TReference;
156 using TConstReference = typename TObjectTraits::TConstReference;
157 using TInfo = typename TObjectTraits::TInfo;
158
159 using TNumTraits = num::NumTraits<TValue>;
160
161 using TObjectIterators = std::vector<TObjectIterator>;
162
163 class Neighbour
164 {
165 public:
166 Neighbour() {}
167 Neighbour(TObjectIterator object, TValue squaredDistance):
168 object_(object), squaredDistance_(squaredDistance) {}
169 TObjectIterator object() const { return object_; }
170 TValue squaredDistance() const { return squaredDistance_; }
171 TObjectIterator operator->() const { return object_; }
172 TObjectReference operator*() const { return TObjectTraits::object(object_); }
173 bool operator<(const Neighbour& other) const { return squaredDistance_ < other.squaredDistance_; }
174 private:
175 TObjectIterator object_;
176 TValue squaredDistance_;
177 };
178
179 constexpr static size_t dimension = TObjectTraits::dimension;
180 constexpr static size_t defaultMaxObjectsPerLeaf = 1;
181 constexpr static size_t defaultMaxDepth = 20;
182
183 Aabb8Tree(TSplitHeuristics heuristics = TSplitHeuristics(defaultMaxObjectsPerLeaf, defaultMaxDepth));
184 Aabb8Tree(TObjectIterator first, TObjectIterator last, TSplitHeuristics heuristics = TSplitHeuristics(defaultMaxObjectsPerLeaf, defaultMaxDepth));
185 Aabb8Tree(TSelf&& other) noexcept;
186
187 TSelf& operator=(TSelf&& other) noexcept;
188
189 void reset();
190 void reset(TObjectIterator first, TObjectIterator last);
191
192 const TAabb aabb() const;
193
194 bool contains(const TPoint& point, const TInfo* info = 0) const;
195
196 template <typename OutputIterator>
197 OutputIterator find(const TPoint& point, OutputIterator result, const TInfo* info = 0) const;
198 template <typename OutputIterator>
199 OutputIterator find(const TAabb& box, OutputIterator result, const TInfo* info = 0) const;
200 template <typename OutputIterator>
201 OutputIterator find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info = 0) const;
202
203 TObjectIterator intersect(const TRay& ray, TReference t, TParam tMin = 0, const TInfo* info = 0) const;
204 bool intersects(const TRay& ray, TParam tMin = 0, TParam tMax = std::numeric_limits<TValue>::infinity(), const TInfo* info = 0) const;
205
206 const Neighbour nearestNeighbour(const TPoint& point, const TInfo* info = 0) const;
207 template <typename RandomIterator>
208 RandomIterator rangeSearch(const TPoint& center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo* info = 0) const;
209
210 bool isEmpty() const;
211 const TObjectIterator end() const;
212 void swap(TSelf& other);
213
214private:
215
216 constexpr static size_t stackSize_ = 256;
217
218 using TIndex = unsigned;
219 using TAxis = int;
220
221 struct Input
222 {
223 TAabb aabb;
224 TObjectIterator object;
225 Input(const TAabb& aabb, TObjectIterator object): aabb(aabb), object(object) {}
226 };
227 using TInputs = std::vector<Input>;
228 using TInputIterator = typename TInputs::iterator;
229
230 class Child
231 {
232 public:
233 constexpr static size_t countBits = 4; ///< number of bits for object count
234 constexpr static TIndex maxCount = 1 << countBits; ///< max number of objects per node
235 constexpr static TIndex maxObject = static_cast<TIndex>(std::numeric_limits<int>::max()) >> countBits; ///< max object index
236 constexpr static TIndex maxNode = static_cast<TIndex>(std::numeric_limits<int>::max()) - 1; ///< max node index
237
238 /** Constructs an empty node */
239 Child(): index_(0) {}
240
241 /** Constructs an internal node */
242 explicit Child(TIndex node):
243 index_(static_cast<int>(node + 1))
244 {
245 LASS_ASSERT(index_ > 0);
246 }
247
248 /** Constructs a leaf node with the given range of objects. */
249 Child(TIndex first, TIndex count):
250 index_( -static_cast<int>((first << countBits) | (count - 1)) - 1 )
251 {
252 LASS_ASSERT(index_ < 0);
253 LASS_ASSERT((first >= 0) && (count > 0 && count <= maxCount) && (first + count -1 <= maxObject));
254 }
255
256 bool isEmpty() const { return !index_; }
257 bool isInternal() const { return index_ > 0; } ///< true if child is internal node
258 bool isLeaf() const { return index_ < 0; } ///< true if child contains objects
259 TIndex node() const { LASS_ASSERT(index_ > 0); return static_cast<TIndex>(index_ - 1); } ///< index of internal child node
260 TIndex first() const { LASS_ASSERT(index_ < 0); return static_cast<TIndex>(-(index_ + 1)) >> countBits; } ///< index of first object in leaf node
261 TIndex count() const { LASS_ASSERT(index_ < 0); return (static_cast<TIndex>(-(index_ + 1)) & (maxCount - 1)) + 1; } /// < number of objects in leaf node
262
263 private:
264 int index_;
265 };
266
267 using TPoint_ = impl::aabb8::Point<TValue, dimension>;
268 using TRay_ = impl::aabb8::Ray<TValue, dimension>;
269 using TAabb_ = impl::aabb8::Aabb<TValue, dimension>;
270 using TQAabb_ = impl::aabb8::Aabb8<TValue, dimension>;
271
272 struct alignas(64) Node
273 {
274 TQAabb_ bounds;
275 Child children[8];
276 TAxis axis[7];
277 };
278
279 using TNodes = std::vector<Node>;
280
281 using TSplitInfo = SplitInfo<TObjectTraits>;
282
283 Child balance(TInputIterator first, TInputIterator last, TAabb& bounds);
284 TSplitInfo forceSplit(const TAabb& bounds);
285
286 bool doContains(Child root, const TPoint& point, const TInfo* info) const;
287
288 template <typename OutputIterator>
289 OutputIterator doFind(Child root, const TPoint& point, OutputIterator result, const TInfo* info) const;
290 template <typename OutputIterator>
291 OutputIterator doFind(Child root, const TAabb& box, OutputIterator result, const TInfo* info) const;
292 template <typename OutputIterator>
293 OutputIterator doFind(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const;
294
295 TObjectIterator doIntersect(Child root, const TRay& ray, const TVector& invDir, TReference t, TParam tMin, const TInfo* info) const;
296 bool doIntersects(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, const TInfo* info) const;
297
298 void doNearestNeighbour(Child root, const TPoint& point, const TInfo* info, Neighbour& best) const;
299 template <typename RandomIterator>
300 RandomIterator doRangeSearch(Child root, const TPoint& center, TReference squaredRadius, size_t maxCount, RandomIterator first, RandomIterator last, const TInfo* info) const;
301
302 bool volumeIntersect(const TAabb& box, const TRay& ray, const TVector& invDir, TReference t, TParam tMin) const;
303 bool volumeIntersects(const TAabb& box, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax) const;
304
305 static TPoint_ makePoint(const TPoint& point);
306 static TRay_ makeRay(const TRay& ray);
307 static TAabb_ makeAabb(const TAabb& aabb);
308
309 TAabb aabb_;
310 TNodes nodes_;
311 TObjectIterators objects_;
312 std::unique_ptr<TObjectIterator> end_;
313 Child root_;
314};
315
316} // namespace spat
317} // namespace lass
318
319#include "aabb8_tree.inl"
bool isEmpty() const
Returns true if there are no objects in the tree.
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.
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.
const TObjectIterator end() const
Returns an iterator not pointing to any object, used to indicate when no object is found.
const TAabb aabb() const
Return the total bounding box of all objecs in the tree.
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.
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].
void swap(TSelf &other)
Swap the contents of this tree with another.
void reset()
Reset the tree to an empty one.
void reset(TObjectIterator first, TObjectIterator last)
Reset the tree to a new one with objects in the range [first, last)
OutputIterator find(const TAabb &box, OutputIterator result, const TInfo *info=0) const
Find all objects that intersect with box.
OutputIterator find(const TPoint &point, OutputIterator result, const TInfo *info=0) const
Find all objects that contain point.
T sign(const T &x)
if x < 0 return -1, else if x > 0 return 1, else return 0.
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