Code_TYMPAN  4.4.0
Industrial site acoustic simulation
KdtreeAccelerator.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 "KdtreeAccelerator.h"
17 
18 #include <math.h>
19 #include <algorithm>
20 #include <sstream>
21 #include <limits>
22 
29 void KDNode::createLeaf(unsigned int _nbPrims, unsigned int _firstIndex, unsigned int* _prims)
30 {
31  flag = 3;
32  nbPrims |= _nbPrims << 2;
33  if (_nbPrims < 2)
34  {
35  firstIndex = _firstIndex;
36  }
37  else
38  {
39  prims = _prims;
40  }
41 }
42 
43 void KDNode::createNode(int axis, float _split, unsigned int nextChild)
44 {
45  flag = axis;
46  split = _split;
47  secondChild |= nextChild << 2;
48 }
49 
50 KdtreeAccelerator::KdtreeAccelerator(std::vector<Shape*>* _initialMesh, BBox _globalBox)
51  : Accelerator(_initialMesh, _globalBox)
52 {
53  initialMesh = _initialMesh;
54  // globalBox = BBox(_globalBox);
55 
56  maxProfondeur = 16;
57  maxPrimPerLeaf = 2;
58 
60 
61  alreadyFail = false;
62  nbFail = 0;
63  trace = false;
64 
65  isectCost = 4.f;
66  emptyBonus = 0.5f;
67  traversalCost = 1.f;
68 }
69 
71 
73 {
74  if (initialMesh == NULL || initialMesh->empty())
75  {
76  return false;
77  }
78 
79  unsigned int* prims = (unsigned int*)malloc(initialMesh->size() * sizeof(unsigned int));
80  for (unsigned int i = 0; i < initialMesh->size(); i++)
81  {
82  InfoPrim newPrim;
83  newPrim.box = BBox(initialMesh->at(i)->getBBox());
84  newPrim.indexPrim = i;
85  tablePrimitive.push_back(newPrim);
86  prims[i] = i;
87  }
88 
89  // Allocate working memory for kd-tree construction
90  TaBRecBoundEdge* edges[3];
91  for (int i = 0; i < 3; ++i)
92  {
93  edges[i] = new TaBRecBoundEdge[2 * initialMesh->size()];
94  }
95 
96  generateSAHKdTree(0, globalBox, edges, static_cast<unsigned int>(initialMesh->size()), prims);
97 
98  for (int i = 0; i < 3; ++i)
99  {
100  delete[] edges[i];
101  }
102 
103  return true;
104 }
105 
106 void KdtreeAccelerator::generateMidKdTree(int currentProfondeur, BBox& localBox, unsigned int nbPrims,
107  unsigned int* prims)
108 {
109 
110  if (currentProfondeur >= maxProfondeur || (int)(nbPrims) <= maxPrimPerLeaf)
111  {
112  KDNode node;
113  unsigned int* newPrims = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
114  for (unsigned int i = 0; i < nbPrims; i++)
115  {
116  newPrims[i] = prims[i];
117  }
118  node.createLeaf(nbPrims, newPrims[0], newPrims);
119  tableNode.push_back(node);
120  tableBox.push_back(localBox);
121  if (currentProfondeur > realMaxProfondeur)
122  {
123  realMaxProfondeur = currentProfondeur;
124  }
125  free(prims);
126  return;
127  }
128 
129  unsigned int n0(0), n1(0);
130  unsigned int *prims0 = nullptr, *prims1 = nullptr;
131  prims0 = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
132  prims1 = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
133 
134  // Coupe au milieu, on selectionne la plus grande dimension
135  int axis = localBox.MaximumExtend();
136  float split = (localBox.pMin[axis] + localBox.pMax[axis]) / 2.f;
137 
138  for (unsigned int i = 0; i < nbPrims; i++)
139  {
140  Shape* shape = initialMesh->at(prims[i]);
141  if (shape->getBBox().pMax[axis] < split)
142  {
143  prims0[n0] = prims[i];
144  n0++;
145  }
146  else if (shape->getBBox().pMin[axis] > split)
147  {
148  prims1[n1] = prims[i];
149  n1++;
150  }
151  else
152  {
153  prims0[n0] = prims[i];
154  n0++;
155  prims1[n1] = prims[i];
156  n1++;
157  }
158  }
159 
160  free(prims);
161 
162  KDNode currentNode;
163  size_t indexCurrentNode = tableNode.size();
164  tableNode.push_back(currentNode);
165  tableBox.push_back(localBox);
166 
167  BBox firstBox = BBox(localBox);
168  firstBox.pMax[axis] = split;
169  generateMidKdTree(currentProfondeur + 1, firstBox, n0, prims0);
170 
171  tableNode.at(indexCurrentNode).createNode(axis, split, static_cast<unsigned int>(tableNode.size()));
172 
173  BBox secondBox = BBox(localBox);
174  secondBox.pMin[axis] = split;
175  generateMidKdTree(currentProfondeur + 1, secondBox, n1, prims1);
176 }
177 
178 decimal KdtreeAccelerator::traverse(Ray* r, std::list<Intersection>& result) const
179 {
180 
181  decimal tmin = NAN, tmax = NAN, intermin = -1.;
182  if (!globalBox.IntersectP(r->getPosition(), r->getDirection(), &tmin, &tmax))
183  {
184  return -1.;
185  }
186 
187  stringstream logs;
188 
189  // Prepare to traverse kd-tree for ray
190  vec3 invDir(1.f / r->getDirection().x, 1.f / r->getDirection().y, 1.f / r->getDirection().z);
191 #define MAX_TODO 64
192  KdToDo todo[MAX_TODO];
193  int todoPos = 0;
194 
195  // Traverse kd-tree nodes in order for ray
196  KDNode* node = const_cast<KDNode*>(&tableNode[0]);
197  while (node != NULL)
198  {
199  // Bail out if we found a hit closer than the current node
200  if (r->getMaxt() < tmin)
201  {
202  break;
203  }
204  if (!node->isLeaf())
205  {
206  // Process kd-tree interior node
207  // Compute parametric distance along ray to split plane
208  int axis = node->getAxe();
209  float tplane = (node->getAxeValue() - r->getPosition()[axis]) * invDir[axis];
210 
211  // Get node children pointers for ray
212  KDNode *firstChild = nullptr, *secondChild = nullptr;
213  int belowFirst = (r->getPosition()[axis] < node->getAxeValue()) ||
214  (r->getPosition()[axis] == node->getAxeValue() && r->getDirection()[axis] >= 0);
215  if (belowFirst)
216  {
217  firstChild = node + 1;
218  secondChild = const_cast<KDNode*>(&tableNode[node->AboveChild()]);
219  }
220  else
221  {
222  firstChild = const_cast<KDNode*>(&tableNode[node->AboveChild()]);
223  secondChild = node + 1;
224  }
225 
226  // Advance to next child node, possibly enqueue other child
227  if (tplane > tmax || tplane < 0)
228  {
229  node = firstChild;
230  }
231  else if (tplane < tmin)
232  {
233  node = secondChild;
234  }
235  else
236  {
237  // Enqueue _secondChild_ in todo list
238  todo[todoPos].node = secondChild;
239  todo[todoPos].tmin = tplane;
240  todo[todoPos].tmax = tmax;
241  ++todoPos;
242  node = firstChild;
243  tmax = tplane;
244  }
245  }
246  else
247  {
248  // Check for intersections inside leaf node
249  unsigned int nPrimitives = node->getNbPrimitives();
250  if (nPrimitives == 1)
251  {
252  Shape* prim = initialMesh->at(node->getFirstIndex());
253  // Check one primitive inside leaf node
254 
255  Intersection currentIntersection;
256  if (prim->getIntersection(*r, currentIntersection) && currentIntersection.t > 0.0001)
257  {
258  result.push_back(currentIntersection);
259  // intermin = leafTreatment::keepFunction(intersectionChoice, result,
260  // intermin);
261  intermin = (*pLeafTreatmentFunction)(result, intermin);
262  }
263  }
264  else if (nPrimitives > 1)
265  {
266  unsigned int* prims = node->getPrims();
267  for (unsigned int i = 0; i < nPrimitives; i++)
268  {
269  Shape* prim = initialMesh->at(prims[i]);
270 
271  // Check one primitive inside leaf node
272 
273  Intersection currentIntersection;
274  if (prim->getIntersection(*r, currentIntersection) && currentIntersection.t > 0.0001)
275  {
276  result.push_back(currentIntersection);
277  // intermin = leafTreatment::keepFunction(intersectionChoice, result, intermin);
278  intermin = (*pLeafTreatmentFunction)(result, intermin);
279  }
280  }
281  }
282 
283  // Grab next node to process from todo list
284  if (todoPos > 0)
285  {
286  --todoPos;
287  node = todo[todoPos].node;
288  tmin = todo[todoPos].tmin;
289  tmax = todo[todoPos].tmax;
290  }
291  else
292  {
293  break;
294  }
295  }
296  }
297 
298  return intermin;
299 }
300 
302 {
303  for (unsigned int i = 0; i < tableNode.size(); i++)
304  {
305  if (tableNode.at(i).isLeaf())
306  {
307  if (tableNode.at(i).getNbPrimitives() == 1)
308  {
309  }
310  else
311  {
312  }
313  }
314  else
315  {
316  }
317  }
318 }
319 
320 void KdtreeAccelerator::generateSAHKdTree(int currentProfondeur, BBox& localBox, TaBRecBoundEdge* edges[3],
321  unsigned int nbPrims, unsigned int* prims)
322 {
323  if (currentProfondeur >= maxProfondeur || (int)(nbPrims) <= maxPrimPerLeaf)
324  {
325  KDNode node;
326  unsigned int* newPrims = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
327  for (unsigned int i = 0; i < nbPrims; i++)
328  {
329  newPrims[i] = prims[i];
330  }
331  node.createLeaf(nbPrims, newPrims[0], newPrims);
332  tableNode.push_back(node);
333  tableBox.push_back(localBox);
334  if (currentProfondeur > realMaxProfondeur)
335  {
336  realMaxProfondeur = currentProfondeur;
337  }
338  free(prims);
339  return;
340  }
341 
342  unsigned int n0(0), n1(0);
343  unsigned int *prims0 = nullptr, *prims1 = nullptr;
344  prims0 = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
345  prims1 = (unsigned int*)malloc(nbPrims * sizeof(unsigned int));
346 
347  // Initialize interior node and continue recursion
348  // Choose split axis position for interior node
349  int bestAxis = -1, bestOffset = -1;
350  float bestCost = std::numeric_limits<float>::infinity(); // INFINITY;
351  vec3 d = localBox.pMax - localBox.pMin;
352  float totalSA = (2.f * (d.x * d.y + d.x * d.z + d.y * d.z));
353  float invTotalSA = 1.f / totalSA;
354  // Choose which axis to split along
355  int axis = 0;
356  if (d.x > d.y && d.x > d.z)
357  {
358  axis = 0;
359  }
360  else
361  {
362  axis = (d.y > d.z) ? 1 : 2;
363  }
364  int retries = 0;
365 
366 retrySplit:
367  // Initialize edges for _axis_
368  for (int i = 0; i < static_cast<int>(nbPrims); ++i)
369  {
370  int pn = prims[i];
371  const BBox& bbox = initialMesh->at(pn)->getBBox();
372  edges[axis][2 * i] = TaBRecBoundEdge(bbox.pMin[axis], pn, true);
373  edges[axis][2 * i + 1] = TaBRecBoundEdge(bbox.pMax[axis], pn, false);
374  }
375  std::sort(&edges[axis][0], &edges[axis][2 * nbPrims]);
376  // Compute cost of all splits for _axis_ to find best
377  int nBelow = 0, nAbove = nbPrims;
378  for (int i = 0; i < static_cast<int>(2 * nbPrims); ++i)
379  {
380  if (edges[axis][i].type == TaBRecBoundEdge::END)
381  {
382  --nAbove;
383  }
384  float edget = edges[axis][i].t;
385  if (edget > localBox.pMin[axis] && edget < localBox.pMax[axis])
386  {
387  // Compute cost for split at _i_th edge
388  int otherAxis[3][2] = {{1, 2}, {0, 2}, {0, 1}};
389  int otherAxis0 = otherAxis[axis][0];
390  int otherAxis1 = otherAxis[axis][1];
391  float belowSA = 2 * (d[otherAxis0] * d[otherAxis1] +
392  (edget - localBox.pMin[axis]) * (d[otherAxis0] + d[otherAxis1]));
393  float aboveSA = 2 * (d[otherAxis0] * d[otherAxis1] +
394  (localBox.pMax[axis] - edget) * (d[otherAxis0] + d[otherAxis1]));
395  float pBelow = belowSA * invTotalSA;
396  float pAbove = aboveSA * invTotalSA;
397  float eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0.f;
398  float cost = traversalCost + isectCost * (1.f - eb) * (pBelow * nBelow + pAbove * nAbove);
399  // Update best split if this is lowest cost so far
400  if (cost < bestCost)
401  {
402  bestCost = cost;
403  bestAxis = axis;
404  bestOffset = i;
405  }
406  }
407  if (edges[axis][i].type == TaBRecBoundEdge::START)
408  {
409  ++nBelow;
410  }
411  }
412 
413  // Create leaf if no good splits were found
414  if (bestAxis == -1 && retries < 2)
415  {
416  ++retries;
417  axis = (axis + 1) % 3;
418  goto retrySplit;
419  }
420 
421  float split = edges[bestAxis][bestOffset].t;
422 
423  for (unsigned int i = 0; i < nbPrims; i++)
424  {
425  Shape* triangle = initialMesh->at(prims[i]);
426  if (triangle->getBBox().pMax[axis] < split)
427  {
428  prims0[n0] = prims[i];
429  n0++;
430  }
431  else if (triangle->getBBox().pMin[axis] > split)
432  {
433  prims1[n1] = prims[i];
434  n1++;
435  }
436  else
437  {
438  prims0[n0] = prims[i];
439  n0++;
440  prims1[n1] = prims[i];
441  n1++;
442  }
443  }
444 
445  free(prims);
446 
447  KDNode currentNode;
448  size_t indexCurrentNode = tableNode.size();
449  tableNode.push_back(currentNode);
450  tableBox.push_back(localBox);
451 
452  BBox firstBox = BBox(localBox);
453  firstBox.pMax[axis] = split;
454  generateMidKdTree(currentProfondeur + 1, firstBox, n0, prims0);
455 
456  tableNode.at(indexCurrentNode).createNode(axis, split, static_cast<unsigned int>(tableNode.size()));
457 
458  BBox secondBox = BBox(localBox);
459  secondBox.pMin[axis] = split;
460  generateMidKdTree(currentProfondeur + 1, secondBox, n1, prims1);
461 }
#define MAX_TODO
Base class for accelerators.
Definition: Accelerator.h:27
BBox globalBox
Global bounding box.
Definition: Accelerator.h:103
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
bool IntersectP(vec3 rayPos, vec3 rayDir, decimal *hitt0=NULL, decimal *hitt1=NULL) const
Compute the intersection between a Ray and this BBox.
Definition: BBox.h:243
vec3 pMin
Lower point of the BBox.
Definition: BBox.h:35
int MaximumExtend() const
Return the index of the dominant direction (maximal dimension). Index is 0 for x, 1 for y,...
Definition: BBox.h:218
int realMaxProfondeur
Real max depth.
std::vector< BBox > tableBox
Bounding boxes list of the tree.
float emptyBonus
Parameter for best splitting.
virtual decimal traverse(Ray *r, std::list< Intersection > &result) const
Run this accelerator.
void generateSAHKdTree(int currentProfondeur, BBox &localBox, TaBRecBoundEdge *edges[3], unsigned int nbPrims, unsigned int *prims)
Generate the tree with SAH (Surface Area Heuristic) method.
Ray r
Unused attribute.
int maxPrimPerLeaf
Maximal primitives per leaf.
virtual ~KdtreeAccelerator()
Destructor.
float isectCost
Parameter for best splitting.
KdtreeAccelerator(std::vector< Shape * > *_initialMesh=NULL, BBox _globalBox=BBox())
Constructor.
std::vector< Shape * > * initialMesh
Pointer to the mesh.
float traversalCost
Parameter for best splitting.
int maxProfondeur
Maximal depth.
bool alreadyFail
Unused attribute.
virtual bool build()
Build this accelerator.
void generateMidKdTree(int currentProfondeur, BBox &localBox, unsigned int nbPrims, unsigned int *prims)
Generate the tree by middle subdivision.
void print()
Print the tree (not implemented yet)
unsigned int nbFail
Unused attribute.
std::vector< InfoPrim > tablePrimitive
List of primitives and their bounding box.
bool trace
Unused attribute.
std::vector< KDNode > tableNode
: 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
vec3 getPosition() const
Return starting point ray.
Definition: Ray.h:356
vec3 getDirection() const
Return direction of the ray.
Definition: Ray.h:346
base class for shapes (Cylindre, Mesh, Sphere, Triangle,...)
Definition: Shape.h:57
virtual bool getIntersection(Ray &ray, Intersection &inter)
Get the Intersection between a ray and this shape.
Definition: Shape.h:109
BBox getBBox()
Return the bounding box.
Definition: Shape.h:117
float decimal
Definition: mathlib.h:45
base_vec3< decimal > vec3
Definition: mathlib.h:381
KdToDo struct.
KDNode * node
Intersection struct.
Definition: Shape.h:46
decimal t
Definition: Shape.h:48
unsigned int indexPrim
Node structure (optimized to be stored on 2 bytes)
unsigned int AboveChild()
Return second child.
int flag
0 : node, x axis, 1 : node, y axis, 2 : node, z axis, 3 : leaf. 2 bits used
unsigned int * prims
Leaf : array containing primitives indexes.
int getAxe()
Return the axis number of the separator plane (node)
unsigned int nbPrims
Leaf : number of primitives. Use 30 bits integer.
unsigned int secondChild
Node : the first child is just after current node, second one is further.
void createLeaf(unsigned int _nbPrims, unsigned int _firstIndex, unsigned int *_prims)
Leaf initialization.
unsigned int firstIndex
Leaf : index of the first primitive in the list.
float getAxeValue()
Return the axis value of the separator plane (node)
bool isLeaf()
Return true if the node is a leaf.
void createNode(int axis, float _split, unsigned int nextChild)
Node initialization.
unsigned int getFirstIndex()
Return the index of the first node primitive (all)
float split
Node : separator axis value.
unsigned int * getPrims()
Get the primitives.
unsigned int getNbPrimitives()
Return primitives number.