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