1+ let buildnodes = [ ] ;
2+ let leftWorklist = [ ] ;
3+ let rightWorklist = [ ] ;
4+ let nodesUsed = 1 ;
5+ let aabb_array_copy ;
6+ let k , value , side0 , side1 , side2 ;
7+ let bestSplit , goodSplit , okaySplit ;
8+ let bestAxis , goodAxis , okayAxis ;
9+ let currentMinCorner = new THREE . Vector3 ( ) ;
10+ let currentMaxCorner = new THREE . Vector3 ( ) ;
11+ let testMinCorner = new THREE . Vector3 ( ) ;
12+ let testMaxCorner = new THREE . Vector3 ( ) ;
13+ let testCentroid = new THREE . Vector3 ( ) ;
14+ let spatialAverage = new THREE . Vector3 ( ) ;
15+
16+
17+ function BVH_Node ( )
18+ {
19+ this . minCorner = new THREE . Vector3 ( ) ;
20+ this . maxCorner = new THREE . Vector3 ( ) ;
21+ this . primitiveCount = 0 ;
22+ this . leafOrChild_ID = 0 ;
23+ }
24+
25+
26+ function BVH_Create_Node ( worklist , nodeIndex )
27+ {
28+ // re-initialize bounding box extents
29+ currentMinCorner . set ( Infinity , Infinity , Infinity ) ;
30+ currentMaxCorner . set ( - Infinity , - Infinity , - Infinity ) ;
31+
32+ if ( worklist . length == 1 )
33+ {
34+ // if we're down to 1 primitive aabb, quickly create a leaf node and return.
35+ k = worklist [ 0 ] ;
36+ // create leaf node
37+ let leafNode = buildnodes [ nodeIndex ] ;
38+ leafNode . minCorner . set ( aabb_array_copy [ 9 * k + 0 ] , aabb_array_copy [ 9 * k + 1 ] , aabb_array_copy [ 9 * k + 2 ] ) ;
39+ leafNode . maxCorner . set ( aabb_array_copy [ 9 * k + 3 ] , aabb_array_copy [ 9 * k + 4 ] , aabb_array_copy [ 9 * k + 5 ] ) ;
40+ leafNode . primitiveCount = 1 ;
41+ leafNode . leafOrChild_ID = k ;
42+ return ;
43+ } // end if (worklist.length == 1)
44+
45+ else if ( worklist . length > 1 )
46+ {
47+ // this is where the real work happens: we must sort an arbitrary number of primitives (usually triangles).
48+ // to get a balanced tree, we hope for about half to be placed in left child, other half to be placed in right child.
49+
50+ // construct/grow bounding box around all of the current worklist's primitives
51+ for ( let i = 0 ; i < worklist . length ; i ++ )
52+ {
53+ k = worklist [ i ] ;
54+ testMinCorner . set ( aabb_array_copy [ 9 * k + 0 ] , aabb_array_copy [ 9 * k + 1 ] , aabb_array_copy [ 9 * k + 2 ] ) ;
55+ testMaxCorner . set ( aabb_array_copy [ 9 * k + 3 ] , aabb_array_copy [ 9 * k + 4 ] , aabb_array_copy [ 9 * k + 5 ] ) ;
56+ currentMinCorner . min ( testMinCorner ) ;
57+ currentMaxCorner . max ( testMaxCorner ) ;
58+ }
59+
60+ // create an inner node to represent this newly grown bounding box
61+ let innerNode = buildnodes [ nodeIndex ] ;
62+ // this inner node will spawn 2 children: a leftChild and a rightChild
63+ nodesUsed ++ ; // 'nodesUsed' now matches the index of the leftChild (the 1st child to be created)
64+ innerNode . minCorner . copy ( currentMinCorner ) ;
65+ innerNode . maxCorner . copy ( currentMaxCorner ) ;
66+ innerNode . primitiveCount = 0 ; //worklist.length;
67+ innerNode . leafOrChild_ID = nodesUsed ; // 'innerNode.leafOrChild_ID' now also points to the index of the leftChild
68+ // we must now incrememnt the nodesUsed counter, because a 2nd child (the rightChild) will also be created
69+ nodesUsed ++ ; // 'nodesUsed' now matches the index of the rightChild (the 2nd child to be created)
70+
71+ let leftChildIndex = innerNode . leafOrChild_ID ;
72+ let rightChildIndex = innerNode . leafOrChild_ID + 1 ; // the rightChild's index is always the leftChild's index + 1
73+
74+ // Begin Spatial Median split plane determination and primitive sorting
75+
76+ side0 = currentMaxCorner . x - currentMinCorner . x ; // length along X-axis
77+ side1 = currentMaxCorner . y - currentMinCorner . y ; // length along Y-axis
78+ side2 = currentMaxCorner . z - currentMinCorner . z ; // length along Z-axis
79+
80+ // calculate the middle point of this newly-grown bounding box (aka the 'spatial median')
81+ // this simply uses the spatial average of the longest box extent to determine the split plane,
82+ // which is very fast and results in a fair quality, fairly balanced binary tree structure
83+ spatialAverage . copy ( currentMinCorner ) . add ( currentMaxCorner ) . multiplyScalar ( 0.5 ) ;
84+
85+ // initialize variables
86+ bestAxis = 0 ; goodAxis = 1 ; okayAxis = 2 ;
87+ bestSplit = spatialAverage . x ; goodSplit = spatialAverage . y ; okaySplit = spatialAverage . z ;
88+
89+ // determine the longest extent of the box, and start with that as splitting dimension
90+ if ( side0 >= side1 && side0 >= side2 )
91+ {
92+ bestAxis = 0 ;
93+ bestSplit = spatialAverage . x ;
94+ if ( side1 >= side2 )
95+ {
96+ goodAxis = 1 ;
97+ goodSplit = spatialAverage . y ;
98+ okayAxis = 2 ;
99+ okaySplit = spatialAverage . z ;
100+ }
101+ else
102+ {
103+ goodAxis = 2 ;
104+ goodSplit = spatialAverage . z ;
105+ okayAxis = 1 ;
106+ okaySplit = spatialAverage . y ;
107+ }
108+ }
109+ else if ( side1 >= side0 && side1 >= side2 )
110+ {
111+ bestAxis = 1 ;
112+ bestSplit = spatialAverage . y ;
113+ if ( side0 >= side2 )
114+ {
115+ goodAxis = 0 ;
116+ goodSplit = spatialAverage . x ;
117+ okayAxis = 2 ;
118+ okaySplit = spatialAverage . z ;
119+ }
120+ else
121+ {
122+ goodAxis = 2 ;
123+ goodSplit = spatialAverage . z ;
124+ okayAxis = 0 ;
125+ okaySplit = spatialAverage . x ;
126+ }
127+ }
128+ else // if (side2 >= side0 && side2 >= side1)
129+ {
130+ bestAxis = 2 ;
131+ bestSplit = spatialAverage . z ;
132+ if ( side0 >= side1 )
133+ {
134+ goodAxis = 0 ;
135+ goodSplit = spatialAverage . x ;
136+ okayAxis = 1 ;
137+ okaySplit = spatialAverage . y ;
138+ }
139+ else
140+ {
141+ goodAxis = 1 ;
142+ goodSplit = spatialAverage . y ;
143+ okayAxis = 0 ;
144+ okaySplit = spatialAverage . x ;
145+ }
146+ }
147+
148+
149+ // try best axis first, then try the other two if necessary
150+ for ( let axis = 0 ; axis < 3 ; axis ++ )
151+ {
152+ // distribute the triangle AABBs in either the left child or right child
153+ leftWorklist = [ ] ;
154+ rightWorklist = [ ] ;
155+
156+ // this loop is to count how many elements we will need for the left branch and the right branch
157+ for ( let i = 0 ; i < worklist . length ; i ++ )
158+ {
159+ k = worklist [ i ] ;
160+ testCentroid . set ( aabb_array_copy [ 9 * k + 6 ] , aabb_array_copy [ 9 * k + 7 ] , aabb_array_copy [ 9 * k + 8 ] ) ;
161+
162+ // get bbox center
163+ if ( bestAxis == 0 ) value = testCentroid . x ; // X-axis
164+ else if ( bestAxis == 1 ) value = testCentroid . y ; // Y-axis
165+ else value = testCentroid . z ; // Z-axis
166+
167+ if ( value < bestSplit )
168+ leftWorklist . push ( k ) ;
169+ else
170+ rightWorklist . push ( k ) ;
171+
172+ }
173+
174+ if ( leftWorklist . length > 0 && rightWorklist . length > 0 )
175+ {
176+ break ; // success, move on to the next part
177+ }
178+ else // if (leftWorklist.length == 0 || rightWorklist.length == 0)
179+ {
180+ // try another axis
181+ if ( axis == 0 )
182+ {
183+ bestAxis = goodAxis ;
184+ bestSplit = goodSplit ;
185+ }
186+ else if ( axis == 1 )
187+ {
188+ bestAxis = okayAxis ;
189+ bestSplit = okaySplit ;
190+ }
191+ }
192+
193+ } // end for (let axis = 0; axis < 3; axis++)
194+
195+
196+ // if the below if statement is true, then we have successfully sorted the primitive(triangle) AABBs
197+ if ( leftWorklist . length > 0 && rightWorklist . length > 0 )
198+ {
199+ let leftWorklistCopy = new Uint32Array ( leftWorklist ) ;
200+ let rightWorklistCopy = new Uint32Array ( rightWorklist ) ;
201+ // recurse
202+ BVH_Create_Node ( leftWorklistCopy , leftChildIndex ) ;
203+ BVH_Create_Node ( rightWorklistCopy , rightChildIndex ) ;
204+ }
205+
206+ else //if (leftWorklist.length == 0 || rightWorklist.length == 0)
207+ {
208+ // if we reached this point, the builder failed to find a decent splitting plane axis, so
209+ // we try another strategy to populate the current leftWorkLists and rightWorklists.
210+ leftWorklist = [ ] ;
211+ rightWorklist = [ ] ;
212+
213+ spatialAverage . set ( 0 , 0 , 0 ) ;
214+
215+ // this loop is to count how many elements we will need for the left branch and the right branch
216+ for ( let i = 0 ; i < worklist . length ; i ++ )
217+ {
218+ k = worklist [ i ] ;
219+ testCentroid . set ( aabb_array_copy [ 9 * k + 6 ] , aabb_array_copy [ 9 * k + 7 ] , aabb_array_copy [ 9 * k + 8 ] ) ;
220+ spatialAverage . add ( testCentroid ) ;
221+ }
222+ spatialAverage . multiplyScalar ( 1 / worklist . length ) ;
223+
224+ for ( let i = 0 ; i < worklist . length ; i ++ )
225+ {
226+ k = worklist [ i ] ;
227+ testCentroid . set ( aabb_array_copy [ 9 * k + 6 ] , aabb_array_copy [ 9 * k + 7 ] , aabb_array_copy [ 9 * k + 8 ] ) ;
228+
229+ if ( testCentroid . x != spatialAverage . x )
230+ {
231+ if ( testCentroid . x < spatialAverage . x )
232+ leftWorklist . push ( k ) ;
233+ else
234+ rightWorklist . push ( k ) ;
235+ }
236+ else if ( testCentroid . y != spatialAverage . y )
237+ {
238+ if ( testCentroid . y < spatialAverage . y )
239+ leftWorklist . push ( k ) ;
240+ else
241+ rightWorklist . push ( k ) ;
242+ }
243+ else if ( testCentroid . z != spatialAverage . z )
244+ {
245+ if ( testCentroid . z < spatialAverage . z )
246+ leftWorklist . push ( k ) ;
247+ else
248+ rightWorklist . push ( k ) ;
249+ }
250+ }
251+
252+ let leftWorklistCopy = new Uint32Array ( leftWorklist ) ;
253+ let rightWorklistCopy = new Uint32Array ( rightWorklist ) ;
254+ // recurse
255+ BVH_Create_Node ( leftWorklistCopy , leftChildIndex ) ;
256+ BVH_Create_Node ( rightWorklistCopy , rightChildIndex ) ;
257+
258+ } // end else //if (leftWorklist.length == 0 || rightWorklist.length == 0)
259+
260+ if ( leftWorklist . length == 0 || rightWorklist . length == 0 )
261+ {
262+ //console.log("entered fail case, worklist item count: " + worklist.length);
263+ // if we reached this point, the builder failed to find a decent splitting plane axis, so
264+ // manually populate the current leftWorkLists and rightWorklists.
265+ leftWorklist = [ ] ;
266+ rightWorklist = [ ] ;
267+
268+ for ( let i = 0 ; i < worklist . length ; i ++ )
269+ {
270+ k = worklist [ i ] ;
271+ if ( i % 2 != 0 )
272+ leftWorklist . push ( k ) ;
273+ else
274+ rightWorklist . push ( k ) ;
275+ }
276+
277+ let leftWorklistCopy = new Uint32Array ( leftWorklist ) ;
278+ let rightWorklistCopy = new Uint32Array ( rightWorklist ) ;
279+ // recurse
280+ BVH_Create_Node ( leftWorklistCopy , leftChildIndex ) ;
281+ BVH_Create_Node ( rightWorklistCopy , rightChildIndex ) ;
282+ } // end if (leftWorklist.length == 0 || rightWorklist.length == 0)
283+
284+ } // end if (worklist.length > 1)
285+
286+
287+ return ; // finished
288+
289+ } // end function BVH_Create_Node(worklist, nodeIndex)
290+
291+
292+
293+ function BVH_QuickBuild ( primitiveAABB_IndexList , aabb_array )
294+ {
295+ // the user of this builder has to supply the aabb_array. Then we make a copy of the aabb_array,
296+ // so that this builder can use it, while referring to it with a generic variable name (like 'aabb_array_copy').
297+ // This allows users to build multiple different BVHs for all scene models, while using the same BVH_QuickBuild() function
298+ aabb_array_copy = new Float32Array ( aabb_array ) ;
299+
300+ // the 'primitiveAABB_IndexList' is a raw list of integer numbers in simple sequential order [0,1,2,3,4,5,6,..N-1](one for every primitive),
301+ // where each number refers to a unique triangle (or other type of primitive) from the model's unordered 'triangle soup'(or 'primitive soup').
302+
303+ // now build root node (root nodeIndex = 0), and then recursively build the rest of the binary tree
304+ BVH_Create_Node ( primitiveAABB_IndexList , 0 ) ;
305+
306+ let nx8 = 0 ;
307+ // Copy the buildnodes array into the aabb_array
308+ for ( let n = 0 ; n < buildnodes . length ; n ++ )
309+ {
310+ nx8 = n * 8 ;
311+ // slot 0
312+ aabb_array [ nx8 + 0 ] = buildnodes [ n ] . minCorner . x ; // r or x component
313+ aabb_array [ nx8 + 1 ] = buildnodes [ n ] . minCorner . y ; // g or y component
314+ aabb_array [ nx8 + 2 ] = buildnodes [ n ] . minCorner . z ; // b or z component
315+ aabb_array [ nx8 + 3 ] = buildnodes [ n ] . maxCorner . x ; // a or w component
316+
317+ // slot 1
318+ aabb_array [ nx8 + 4 ] = buildnodes [ n ] . maxCorner . y ; // r or x component
319+ aabb_array [ nx8 + 5 ] = buildnodes [ n ] . maxCorner . z ; // g or y component
320+ aabb_array [ nx8 + 6 ] = buildnodes [ n ] . primitiveCount ; // b or z component
321+ aabb_array [ nx8 + 7 ] = buildnodes [ n ] . leafOrChild_ID ; // a or w component
322+ }
323+
324+ } // end function BVH_QuickBuild(primitiveAABB_IndexList, aabb_array)
0 commit comments