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