SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLCompactGrid.cpp
Go to the documentation of this file.
1 /**
2  * \file SLCompactGrid.cpp
3  * \authors Manuel Frischknecht, Marcus Hudritsch
4  * \date July 2015
5  * \authors Manuel Frischknecht, Marcus Hudritsch
6  * \copyright http://opensource.org/licenses/GPL-3.0
7 */
8 
9 #include <SLCompactGrid.h>
10 #include <SLNode.h>
11 #include <SLRay.h>
12 #include <Moeller/TriangleBoxIntersect.h>
13 #include <Profiler.h>
14 
15 //-----------------------------------------------------------------------------
17 {
18  _voxelCnt = 0;
19  _voxelCntEmpty = 0;
20  _voxelMaxTria = 0;
21 }
22 //-----------------------------------------------------------------------------
23 //! Returns the indices of the voxel around a given point
25 {
26  SLVec3i pos;
27  SLVec3f delta = p - _minV;
28  pos.x = (SLint)(delta.x / _voxelSize.x);
29  pos.y = (SLint)(delta.y / _voxelSize.y);
30  pos.z = (SLint)(delta.z / _voxelSize.z);
31 
32  // Check bounds of voxel indices
33  if (pos.x >= (SLint)_size.x) pos.x = (SLint)_size.x - 1;
34  if (pos.x < 0) pos.x = 0;
35  if (pos.y >= (SLint)_size.y) pos.y = (SLint)_size.y - 1;
36  if (pos.y < 0) pos.y = 0;
37  if (pos.z >= (SLint)_size.z) pos.z = (SLint)_size.z - 1;
38  if (pos.z < 0) pos.z = 0;
39 
40  return pos;
41 }
42 //-----------------------------------------------------------------------------
43 //! Returns the voxel center point for a given voxel by index
45 {
46  return _minV + SLVec3f((pos.x + .5f) * _voxelSize.x,
47  (pos.y + .5f) * _voxelSize.y,
48  (pos.z + .5f) * _voxelSize.z);
49 }
50 //-----------------------------------------------------------------------------
51 //! Returns the min. and max. voxel of a triangle
53  SLVec3i& minCell,
54  SLVec3i& maxCell)
55 {
56  minCell = maxCell = containingVoxel(triangle[0]);
57  for (SLuint i = 1; i < 3; ++i)
58  {
59  auto& vertex = triangle[i];
60  minCell.setMin(containingVoxel(vertex));
61  maxCell.setMax(containingVoxel(vertex));
62  }
63 }
64 //-----------------------------------------------------------------------------
65 //! Deletes the entire uniform grid data
67 {
68  _voxelCnt = 0;
69  _voxelCntEmpty = 0;
70  _voxelMaxTria = 0;
71  _voxelAvgTria = 0;
72 
73  _voxelOffsets.clear();
74  _triangleIndexes16.clear();
75  _triangleIndexes32.clear();
76 
78 }
79 //-----------------------------------------------------------------------------
80 //! Loops over triangles gets their voxels and calls the callback function
82 {
83  assert(callback && "No callback function passed");
84 
85  for (SLuint i = 0; i < _numTriangles; ++i)
86  {
87  auto index = [&](SLuint j)
88  { return _m->I16.size()
89  ? _m->I16[i * 3 + j]
90  : _m->I32[i * 3 + j]; };
91  Triangle triangle = {_m->finalP(index(0)),
92  _m->finalP(index(1)),
93  _m->finalP(index(2))};
94  SLVec3i min, max, pos;
95  getMinMaxVoxel(triangle, min, max);
96 
97  for (pos.z = min.z; pos.z <= max.z; ++pos.z)
98  {
99  for (pos.y = min.y; pos.y <= max.y; ++pos.y)
100  {
101  for (pos.x = min.x; pos.x <= max.x; ++pos.x)
102  {
103  SLuint voxIndex = indexAtPos(pos);
104  SLVec3f voxCenter = voxelCenter(pos);
105  if (triBoxOverlap(*((float(*)[3]) & voxCenter),
106  *((float(*)[3]) & _voxelSizeHalf),
107  *((float(*)[3][3]) & triangle)))
108  {
109  callback(i, voxIndex);
110  }
111  }
112  }
113  }
114  }
115 }
116 //-----------------------------------------------------------------------------
117 /*!
118 SLCompactGrid::build implements the data structure proposed by Lagae & Dutre in
119 their paper "Compact, Fast and Robust Grids for Ray Tracing".
120 */
122 {
124 
125  assert(_m->I16.size() || _m->I32.size());
126 
127  deleteAll();
128 
129  _minV = minV;
130  _maxV = maxV;
131  _numTriangles = _m->numI() / 3;
132 
133  // Calculate grid size
134  const SLfloat DENSITY = 8;
135  SLVec3f size = _maxV - _minV;
136  SLfloat volume = size.x * size.y * size.z;
137  if (volume < FLT_EPSILON)
138  {
139  SL_WARN_MSG("\n\n **** SLCompactGrid::build: Zero Volume. ****");
140  return;
141  }
142 
143  float f = cbrtf(DENSITY * _numTriangles / volume);
144  _voxelSize.x = size.x / ceil(size.x * f);
145  _voxelSize.y = size.y / ceil(size.y * f);
146  _voxelSize.z = size.z / ceil(size.z * f);
147  _voxelSizeHalf = _voxelSize * 0.5f;
148  _size.x = (SLuint)ceil(size.x / _voxelSize.x);
149  _size.y = (SLuint)ceil(size.y / _voxelSize.y);
150  _size.z = (SLuint)ceil(size.z / _voxelSize.z);
151  _voxelCnt = _size.x * _size.y * _size.z;
152  _voxelOffsets.assign(_voxelCnt + 1, 0);
153 
154  ifTriangleInVoxelDo([&](const SLuint& i, const SLuint& voxIndex)
155  { ++_voxelOffsets[voxIndex]; });
156 
157  // The last counter doesn't count and is always empty.
159  _voxelCntEmpty = (_voxelOffsets[0] == 0) - 1;
160  for (SLuint i = 1; i < _voxelOffsets.size(); ++i)
161  {
163  _voxelCntEmpty += _voxelOffsets[i] == 0;
164  _voxelOffsets[i] += _voxelOffsets[i - 1];
165  }
166 
167  if (_m->I16.size())
168  {
169  _triangleIndexes16.resize(_voxelOffsets.back());
171  [&](const SLushort& i, const SLuint& voxIndex)
172  {
173  assert(_voxelOffsets[voxIndex] != 0);
174  SLuint location = --_voxelOffsets[voxIndex];
175  _triangleIndexes16[location] = i;
176  });
177  _triangleIndexes16.shrink_to_fit();
178  }
179  else
180  {
181  _triangleIndexes32.resize(_voxelOffsets.back());
183  [&](const SLuint& i, const SLuint& voxIndex)
184  {
185  assert(_voxelOffsets[voxIndex] != 0);
186  SLuint location = --_voxelOffsets[voxIndex];
187  _triangleIndexes32[location] = i;
188  });
189  _triangleIndexes32.shrink_to_fit();
190  }
191 
192  _voxelOffsets.shrink_to_fit();
193 }
194 //-----------------------------------------------------------------------------
195 //! Updates the statistics in the parent node
197 {
198  stats.numVoxels += _voxelCnt;
199  stats.numVoxEmpty += _voxelCntEmpty;
200 
201  stats.numBytesAccel += sizeof(SLCompactGrid);
203  stats.numBytesAccel += _m->I16.size()
206 
207  stats.numVoxMaxTria = std::max(_voxelMaxTria, stats.numVoxMaxTria);
208 }
209 //-----------------------------------------------------------------------------
210 //! SLCompactGrid::draw draws the non-empty voxels of the uniform grid
212 {
213  if (_voxelCnt > 0)
214  {
215  if (!_vao.vaoID())
216  {
217  SLuint x, y, z;
218  SLVec3f v;
219  SLVVec3f P;
220 
221  // Loop through voxels
222  v.z = _minV.z;
223  for (z = 0; z < _size.z; ++z, v.z += _voxelSize.z)
224  {
225  v.y = _minV.y;
226  for (y = 0; y < _size.y; ++y, v.y += _voxelSize.y)
227  {
228  v.x = _minV.x;
229  for (x = 0; x < _size.x; ++x, v.x += _voxelSize.x)
230  {
231  SLuint voxelID = indexAtPos(SLVec3i((SLint)x, (SLint)y, (SLint)z));
232 
233  if (_voxelOffsets[voxelID] < _voxelOffsets[voxelID + 1])
234  {
235  P.push_back(SLVec3f(v.x, v.y, v.z));
236  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z));
237  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z));
238  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z + _voxelSize.z));
239  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z + _voxelSize.z));
240  P.push_back(SLVec3f(v.x, v.y, v.z + _voxelSize.z));
241  P.push_back(SLVec3f(v.x, v.y, v.z + _voxelSize.z));
242  P.push_back(SLVec3f(v.x, v.y, v.z));
243 
244  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z));
245  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z));
246  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z));
247  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
248  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
249  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
250  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
251  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z));
252 
253  P.push_back(SLVec3f(v.x, v.y, v.z));
254  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z));
255  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z));
256  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z));
257  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y, v.z + _voxelSize.z));
258  P.push_back(SLVec3f(v.x + _voxelSize.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
259  P.push_back(SLVec3f(v.x, v.y, v.z + _voxelSize.z));
260  P.push_back(SLVec3f(v.x, v.y + _voxelSize.y, v.z + _voxelSize.z));
261  }
262  }
263  }
264  }
265 
267  }
268 
270  }
271 }
272 //-----------------------------------------------------------------------------
273 /*!
274 Ray Mesh intersection method using the regular grid space subdivision structure
275 and a voxel traversal algorithm described in "A Fast Voxel Traversal Algorithm
276 for Ray Tracing" by John Amanatides and Andrew Woo.
277 */
279 {
280  // Check first if the AABB is hit at all
281  if (node->aabb()->isHitInOS(ray))
282  {
283  SLbool wasHit = false;
284 
285  if (_voxelCnt > 0)
286  { // Calculate the intersection point with the AABB
287  SLVec3f O = ray->originOS;
288  SLVec3f D = ray->dirOS;
289  SLVec3f invD = ray->invDirOS;
290  SLVec3f startPoint = O;
291 
292  ////Determine start voxel of the grid
293  if (ray->tmin > 0) startPoint += ray->tmin * D;
294  SLVec3i startVox = containingVoxel(startPoint);
295 
296  // Calculate the voxel ID into our 1D-voxel array
297  SLuint voxID = indexAtPos(startVox);
298 
299  // Calculate steps: -1 or 1 on each axis
300  // clang-format off
301  SLint stepX = (D.x > 0) ? 1 : (D.x < 0) ? -1 : 0;
302  SLint stepY = (D.y > 0) ? 1 : (D.y < 0) ? -1 : 0;
303  SLint stepZ = (D.z > 0) ? 1 : (D.z < 0) ? -1 : 0;
304 
305  // Calculate the min. & max point of the start voxel
306  SLVec3f minVox(_minV.x + startVox.x * _voxelSize.x,
307  _minV.y + startVox.y * _voxelSize.y,
308  _minV.z + startVox.z * _voxelSize.z);
309  SLVec3f maxVox(minVox + _voxelSize);
310 
311  // Calculate max. dist along the ray for each component in tMaxX,Y,Z
312  SLfloat tMaxX = FLT_MAX, tMaxY = FLT_MAX, tMaxZ = FLT_MAX;
313  if (stepX == 1) tMaxX = (maxVox.x - O.x) * invD.x; else
314  if (stepX == -1) tMaxX = (minVox.x - O.x) * invD.x;
315  if (stepY == 1) tMaxY = (maxVox.y - O.y) * invD.y; else
316  if (stepY == -1) tMaxY = (minVox.y - O.y) * invD.y;
317  if (stepZ == 1) tMaxZ = (maxVox.z - O.z) * invD.z; else
318  if (stepZ == -1) tMaxZ = (minVox.z - O.z) * invD.z;
319  // clang-format on
320 
321  // tMax is max. distance along the ray to stay in the current voxel
322  SLfloat tMax = std::min(tMaxX, std::min(tMaxY, tMaxZ));
323 
324  // Precalculate the voxel id increment
325  SLint incIDX = stepX;
326  SLint incIDY = stepY * (SLint)_size.x;
327  SLint incIDZ = stepZ * (SLint)_size.x * (SLint)_size.y;
328 
329  // Calculate tDeltaX,Y & Z (=dist. along the ray in a voxel)
330  SLfloat tDeltaX = (_voxelSize.x * invD.x) * stepX;
331  SLfloat tDeltaY = (_voxelSize.y * invD.y) * stepY;
332  SLfloat tDeltaZ = (_voxelSize.z * invD.z) * stepZ;
333 
334  // Now traverse the voxels
335  while (!wasHit)
336  {
337  if (_m->I16.size())
338  {
339  for (SLuint i = _voxelOffsets[voxID]; i < _voxelOffsets[voxID + 1]; ++i)
340  {
341  if (_m->hitTriangleOS(ray, node, _triangleIndexes16[i] * 3))
342  {
343  if (ray->length <= tMax && !wasHit)
344  wasHit = true;
345  }
346  }
347  }
348  else
349  {
350  for (SLuint i = _voxelOffsets[voxID]; i < _voxelOffsets[voxID + 1]; ++i)
351  {
352  if (_m->hitTriangleOS(ray, node, _triangleIndexes32[i] * 3))
353  {
354  if (ray->length <= tMax && !wasHit)
355  wasHit = true;
356  }
357  }
358  }
359 
360  // step Voxel
361  if (tMaxX < tMaxY)
362  {
363  if (tMaxX < tMaxZ)
364  {
365  startVox.x += stepX;
366  if (startVox.x >= (SLint)_size.x || startVox.x < 0) return wasHit;
367  tMaxX += tDeltaX;
368  voxID += (SLuint)incIDX;
369  tMax = tMaxX;
370  }
371  else
372  {
373  startVox.z += stepZ;
374  if (startVox.z >= (SLint)_size.z || startVox.z < 0) return wasHit;
375  tMaxZ += tDeltaZ;
376  voxID += (SLuint)incIDZ;
377  tMax = tMaxZ;
378  }
379  }
380  else
381  {
382  if (tMaxY < tMaxZ)
383  {
384  startVox.y += stepY;
385  if (startVox.y >= (SLint)_size.y || startVox.y < 0) return wasHit;
386  tMaxY += tDeltaY;
387  voxID += (SLuint)incIDY;
388  tMax = tMaxY;
389  }
390  else
391  {
392  startVox.z += stepZ;
393  if (startVox.z >= (SLint)_size.z || startVox.z < 0) return wasHit;
394  tMaxZ += tDeltaZ;
395  voxID += (SLuint)incIDZ;
396  tMax = tMaxZ;
397  }
398  }
399  }
400  return wasHit;
401  }
402  else
403  { // not enough triangles for regular grid > check them all
404  for (SLuint t = 0; t < _m->numI(); t += 3)
405  {
406  if (_m->hitTriangleOS(ray, node, t) && !wasHit) wasHit = true;
407  }
408  return wasHit;
409  }
410  }
411  else
412  return false; // did not hit aabb
413 }
414 //-----------------------------------------------------------------------------
#define PROFILE_FUNCTION()
Definition: Instrumentor.h:41
float SLfloat
Definition: SL.h:173
unsigned int SLuint
Definition: SL.h:171
#define SL_WARN_MSG(message)
Definition: SL.h:241
bool SLbool
Definition: SL.h:175
unsigned short SLushort
Definition: SL.h:169
SLuint SL_sizeOfVector(const T &vector)
Definition: SL.h:217
int SLint
Definition: SL.h:170
std::function< void(const SLuint, const SLuint)> triVoxCallback
Definition: SLCompactGrid.h:22
@ PT_lines
Definition: SLGLEnums.h:32
vector< SLVec3f > SLVVec3f
Definition: SLVec3.h:325
SLVec3< SLfloat > SLVec3f
Definition: SLVec3.h:318
SLVec3< SLint > SLVec3i
Definition: SLVec3.h:320
SLbool isHitInOS(SLRay *ray)
SLAABBox::isHitInWS: Ray - AABB Intersection Test in object space.
Definition: SLAABBox.cpp:331
SLAccelStruct is an abstract base class for acceleration structures.
Definition: SLAccelStruct.h:22
SLuint _voxelCntEmpty
NO. of empty voxels.
Definition: SLAccelStruct.h:39
SLMesh * _m
Pointer to the mesh.
Definition: SLAccelStruct.h:34
SLVec3f _maxV
max. point of AABB
Definition: SLAccelStruct.h:36
SLfloat _voxelAvgTria
avg. no. of triangles per voxel
Definition: SLAccelStruct.h:41
SLuint _voxelCnt
NO. of voxels in accelerator.
Definition: SLAccelStruct.h:38
SLuint _voxelMaxTria
max. no. of triangles pre voxel
Definition: SLAccelStruct.h:40
SLVec3f _minV
min. point of AABB
Definition: SLAccelStruct.h:35
SLVec3f _voxelSizeHalf
half size of a voxel
Definition: SLCompactGrid.h:65
SLVec3f voxelCenter(const SLVec3i &pos) const
Returns the voxel center point for a given voxel by index.
SLCompactGrid(SLMesh *m)
SLVec3f _voxelSize
size of a voxel
Definition: SLCompactGrid.h:64
SLuint _numTriangles
NO. of triangles in the mesh.
Definition: SLCompactGrid.h:63
void build(SLVec3f minV, SLVec3f maxV)
SLuint indexAtPos(const SLVec3i &p) const
Definition: SLCompactGrid.h:49
void updateStats(SLNodeStats &stats)
Updates the statistics in the parent node.
SLVuint _voxelOffsets
Offset array (C in the paper)
Definition: SLCompactGrid.h:66
void disposeBuffers()
Definition: SLCompactGrid.h:43
void deleteAll()
Deletes the entire uniform grid data.
SLVec3ui _size
num. of voxel in grid dir.
Definition: SLCompactGrid.h:62
SLVushort _triangleIndexes16
16 bit triangle index array (L in the paper)
Definition: SLCompactGrid.h:67
SLVuint _triangleIndexes32
32 bit triangle index array (L in the paper)
Definition: SLCompactGrid.h:68
SLbool intersect(SLRay *ray, SLNode *node)
void draw(SLSceneView *sv)
SLCompactGrid::draw draws the non-empty voxels of the uniform grid.
SLGLVertexArrayExt _vao
Vertex array object for rendering.
Definition: SLCompactGrid.h:69
void getMinMaxVoxel(const Triangle &triangle, SLVec3i &minCell, SLVec3i &maxCell)
Returns the min. and max. voxel of a triangle.
SLVec3i containingVoxel(const SLVec3f &p) const
Returns the indices of the voxel around a given point.
void ifTriangleInVoxelDo(triVoxCallback cb)
Loops over triangles gets their voxels and calls the callback function.
std::array< SLVec3f, 3 > Triangle
Definition: SLCompactGrid.h:32
void generateVertexPos(SLVVec2f *p)
Adds or updates & generates a position vertex attribute for colored line or point drawing.
void drawArrayAsColored(SLGLPrimitiveType primitiveType, SLCol4f color, SLfloat lineOrPointSize=1.0f, SLuint indexFirstVertex=0, SLuint countVertices=0)
Draws the array as the specified primitive with the color.
SLuint vaoID() const
Returns either the VAO id or the VBO id.
An SLMesh object is a triangulated mesh, drawn with one draw call.
Definition: SLMesh.h:134
SLVuint I32
Vector of vertex indices 32 bit.
Definition: SLMesh.h:215
SLVushort I16
Vector of vertex indices 16 bit.
Definition: SLMesh.h:214
SLVec3f finalP(SLuint i)
Definition: SLMesh.h:187
SLbool hitTriangleOS(SLRay *ray, SLNode *node, SLuint iT)
Definition: SLMesh.cpp:1345
SLuint numI() const
Definition: SLMesh.h:181
SLNode represents a node in a hierarchical scene graph.
Definition: SLNode.h:147
SLAABBox * aabb()
Definition: SLNode.h:301
Ray class with ray and intersection properties.
Definition: SLRay.h:40
SLVec3f invDirOS
Inverse ray dir for fast AABB hit in OS.
Definition: SLRay.h:117
SLfloat tmin
min. dist. of last AABB intersection
Definition: SLRay.h:120
SLfloat length
length from origin to an intersection
Definition: SLRay.h:77
SLVec3f dirOS
Direction vector of ray in OS.
Definition: SLRay.h:81
SLVec3f originOS
Vector to the origin of ray in OS.
Definition: SLRay.h:80
SceneView class represents a dynamic real time 3D view onto the scene.
Definition: SLSceneView.h:69
3D vector template class for standard 3D vector algebra.
Definition: SLVec3.h:40
void setMax(const SLVec3 &v)
Definition: SLVec3.h:147
T y
Definition: SLVec3.h:43
T x
Definition: SLVec3.h:43
T z
Definition: SLVec3.h:43
void setMin(const SLVec3 &v)
Definition: SLVec3.h:144
static SLVec4 CYAN
Definition: SLVec4.h:220
T ceil(T a)
Definition: Utils.h:247
Struct for scene graph statistics.
Definition: SLNode.h:37
SLuint numVoxMaxTria
Max. no. of triangles per voxel.
Definition: SLNode.h:51
SLuint numBytesAccel
NO. of bytes in accel. structs.
Definition: SLNode.h:40
SLuint numVoxels
NO. of voxels.
Definition: SLNode.h:49
SLfloat numVoxEmpty
NO. of empty voxels.
Definition: SLNode.h:50