SPH
Bvh.inl.h
Go to the documentation of this file.
1 #include "objects/finders/Bvh.h"
2 
4 
5 struct BvhTraversal {
8 };
9 
10 struct BvhBuildEntry {
14 };
15 
16 bool intersectBox(const Box& box, const Ray& ray, Float& t_min, Float& t_max) {
17  StaticArray<Vector, 2> b = { box.lower(), box.upper() };
18  Float tmin = (b[ray.signs[X]][X] - ray.orig[X]) * ray.invDir[X];
19  Float tmax = (b[1 - ray.signs[X]][X] - ray.orig[X]) * ray.invDir[X];
20  SPH_ASSERT(!std::isnan(tmin) && !std::isnan(tmax)); // they may be inf though
21  const Float tymin = (b[ray.signs[Y]][Y] - ray.orig[Y]) * ray.invDir[Y];
22  const Float tymax = (b[1 - ray.signs[Y]][Y] - ray.orig[Y]) * ray.invDir[Y];
23  SPH_ASSERT(!std::isnan(tymin) && !std::isnan(tymax));
24 
25  if ((tmin > tymax) || (tymin > tmax)) {
26  return false;
27  }
28  tmin = max(tmin, tymin);
29  tmax = min(tmax, tymax);
30 
31  const Float tzmin = (b[ray.signs[Z]][Z] - ray.orig[Z]) * ray.invDir[Z];
32  const Float tzmax = (b[1 - ray.signs[Z]][Z] - ray.orig[Z]) * ray.invDir[Z];
33  SPH_ASSERT(!std::isnan(tzmin) && !std::isnan(tzmax));
34 
35  if ((tmin > tzmax) || (tzmin > tmax)) {
36  return false;
37  }
38  tmin = max(tmin, tzmin);
39  tmax = min(tmax, tzmax);
40 
41  t_min = tmin;
42  t_max = tmax;
43 
44  // reject intersections at t<0
45  return t_max > 0.f;
46 }
47 
48 template <typename TBvhObject>
49 template <typename TAddIntersection>
50 void Bvh<TBvhObject>::getIntersections(const Ray& ray, const TAddIntersection& addIntersection) const {
52  Size closer;
53  Size other;
54 
56  int stackIdx = 0;
57 
58  stack[stackIdx].idx = 0;
59  stack[stackIdx].t_min = 0._f; // -INFTY;
60 
61  while (stackIdx >= 0) {
62  const Size idx = stack[stackIdx].idx;
63  // const Float t_min = stack[stackIdx].t_min;
64  stackIdx--;
65  const BvhNode& node = nodes[idx];
66 
68  /* if (t_min > intersection.t) {
69  continue;
70  }*/
71  if (node.rightOffset == 0) {
72  // leaf
73  for (Size primIdx = 0; primIdx < node.primCnt; ++primIdx) {
74  IntersectionInfo current;
75 
76  const TBvhObject& obj = objects[node.start + primIdx];
77  const bool hit = obj.getIntersection(ray, current);
78 
79  if (hit) {
80  SPH_ASSERT(current.t >= 0.f); // objects should not give us intersections at t<0
81  const bool doContinue = addIntersection(current);
82  if (!doContinue) {
83  return;
84  }
85  }
86  }
87  } else {
88  // inner node
89  const bool hitc0 = intersectBox(nodes[idx + 1].box, ray, boxHits[0], boxHits[1]);
90  const bool hitc1 = intersectBox(nodes[idx + node.rightOffset].box, ray, boxHits[2], boxHits[3]);
91 
92  if (hitc0 && hitc1) {
93  closer = idx + 1;
94  other = idx + node.rightOffset;
95 
96  if (boxHits[2] < boxHits[0]) {
97  std::swap(boxHits[0], boxHits[2]);
98  std::swap(boxHits[1], boxHits[3]);
99  std::swap(closer, other);
100  }
101 
102  stack[++stackIdx] = BvhTraversal{ other, boxHits[2] };
103  stack[++stackIdx] = BvhTraversal{ closer, boxHits[0] };
104  } else if (hitc0) {
105  stack[++stackIdx] = BvhTraversal{ idx + 1, boxHits[0] };
106  } else if (hitc1) {
107  stack[++stackIdx] = BvhTraversal{ idx + node.rightOffset, boxHits[2] };
108  }
109  }
110  }
111 }
112 
113 template <typename TBvhObject>
114 bool Bvh<TBvhObject>::getFirstIntersection(const Ray& ray, IntersectionInfo& intersection) const {
115  intersection.t = INFTY;
116  intersection.object = nullptr;
117 
118  this->getIntersections(ray, [&intersection](IntersectionInfo& current) {
119  if (current.t < intersection.t) {
120  intersection = current;
121  }
122  return true;
123  });
124  return intersection.object != nullptr;
125 }
126 
127 template <typename TBvhObject>
128 template <typename TOutIter>
129 Size Bvh<TBvhObject>::getAllIntersections(const Ray& ray, TOutIter iter) const {
130  Size count = 0;
131  this->getIntersections(ray, [&iter, &count](IntersectionInfo& current) { //
132  *iter = current;
133  ++iter;
134  ++count;
135  return true;
136  });
137  return count;
138 }
139 
140 template <typename TBvhObject>
141 bool Bvh<TBvhObject>::isOccluded(const Ray& ray) const {
142  bool occluded = false;
143  this->getIntersections(ray, [&occluded](IntersectionInfo&) {
144  occluded = true;
145  return false; // do not continue with traversal
146  });
147  return occluded;
148 }
149 
150 template <typename TBvhObject>
152  objects = std::move(objs);
153  SPH_ASSERT(!objects.empty());
154  nodeCnt = 0;
155  leafCnt = 0;
156 
158  Size stackIdx = 0;
159  constexpr Size UNTOUCHED = 0xffffffff;
160  constexpr Size NO_PARENT = 0xfffffffc;
161  constexpr Size TOUCHED_TWICE = 0xfffffffd;
162 
163  // Push the root
164  stack[stackIdx].start = 0;
165  stack[stackIdx].end = objects.size();
166  stack[stackIdx].parent = NO_PARENT;
167  stackIdx++;
168 
169  BvhNode node;
170  Array<BvhNode> buildNodes;
171  buildNodes.reserve(2 * objects.size());
172 
173  while (stackIdx > 0) {
174  BvhBuildEntry& nodeEntry = stack[--stackIdx];
175  const Size start = nodeEntry.start;
176  const Size end = nodeEntry.end;
177  const Size primCnt = end - start;
178 
179  nodeCnt++;
180  node.start = start;
181  node.primCnt = primCnt;
182  node.rightOffset = UNTOUCHED;
183 
184  Box bbox = objects[start].getBBox();
185  const Vector center = objects[start].getCenter();
186  Box boxCenter(center, center);
187  for (Size i = start + 1; i < end; ++i) {
188  bbox.extend(objects[i].getBBox());
189  boxCenter.extend(objects[i].getCenter());
190  }
191  node.box = bbox;
192 
193  if (primCnt <= leafSize) {
194  node.rightOffset = 0;
195  leafCnt++;
196  }
197  buildNodes.push(node);
198 
199  if (nodeEntry.parent != NO_PARENT) {
200  buildNodes[nodeEntry.parent].rightOffset--;
201 
202  if (buildNodes[nodeEntry.parent].rightOffset == TOUCHED_TWICE) {
203  buildNodes[nodeEntry.parent].rightOffset = nodeCnt - 1 - nodeEntry.parent;
204  }
205  }
206 
207  if (node.rightOffset == 0) {
208  continue;
209  }
210 
211  const Size splitDim = argMax(boxCenter.size());
212  const Float split = 0.5_f * (boxCenter.lower()[splitDim] + boxCenter.upper()[splitDim]);
213 
214  Size mid = start;
215  for (Size i = start; i < end; ++i) {
216  if (objects[i].getCenter()[splitDim] < split) {
217  std::swap(objects[i], objects[mid]);
218  ++mid;
219  }
220  }
221 
222  if (mid == start || mid == end) {
223  mid = start + (end - start) / 2;
224  }
225 
226  stack[stackIdx++] = { nodeCnt - 1, mid, end };
227  stack[stackIdx++] = { nodeCnt - 1, start, mid };
228  }
229 
230  SPH_ASSERT(buildNodes.size() == nodeCnt);
231  nodes = std::move(buildNodes);
232 }
233 
234 template <typename TBvhObject>
236  return nodes[0].box;
237 }
238 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
bool intersectBox(const Box &box, const Ray &ray, Float &t_min, Float &t_max)
Finds intersections of ray with a box.
Definition: Bvh.inl.h:16
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
NAMESPACE_SPH_BEGIN constexpr INLINE T min(const T &f1, const T &f2)
Minimum & Maximum value.
Definition: MathBasic.h:15
constexpr Float INFTY
Definition: MathUtils.h:38
constexpr Float LARGE
Definition: MathUtils.h:34
#define NAMESPACE_SPH_END
Definition: Object.h:12
Array< std::string > split(const std::string &s, const char delimiter)
Splits a string into an array of string using given delimiter.
INLINE Size argMax(const Vector &v)
Returns the index of the maximum element.
Definition: Vector.h:697
@ Y
Definition: Vector.h:23
@ X
Definition: Vector.h:22
@ Z
Definition: Vector.h:24
void reserve(const TCounter newMaxSize)
Allocates enough memory to store the given number of elements.
Definition: Array.h:279
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
INLINE TCounter size() const noexcept
Definition: Array.h:193
Helper object defining three-dimensional interval (box).
Definition: Box.h:17
INLINE void extend(const Vector &v)
Enlarges the box to contain the vector.
Definition: Box.h:49
INLINE const Vector & lower() const
Returns lower bounds of the box.
Definition: Box.h:82
INLINE const Vector & upper() const
Returns upper bounds of the box.
Definition: Box.h:94
INLINE Vector size() const
Returns box dimensions.
Definition: Box.h:106
Simple bounding volume hierarchy.
Definition: Bvh.h:198
void build(Array< TBvhObject > &&objects)
Contructs the BVH from given set of objects.
Definition: Bvh.inl.h:151
Box getBoundingBox() const
Returns the bounding box of all objects in BVH.
Definition: Bvh.inl.h:235
bool isOccluded(const Ray &ray) const
Returns true if the ray is occluded by some geometry.
Definition: Bvh.inl.h:141
Size getAllIntersections(const Ray &ray, TOutIter iter) const
Returns all intersections of the ray.
Definition: Bvh.inl.h:129
bool getFirstIntersection(const Ray &ray, IntersectionInfo &intersection) const
Finds the closest intersection of the ray.
Definition: Bvh.inl.h:114
Definition: Bvh.h:10
Array with fixed number of allocated elements.
Definition: StaticArray.h:19
INLINE Iterator< StorageType > end()
Definition: StaticArray.h:210
void swap(Sph::Array< T, TCounter > &ar1, Sph::Array< T, TCounter > &ar2)
Definition: Array.h:580
Size end
Definition: Bvh.inl.h:13
Size parent
Definition: Bvh.inl.h:11
Size start
Definition: Bvh.inl.h:12
Size idx
Definition: Bvh.inl.h:6
Float t_min
Definition: Bvh.inl.h:7
Holds intormation about intersection.
Definition: Bvh.h:53
Float t
Distance of the hit in units of ray.direction().
Definition: Bvh.h:55
const BvhPrimitive * object
Object hit by the ray, or nullptr if nothing has been hit.
Definition: Bvh.h:58