Code_TYMPAN  4.4.0
Industrial site acoustic simulation
BvhAccelerator.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) <2012> <EDF-R&D> <FRANCE>
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10  * See the GNU General Public License for more details.
11  * You should have received a copy of the GNU General Public License along
12  * with this program; if not, write to the Free Software Foundation, Inc.,
13  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
14  */
15 
16 #include "BvhAccelerator.h"
17 #include <algorithm>
18 #include <stdint.h>
19 
20 // BVHAccel Local Declarations
22 {
24  BVHPrimitiveInfo(int pn, const BBox& b) : primitiveNumber(pn), bounds(b)
25  {
26  centroid = b.pMin * 0.5 + b.pMax * .5f;
27  }
31 };
32 
34 {
35  // BVHBuildNode Public Methods
37  {
38  children[0] = children[1] = NULL;
39  }
40  void InitLeaf(unsigned int first, unsigned int n, const BBox& b)
41  {
42  firstPrimOffset = first;
43  nPrimitives = n;
44  bounds = b;
45  }
46  void InitInterior(unsigned int axis, BVHBuildNode* c0, BVHBuildNode* c1)
47  {
48  children[0] = c0;
49  children[1] = c1;
50  bounds = c0->bounds.Union(c1->bounds);
51  splitAxis = axis;
52  nPrimitives = 0;
53  }
57 };
58 
60 {
62  {
63  dim = d;
64  mid = m;
65  }
66  int dim;
68  bool operator()(const BVHPrimitiveInfo& a) const
69  {
70  return a.centroid[dim] < mid;
71  }
72 };
73 
75 {
77  {
78  dim = d;
79  }
80  int dim;
81  bool operator()(const BVHPrimitiveInfo& a, const BVHPrimitiveInfo& b) const
82  {
83  return a.centroid[dim] < b.centroid[dim];
84  }
85 };
86 
88 {
89  CompareToBucket(int split, int num, int d, const BBox& b) : centroidBounds(b)
90  {
91  splitBucket = split;
92  nBuckets = num;
93  dim = d;
94  }
95  bool operator()(const BVHPrimitiveInfo& p) const;
96 
99 };
100 
102 {
103  int b = (int)(nBuckets * ((p.centroid[dim] - centroidBounds.pMin[dim]) /
105  if (b == nBuckets)
106  {
107  b = nBuckets - 1;
108  }
109 
110  return b <= splitBucket;
111 }
112 
114 {
116  union
117  {
118  uint32_t primitivesOffset;
119  uint32_t secondChildOffset;
120  };
121 
122  uint8_t nPrimitives;
123  uint8_t axis;
124  uint8_t pad[2];
125 };
126 
127 static inline bool IntersectP(const BBox& bounds, const Ray& ray, const vec3& invDir,
128  const uint32_t dirIsNeg[3])
129 {
130  // Check for ray intersection against $x$ and $y$ slabs
131 
132  float tmin = (bounds[dirIsNeg[0]].x - ray.getPosition().x) * invDir.x;
133  float tmax = (bounds[1 - dirIsNeg[0]].x - ray.getPosition().x) * invDir.x;
134  float tymin = (bounds[dirIsNeg[1]].y - ray.getPosition().y) * invDir.y;
135  float tymax = (bounds[1 - dirIsNeg[1]].y - ray.getPosition().y) * invDir.y;
136  if ((tmin > tymax) || (tymin > tmax))
137  {
138  return false;
139  }
140  if (tymin > tmin)
141  {
142  tmin = tymin;
143  }
144  if (tymax < tmax)
145  {
146  tmax = tymax;
147  }
148 
149  // Check for ray intersection against $z$ slab
150  float tzmin = (bounds[dirIsNeg[2]].z - ray.getPosition().z) * invDir.z;
151  float tzmax = (bounds[1 - dirIsNeg[2]].z - ray.getPosition().z) * invDir.z;
152  if ((tmin > tzmax) || (tzmin > tmax))
153  {
154  return false;
155  }
156  if (tzmin > tmin)
157  {
158  tmin = tzmin;
159  }
160  if (tzmax < tmax)
161  {
162  tmax = tzmax;
163  }
164  return (tmin < ray.getMaxt()) && (tmax > ray.getMint());
165 }
166 
167 // BVHAccel Method Definitions
168 BvhAccelerator::BvhAccelerator(std::vector<Shape*>* _initialMesh, BBox _globalBox, unsigned int maxProf,
169  const string& sm)
170  : Accelerator(_initialMesh, _globalBox)
171 {
172  maxPrimsInNode = min(255u, maxProf);
173 
174  if (sm == "sah")
175  {
177  }
178  else if (sm == "middle")
179  {
181  }
182  else if (sm == "equal")
183  {
185  }
186  else
187  {
189  }
190 
191  for (unsigned int i = 0; i < _initialMesh->size(); i++)
192  {
193  primitives.push_back(_initialMesh->at(i));
194  }
195 }
196 
197 BVHBuildNode* BvhAccelerator::recursiveBuild(std::vector<BVHPrimitiveInfo>& buildData, uint32_t start,
198  uint32_t end, uint32_t* totalNodes,
199  std::vector<Shape*>& orderedPrims)
200 {
201 
202  (*totalNodes)++;
203  BVHBuildNode* node = new BVHBuildNode();
204  // Compute bounds of all primitives in BVH node
205  BBox bbox;
206  for (uint32_t i = start; i < end; ++i)
207  {
208  bbox = bbox.Union(buildData[i].bounds);
209  }
210  uint32_t nPrimitives = end - start;
211  if (nPrimitives == 1)
212  {
213  // Create leaf _BVHBuildNode_
214  uint32_t firstPrimOffset = static_cast<uint32_t>(orderedPrims.size());
215  for (uint32_t i = start; i < end; ++i)
216  {
217  uint32_t primNum = buildData[i].primitiveNumber;
218  orderedPrims.push_back(primitives.at(primNum));
219  }
220  node->InitLeaf(firstPrimOffset, nPrimitives, bbox);
221  }
222  else
223  {
224  // Compute bound of primitive centroids, choose split dimension _dim_
225  BBox centroidBounds;
226  for (uint32_t i = start; i < end; ++i)
227  {
228  centroidBounds = centroidBounds.Union(buildData[i].centroid);
229  }
230  int dim = centroidBounds.MaximumExtend();
231 
232  // Partition primitives into two sets and build children
233  uint32_t mid = (start + end) / 2;
234 
235  if (centroidBounds.pMax[dim] == centroidBounds.pMin[dim])
236  {
237  // Create leaf _BVHBuildNode_
238  uint32_t firstPrimOffset = static_cast<uint32_t>(orderedPrims.size());
239  for (uint32_t i = start; i < end; ++i)
240  {
241  uint32_t primNum = buildData[i].primitiveNumber;
242  orderedPrims.push_back(primitives.at(primNum));
243  }
244  node->InitLeaf(firstPrimOffset, nPrimitives, bbox);
245  return node;
246  }
247 
248  // Partition primitives based on _splitMethod_
249  switch (splitMethod)
250  {
251  case SPLIT_MIDDLE:
252  {
253  // Partition primitives through node's midpoint
254  float pmid = .5f * (centroidBounds.pMin[dim] + centroidBounds.pMax[dim]);
255  BVHPrimitiveInfo* midPtr =
256  std::partition(&buildData[start], &buildData[end - 1] + 1, CompareToMid(dim, pmid));
257  mid = static_cast<uint32_t>(midPtr - &buildData[0]);
258  if (mid != start && mid != end)
259  // for lots of prims with large overlapping bounding boxes, this
260  // may fail to partition; in that case don't break and fall through
261  // to SPLIT_EQUAL_COUNTS
262  {
263  break;
264  }
265  }
266  case SPLIT_EQUAL_COUNTS:
267  {
268  // Partition primitives into equally-sized subsets
269  mid = (start + end) / 2;
270  std::nth_element(&buildData[start], &buildData[mid], &buildData[end - 1] + 1,
271  ComparePoints(dim));
272  break;
273  }
274  case SPLIT_SAH:
275  default:
276  {
277  // Partition primitives using approximate SAH
278  if (nPrimitives <= 4)
279  {
280  // Partition primitives into equally-sized subsets
281  mid = (start + end) / 2;
282  std::nth_element(&buildData[start], &buildData[mid], &buildData[end - 1] + 1,
283  ComparePoints(dim));
284  }
285  else
286  {
287  // Allocate _BucketInfo_ for SAH partition buckets
288  const int nBuckets = 12;
289  struct BucketInfo
290  {
291  BucketInfo()
292  {
293  count = 0;
294  }
295  int count;
296  BBox bounds;
297  };
298  BucketInfo buckets[nBuckets];
299 
300  // Initialize _BucketInfo_ for SAH partition buckets
301  for (uint32_t i = start; i < end; ++i)
302  {
303  int b = (int)(nBuckets * ((buildData[i].centroid[dim] - centroidBounds.pMin[dim]) /
304  (centroidBounds.pMax[dim] - centroidBounds.pMin[dim])));
305  if (b == nBuckets)
306  {
307  b = nBuckets - 1;
308  }
309  buckets[b].count++;
310  buckets[b].bounds = buckets[b].bounds.Union(buildData[i].bounds);
311  }
312 
313  // Compute costs for splitting after each bucket
314  float cost[nBuckets - 1];
315  for (int i = 0; i < nBuckets - 1; ++i)
316  {
317  BBox b0, b1;
318  int count0 = 0, count1 = 0;
319  for (int j = 0; j <= i; ++j)
320  {
321  b0 = b0.Union(buckets[j].bounds);
322  count0 += buckets[j].count;
323  }
324  for (int j = i + 1; j < nBuckets; ++j)
325  {
326  b1 = b1.Union(buckets[j].bounds);
327  count1 += buckets[j].count;
328  }
329  cost[i] = .125f + (decimal)((count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea()) /
330  bbox.SurfaceArea());
331  }
332 
333  // Find bucket to split at that minimizes SAH metric
334  float minCost = cost[0];
335  uint32_t minCostSplit = 0;
336  for (int i = 1; i < nBuckets - 1; ++i)
337  {
338  if (cost[i] < minCost)
339  {
340  minCost = cost[i];
341  minCostSplit = i;
342  }
343  }
344 
345  // Either create leaf or split primitives at selected SAH bucket
346  if (nPrimitives > maxPrimsInNode || minCost < nPrimitives)
347  {
348  BVHPrimitiveInfo* pmid =
349  std::partition(&buildData[start], &buildData[end - 1] + 1,
350  CompareToBucket(minCostSplit, nBuckets, dim, centroidBounds));
351  mid = static_cast<uint32_t>(pmid - &buildData[0]);
352  }
353 
354  else
355  {
356  // Create leaf _BVHBuildNode_
357  uint32_t firstPrimOffset = static_cast<uint32_t>(orderedPrims.size());
358  for (uint32_t i = start; i < end; ++i)
359  {
360  uint32_t primNum = buildData[i].primitiveNumber;
361  orderedPrims.push_back(primitives.at(primNum));
362  }
363  node->InitLeaf(firstPrimOffset, nPrimitives, bbox);
364  return node;
365  }
366  }
367  break;
368  }
369  }
370  node->InitInterior(dim, recursiveBuild(buildData, start, mid, totalNodes, orderedPrims),
371  recursiveBuild(buildData, mid, end, totalNodes, orderedPrims));
372  }
373  return node;
374 }
375 
376 uint32_t BvhAccelerator::flattenBVHTree(BVHBuildNode* node, uint32_t* offset)
377 {
378  LinearBVHNode* linearNode = &nodes[*offset];
379  linearNode->bounds = node->bounds;
380  uint32_t myOffset = (*offset)++;
381  if (node->nPrimitives > 0)
382  {
383  linearNode->primitivesOffset = node->firstPrimOffset;
384  linearNode->nPrimitives = node->nPrimitives;
385  }
386  else
387  {
388  // Creater interior flattened BVH node
389  linearNode->axis = node->splitAxis;
390  linearNode->nPrimitives = 0;
391  flattenBVHTree(node->children[0], offset);
392  linearNode->secondChildOffset = flattenBVHTree(node->children[1], offset);
393  }
394  return myOffset;
395 }
396 
397 decimal BvhAccelerator::traverse(Ray* ray, std::list<Intersection>& result) const
398 {
399  if (!nodes)
400  {
401  return -1.;
402  }
403 
404  decimal intermin = -1.;
405  vec3 invDir(1.f / ray->getDirection().x, 1.f / ray->getDirection().y, 1.f / ray->getDirection().z);
406  uint32_t dirIsNeg[3] = {invDir.x < 0, invDir.y < 0, invDir.z < 0};
407  // Follow ray through BVH nodes to find primitive intersections
408  uint32_t todoOffset = 0, nodeNum = 0;
409  uint32_t todo[64];
410  while (true)
411  {
412  const LinearBVHNode* node = &nodes[nodeNum];
413  // Check ray against BVH node
414  if (::IntersectP(node->bounds, ray, invDir, dirIsNeg))
415  {
416  if (node->nPrimitives > 0)
417  {
418  // Intersect ray with primitives in leaf BVH node
419  // PBRT_BVH_INTERSECTION_TRAVERSED_LEAF_NODE(const_cast<LinearBVHNode *>(node));
420 
421  Intersection currentIntersection;
422  for (uint32_t i = 0; i < node->nPrimitives; ++i)
423  {
424  // PBRT_BVH_INTERSECTION_PRIMITIVE_TEST(const_cast<Primitive
425  // *>(primitives[node->primitivesOffset+i].GetPtr()));
426  if (primitives.at(node->primitivesOffset + i)
427  ->getIntersection(*ray, currentIntersection) &&
428  currentIntersection.t > 0.0001)
429  {
430  // PBRT_BVH_INTERSECTION_PRIMITIVE_HIT(const_cast<Primitive
431  // *>(primitives[node->primitivesOffset+i].GetPtr()));
432  result.push_back(currentIntersection);
433 
434  // intermin = leafTreatment::keepFunction(intersectionChoice, result, intermin);
435  intermin = (*pLeafTreatmentFunction)(result, intermin);
436  }
437  else
438  {
439  // PBRT_BVH_INTERSECTION_PRIMITIVE_MISSED(const_cast<Primitive
440  // *>(primitives[node->primitivesOffset+i].GetPtr()));
441  }
442  }
443  if (todoOffset == 0)
444  {
445  break;
446  }
447  nodeNum = todo[--todoOffset];
448  }
449  else
450  {
451  // Put far BVH node on _todo_ stack, advance to near node
452  // PBRT_BVH_INTERSECTION_TRAVERSED_INTERIOR_NODE(const_cast<LinearBVHNode *>(node));
453  if (dirIsNeg[node->axis])
454  {
455  todo[todoOffset++] = nodeNum + 1;
456  nodeNum = node->secondChildOffset;
457  }
458  else
459  {
460  todo[todoOffset++] = node->secondChildOffset;
461  nodeNum = nodeNum + 1;
462  }
463  }
464  }
465  else
466  {
467  if (todoOffset == 0)
468  {
469  break;
470  }
471  nodeNum = todo[--todoOffset];
472  }
473  }
474  // PBRT_BVH_INTERSECTION_FINISHED();
475  return intermin;
476 }
477 
479 {
480  if (shapes->size() == 0)
481  {
482  nodes = NULL;
483  return false;
484  }
485 
486  // Initialize _buildData_ array for primitives
487  std::vector<BVHPrimitiveInfo> buildData;
488  buildData.reserve(shapes->size());
489  for (uint32_t i = 0; i < shapes->size(); ++i)
490  {
491  BBox bbox = shapes->at(i)->getBBox();
492  buildData.push_back(BVHPrimitiveInfo(i, bbox));
493  }
494 
495  // Recursively build BVH tree for primitives
496  // MemoryArena buildArena;
497  uint32_t totalNodes = 0;
498  std::vector<Shape*> orderedPrims;
499  orderedPrims.reserve(shapes->size());
500  BVHBuildNode* root =
501  recursiveBuild(buildData, 0, static_cast<uint32_t>(primitives.size()), &totalNodes, orderedPrims);
502  primitives.swap(orderedPrims);
503 
504  // Compute representation of depth-first traversal of BVH tree
505  nodes = (LinearBVHNode*)malloc(totalNodes * sizeof(LinearBVHNode));
506  for (uint32_t i = 0; i < totalNodes; ++i)
507  {
508  new (&nodes[i]) LinearBVHNode;
509  }
510  uint32_t offset = 0;
511  flattenBVHTree(root, &offset);
512 
513  return true;
514 }
#define min(a, b)
Base class for accelerators.
Definition: Accelerator.h:27
std::vector< Shape * > * shapes
Vector of pointers to shapes.
Definition: Accelerator.h:102
Definition of a bounding box which is aligned along the axis (BBox AABB).
Definition: BBox.h:32
vec3 pMax
Upper point of the BBox.
Definition: BBox.h:36
BBox Union(const BBox &b, const vec3 &p)
Union of a point and a BBox. A new BBox is created.
Definition: BBox.h:110
vec3 pMin
Lower point of the BBox.
Definition: BBox.h:35
double SurfaceArea() const
Return the BBox area (sum of the lateral areas). Used for the SAH calculation of the accelerators.
Definition: BBox.h:98
int MaximumExtend() const
Return the index of the dominant direction (maximal dimension). Index is 0 for x, 1 for y,...
Definition: BBox.h:218
virtual decimal traverse(Ray *r, std::list< Intersection > &result) const
Run this accelerator.
BVHBuildNode * recursiveBuild(std::vector< BVHPrimitiveInfo > &buildData, unsigned int start, unsigned int end, unsigned int *totalNodes, std::vector< Shape * > &orderedPrims)
std::vector< Shape * > primitives
Pointer to all the shapes (different from initialMesh) as it is reordered.
virtual bool build()
Build this accelerator.
SplitMethod splitMethod
Split method.
unsigned int maxPrimsInNode
Maximal primitives in node.
unsigned int flattenBVHTree(BVHBuildNode *node, unsigned int *offset)
BvhAccelerator(std::vector< Shape * > *_initialMesh=NULL, BBox _globalBox=BBox(), unsigned int maxProf=8, const string &sm="sah")
Default constructor.
LinearBVHNode * nodes
Nodes list.
: Describes a ray by a pair of unsigned int. The first one gives the source number (in the range 0-40...
Definition: Ray.h:38
decimal getMaxt() const
Return maxt.
Definition: Ray.h:326
decimal getMint() const
Return mint.
Definition: Ray.h:316
vec3 getPosition() const
Return starting point ray.
Definition: Ray.h:356
vec3 getDirection() const
Return direction of the ray.
Definition: Ray.h:346
float decimal
Definition: mathlib.h:45
base_vec3< decimal > vec3
Definition: mathlib.h:381
void InitInterior(unsigned int axis, BVHBuildNode *c0, BVHBuildNode *c1)
void InitLeaf(unsigned int first, unsigned int n, const BBox &b)
unsigned int firstPrimOffset
unsigned int nPrimitives
BVHBuildNode * children[2]
unsigned int splitAxis
BVHPrimitiveInfo(int pn, const BBox &b)
bool operator()(const BVHPrimitiveInfo &a, const BVHPrimitiveInfo &b) const
const BBox & centroidBounds
CompareToBucket(int split, int num, int d, const BBox &b)
bool operator()(const BVHPrimitiveInfo &p) const
CompareToMid(int d, decimal m)
bool operator()(const BVHPrimitiveInfo &a) const
uint32_t primitivesOffset
leaf
uint8_t axis
interior node: xyz
uint8_t pad[2]
ensure 32 byte total size
uint8_t nPrimitives
0 -> interior node
uint32_t secondChildOffset
interior
Intersection struct.
Definition: Shape.h:46
decimal t
Definition: Shape.h:48