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