MobileRT  1.0
A multi platform C++ CPU progressive Ray Tracer.
RegularGrid.hpp
Go to the documentation of this file.
1 #ifndef MOBILERT_ACCELERATORS_REGULARGRID_HPP
2 #define MOBILERT_ACCELERATORS_REGULARGRID_HPP
3 
5 #include "MobileRT/Scene.hpp"
6 #include <mutex>
7 #include <glm/glm.hpp>
8 #include <vector>
9 
10 namespace MobileRT {
11 
23  template<typename T>
24  class RegularGrid final {
25  private:
26 
30  ::std::vector<::std::vector<T*>> grid_;
31 
35  ::std::vector<T> primitives_;
36 
40  ::std::int32_t gridSize_ {};
41 
45  ::std::uint32_t gridShift_ {};
46 
51 
55  ::glm::vec3 cellSizeInverted_ {};
56 
60  ::glm::vec3 cellSize_ {};
61 
65  ::std::vector<::std::mutex> mutexes_ {};
66 
67  private:
68  void addPrimitives();
69 
73  void addPrimitivesThreadWork(::std::uint32_t threadId, ::std::uint32_t numberOfThreads);
74 
75  Intersection intersect(Intersection intersection);
76 
77  ::std::uint32_t bitCounter(::std::uint32_t value) const;
78 
79  ::std::int32_t getCellIndex(::std::int32_t cellX, ::std::int32_t cellY, ::std::int32_t cellZ) const;
80 
81  public:
82  explicit RegularGrid() = default;
83 
84  explicit RegularGrid(::std::vector<T> &&primitives, ::std::uint32_t gridSize);
85 
86  RegularGrid(const RegularGrid &regularGrid) = delete;
87 
88  RegularGrid(RegularGrid &&regularGrid) noexcept = default;
89 
90  ~RegularGrid();
91 
92  RegularGrid &operator=(const RegularGrid &regularGrid) = delete;
93 
94  RegularGrid &operator=(RegularGrid &&regularGrid) noexcept = default;
95 
96  Intersection trace(Intersection intersection);
97 
99 
100  const ::std::vector<T>& getPrimitives() const;
101  };
102 
103 
104 
112  template<typename T>
114  ::std::vector<T> &&primitives, const ::std::uint32_t gridSize
115  ) :
116  grid_ {
117  ::std::vector<::std::vector<T*>> {
118  static_cast<::std::uint32_t> (gridSize * gridSize * gridSize)
119  }
120  },
121  primitives_ {::std::move(primitives)},
122  gridSize_ {static_cast<::std::int32_t> (gridSize)},
123  gridShift_ {bitCounter(gridSize - 1U)},
124  worldBoundaries_ {Scene::getBounds<T> (primitives_)},
125  // precalculate 1 / size of a cell (for x, y and z)
126  cellSizeInverted_ {
127  static_cast<float> (gridSize_) / (worldBoundaries_.getPointMax() - worldBoundaries_.getPointMin())[0],
128  static_cast<float> (gridSize_) / (worldBoundaries_.getPointMax() - worldBoundaries_.getPointMin())[1],
129  static_cast<float> (gridSize_) / (worldBoundaries_.getPointMax() - worldBoundaries_.getPointMin())[2]
130  },
131  // precalculate size of a cell (for x, y, and z)
132  cellSize_ {(worldBoundaries_.getPointMax() - worldBoundaries_.getPointMin()) * (1.0F / static_cast<float> (gridSize_))} {
133  ::MobileRT::checkSystemError("RegularGrid constructor start");
134  LOG_INFO("Building RegularGrid for: ", typeid(T).name());
135  const ::glm::vec3 worldBoundsMin {this->worldBoundaries_.getPointMin()};
136  const ::glm::vec3 worldBoundsMax {this->worldBoundaries_.getPointMax()};
137  LOG_INFO("scene min=(",
138  worldBoundsMin[0], ", ",
139  worldBoundsMin[1], ", ",
140  worldBoundsMin[2], ") max=(",
141  worldBoundsMax[0], ", ",
142  worldBoundsMax[1], ", ",
143  worldBoundsMax[2], ")"
144  );
145 
146  LOG_INFO("PRIMITIVES = ", this->primitives_.size());
147 
148  ::MobileRT::checkSystemError("RegularGrid constructor before adding primitives");
149  addPrimitives();
150 
151  ::MobileRT::checkSystemError("RegularGrid constructor end");
152  }
153 
159  template<typename T>
161  this->grid_.clear();
162  ::std::vector<::std::vector<T*>> {}.swap(this->grid_);
163  }
164 
172  template<typename T>
173  ::std::uint32_t RegularGrid<T>::bitCounter(::std::uint32_t value) const {
174  ::std::uint32_t counter {};
175  while (value > 0) {
176  ++counter;
177  value >>= 1;
178  }
179  return counter;
180  }
181 
187  template<typename T>
189  LOG_INFO("Will add primitives to RegularGrid (", typeid(T).name(), ")");
190  ::MobileRT::checkSystemError("RegularGrid addPrimitives start");
191  this->mutexes_ = ::std::vector<::std::mutex> (this->grid_.size());
192  ::MobileRT::checkSystemError("RegularGrid created mutexes");
193 
194  LOG_INFO("Adding primitives to RegularGrid (", typeid(T).name(), ") (mutexes: ", this->mutexes_.size(), " [", this->mutexes_.capacity(), ", ", this->mutexes_.max_size(), "])");
195  const ::std::uint32_t numChildren {::std::thread::hardware_concurrency()};
196  if (numChildren <= 0) {
197  LOG_ERROR("Number of available CPU cores is ", numChildren);
198  return;
199  }
200  ::std::vector<::std::thread> threads {};
201  threads.reserve(numChildren);
202  LOG_INFO("Created mutexes and it will fill the Regular Grid using ", numChildren + 1, " threads");
203  for (::std::uint32_t i {}; i < numChildren; ++i) {
204  threads.emplace_back(&RegularGrid::addPrimitivesThreadWork, this, i, numChildren + 1);
205  }
206  addPrimitivesThreadWork(numChildren, numChildren + 1);
207  for (::std::thread &thread : threads) {
208  thread.join();
209  }
210 
211  LOG_INFO("Added primitives to RegularGrid (", typeid(T).name(), ")");
212  ::MobileRT::checkSystemError("RegularGrid addPrimitives end");
213  }
214 
215  template<typename T>
216  void RegularGrid<T>::addPrimitivesThreadWork(const ::std::uint32_t threadId, const ::std::uint32_t numberOfThreads) {
217  const ::glm::vec3 worldBoundsMin {this->worldBoundaries_.getPointMin()};
218  const ::glm::vec3 worldBoundsMax {this->worldBoundaries_.getPointMax()};
219 
220  // calculate cell width, height and depth
221  const ::glm::vec3 size {worldBoundsMax - worldBoundsMin};
222  const float dx {size[0] / this->gridSize_};
223  const float dy {size[1] / this->gridSize_};
224  const float dz {size[2] / this->gridSize_};
225  const float dxReci {dx > 0 ? 1.0F / dx : 1.0F};
226  const float dyReci {dy > 0 ? 1.0F / dy : 1.0F};
227  const float dzReci {dz > 0 ? 1.0F / dz : 1.0F};
228  const ::std::uint32_t numPrimitives {static_cast<::std::uint32_t> (this->primitives_.size())};
229 
230  for (::std::int32_t index {static_cast<::std::int32_t>(threadId)}; index < static_cast<::std::int32_t> (numPrimitives); index += static_cast<::std::int32_t>(numberOfThreads)) {
231  // LOG_DEBUG("Adding primitive ", index, " to RegularGrid (", typeid(T).name(), ")");
232  ::MobileRT::checkSystemError(::std::string("RegularGrid addPrimitives (" + ::std::to_string(index) + ")").c_str());
233  T &primitive {this->primitives_[static_cast<::std::uint32_t> (index)]};
234  const AABB bound {primitive.getAABB()};
235  const ::glm::vec3 &bv1 {bound.getPointMin()};
236  const ::glm::vec3 &bv2 {bound.getPointMax()};
237 
238  // find out which cells could contain the primitive (based on aabb)
239  ::std::int32_t x1 {static_cast<::std::int32_t> ((bv1[0] - worldBoundsMin[0]) * dxReci)};
240  ::std::int32_t x2 {static_cast<::std::int32_t> ((bv2[0] - worldBoundsMin[0]) * dxReci) + 1};
241  x1 = ::std::max(0, x1);
242  x2 = ::std::min(x2, this->gridSize_ - 1);
243  x2 = ::std::fabs(size[0]) < ::std::numeric_limits<float>::epsilon()? 0 : x2;
244  x1 = ::std::min(x1, x2);
245  ::std::int32_t y1 {static_cast<::std::int32_t> ((bv1[1] - worldBoundsMin[1]) * dyReci)};
246  ::std::int32_t y2 {static_cast<::std::int32_t> ((bv2[1] - worldBoundsMin[1]) * dyReci) + 1};
247  y1 = ::std::max(0, y1);
248  y2 = ::std::min(y2, this->gridSize_ - 1);
249  y2 = ::std::fabs(size[1]) < ::std::numeric_limits<float>::epsilon()? 0 : y2;
250  y1 = ::std::min(y1, y2);
251  ::std::int32_t z1 {static_cast<::std::int32_t> ((bv1[2] - worldBoundsMin[2]) * dzReci)};
252  ::std::int32_t z2 {static_cast<::std::int32_t> ((bv2[2] - worldBoundsMin[2]) * dzReci) + 1};
253  z1 = ::std::max(0, z1);
254  z2 = ::std::min(z2, this->gridSize_ - 1);
255  z2 = ::std::fabs(size[2]) < ::std::numeric_limits<float>::epsilon()? 0 : z2;
256  z1 = ::std::min(z1, z2);
257 
258  // LOG_DEBUG("Looping over candidate cells for primitive ", index, " to RegularGrid (", typeid(T).name(), ") on coordinates: (", x1, "-", x2, ", ", y1, "-", y2, ", ", z1, "-", z2, ")");
259  for (::std::int32_t x {x1}; x <= x2; ++x) {
260  for (::std::int32_t y {y1}; y <= y2; ++y) {
261  for (::std::int32_t z {z1}; z <= z2; ++z) {
262  // construct aabb for current cell
263  const ::std::uint32_t idx {static_cast<::std::uint32_t> (
264  x +
265  y * this->gridSize_ +
266  z * this->gridSize_ * this->gridSize_
267  )};
268  const ::glm::vec3 &pos {
269  worldBoundsMin[0] + static_cast<float>(x) * dx,
270  worldBoundsMin[1] + static_cast<float>(y) * dy,
271  worldBoundsMin[2] + static_cast<float>(z) * dz
272  };
273  const AABB cell {pos, pos + ::glm::vec3 {dx, dy, dz}};
274  // do an accurate aabb / primitive intersection test
275  const bool intersectedBox {primitive.intersect(cell)};
276  if (intersectedBox) {
277  // LOG_DEBUG("Adding primitive ", index, " (", idx, ") to RegularGrid (", typeid(T).name(), ") on coordinates: (", x, ", ", y, ", ", z, ")");
278  const ::std::lock_guard<::std::mutex> lock {this->mutexes_[idx]};
279  this->grid_[idx].emplace_back(&primitive);
280  // LOG_DEBUG("Added primitive ", index, " to RegularGrid (", typeid(T).name(), ") on coordinates: (", x, ", ", y, ", ", z, ")");
281  }
282  }
283  }
284  }
285  // LOG_DEBUG("Added primitive ", index, " to RegularGrid (", typeid(T).name(), ")");
286  ::MobileRT::checkSystemError(::std::string("RegularGrid addPrimitives end (" + ::std::to_string(index) + ")").c_str());
287  }
288  LOG_INFO("Added '", numPrimitives, "' primitives to RegularGrid ", typeid(T).name(), ": (", dx, ", ", dy, ", ", dz, ")");
289  }
290 
299  template<typename T>
301  intersection = intersect (intersection);
302  return intersection;
303  }
304 
314  template<typename T>
316  intersection = intersect (intersection);
317  return intersection;
318  }
319 
332  template<typename T>
334  const ::glm::vec3 &worldBoundsMin {this->worldBoundaries_.getPointMin()};
335 
336  // setup 3DDDA (double check reusability of primary ray data)
337  const ::glm::vec3 &cell {(intersection.ray_.origin_ - worldBoundsMin) * this->cellSizeInverted_};
338  ::std::int32_t cellX {static_cast<::std::int32_t> (cell[0])};
339  ::std::int32_t cellY {static_cast<::std::int32_t> (cell[1])};
340  ::std::int32_t cellZ {static_cast<::std::int32_t> (cell[2])};
341 
342  cellX = ::std::min(cellX, this->gridSize_ - 1);
343  cellX = ::std::max(cellX, 0);
344  cellY = ::std::min(cellY, this->gridSize_ - 1);
345  cellY = ::std::max(cellY, 0);
346  cellZ = ::std::min(cellZ, this->gridSize_ - 1);
347  cellZ = ::std::max(cellZ, 0);
348 
349  ::std::int32_t stepX {}, outX {};
350  ::std::int32_t stepY {}, outY {};
351  ::std::int32_t stepZ {}, outZ {};
352  ::glm::vec3 cb {};
353  if (intersection.ray_.direction_[0] > 0) {
354  stepX = 1;
355  outX = this->gridSize_;
356  cb[0] = (worldBoundsMin[0] + (static_cast<float> (cellX) + 1.0F) * this->cellSize_[0]);
357  } else {
358  stepX = -1;
359  outX = -1;
360  cb[0] = (worldBoundsMin[0] + static_cast<float> (cellX) * this->cellSize_[0]);
361  }
362 
363  if (intersection.ray_.direction_[1] > 0) {
364  stepY = 1;
365  outY = this->gridSize_;
366  cb[1] = (worldBoundsMin[1] + (static_cast<float> (cellY) + 1.0F) * this->cellSize_[1]);
367  } else {
368  stepY = -1;
369  outY = -1;
370  cb[1] = (worldBoundsMin[1] + static_cast<float> (cellY) * this->cellSize_[1]);
371  }
372 
373  if (intersection.ray_.direction_[2] > 0) {
374  stepZ = 1;
375  outZ = this->gridSize_;
376  cb[2] = (worldBoundsMin[2] + (static_cast<float> (cellZ) + 1.0F) * this->cellSize_[2]);
377  } else {
378  stepZ = -1;
379  outZ = -1;
380  cb[2] = (worldBoundsMin[2] + static_cast<float> (cellZ) * this->cellSize_[2]);
381  }
382 
383  ::glm::vec3 tmax {}, tdelta {};
384  if (::std::fabs(intersection.ray_.direction_[0]) > ::std::numeric_limits<float>::epsilon()) {
385  const float rxr {1.0F / intersection.ray_.direction_[0]};
386  tmax[0] = ((cb[0] - intersection.ray_.origin_[0]) * rxr);
387  tdelta[0] = (this->cellSize_[0] * static_cast<float> (stepX) * rxr);
388  } else {
389  tmax[0] = RayLengthMax;
390  }
391 
392  if (::std::fabs(intersection.ray_.direction_[1]) > ::std::numeric_limits<float>::epsilon()) {
393  const float ryr {1.0F / intersection.ray_.direction_[1]};
394  tmax[1] = ((cb[1] - intersection.ray_.origin_[1]) * ryr);
395  tdelta[1] = (this->cellSize_[1] * static_cast<float> (stepY) * ryr);
396  } else {
397  tmax[1] = RayLengthMax;
398  }
399 
400  if (::std::fabs(intersection.ray_.direction_[2]) > ::std::numeric_limits<float>::epsilon()) {
401  const float rzr {1.0F / intersection.ray_.direction_[2]};
402  tmax[2] = ((cb[2] - intersection.ray_.origin_[2]) * rzr);
403  tdelta[2] = (this->cellSize_[2] * static_cast<float> (stepZ) * rzr);
404  } else {
405  tmax[2] = RayLengthMax;
406  }
407 
408  // start stepping
409  // trace primary ray
410  while (true) {
411 
412  // Get the primitives inside the cell.
413  const ::std::int32_t index {getCellIndex(cellX, cellY, cellZ)};
414  const auto itPrimitives {this->grid_.begin() + index};
415  ::std::vector<T*> primitivesList {*itPrimitives};
416 
417  // Check if the ray intersects any primitive in the cell.
418  for (T *const primitive : primitivesList) {
419  const float lastDist {intersection.length_};
420  intersection = primitive->intersect(intersection);
421  if (intersection.length_ < lastDist) {
422  if (intersection.ray_.shadowTrace_) {
423  return intersection;
424  }
425  goto testloop;
426  }
427  }
428 
429  if (tmax[0] < tmax[1]) {
430  if (tmax[0] < tmax[2]) {
431  cellX += stepX;
432  if (cellX == outX) {
433  return intersection;
434  }
435  tmax[0] = (tmax[0] + tdelta[0]);
436  } else {
437  cellZ += stepZ;
438  if (cellZ == outZ) {
439  return intersection;
440  }
441  tmax[2] = (tmax[2] + tdelta[2]);
442  }
443  } else {
444  if (tmax[1] < tmax[2]) {
445  cellY += stepY;
446  if (cellY == outY) {
447  return intersection;
448  }
449  tmax[1] = (tmax[1] + tdelta[1]);
450  } else {
451  cellZ += stepZ;
452  if (cellZ == outZ) {
453  return intersection;
454  }
455  tmax[2] = (tmax[2] + tdelta[2]);
456  }
457  }
458 
459  }
460  testloop:
461  while (true) {
462 
463  // Get the primitives in the cell.
464  const ::std::int32_t index {getCellIndex(cellX, cellY, cellZ)};
465  const auto itPrimitives {this->grid_.begin() + index};
466  ::std::vector<T*> primitivesList {*itPrimitives};
467 
468  // Check if the ray intersects any primitive in the cell.
469  for (T *const primitive : primitivesList) {
470  intersection = primitive->intersect(intersection);
471  }
472  if (tmax[0] < tmax[1]) {
473  if (tmax[0] < tmax[2]) {
474  if (intersection.length_ < tmax[0]) {
475  break;
476  }
477  cellX += stepX;
478  if (cellX == outX) {
479  break;
480  }
481  tmax[0] = (tmax[0] + tdelta[0]);
482  } else {
483  if (intersection.length_ < tmax[2]) {
484  break;
485  }
486  cellZ += stepZ;
487  if (cellZ == outZ) {
488  break;
489  }
490  tmax[2] = (tmax[2] + tdelta[2]);
491  }
492  } else {
493  if (tmax[1] < tmax[2]) {
494  if (intersection.length_ < tmax[1]) {
495  break;
496  }
497  cellY += stepY;
498  if (cellY == outY) {
499  break;
500  }
501  tmax[1] = (tmax[1] + tdelta[1]);
502  } else {
503  if (intersection.length_ < tmax[2]) {
504  break;
505  }
506  cellZ += stepZ;
507  if (cellZ == outZ) {
508  break;
509  }
510  tmax[2] = (tmax[2] + tdelta[2]);
511  }
512  }
513  }
514  return intersection;
515  }
516 
525  template<typename T>
527  const ::std::int32_t cellX,
528  const ::std::int32_t cellY,
529  const ::std::int32_t cellZ) const {
530  const ::std::int32_t index {
531  static_cast<::std::int32_t> (
532  static_cast<::std::uint32_t> (cellX) +
533  (static_cast<::std::uint32_t> (cellY) << (this->gridShift_)) +
534  (static_cast<::std::uint32_t> (cellZ) << (this->gridShift_ * 2U))
535  )
536  };
537  return index;
538  }
539 
546  template<typename T>
547  const ::std::vector<T>& RegularGrid<T>::getPrimitives() const {
548  return this->primitives_;
549  }
550 
551 }//namespace MobileRT
552 
553 #endif //MOBILERT_ACCELERATORS_REGULARGRID_HPP
::glm::vec3 cellSizeInverted_
Definition: RegularGrid.hpp:55
void addPrimitivesThreadWork(::std::uint32_t threadId, ::std::uint32_t numberOfThreads)
Definition: RegularGrid.hpp:216
::std::uint32_t gridShift_
Definition: RegularGrid.hpp:45
::std::int32_t gridSize_
Definition: RegularGrid.hpp:40
const float RayLengthMax
Definition: Constants.hpp:33
void checkSystemError(const char *const message)
Definition: Utils.cpp:237
::glm::vec3 cellSize_
Definition: RegularGrid.hpp:60
Ray ray_
Definition: Intersection.hpp:27
Intersection intersect(Intersection intersection)
Definition: RegularGrid.hpp:333
::glm::vec3 origin_
Definition: Ray.hpp:19
RegularGrid & operator=(const RegularGrid &regularGrid)=delete
Intersection trace(Intersection intersection)
Definition: RegularGrid.hpp:300
Definition: RegularGrid.hpp:24
#define LOG_INFO(...)
Definition: Utils.hpp:43
::glm::vec3 direction_
Definition: Ray.hpp:24
float length_
Definition: Intersection.hpp:19
bool intersect(const Ray &ray) const
Definition: AABB.cpp:34
#define LOG_ERROR(...)
Definition: Utils.hpp:53
Definition: Intersection.hpp:14
void addPrimitives()
Definition: RegularGrid.hpp:188
~RegularGrid()
Definition: RegularGrid.hpp:160
const ::std::vector< T > & getPrimitives() const
Definition: RegularGrid.hpp:547
::std::atomic<::std::uint64_t > counter
Definition: Ray.cpp:11
::std::string to_string(const T &str)
Definition: Utils.hpp:378
Definition: AABB.hpp:18
::std::vector<::std::mutex > mutexes_
Definition: RegularGrid.hpp:65
::std::vector< T > primitives_
Definition: RegularGrid.hpp:35
::std::vector<::std::vector< T * > > grid_
Definition: RegularGrid.hpp:30
AABB worldBoundaries_
Definition: RegularGrid.hpp:50
::std::uint32_t bitCounter(::std::uint32_t value) const
Definition: RegularGrid.hpp:173
::std::int32_t getCellIndex(::std::int32_t cellX, ::std::int32_t cellY, ::std::int32_t cellZ) const
Definition: RegularGrid.hpp:526
bool shadowTrace_
Definition: Ray.hpp:45
Intersection shadowTrace(Intersection intersection)
Definition: RegularGrid.hpp:315
::glm::vec3 getPointMin() const
Definition: AABB.cpp:92
Definition: AABB.cpp:105