TrinityCore
BoundingIntervalHierarchy.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef _BIH_H
19#define _BIH_H
20
21#include <G3D/Vector3.h>
22#include <G3D/Ray.h>
23#include <G3D/AABox.h>
24
25#include "Define.h"
26
27#include <stdexcept>
28#include <vector>
29#include <algorithm>
30#include <limits>
31#include "string.h"
32
33#define MAX_STACK_SIZE 64
34
35// https://stackoverflow.com/a/4328396
36
37static inline uint32 floatToRawIntBits(float f)
38{
39 static_assert(sizeof(float) == sizeof(uint32), "Size of uint32 and float must be equal for this to work");
40 uint32 ret;
41 memcpy(&ret, &f, sizeof(float));
42 return ret;
43}
44
45static inline float intBitsToFloat(uint32 i)
46{
47 static_assert(sizeof(float) == sizeof(uint32), "Size of uint32 and float must be equal for this to work");
48 float ret;
49 memcpy(&ret, &i, sizeof(uint32));
50 return ret;
51}
52
53struct AABound
54{
55 G3D::Vector3 lo, hi;
56};
57
66{
67 private:
69 {
70 tree.clear();
71 objects.clear();
72 bounds = G3D::AABox::empty();
73 // create space for the first node
74 tree.push_back(3u << 30u); // dummy leaf
75 tree.insert(tree.end(), 2, 0);
76 }
77 public:
78 BIH() { init_empty(); }
79 template <class BoundsFunc, class PrimArray>
80 void build(PrimArray const& primitives, BoundsFunc& getBounds, uint32 leafSize = 3, bool printStats = false)
81 {
82 if (primitives.size() == 0)
83 {
84 init_empty();
85 return;
86 }
87
88 buildData dat;
89 dat.maxPrims = leafSize;
90 dat.numPrims = uint32(primitives.size());
91 dat.indices = new uint32[dat.numPrims];
92 dat.primBound = new G3D::AABox[dat.numPrims];
93 getBounds(primitives[0], bounds);
94 for (uint32 i=0; i<dat.numPrims; ++i)
95 {
96 dat.indices[i] = i;
97 getBounds(primitives[i], dat.primBound[i]);
98 bounds.merge(dat.primBound[i]);
99 }
100 std::vector<uint32> tempTree;
101 BuildStats stats;
102 buildHierarchy(tempTree, dat, stats);
103 if (printStats)
104 stats.printStats();
105
106 objects.resize(dat.numPrims);
107 for (uint32 i=0; i<dat.numPrims; ++i)
108 objects[i] = dat.indices[i];
109 //nObjects = dat.numPrims;
110 tree = tempTree;
111 delete[] dat.primBound;
112 delete[] dat.indices;
113 }
114 uint32 primCount() const { return uint32(objects.size()); }
115
116 template<typename RayCallback>
117 void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst = false) const
118 {
119 float intervalMin = -1.f;
120 float intervalMax = -1.f;
121 G3D::Vector3 const& org = r.origin();
122 G3D::Vector3 const& dir = r.direction();
123 G3D::Vector3 const& invDir = r.invDirection();
124 for (int i=0; i<3; ++i)
125 {
126 if (G3D::fuzzyNe(dir[i], 0.0f))
127 {
128 float t1 = (bounds.low()[i] - org[i]) * invDir[i];
129 float t2 = (bounds.high()[i] - org[i]) * invDir[i];
130 if (t1 > t2)
131 std::swap(t1, t2);
132 if (t1 > intervalMin)
133 intervalMin = t1;
134 if (t2 < intervalMax || intervalMax < 0.f)
135 intervalMax = t2;
136 // intervalMax can only become smaller for other axis,
137 // and intervalMin only larger respectively, so stop early
138 if (intervalMax <= 0 || intervalMin >= maxDist)
139 return;
140 }
141 }
142
143 if (intervalMin > intervalMax)
144 return;
145 intervalMin = std::max(intervalMin, 0.f);
146 intervalMax = std::min(intervalMax, maxDist);
147
148 uint32 offsetFront[3];
149 uint32 offsetBack[3];
150 uint32 offsetFront3[3];
151 uint32 offsetBack3[3];
152 // compute custom offsets from direction sign bit
153
154 for (int i=0; i<3; ++i)
155 {
156 offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
157 offsetBack[i] = offsetFront[i] ^ 1;
158 offsetFront3[i] = offsetFront[i] * 3;
159 offsetBack3[i] = offsetBack[i] * 3;
160
161 // avoid always adding 1 during the inner loop
162 ++offsetFront[i];
163 ++offsetBack[i];
164 }
165
167 int stackPos = 0;
168 int node = 0;
169
170 while (true) {
171 while (true)
172 {
173 uint32 tn = tree[node];
174 uint32 axis = (tn & (3 << 30)) >> 30;
175 bool BVH2 = (tn & (1 << 29)) != 0;
176 int offset = tn & ~(7 << 29);
177 if (!BVH2)
178 {
179 if (axis < 3)
180 {
181 // "normal" interior node
182 float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
183 float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
184 // ray passes between clip zones
185 if (tf < intervalMin && tb > intervalMax)
186 break;
187 int back = offset + offsetBack3[axis];
188 node = back;
189 // ray passes through far node only
190 if (tf < intervalMin) {
191 intervalMin = (tb >= intervalMin) ? tb : intervalMin;
192 continue;
193 }
194 node = offset + offsetFront3[axis]; // front
195 // ray passes through near node only
196 if (tb > intervalMax) {
197 intervalMax = (tf <= intervalMax) ? tf : intervalMax;
198 continue;
199 }
200 // ray passes through both nodes
201 // push back node
202 stack[stackPos].node = back;
203 stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
204 stack[stackPos].tfar = intervalMax;
205 stackPos++;
206 // update ray interval for front node
207 intervalMax = (tf <= intervalMax) ? tf : intervalMax;
208 continue;
209 }
210 else
211 {
212 // leaf - test some objects
213 int n = tree[node + 1];
214 while (n > 0) {
215 bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirst);
216 if (stopAtFirst && hit) return;
217 --n;
218 ++offset;
219 }
220 break;
221 }
222 }
223 else
224 {
225 if (axis>2)
226 return; // should not happen
227 float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
228 float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
229 node = offset;
230 intervalMin = (tf >= intervalMin) ? tf : intervalMin;
231 intervalMax = (tb <= intervalMax) ? tb : intervalMax;
232 if (intervalMin > intervalMax)
233 break;
234 continue;
235 }
236 } // traversal loop
237 do
238 {
239 // stack is empty?
240 if (stackPos == 0)
241 return;
242 // move back up the stack
243 stackPos--;
244 intervalMin = stack[stackPos].tnear;
245 if (maxDist < intervalMin)
246 continue;
247 node = stack[stackPos].node;
248 intervalMax = stack[stackPos].tfar;
249 break;
250 } while (true);
251 }
252 }
253
254 template<typename IsectCallback>
255 void intersectPoint(const G3D::Vector3 &p, IsectCallback& intersectCallback) const
256 {
257 if (!bounds.contains(p))
258 return;
259
261 int stackPos = 0;
262 int node = 0;
263
264 while (true) {
265 while (true)
266 {
267 uint32 tn = tree[node];
268 uint32 axis = (tn & (3 << 30)) >> 30;
269 bool BVH2 = (tn & (1 << 29)) != 0;
270 int offset = tn & ~(7 << 29);
271 if (!BVH2)
272 {
273 if (axis < 3)
274 {
275 // "normal" interior node
276 float tl = intBitsToFloat(tree[node + 1]);
277 float tr = intBitsToFloat(tree[node + 2]);
278 // point is between clip zones
279 if (tl < p[axis] && tr > p[axis])
280 break;
281 int right = offset + 3;
282 node = right;
283 // point is in right node only
284 if (tl < p[axis]) {
285 continue;
286 }
287 node = offset; // left
288 // point is in left node only
289 if (tr > p[axis]) {
290 continue;
291 }
292 // point is in both nodes
293 // push back right node
294 stack[stackPos].node = right;
295 stackPos++;
296 continue;
297 }
298 else
299 {
300 // leaf - test some objects
301 int n = tree[node + 1];
302 while (n > 0) {
303 intersectCallback(p, objects[offset]); // !!!
304 --n;
305 ++offset;
306 }
307 break;
308 }
309 }
310 else // BVH2 node (empty space cut off left and right)
311 {
312 if (axis>2)
313 return; // should not happen
314 float tl = intBitsToFloat(tree[node + 1]);
315 float tr = intBitsToFloat(tree[node + 2]);
316 node = offset;
317 if (tl > p[axis] || tr < p[axis])
318 break;
319 continue;
320 }
321 } // traversal loop
322
323 // stack is empty?
324 if (stackPos == 0)
325 return;
326 // move back up the stack
327 stackPos--;
328 node = stack[stackPos].node;
329 }
330 }
331
332 bool writeToFile(FILE* wf) const;
333 bool readFromFile(FILE* rf);
334
335 protected:
336 std::vector<uint32> tree;
337 std::vector<uint32> objects;
338 G3D::AABox bounds;
339
341 {
343 G3D::AABox *primBound;
346 };
348 {
350 float tnear;
351 float tfar;
352 };
353
355 {
356 private:
365 int numLeavesN[6];
367
368 public:
370 numNodes(0), numLeaves(0), sumObjects(0), minObjects(0x0FFFFFFF),
371 maxObjects(0xFFFFFFFF), sumDepth(0), minDepth(0x0FFFFFFF),
372 maxDepth(0xFFFFFFFF), numBVH2(0)
373 {
374 for (int i=0; i<6; ++i) numLeavesN[i] = 0;
375 }
376
377 void updateInner() { numNodes++; }
378 void updateBVH2() { numBVH2++; }
379 void updateLeaf(int depth, int n);
380 void printStats();
381 };
382
383 void buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats);
384
385 void createNode(std::vector<uint32> &tempTree, int nodeIndex, uint32 left, uint32 right) const
386 {
387 // write leaf node
388 tempTree[nodeIndex + 0] = (3 << 30) | left;
389 tempTree[nodeIndex + 1] = right - left + 1;
390 }
391
392 void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
393};
394
395#endif // _BIH_H
static float intBitsToFloat(uint32 i)
#define MAX_STACK_SIZE
static uint32 floatToRawIntBits(float f)
#define TC_COMMON_API
Definition: Define.h:99
uint32_t uint32
Definition: Define.h:142
void build(PrimArray const &primitives, BoundsFunc &getBounds, uint32 leafSize=3, bool printStats=false)
void createNode(std::vector< uint32 > &tempTree, int nodeIndex, uint32 left, uint32 right) const
std::vector< uint32 > objects
std::vector< uint32 > tree
uint32 primCount() const
G3D::AABox bounds
void intersectPoint(const G3D::Vector3 &p, IsectCallback &intersectCallback) const
void intersectRay(const G3D::Ray &r, RayCallback &intersectCallback, float &maxDist, bool stopAtFirst=false) const