Library of Assembled Shared Sources
qbvh_tree.inl
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#pragma once
44
45#include "spat_common.h"
46#include "qbvh_tree.h"
47
48#include <cstddef>
49#include <cstring>
50
51namespace lass
52{
53namespace spat
54{
55namespace impl::qbvh
56{
57
58template <typename T, size_t D>
59void QAabb<T, D>::set(size_t k, const Aabb<T, D>& aabb)
60{
61 for (size_t i = 0; i < D; ++i)
62 {
63 corners[k][i][0] = aabb.corners[i][0];
64 corners[k][i][1] = aabb.corners[i][1];
65 }
66}
67
68template <typename T, size_t D>
69int contains(const QAabb<T, D>& bounds, const Point<T, D>& point)
70{
71 int hitmask = 0;
72 for (size_t k = 0; k < 4; ++k)
73 {
74 int hit = 1;
75 for (size_t d = 0; d < D; ++d)
76 {
77 hit &= bounds.corners[k][d][0] <= point.x[d] && point.x[d] <= bounds.corners[k][d][1];
78 }
79 hitmask |= hit << k;
80 }
81 return hitmask;
82}
83
84template <typename T, size_t D>
85int overlaps(const QAabb<T, D>& bounds, const Aabb<T, D>& box)
86{
87 int hitmask = 0;
88 for (size_t k = 0; k < 4; ++k)
89 {
90 int hit = 1;
91 for (size_t d = 0; d < D; ++d)
92 {
93 hit &= bounds.corners[k][d][0] <= box.corners[d][1] && box.corners[d][0] <= bounds.corners[k][d][1];
94 }
95 hitmask |= hit << k;
96 }
97 return hitmask;
98}
99
100template <typename T, size_t D>
101int intersect(const QAabb<T, D>& bounds, const Ray<T, D>& ray, T tMin, T tMax, T ts[4])
102{
103 int hitmask = 0;
104 for (size_t k = 0; k < 4; ++k)
105 {
106 T tmin = tMin;
107 T tmax = tMax;
108 for (size_t d = 0; d < D; ++d)
109 {
110 const bool sign = ray.sign[d];
111 const T bmin = bounds.corners[k][d][sign];
112 const T bmax = bounds.corners[k][d][sign ^ 1];
113 const T dmin = (bmin - ray.support[d]) * ray.invDir[d];
114 const T dmax = (bmax - ray.support[d]) * ray.invDir[d];
115 tmin = std::max(dmin, tmin);
116 tmax = std::min(dmax, tmax);
117 }
118 tmax *= 1 + 2 * num::NumTraits<T>::gamma(3);
119 hitmask |= tmin <= tmax ? 1 << k : 0;
120 ts[k] = tmin;
121 }
122 return hitmask;
123}
124
125template <typename T, size_t D>
126void squaredDistance(const QAabb<T, D>& bounds, const Point<T, D>& point, T sds[4])
127{
128 for (size_t k = 0; k < 4; ++k)
129 {
130 T sd = 0;
131 for (size_t i = 0; i < D; ++i)
132 {
133 const T bmin = bounds.corners[k][i][0];
134 const T bmax = bounds.corners[k][i][1];
135 const T dmin = bmin - point.x[i];
136 const T dmax = point.x[i] - bmax;
137 const T d = std::max(dmin, dmax);
138 if (d > 0)
139 {
140 sd += d * d;
141 }
142 }
143 sds[k] = sd;
144 }
145}
146
147#if LASS_HAVE_AVX
148
149template <size_t D>
150void QAabb<float, D>::set(size_t k, const Aabb<float, D>& aabb)
151{
152 for (size_t i = 0; i < D; ++i)
153 {
154#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
155 corners[i][0].m128_f32[k] = aabb.corners[i][0];
156 corners[i][1].m128_f32[k] = aabb.corners[i][1];
157#else
158 corners[i][0][k] = aabb.corners[i][0];
159 corners[i][1][k] = aabb.corners[i][1];
160#endif
161 }
162}
163
164template <size_t D>
165void QAabb<double, D>::set(size_t k, const Aabb<double, D>& aabb)
166{
167 for (size_t i = 0; i < D; ++i)
168 {
169#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
170 corners[i][0].m256d_f64[k] = aabb.corners[i][0];
171 corners[i][1].m256d_f64[k] = aabb.corners[i][1];
172#else
173 corners[i][0][k] = aabb.corners[i][0];
174 corners[i][1][k] = aabb.corners[i][1];
175#endif
176 }
177}
178
179template <size_t D>
180int contains(const QAabb<float, D>& bounds, const Point<float, D>& point)
181{
182 __m128 h = _mm_castsi128_ps(_mm_set1_epi32(-1));
183 for (size_t d = 0; d < D; ++d)
184 {
185 const __m128 p = _mm_set_ps1(point.x[d]);
186 const __m128 hmin = _mm_cmp_ps(bounds.corners[d][0], p, _CMP_LE_OQ);
187 const __m128 hmax = _mm_cmp_ps(p, bounds.corners[d][1], _CMP_LE_OQ);
188 h = _mm_and_ps(h, hmin);
189 h = _mm_and_ps(h, hmax);
190 }
191 const int hitmask = _mm_movemask_ps(h);
192 return hitmask;
195template <size_t D>
196int contains(const QAabb<double, D>& bounds, const Point<double, D>& point)
197{
198 __m256d h = _mm256_castsi256_pd(_mm256_set1_epi32(-1));
199 for (size_t d = 0; d < D; ++d)
200 {
201 const __m256d p = _mm256_set1_pd(point.x[d]);
202 const __m256d hmin = _mm256_cmp_pd(bounds.corners[d][0], p, _CMP_LE_OQ);
203 const __m256d hmax = _mm256_cmp_pd(p, bounds.corners[d][1], _CMP_LE_OQ);
204 h = _mm256_and_pd(h, hmin);
205 h = _mm256_and_pd(h, hmax);
206 }
207 const int hitmask = _mm256_movemask_pd(h);
208 return hitmask;
209}
211
212template <size_t D>
213int overlaps(const QAabb<float, D>& bounds, const Aabb<float, D>& box)
215 __m128 h = _mm_castsi128_ps(_mm_set1_epi32(-1));
216 for (size_t d = 0; d < D; ++d)
217 {
218 const __m128 pmin = _mm_set_ps1(box.corners[d][0]);
219 const __m128 pmax = _mm_set_ps1(box.corners[d][1]);
220 const __m128 hmin = _mm_cmp_ps(bounds.corners[d][0], pmax, _CMP_LE_OQ);
221 const __m128 hmax = _mm_cmp_ps(pmin, bounds.corners[d][1], _CMP_LE_OQ);
222 h = _mm_and_ps(h, hmin);
223 h = _mm_and_ps(h, hmax);
224 }
225 const int hitmask = _mm_movemask_ps(h);
226 return hitmask;
227}
228
229
230template <size_t D>
231int overlaps(const QAabb<double, D>& bounds, const Aabb<double, D>& box)
232{
233 __m256d h = _mm256_castsi256_pd(_mm256_set1_epi32(-1));
234 for (size_t d = 0; d < D; ++d)
235 {
236 const __m256d pmin = _mm256_set1_pd(box.corners[d][0]);
237 const __m256d pmax = _mm256_set1_pd(box.corners[d][1]);
238 const __m256d hmin = _mm256_cmp_pd(bounds.corners[d][0], pmax, _CMP_LE_OQ);
239 const __m256d hmax = _mm256_cmp_pd(pmin, bounds.corners[d][1], _CMP_LE_OQ);
240 h = _mm256_and_pd(h, hmin);
241 h = _mm256_and_pd(h, hmax);
242 }
243 const int hitmask = _mm256_movemask_pd(h);
244 return hitmask;
245}
246
247template <size_t D>
248int intersect(const QAabb<float, D>& bounds, const Ray<float, D>& ray, float tMin, float tMax, float ts[4])
249{
250 __m128 tmin = _mm_set_ps1(tMin);
251 __m128 tmax = _mm_set_ps1(tMax);
252 for (size_t d = 0; d < D; ++d)
253 {
254 const __m128 support = _mm_set_ps1(ray.support[d]);
255 const __m128 invDir = _mm_set_ps1(ray.invDir[d]);
256 const bool sign = ray.sign[d];
257 const __m128 bmin = bounds.corners[d][sign];
258 const __m128 bmax = bounds.corners[d][sign ^ 1];
259 const __m128 dmin = _mm_mul_ps(_mm_sub_ps(bmin, support), invDir);
260 const __m128 dmax = _mm_mul_ps(_mm_sub_ps(bmax, support), invDir);
261 tmin = _mm_max_ps(dmin, tmin);
262 tmax = _mm_min_ps(dmax, tmax);
263 }
264 tmax = _mm_mul_ps(tmax, _mm_set_ps1(1 + 2 * num::NumTraits<float>::gamma(3)));
265 _mm_storeu_ps(ts, tmin);
266 __m128 h = _mm_cmp_ps(tmin, tmax, _CMP_LE_OQ);
267 const int hitmask = _mm_movemask_ps(h);
268 return hitmask;
269}
270
271template <size_t D>
272int intersect(const QAabb<double, D>& bounds, const Ray<double, D>& ray, double tMin, double tMax, double ts[4])
273{
274 __m256d tmin = _mm256_set1_pd(tMin);
275 __m256d tmax = _mm256_set1_pd(tMax);
276 for (size_t d = 0; d < D; ++d)
277 {
278 const __m256d support = _mm256_set1_pd(ray.support[d]);
279 const __m256d invDir = _mm256_set1_pd(ray.invDir[d]);
280 const bool sign = ray.sign[d];
281 const __m256d bmin = bounds.corners[d][sign];
282 const __m256d bmax = bounds.corners[d][sign ^ 1];
283 const __m256d dmin = _mm256_mul_pd(_mm256_sub_pd(bmin, support), invDir);
284 const __m256d dmax = _mm256_mul_pd(_mm256_sub_pd(bmax, support), invDir);
285 tmin = _mm256_max_pd(dmin, tmin);
286 tmax = _mm256_min_pd(dmax, tmax);
287 }
288 _mm256_storeu_pd(ts, tmin);
289 tmax = _mm256_mul_pd(tmax, _mm256_set1_pd(1 + 2 * num::NumTraits<double>::gamma(3)));
290 const __m256d h = _mm256_cmp_pd(tmin, tmax, _CMP_LE_OQ);
291 const int hitmask = _mm256_movemask_pd(h);
292 return hitmask;
293}
294
295
296template <size_t D>
297void squaredDistance(const QAabb<float, D>& bounds, const Point<float, D>& point, float sds[4])
298{
299 const __m128 zero = _mm_setzero_ps();
300 __m128 sd = _mm_setzero_ps();
301 for (size_t i = 0; i < D; ++i)
302 {
303 const __m128 p = _mm_set_ps1(point.x[i]);
304 const __m128 dmin = _mm_sub_ps(bounds.corners[i][0], p);
305 const __m128 dmax = _mm_sub_ps(p, bounds.corners[i][1]);
306 const __m128 d = _mm_max_ps(_mm_max_ps(dmin, dmax), zero);
307 const __m128 d2 = _mm_mul_ps(d, d);
308 sd = _mm_add_ps(sd, d2);
309 }
310 _mm_storeu_ps(sds, sd);
311}
312
313template <size_t D>
314void squaredDistance(const QAabb<double, D>& bounds, const Point<double, D>& point, double sds[4])
315{
316 const __m256d zero = _mm256_setzero_pd();
317 __m256d sd = _mm256_setzero_pd();
318 for (size_t i = 0; i < D; ++i)
319 {
320 const __m256d p = _mm256_set1_pd(point.x[i]);
321 const __m256d dmin = _mm256_sub_pd(bounds.corners[i][0], p);
322 const __m256d dmax = _mm256_sub_pd(p, bounds.corners[i][1]);
323 const __m256d d = _mm256_max_pd(_mm256_max_pd(dmin, dmax), zero);
324 const __m256d d2 = _mm256_mul_pd(d, d);
325 sd = _mm256_add_pd(sd, d2);
326 }
327 _mm256_storeu_pd(sds, sd);
328}
329
330#endif
331
332
333}
334
335
336// --- public --------------------------------------------------------------------------------------
337
338
339
340template <typename O, typename OT, typename SH>
341QbvhTree<O, OT, SH>::QbvhTree(TSplitHeuristics heuristics) :
342 SH(std::move(heuristics)),
343 aabb_(TObjectTraits::aabbEmpty()),
344 nodes_(),
345 objects_(),
346 end_(new TObjectIterator),
347 root_()
348{
349}
350
351
352
353template <typename O, typename OT, typename SH>
354QbvhTree<O, OT, SH>::QbvhTree(TObjectIterator first, TObjectIterator last, TSplitHeuristics heuristics):
355 SH(std::move(heuristics)),
356 aabb_(TObjectTraits::aabbEmpty()),
357 nodes_(),
358 objects_(),
359 end_(new TObjectIterator(last)),
360 root_()
361{
362 if (first != last)
363 {
364 std::ptrdiff_t n = last - first;
365 if (n < 0)
366 {
367 LASS_THROW("QbvhTree: invalid range");
368 }
369 if (static_cast<size_t>(n) > static_cast<size_t>(Child::maxNode) + 1)
370 {
371 LASS_THROW("QbvhTree: too many objects");
372 }
373 TInputs inputs;
374 inputs.reserve(static_cast<size_t>(n));
375 for (TObjectIterator i = first; i != last; ++i)
376 {
377 inputs.emplace_back(TObjectTraits::objectAabb(i), i);
378 }
379 root_ = balance(inputs.begin(), inputs.end(), aabb_);
380 }
381}
382
383
384template <typename O, typename OT, typename SH>
385QbvhTree<O, OT, SH>::QbvhTree(TSelf&& other) noexcept:
386 SH(std::forward<TSplitHeuristics>(other)),
387 aabb_(std::move(other.aabb_)),
388 nodes_(std::move(other.nodes_)),
389 objects_(std::move(other.objects_)),
390 end_(std::move(other.end_)),
391 root_(std::move(other.root_))
392{
393}
394
395
396template <typename O, typename OT, typename SH>
397QbvhTree<O, OT, SH>& QbvhTree<O, OT, SH>::operator=(TSelf&& other) noexcept
398{
399 TSplitHeuristics::operator=(std::forward<TSplitHeuristics>(other));
400 aabb_ = std::move(other.aabb_);
401 nodes_ = std::move(other.nodes_);
402 objects_ = std::move(other.objects_);
403 end_ = std::move(other.end_);
404 root_ = std::move(other.root_);
405 return *this;
406}
407
408
409
410/** Reset the tree to an empty one.
411 *
412 * Is equivalent to:
413 * @code
414 * *this = TSelf();
415 * @endcode
416 */
417template <typename O, typename OT, typename SH>
419{
420 TSelf temp;
421 swap(temp);
422}
423
424
425
426/** Reset the tree to a new one with objects in the range [@a first, @a last)
427 *
428 * Is equivalent to:
429 * @code
430 * *this = TSelf(first, last);
431 * @endcode
432 */
433template <typename O, typename OT, typename SH>
434void QbvhTree<O, OT, SH>::reset(TObjectIterator first, TObjectIterator last)
435{
436 TSelf temp(first, last, static_cast<const SH&>(*this));
437 swap(temp);
438}
439
440
441
442/** Return the total bounding box of all objecs in the tree
443 */
444template <typename O, typename OT, typename SH> inline
445const typename QbvhTree<O, OT, SH>::TAabb
447{
448 return aabb_;
449}
450
451
452
453/** Check whether there's any object in the tree that contains @a point.
454 */
455template <typename O, typename OT, typename SH>
456bool QbvhTree<O, OT, SH>::contains(const TPoint& point, const TInfo* info) const
457{
458 if (isEmpty() || !TObjectTraits::aabbContains(aabb_, point))
459 {
460 return false;
461 }
462 LASS_ASSERT(!root_.isEmpty());
463 return doContains(root_, point, info);
464}
465
466
467
468/** Find all objects that contain @a point.
469 */
470template <typename O, typename OT, typename SH>
471template <typename OutputIterator> inline
472OutputIterator QbvhTree<O, OT, SH>::find(const TPoint& point, OutputIterator result, const TInfo* info) const
473{
474 if (isEmpty() || !TObjectTraits::aabbContains(aabb_, point))
475 {
476 return result;
477 }
478 LASS_ASSERT(!root_.isEmpty());
479 return doFind(root_, point, result, info);
480}
481
482
483
484/** Find all objects that intersect with @a box.
485 */
486template <typename O, typename OT, typename SH>
487template <typename OutputIterator> inline
488OutputIterator QbvhTree<O, OT, SH>::find(const TAabb& box, OutputIterator result, const TInfo* info) const
489{
490 if (isEmpty() || !TObjectTraits::aabbIntersects(aabb_, box))
491 {
492 return result;
493 }
494 LASS_ASSERT(!root_.isEmpty());
495 return doFind(root_, box, result, info);
496}
497
498
499
500/** Find all objects that have an intersection with @a ray in the interval [tMin, tMax].
501 */
502template <typename O, typename OT, typename SH>
503template <typename OutputIterator>
504OutputIterator QbvhTree<O, OT, SH>::find(const TRay& ray, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
505{
506 const TVector invDir = TObjectTraits::vectorReciprocal(TObjectTraits::rayDirection(ray));
507 if (isEmpty() || !volumeIntersects(aabb_, ray, invDir, tMin, tMax))
508 {
509 return result;
510 }
511 LASS_ASSERT(!root_.isEmpty());
512 return doFind(root_, ray, invDir, tMin, tMax, result, info);
513}
514
515
516
517/** Find the first object that is intersected by @a ray, so that t >= tMin and t is minimized.
518 */
519template <typename O, typename OT, typename SH>
520typename QbvhTree<O, OT, SH>::TObjectIterator
521QbvhTree<O, OT, SH>::intersect(const TRay& ray, TReference t, TParam tMin, const TInfo* info) const
522{
523 const TVector dir = TObjectTraits::rayDirection(ray);
524 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
525 TValue tRoot;
526 if (isEmpty() || !volumeIntersect(aabb_, ray, invDir, tRoot, tMin))
527 {
528 return *end_;
529 }
530 LASS_ASSERT(!root_.isEmpty());
531 return doIntersect(root_, ray, invDir, t, tMin, info);
532}
533
534
535
536/** Check whether there's any object in the tree that is intersected by @a ray in the interval [tMin, tMax].
537 */
538template <typename O, typename OT, typename SH>
540 const TRay& ray, TParam tMin, TParam tMax, const TInfo* info) const
541{
542 LASS_ASSERT(tMax > tMin || (num::isInf(tMin) && num::isInf(tMax)));
543 const TVector dir = TObjectTraits::rayDirection(ray);
544 const TVector invDir = TObjectTraits::vectorReciprocal(dir);
545 if (isEmpty() || !volumeIntersects(aabb_, ray, invDir, tMin, tMax))
546 {
547 return false;
548 }
549 LASS_ASSERT(!root_.isEmpty());
550 return doIntersects(root_, ray, invDir, tMin, tMax, info);
551}
552
553
554
555/** Find the object that is closest to @a point.
556 */
557template <typename O, typename OT, typename SH>
558const typename QbvhTree<O, OT, SH>::Neighbour
559QbvhTree<O, OT, SH>::nearestNeighbour(const TPoint& point, const TInfo* info) const
560{
561 Neighbour nearest(*end_, std::numeric_limits<TValue>::infinity());
562 if (isEmpty())
563 {
564 return nearest;
565 }
566 LASS_ASSERT(!root_.isEmpty());
567 doNearestNeighbour(root_, point, info, nearest);
568 return nearest;
569}
570
571
572
573/** Find all objects that are within @a maxRadius from @a center, up to @a maxCount.
574 *
575 * If more than @a maxCount objects within @a maxRadius are found, then the closest
576 * @a maxCount objects are returned.
577 *
578 * @param center The center of the search sphere.
579 * @param maxRadius The maximum radius of the search sphere.
580 * @param maxCount The maximum number of objects to find.
581 * @param first An output iterator to a container of Neighbour objects that will be filled with the found objects.
582 * @param info Optional information to pass to the object traits.
583 *
584 * @return The output iterator @a last, pointing to the first element beyond the last found object.
585 *
586 * The range [@a first, @a last) will contain the found objects and form a heap, so that @a first points to the
587 * farthest object from @a center: its squared distance to @a center is the largest in the range, and gives you
588 * the effective maximum radius of the search sphere.
589 *
590 * @note The output iterator must be able to handle at least @a maxCount objects.
591 */
592template <class O, class OT, typename SH>
593template<typename RandomIterator>
594RandomIterator
596 const TPoint& center, TParam maxRadius, size_t maxCount, RandomIterator first, const TInfo* info) const
597{
598 if (isEmpty() || maxRadius == 0)
599 {
600 return first;
601 }
602 TValue squaredRadius = maxRadius * maxRadius;
603 LASS_ASSERT(!root_.isEmpty());
604 return doRangeSearch(root_, center, squaredRadius, maxCount, first, first, info);
605}
606
607
608
609/** Returns true if there are no objects in the tree.
610 */
611template <typename O, typename OT, typename SH>
613{
614 return objects_.empty();
615}
616
617
618
619/** Returns an iterator not pointing to any object, used to indicate when no object is found.
620 *
621 * This iterator can be used as the return value of the find functions when no object is found.
622 * You can compare those return values to the end() function to check if an object was found.
623 */
624template <typename O, typename OT, typename SH>
625const typename QbvhTree<O, OT, SH>::TObjectIterator
627{
628 return *end_;
629}
630
631
632
633/** Swap the contents of this tree with another.
634 *
635 * Is equivalent to:
636 * @code
637 * TSelf tmp = std::move(other);
638 * other = std::move(*this);
639 * *this = std::move(tmp);
640 * @endcode
641 */
642template <typename O, typename OT, typename SH>
644{
645 SH::swap(other);
646 std::swap(aabb_, other.aabb_);
647 nodes_.swap(other.nodes_);
648 objects_.swap(other.objects_);
649 end_.swap(other.end_);
650 std::swap(root_, other.root_);
651}
652
653
654
655// --- private -------------------------------------------------------------------------------------
656
657template <typename O, typename OT, typename SH>
658typename QbvhTree<O, OT, SH>::Child
659QbvhTree<O, OT, SH>::balance(TInputIterator first, TInputIterator last, TAabb& bounds)
660{
661 if (first == last)
662 {
663 bounds = TObjectTraits::aabbEmpty();
664 return Child();
665 }
666
667 LASS_ASSERT(nodes_.size() <= static_cast<size_t>(Child::maxNode));
668 const TIndex index = static_cast<TIndex>(nodes_.size());
669 nodes_.emplace_back();
670
671 auto split0 = TSplitHeuristics::template split<OT>(first, last);
672 bounds = split0.aabb;
673 if (split0.isLeaf())
674 {
675 const auto count = last - first;
676 if (count <= Child::maxCount)
677 {
678 const TIndex frst = static_cast<TIndex>(objects_.size());
679 LASS_ASSERT(frst >= 0 && frst <= Child::maxObject);
680 for (TInputIterator i = first; i != last; ++i)
681 {
682 objects_.push_back(i->object);
683 }
684 LASS_ASSERT(objects_.size() <= static_cast<size_t>(Child::maxObject) + 1);
685 return Child(frst, static_cast<TIndex>(count));
686 }
687 else
688 {
689 split0 = forceSplit(split0.aabb);
690 }
691 }
692 LASS_ASSERT(!split0.isLeaf());
693 nodes_[index].axis[0] = static_cast<TAxis>(split0.axis);
694 TInputIterator middle0 = std::partition(first, last, impl::Splitter<TObjectTraits>(split0));
695 if (middle0 == first || middle0 == last)
696 {
697 const std::ptrdiff_t halfSize = (last - first) / 2;
698 LASS_ASSERT(halfSize > 0);
699 middle0 = first + halfSize;
700 std::nth_element(first, middle0, last, impl::LessAxis<TObjectTraits>(split0.axis));
701 }
702 LASS_ASSERT(middle0 != first && middle0 != last);
703
704 auto split1 = TSplitHeuristics::template split<OT>(first, middle0);
705 if (split1.isLeaf())
706 {
707 const auto count = middle0 - first;
708 if (count <= Child::maxCount)
709 {
710 const TIndex frst = static_cast<TIndex>(objects_.size());
711 LASS_ASSERT(frst >= 0 && frst <= Child::maxObject);
712 for (TInputIterator i = first; i != middle0; ++i)
713 {
714 objects_.push_back(i->object);
715 }
716 LASS_ASSERT(objects_.size() <= static_cast<size_t>(Child::maxObject) + 1);
717 Node& node = nodes_[index];
718 node.children[0] = Child(frst, static_cast<TIndex>(count));
719 node.children[1] = Child();
720 node.bounds.set(0, makeAabb(split1.aabb));
721 node.bounds.set(1, makeAabb(TObjectTraits::aabbEmpty()));
722 node.axis[1] = 0;
723 }
724 else
725 {
726 split1 = forceSplit(split1.aabb);
727 }
728 }
729 if (!split1.isLeaf())
730 {
731 TInputIterator middle1 = std::partition(first, middle0, impl::Splitter<TObjectTraits>(split1));
732 if (middle1 == first || middle1 == middle0)
733 {
734 const std::ptrdiff_t halfSize = (middle0 - first) / 2;
735 LASS_ASSERT(halfSize > 0);
736 middle1 = first + halfSize;
737 std::nth_element(first, middle1, middle0, impl::LessAxis<TObjectTraits>(split1.axis));
738 }
739 TAabb bounds0, bounds1;
740 Child child0 = balance(first, middle1, bounds0);
741 Child child1 = balance(middle1, middle0, bounds1);
742 Node& node = nodes_[index];
743 node.children[0] = child0;
744 node.children[1] = child1;
745 node.bounds.set(0, makeAabb(bounds0));
746 node.bounds.set(1, makeAabb(bounds1));
747 node.axis[1] = static_cast<TAxis>(split1.axis);
748 }
749
750 auto split2 = TSplitHeuristics::template split<OT>(middle0, last);
751 if (split2.isLeaf())
752 {
753 const auto count = last - middle0;
754 if (count <= Child::maxCount)
755 {
756 const TIndex frst = static_cast<TIndex>(objects_.size());
757 LASS_ASSERT(frst >= 0 && frst <= Child::maxObject);
758 for (TInputIterator i = middle0; i != last; ++i)
759 {
760 objects_.push_back(i->object);
761 }
762 LASS_ASSERT(objects_.size() <= static_cast<size_t>(Child::maxObject) + 1);
763 Node& node = nodes_[index];
764 node.children[2] = Child(frst, static_cast<TIndex>(count));
765 node.children[3] = Child();
766 node.bounds.set(2, makeAabb(split2.aabb));
767 node.bounds.set(3, makeAabb(TObjectTraits::aabbEmpty()));
768 node.axis[2] = 0;
769 }
770 else
771 {
772 split2 = forceSplit(split2.aabb);
773 }
774 }
775 if (!split2.isLeaf())
776 {
777 TInputIterator middle2 = std::partition(middle0, last, impl::Splitter<TObjectTraits>(split2));
778 if (middle2 == middle0 || middle2 == last)
779 {
780 const std::ptrdiff_t halfSize = (last - middle0) / 2;
781 LASS_ASSERT(halfSize > 0);
782 middle2 = middle0 + halfSize;
783 std::nth_element(middle0, middle2, last, impl::LessAxis<TObjectTraits>(split2.axis));
784 }
785 TAabb bounds2, bounds3;
786 Child child2 = balance(middle0, middle2, bounds2);
787 Child child3 = balance(middle2, last, bounds3);
788 Node& node = nodes_[index];
789 node.children[2] = child2;
790 node.children[3] = child3;
791 node.bounds.set(2, makeAabb(bounds2));
792 node.bounds.set(3, makeAabb(bounds3));
793 node.axis[2] = static_cast<TAxis>(split2.axis);
794 }
795
796 Node& node = nodes_[index];
797 node.usedMask = 0;
798 for (size_t k = 0; k < 4; ++k)
799 {
800 node.usedMask |= node.children[k].isEmpty() ? 0 : 1 << k;
801 }
802
803 return Child(index);
804}
805
806
807
808template <typename O, typename OT, typename SH>
809typename QbvhTree<O, OT, SH>::TSplitInfo
810QbvhTree<O, OT, SH>::forceSplit(const TAabb& bounds)
811{
812 const TPoint min = TObjectTraits::aabbMin(bounds);
813 const TPoint max = TObjectTraits::aabbMax(bounds);
814 size_t axis = 0;
815 TValue maxSize = TObjectTraits::coord(max, 0) - TObjectTraits::coord(min, 0);
816 for (size_t k = 1; k < TObjectTraits::dimension; ++k)
817 {
818 const TValue size = TObjectTraits::coord(max, k) - TObjectTraits::coord(min, k);
819 if (size > maxSize)
820 {
821 axis = k;
822 maxSize = size;
823 }
824 }
825 const TValue x = (TObjectTraits::coord(min, axis) + TObjectTraits::coord(max, axis)) / 2;
826 return TSplitInfo(bounds, x, axis);
827}
828
829
830
831template <typename O, typename OT, typename SH>
832bool QbvhTree<O, OT, SH>::doContains(Child root, const TPoint& point, const TInfo* info) const
833{
834 const auto pnt = makePoint(point);
835
836 Child stack[stackSize_];
837 TIndex stackSize = 0;
838 stack[stackSize++] = root;
839 while (stackSize > 0)
840 {
841 const Child index = stack[--stackSize];
842 LASS_ASSERT(!index.isEmpty());
843
844 if (index.isLeaf())
845 {
846 const TIndex first = index.first();
847 const TIndex last = first + index.count();
848 for (TIndex i = first; i != last; ++i)
849 {
850 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
851 if (TObjectTraits::objectContains(objects_[i], point, info))
852 {
853 return true;
854 }
855 }
856 continue;
857 }
858
859 LASS_ASSERT(index.isInternal());
860 LASS_ASSERT(index.node() < nodes_.size());
861 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
862 const Node& node = nodes_[index.node()];
863
864 const int hits = node.usedMask & impl::qbvh::contains(node.bounds, pnt);
865 for (int k = 3; k >= 0; --k)
866 {
867 if (!(hits & (1 << k)))
868 {
869 continue;
870 }
871 const Child child = node.children[k];
872 if (child.isEmpty())
873 {
874 continue;
875 }
876 if (stackSize < stackSize_)
877 {
878 stack[stackSize++] = child;
879 }
880 else if (doContains(child, point, info))
881 {
882 return true;
883 }
884 }
885 }
886 return false;
887}
888
889
890
891template <typename O, typename OT, typename SH>
892template <typename OutputIterator>
893OutputIterator QbvhTree<O, OT, SH>::doFind(Child root, const TPoint& point, OutputIterator result, const TInfo* info) const
894{
895 const auto pnt = makePoint(point);
896
897 Child stack[stackSize_];
898 TIndex stackSize = 0;
899 stack[stackSize++] = root;
900 while (stackSize > 0)
901 {
902 const Child index = stack[--stackSize];
903 LASS_ASSERT(!index.isEmpty());
904
905 if (index.isLeaf())
906 {
907 const TIndex first = index.first();
908 const TIndex last = first + index.count();
909 for (TIndex i = first; i != last; ++i)
910 {
911 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
912 if (TObjectTraits::objectContains(objects_[i], point, info))
913 {
914 *result++ = objects_[i];
915 }
916 }
917 continue;
918 }
919
920 LASS_ASSERT(index.isInternal());
921 LASS_ASSERT(index.node() < nodes_.size());
922 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
923 const Node& node = nodes_[index.node()];
924
925 const int hits = node.usedMask & impl::qbvh::contains(node.bounds, pnt);
926 for (int k = 3; k >= 0; --k)
927 {
928 if (!(hits & (1 << k)))
929 {
930 continue;
931 }
932 const Child child = node.children[k];
933 if (child.isEmpty())
934 {
935 continue;
936 }
937 if (stackSize < stackSize_)
938 {
939 stack[stackSize++] = child;
940 }
941 else
942 {
943 result = doFind(child, point, result, info);
944 }
945 }
946 }
947 return result;
948}
949
950
951template <typename O, typename OT, typename SH>
952template <typename OutputIterator>
953OutputIterator QbvhTree<O, OT, SH>::doFind(Child root, const TAabb& box, OutputIterator result, const TInfo* info) const
954{
955 const auto bx = makeAabb(box);
956
957 Child stack[stackSize_];
958 TIndex stackSize = 0;
959 stack[stackSize++] = root;
960 while (stackSize > 0)
961 {
962 const Child index = stack[--stackSize];
963 LASS_ASSERT(!index.isEmpty());
964
965 if (index.isLeaf())
966 {
967 const TIndex first = index.first();
968 const TIndex last = first + index.count();
969 for (TIndex i = first; i != last; ++i)
970 {
971 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
972 if (TObjectTraits::objectIntersects(objects_[i], box, info))
973 {
974 *result++ = objects_[i];
975 }
976 }
977 continue;
978 }
979
980 LASS_ASSERT(index.isInternal());
981 LASS_ASSERT(index.node() < nodes_.size());
982 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
983 const Node& node = nodes_[index.node()];
984
985 const int hits = node.usedMask & impl::qbvh::overlaps(node.bounds, bx);
986 for (int k = 3; k >= 0; --k)
987 {
988 if (!(hits & (1 << k)))
989 {
990 continue;
991 }
992 const Child child = node.children[k];
993 if (child.isEmpty())
994 {
995 continue;
996 }
997 if (stackSize < stackSize_)
998 {
999 stack[stackSize++] = child;
1000 }
1001 else
1002 {
1003 result = doFind(child, box, result, info);
1004 }
1005 }
1006 }
1007 return result;
1008}
1009
1010
1011
1012template <typename O, typename OT, typename SH>
1013template <typename OutputIterator>
1014OutputIterator QbvhTree<O, OT, SH>::doFind(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, OutputIterator result, const TInfo* info) const
1015{
1016 const auto r = makeRay(ray);
1017
1018 Child stack[stackSize_];
1019 TIndex stackSize = 0;
1020 stack[stackSize++] = root;
1021 while (stackSize > 0)
1022 {
1023 const Child index = stack[--stackSize];
1024 LASS_ASSERT(!index.isEmpty());
1025
1026 if (index.isLeaf())
1027 {
1028 const TIndex first = index.first();
1029 const TIndex last = first + index.count();
1030 for (TIndex i = first; i != last; ++i)
1031 {
1032 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1033 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
1034 {
1035 *result++ = objects_[i];
1036 }
1037 }
1038 continue;
1039 }
1040
1041 LASS_ASSERT(index.isInternal());
1042 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1043 LASS_ASSERT(index.node() < nodes_.size());
1044 const Node& node = nodes_[index.node()];
1045
1046 TValue ts[4];
1047 const int hits = node.usedMask & impl::qbvh::intersect(node.bounds, r, tMin, tMax, ts);
1048 for (int k = 3; k >= 0; --k)
1049 {
1050 if (!(hits & (1 << k)))
1051 {
1052 continue;
1053 }
1054 const Child child = node.children[k];
1055 if (stackSize < stackSize_)
1056 {
1057 stack[stackSize++] = child;
1058 }
1059 else
1060 {
1061 result = doFind(child, ray, invDir, tMin, tMax, result, info);
1062 }
1063 }
1064 }
1065 return result;
1066}
1067
1068
1069
1070
1071
1072template <typename O, typename OT, typename SH>
1073typename QbvhTree<O, OT, SH>::TObjectIterator
1074QbvhTree<O, OT, SH>::doIntersect(Child root, const TRay& ray, const TVector& invDir, TReference t, TParam tMin, const TInfo* info) const
1075{
1076 const auto r = makeRay(ray);
1077#if LASS_HAVE_AVX
1078 __m128i signs[dimension];
1079 for (size_t i = 0; i < dimension; ++i)
1080 {
1081 signs[i] = _mm_set1_epi32(r.sign[i] ? -1 : 0);
1082 }
1083#endif
1084
1085 struct Visit
1086 {
1087 TValue tNear;
1088 Child index;
1089 Visit() = default;
1090 Visit(Child index, TValue tNear): tNear(tNear), index(index) {}
1091 };
1092 Visit stack[stackSize_];
1093 size_t stackSize = 0;
1094 stack[stackSize++] = Visit(root, tMin);
1095 TValue tBest = TNumTraits::infinity;
1096 TObjectIterator best = *end_;
1097 while (stackSize > 0)
1098 {
1099 const Visit visit = stack[--stackSize];
1100 if (tBest <= visit.tNear)
1101 {
1102 continue; // we already have a closer hit than what this node can offer
1103 }
1104
1105 const Child index = visit.index;
1106 LASS_ASSERT(!index.isEmpty());
1107
1108 if (index.isLeaf())
1109 {
1110 const TIndex first = index.first();
1111 const TIndex last = first + index.count();
1112 for (TIndex i = first; i != last; ++i)
1113 {
1114 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1115 TValue tCandidate = 0;
1116 if (TObjectTraits::objectIntersect(objects_[i], ray, tCandidate, tMin, info))
1117 {
1118 if (best == *end_ || tCandidate < tBest)
1119 {
1120 tBest = tCandidate;
1121 best = objects_[i];
1122 }
1123 }
1124 }
1125 continue;
1126 }
1127
1128 LASS_ASSERT(index.isInternal());
1129 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1130 LASS_ASSERT(index.node() < nodes_.size());
1131 const Node& node = nodes_[index.node()];
1132
1133 TValue ts[4];
1134 const int hits = node.usedMask & impl::qbvh::intersect(node.bounds, r, tMin, tBest, ts);
1135
1136#if LASS_HAVE_AVX
1137 alignas(16) int order[4];
1138 {
1139 auto order_ = _mm_set_epi32(3, 2, 1, 0);
1140 const auto m0 = _mm_set_epi32(-1, -1, -1, -1);
1141 const auto m1 = _mm_set_epi32(0, 0, -1, -1);
1142 const auto m2 = _mm_set_epi32(-1, -1, 0, 0);
1143 const auto mask0 = _mm_and_si128(m0, signs[node.axis[0]]);
1144 const auto mask1 = _mm_and_si128(m1, signs[node.axis[1]]);
1145 const auto mask2 = _mm_and_si128(m2, signs[node.axis[2]]);
1146 const auto mask21 = _mm_or_si128(mask2, mask1);
1147 const auto order21 = _mm_shuffle_epi32(order_, 0xB1);
1148 order_ = _mm_blendv_epi8(order_, order21, mask21);
1149 const auto order0 = _mm_shuffle_epi32(order_, 0x4E);
1150 order_ = _mm_blendv_epi8(order_, order0, mask0);
1151 static_assert(sizeof(order) == sizeof(order_));
1152 memcpy(order, &order_, sizeof(order_));
1153 }
1154#else
1155 int order[4] = { 0, 1, 2, 3 };
1156 if (r.sign[node.axis[1]])
1157 {
1158 std::swap(order[0], order[1]);
1159 }
1160 if (r.sign[node.axis[2]])
1161 {
1162 std::swap(order[2], order[3]);
1163 }
1164 if (r.sign[node.axis[0]])
1165 {
1166 std::swap(order[0], order[2]);
1167 std::swap(order[1], order[3]);
1168 }
1169#endif
1170
1171 // push in reverse order, so that the first child is on top of the stack
1172 for (int k = 3; k >= 0; --k)
1173 {
1174 const auto o = order[k];
1175 if (!(hits & (1 << o)))
1176 {
1177 continue;
1178 }
1179 const Child child = node.children[o];
1180 if (stackSize < stackSize_)
1181 {
1182 stack[stackSize++] = Visit(child, ts[o]);
1183 }
1184 else
1185 {
1186 TValue tb;
1187 TObjectIterator b = doIntersect(child, ray, invDir, tb, tMin, info);
1188 if (b != *end_ && tb < tBest)
1189 {
1190 best = b;
1191 tBest = tb;
1192 }
1193 }
1194 }
1195 }
1196 if (best != *end_)
1197 {
1198 t = tBest;
1199 }
1200 return best;
1201}
1202
1203
1204
1205template <typename O, typename OT, typename SH>
1206bool QbvhTree<O, OT, SH>::doIntersects(Child root, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax, const TInfo* info) const
1207{
1208 const auto r = makeRay(ray);
1209
1210 Child stack[stackSize_];
1211 TIndex stackSize = 0;
1212 stack[stackSize++] = root;
1213 while (stackSize > 0)
1214 {
1215 const Child index = stack[--stackSize];
1216 LASS_ASSERT(!index.isEmpty());
1217
1218 if (index.isLeaf())
1219 {
1220 const TIndex first = index.first();
1221 const TIndex last = first + index.count();
1222 for (TIndex i = first; i != last; ++i)
1223 {
1224 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1225 if (TObjectTraits::objectIntersects(objects_[i], ray, tMin, tMax, info))
1226 {
1227 return true;
1228 }
1229 }
1230 continue;
1231 }
1232
1233 LASS_ASSERT(index.isInternal());
1234 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1235 LASS_ASSERT(index.node() < nodes_.size());
1236 const Node& node = nodes_[index.node()];
1237
1238 TValue ts[4];
1239 const int hits = node.usedMask & impl::qbvh::intersect(node.bounds, r, tMin, tMax, ts);
1240 for (int k = 3; k >= 0; --k)
1241 {
1242 if (!(hits & (1 << k)))
1243 {
1244 continue;
1245 }
1246 const Child child = node.children[k];
1247 if (stackSize < stackSize_)
1248 {
1249 stack[stackSize++] = child;
1250 }
1251 else if (doIntersects(child, ray, invDir, tMin, tMax, info))
1252 {
1253 return true;
1254 }
1255 }
1256 }
1257 return false;
1258}
1259
1260
1261namespace impl::qbvh
1262{
1263
1264template <typename T>
1265void sort4(size_t order[4], const T values[4])
1266{
1267 if (values[order[0]] > values[order[2]])
1268 {
1269 std::swap(order[0], order[2]);
1270 }
1271 if (values[order[1]] > values[order[3]])
1272 {
1273 std::swap(order[1], order[3]);
1274 }
1275 if (values[order[0]] > values[order[1]])
1276 {
1277 std::swap(order[0], order[1]);
1278 }
1279 if (values[order[2]] > values[order[3]])
1280 {
1281 std::swap(order[2], order[3]);
1282 }
1283 if (values[order[1]] > values[order[2]])
1284 {
1285 std::swap(order[1], order[2]);
1286 }
1287 LASS_ASSERT(values[order[0]] <= values[order[1]]);
1288 LASS_ASSERT(values[order[1]] <= values[order[2]]);
1289 LASS_ASSERT(values[order[2]] <= values[order[3]]);
1290}
1291
1292}
1293
1294
1295
1296template <typename O, typename OT, typename SH>
1297void QbvhTree<O, OT, SH>::doNearestNeighbour(
1298 Child root, const TPoint& target, const TInfo* info, Neighbour& nearest) const
1299{
1300 const auto pnt = makePoint(target);
1301
1302 struct Visit
1303 {
1304 TValue squaredDistance;
1305 Child index;
1306 Visit() = default;
1307 Visit(Child index, TValue squaredDistance): squaredDistance(squaredDistance), index(index) {}
1308 };
1309 Visit stack[stackSize_];
1310 size_t stackSize = 0;
1311 stack[stackSize++] = Visit(root, 0);
1312 while (stackSize > 0)
1313 {
1314 LASS_ASSERT(nearest.squaredDistance() >= 0);
1315 const Visit visit = stack[--stackSize];
1316 if (nearest.squaredDistance() <= visit.squaredDistance)
1317 {
1318 continue; // we already have a closer neighbour than what this node can offer
1319 }
1320
1321 const Child index = visit.index;
1322 LASS_ASSERT(!index.isEmpty());
1323
1324 if (index.isLeaf())
1325 {
1326 const auto first = index.first();
1327 const auto last = first + index.count();
1328 for (auto i = first; i != last; ++i)
1329 {
1330 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1331 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], target, info);
1332 if (squaredDistance < nearest.squaredDistance())
1333 {
1334 nearest = Neighbour(objects_[i], squaredDistance);
1335 }
1336 }
1337 continue;
1338 }
1339
1340 LASS_ASSERT(index.isInternal());
1341 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1342 LASS_ASSERT(index.node() < nodes_.size());
1343 const Node& node = nodes_[index.node()];
1344
1345 TValue squaredDistances[4];
1346 impl::qbvh::squaredDistance(node.bounds, pnt, squaredDistances);
1347
1348 size_t order[4] = { 0, 1, 2, 3 };
1349 impl::qbvh::sort4(order, squaredDistances);
1350
1351 // push in reverse order, so that the first child is on top of the stack
1352 for (int k = 3; k >= 0; --k)
1353 {
1354 const size_t o = order[k];
1355 const Child child = node.children[o];
1356 if (child.isEmpty())
1357 {
1358 continue;
1359 }
1360 if (stackSize < stackSize_)
1361 {
1362 stack[stackSize++] = Visit(child, squaredDistances[o]);
1363 }
1364 else
1365 {
1366 doNearestNeighbour(child, target, info, nearest);
1367 }
1368 }
1369 }
1370}
1371
1372
1373
1374template <typename O, typename OT, typename SH>
1375template <typename RandomIterator>
1376RandomIterator
1377QbvhTree<O, OT, SH>::doRangeSearch(
1378 Child root, const TPoint& center, TReference squaredRadius, size_t maxCount, RandomIterator first, RandomIterator last, const TInfo* info) const
1379{
1380 const auto pnt = makePoint(center);
1381
1382 struct Visit
1383 {
1384 TValue squaredDistance;
1385 Child index;
1386 Visit() = default;
1387 Visit(Child index, TValue squaredDistance): squaredDistance(squaredDistance), index(index) {}
1388 };
1389 Visit stack[stackSize_];
1390 size_t stackSize = 0;
1391 stack[stackSize++] = Visit(root, 0);
1392 while (stackSize > 0)
1393 {
1394 LASS_ASSERT(squaredRadius >= 0);
1395 const Visit visit = stack[--stackSize];
1396 if (squaredRadius <= visit.squaredDistance)
1397 {
1398 continue; // node is entirely outside range
1399 }
1400
1401 const Child index = visit.index;
1402 LASS_ASSERT(!index.isEmpty());
1403
1404 if (index.isLeaf())
1405 {
1406 const auto frst = index.first();
1407 const auto lst = frst + index.count();
1408 for (auto i = frst; i != lst; ++i)
1409 {
1410 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_VISIT_OBJECT;
1411 const TValue squaredDistance = TObjectTraits::objectSquaredDistance(objects_[i], center, info);
1412 if (squaredDistance < squaredRadius)
1413 {
1414 *last++ = Neighbour(objects_[i], squaredDistance);
1415 std::push_heap(first, last);
1416 LASS_ASSERT(last >= first);
1417 if (static_cast<size_t>(last - first) > maxCount)
1418 {
1419 std::pop_heap(first, last);
1420 --last;
1421 squaredRadius = first->squaredDistance();
1422 }
1423 }
1424 }
1425 continue;
1426 }
1427
1428 LASS_ASSERT(index.isInternal());
1429 LASS_SPAT_OBJECT_TREES_DIAGNOSTICS_INIT_NODE(TInfo, info);
1430 LASS_ASSERT(index.node() < nodes_.size());
1431 const Node& node = nodes_[index.node()];
1432
1433 TValue squaredDistances[4];
1434 impl::qbvh::squaredDistance(node.bounds, pnt, squaredDistances);
1435
1436 size_t order[4] = { 0, 1, 2, 3 };
1437 impl::qbvh::sort4(order, squaredDistances);
1438
1439 // push in reverse order, so that the first child is on top of the stack
1440 for (int k = 3; k >= 0; --k)
1441 {
1442 size_t o = order[k];
1443 const Child child = node.children[o];
1444 if (child.isEmpty())
1445 {
1446 continue;
1447 }
1448 if (stackSize < stackSize_)
1449 {
1450 stack[stackSize++] = Visit(child, squaredDistances[o]);
1451 }
1452 else
1453 {
1454 last = doRangeSearch(child, center, squaredRadius, maxCount, first, last, info);
1455 }
1456 }
1457 }
1458
1459 return last;
1460}
1461
1462
1463
1464template <typename O, typename OT, typename SH>
1465bool QbvhTree<O, OT, SH>::volumeIntersect(const TAabb& box, const TRay& ray, const TVector& invDir, TReference t, TParam tMin) const
1466{
1467 if (TObjectTraits::aabbContains(box, TObjectTraits::rayPoint(ray, tMin)))
1468 {
1469 t = tMin;
1470 return true;
1471 }
1472 return TObjectTraits::aabbIntersect(box, ray, invDir, t, tMin);
1473}
1474
1475
1476
1477template <typename O, typename OT, typename SH>
1478bool QbvhTree<O, OT, SH>::volumeIntersects(const TAabb& box, const TRay& ray, const TVector& invDir, TParam tMin, TParam tMax) const
1479{
1480 TValue t = 0;
1481 return volumeIntersect(box, ray, invDir, t, tMin) && t <= tMax;
1482}
1483
1484
1485
1486template <typename O, typename OT, typename SH>
1487typename QbvhTree<O, OT, SH>::TPoint_
1488QbvhTree<O, OT, SH>::makePoint(const TPoint& point)
1489{
1490 TPoint_ result;
1491 for (size_t d = 0; d < dimension; ++d)
1492 {
1493 result.x[d] = OT::coord(point, d);
1494 }
1495 return result;
1496}
1497
1498
1499
1500template <typename O, typename OT, typename SH>
1501typename QbvhTree<O, OT, SH>::TRay_
1502QbvhTree<O, OT, SH>::makeRay(const TRay& ray)
1503{
1504 TRay_ result;
1505 const auto support = OT::raySupport(ray);
1506 const auto direction = OT::rayDirection(ray);
1507 const auto invDir = TObjectTraits::vectorReciprocal(direction);
1508 for (size_t d = 0; d < dimension; ++d)
1509 {
1510 result.support[d] = OT::coord(support, d);
1511 result.invDir[d] = OT::coord(invDir, d);
1512 result.sign[d] = std::signbit(OT::coord(direction, d));
1513 }
1514 return result;
1515}
1516
1517
1518
1519template <typename O, typename OT, typename SH>
1520typename QbvhTree<O, OT, SH>::TAabb_
1521QbvhTree<O, OT, SH>::makeAabb(const TAabb& aabb)
1522{
1523 TAabb_ result;
1524 const auto min = OT::aabbMin(aabb);
1525 const auto max = OT::aabbMax(aabb);
1526 for (size_t d = 0; d < dimension; ++d)
1527 {
1528 result.corners[d][0] = OT::coord(min, d);
1529 result.corners[d][1] = OT::coord(max, d);
1530 }
1531 return result;
1532}
1533
1534
1535
1536}
1537
1538}
an 4-ary AABB tree
Definition qbvh_tree.h:142
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
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.
T sign(const T &x)
if x < 0 return -1, else if x > 0 return 1, else return 0.
bool isInf(const C &iV)
return true if iV equals minus or plus Infinity
Definition num_traits.h:132
spatial subdivisions, quadtrees, octrees, meshes in 2D and 3D, triangulators, ...
Definition aabb8_tree.h:80
Library for Assembled Shared Sources.
Definition config.h:53