TTK
Loading...
Searching...
No Matches
BoundingVolumeHierarchy.h
Go to the documentation of this file.
1
10
11#pragma once
12
13#include "Ray.h"
14#include <Geometry.h>
15
16#include <algorithm>
17#include <limits>
18#include <memory>
19#include <stack>
20#include <vector>
21
22namespace ttk {
23 template <typename IT>
25 protected:
26 struct Node {
27 Node() = default;
28 // leaf constructor
29 Node(const std::vector<int> &triangleIndices,
30 const size_t nTriangles,
31 const float *pMin,
32 const float *pMax) {
33 m_minX = pMin[0];
34 m_minY = pMin[1];
35 m_minZ = pMin[2];
36 m_maxX = pMax[0];
37 m_maxY = pMax[1];
38 m_maxZ = pMax[2];
39 indices = triangleIndices;
40 numTriangles = nTriangles;
41 }
42 // interior constructor
43 Node(const int axis,
44 const std::shared_ptr<Node> &left,
45 const std::shared_ptr<Node> &right) {
46 numTriangles = 0;
47 m_left = left;
48 m_right = right;
49 m_splitAxis = axis;
50 m_minX = std::min(left->m_minX, right->m_minX);
51 m_minY = std::min(left->m_minY, right->m_minY);
52 m_minZ = std::min(left->m_minZ, right->m_minZ);
53 m_maxX = std::max(left->m_maxX, right->m_maxX);
54 m_maxY = std::max(left->m_maxY, right->m_maxY);
55 m_maxZ = std::max(left->m_maxZ, right->m_maxZ);
56 }
57
58 ~Node() = default;
59
60 std::vector<int> indices;
64 std::shared_ptr<Node> m_left;
65 std::shared_ptr<Node> m_right;
67 };
68
69 struct Triangle {
74 Triangle() = default;
75
76 void init(const int &index,
77 const float &centroid_x,
78 const float &centroid_y,
79 const float &centroid_z,
80 const float *pMin,
81 const float *pMax) {
82 m_index = index;
83 m_centroid_x = centroid_x;
84 m_centroid_y = centroid_y;
85 m_centroid_z = centroid_z;
86 m_minX = pMin[0];
87 m_minY = pMin[1];
88 m_minZ = pMin[2];
89
90 m_maxX = pMax[0];
91 m_maxY = pMax[1];
92 m_maxZ = pMax[2];
93 }
94 };
95
96 public:
97 BoundingVolumeHierarchy(const float *coords,
98 const IT *connectivityList,
99 const size_t &nTriangles) {
100 std::vector<Triangle> triangles;
101 buildTriangleList(triangles, coords, connectivityList, nTriangles);
102
103 this->nodes = buildTree(triangles, 0, nTriangles);
104 }
105
107
108 std::shared_ptr<Node>
109 buildTree(std::vector<Triangle> &triangles, size_t start, size_t end) {
110
111 float minX, minY, minZ;
112 float maxX, maxY, maxZ;
113 minX = minY = minZ = std::numeric_limits<float>::max();
114 maxX = maxY = maxZ = std::numeric_limits<float>::min();
115 for(size_t i = start; i < end; i++) {
116 const Triangle &t = triangles[i];
117 minX = std::min(t.m_minX, minX);
118 minY = std::min(t.m_minY, minY);
119 minZ = std::min(t.m_minZ, minZ);
120
121 maxX = std::max(t.m_maxX, maxX);
122 maxY = std::max(t.m_maxY, maxY);
123 maxZ = std::max(t.m_maxZ, maxZ);
124 }
125 const int numberTriangles = end - start;
126 if(numberTriangles == 1) {
127 const Triangle &t = triangles[start];
128 std::vector<int> const indices = {t.m_index};
129 float pMin[3] = {t.m_minX, t.m_minY, t.m_minZ};
130 float pMax[3] = {t.m_maxX, t.m_maxY, t.m_maxZ};
131 return std::make_shared<Node>(indices, 1, pMin, pMax);
132 } else {
133 // find the bounds of the centroids, figure out what dimension to split
134 // on
135 float cminX, cminY, cminZ;
136 float cmaxX, cmaxY, cmaxZ;
137 cminX = cminY = cminZ = std::numeric_limits<float>::max();
138 cmaxX = cmaxY = cmaxZ = std::numeric_limits<float>::min();
139 for(size_t i = start; i < end; i++) {
140 const Triangle &t = triangles[i];
141 cminX = std::min(t.m_centroid_x, cminX);
142 cminY = std::min(t.m_centroid_y, cminY);
143 cminZ = std::min(t.m_centroid_z, cminZ);
144
145 cmaxX = std::max(t.m_centroid_x, cmaxX);
146 cmaxY = std::max(t.m_centroid_y, cmaxY);
147 cmaxZ = std::max(t.m_centroid_z, cmaxZ);
148 }
149 // figure out the biggest extent, use that dimension
150 const float diffX = std::abs(cmaxX - cminX);
151 const float diffY = std::abs(cmaxY - cminY);
152 const float diffZ = std::abs(cmaxZ - cminZ);
153 const float maximumExtent = std::max({diffX, diffY, diffZ});
154 int axis;
155 float minToCheck, maxToCheck;
156 if(maximumExtent == diffX) {
157 axis = 0;
158 minToCheck = cminX;
159 maxToCheck = cmaxX;
160 } else if(maximumExtent == diffY) {
161 axis = 1;
162 minToCheck = cminY;
163 maxToCheck = cmaxY;
164 } else {
165 axis = 2;
166 minToCheck = cminZ;
167 maxToCheck = cmaxZ;
168 }
169 size_t const half = (start + end) / 2;
170 // partition triangles into two sets and build children
171 if(minToCheck == maxToCheck) {
172
173 std::vector<int> triangleIndices;
174 for(size_t i = start; i < end; i++) {
175 triangleIndices.push_back(triangles[i].m_index);
176 }
177 float pMin[3] = {minX, minY, minZ};
178 float pMax[3] = {maxX, maxY, maxZ};
179 return std::make_shared<Node>(
180 triangleIndices, triangleIndices.size(), pMin, pMax);
181 } else {
182 // partition triangles into equally sized subsets
183 std::nth_element(&triangles[start], &triangles[half],
184 &triangles[end - 1] + 1,
185 [axis](const Triangle &t1, const Triangle &t2) {
186 switch(axis) {
187 case 0:
188 default:
189 return t1.m_centroid_x < t2.m_centroid_x;
190 break;
191 case 1:
192 return t1.m_centroid_y < t2.m_centroid_y;
193 break;
194 case 2:
195 return t1.m_centroid_z < t2.m_centroid_z;
196 break;
197 }
198 });
199
200 return std::make_shared<Node>(axis, buildTree(triangles, start, half),
201 buildTree(triangles, half, end));
202 }
203 }
204
205 return nullptr;
206 }
207 int buildTriangleList(std::vector<Triangle> &triangles,
208 const float *coords,
209 const IT *connectivityList,
210 const size_t &nTriangles) {
211 triangles.resize(nTriangles);
212 for(size_t ti = 0; ti < nTriangles; ti++) {
213
214 const IT v1 = connectivityList[ti * 3 + 0] * 3;
215 const IT v2 = connectivityList[ti * 3 + 1] * 3;
216 const IT v3 = connectivityList[ti * 3 + 2] * 3;
217
218 const float &x1 = coords[v1 + 0];
219 const float &x2 = coords[v2 + 0];
220 const float &x3 = coords[v3 + 0];
221
222 const float &y1 = coords[v1 + 1];
223 const float &y2 = coords[v2 + 1];
224 const float &y3 = coords[v3 + 1];
225
226 const float &z1 = coords[v1 + 2];
227 const float &z2 = coords[v2 + 2];
228 const float &z3 = coords[v3 + 2];
229
230 const float pMin[3] = {std::min({x1, x2, x3}), std::min({y1, y2, y3}),
231 std::min({z1, z2, z3})};
232 const float pMax[3] = {std::max({x1, x2, x3}), std::max({y1, y2, y3}),
233 std::max({z1, z2, z3})};
234
235 triangles[ti].init(ti, findCentroid(x1, x2, x3),
236 findCentroid(y1, y2, y3), findCentroid(z1, z2, z3),
237 pMin, pMax);
238 }
239
240 return 1;
241 }
242
244 const IT v0,
245 const IT v1,
246 const IT v2,
247 const float *vertexCoords) const {
248 constexpr float kEpsilon = 1e-8;
249
250 float v0v1[3], v0v2[3], pvec[3], tvec[3], qvec[3];
252 &vertexCoords[v0], &vertexCoords[v1], v0v1);
254 &vertexCoords[v0], &vertexCoords[v2], v0v2);
256 const float det = ttk::Geometry::dotProduct(v0v1, pvec);
257 if(det > -kEpsilon && det < kEpsilon)
258 return false;
259
260 const float invDet = 1.0f / det;
261
262 ttk::Geometry::subtractVectors(&vertexCoords[v0], ray.m_origin, tvec);
263 const float u = ttk::Geometry::dotProduct(tvec, pvec) * invDet;
264 if(u < 0.0 || u > 1.0)
265 return false;
266
267 ttk::Geometry::crossProduct(tvec, v0v1, qvec);
268 const float v = ttk::Geometry::dotProduct(ray.m_direction, qvec) * invDet;
269 if(v < 0.0 || u + v > 1.0)
270 return false;
271
272 const float t = ttk::Geometry::dotProduct(v0v2, qvec) * invDet;
273 ray.distance = t;
274 ray.u = u;
275 ray.v = v;
276
277 return true;
278 }
279
280 bool intersect(Ray &r,
281 const IT *connectivityList,
282 const float *vertexCoords,
283 int *triangleIndex,
284 float *distance) const {
285 bool wasHit = false;
286 float nearestTriangle = std::numeric_limits<float>::max();
287 std::stack<Node *> stack;
288 Node *node = &nodes.get()[0];
289 if(!wasNodeHit(r, node)) {
290 return false;
291 }
292 stack.push(node);
293 while(stack.size() != 0) {
294 node = stack.top();
295 stack.pop();
296 if(wasNodeHit(r, node)) {
297
298 if(node->numTriangles > 0) {
299
300 for(int i = 0; i < node->numTriangles; i++) {
301 bool hasHit = false;
302 int const triIdx = node->indices[i];
303
304 IT v0 = connectivityList[triIdx * 3 + 0];
305 IT v1 = connectivityList[triIdx * 3 + 1];
306 IT v2 = connectivityList[triIdx * 3 + 2];
307 v0 *= 3;
308 v1 *= 3;
309 v2 *= 3;
310 hasHit = MollerTrumbore(r, v0, v1, v2, vertexCoords);
311 if(hasHit && r.distance < nearestTriangle) {
312 *triangleIndex = triIdx;
313 nearestTriangle = r.distance;
314 wasHit = true;
315 *distance = r.distance;
316 }
317 }
318 } else {
319
320 if(node->m_right != nullptr) {
321 stack.push(node->m_right.get());
322 }
323 if(node->m_left != nullptr) {
324 stack.push(node->m_left.get());
325 }
326 }
327 }
328 }
329 return wasHit;
330 }
331
332 bool wasNodeHit(const Ray &r, Node *n) const {
333 float tmin = (n->m_minX - r.m_origin[0]) / r.m_direction[0];
334 float tmax = (n->m_maxX - r.m_origin[0]) / r.m_direction[0];
335
336 if(tmin > tmax)
337 std::swap(tmin, tmax);
338
339 float tymin = (n->m_minY - r.m_origin[1]) / r.m_direction[1];
340 float tymax = (n->m_maxY - r.m_origin[1]) / r.m_direction[1];
341
342 if(tymin > tymax)
343 std::swap(tymin, tymax);
344
345 if((tmin > tymax) || (tymin > tmax))
346 return false;
347
348 if(tymin > tmin)
349 tmin = tymin;
350
351 if(tymax < tmax)
352 tmax = tymax;
353
354 float tzmin = (n->m_minZ - r.m_origin[2]) / r.m_direction[2];
355 float tzmax = (n->m_maxZ - r.m_origin[2]) / r.m_direction[2];
356
357 if(tzmin > tzmax)
358 std::swap(tzmin, tzmax);
359
360 if((tmin > tzmax) || (tzmin > tmax))
361 return false;
362
363 if(tzmin > tmin)
364 tmin = tzmin;
365
366 if(tzmax < tmax)
367 tmax = tzmax;
368
369 return true;
370 }
371
372 private:
373 std::shared_ptr<Node> nodes;
374 float findCentroid(const float &v1, const float &v2, const float &v3) {
375 return (v1 + v2 + v3) / 3;
376 }
377 };
378} // namespace ttk
Acceleration structure for native ray tracer. Based on implementation described in Physically Based R...
int buildTriangleList(std::vector< Triangle > &triangles, const float *coords, const IT *connectivityList, const size_t &nTriangles)
bool MollerTrumbore(Ray &ray, const IT v0, const IT v1, const IT v2, const float *vertexCoords) const
bool intersect(Ray &r, const IT *connectivityList, const float *vertexCoords, int *triangleIndex, float *distance) const
BoundingVolumeHierarchy(const float *coords, const IT *connectivityList, const size_t &nTriangles)
bool wasNodeHit(const Ray &r, Node *n) const
std::shared_ptr< Node > buildTree(std::vector< Triangle > &triangles, size_t start, size_t end)
Data structure for a ray.
Definition Ray.h:11
float distance
Definition Ray.h:22
float v
Definition Ray.h:24
float * m_origin
Definition Ray.h:21
float u
Definition Ray.h:23
float * m_direction
Definition Ray.h:20
T dotProduct(const T *vA0, const T *vA1, const T *vB0, const T *vB1)
Definition Geometry.cpp:388
int subtractVectors(const T *a, const T *b, T *out, const int &dimension=3)
Definition Geometry.cpp:538
int crossProduct(const T *vA0, const T *vA1, const T *vB0, const T *vB1, std::array< T, 3 > &crossProduct)
Definition Geometry.cpp:332
The Topology ToolKit.
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:472
Node(const int axis, const std::shared_ptr< Node > &left, const std::shared_ptr< Node > &right)
Node(const std::vector< int > &triangleIndices, const size_t nTriangles, const float *pMin, const float *pMax)
void init(const int &index, const float &centroid_x, const float &centroid_y, const float &centroid_z, const float *pMin, const float *pMax)