rayzee 7.31.1 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -0
- package/dist/rayzee.es.js +6728 -4735
- package/dist/rayzee.umd.js +121 -73
- package/package.json +2 -2
- package/src/EngineDefaults.js +168 -13
- package/src/EngineIssues.js +5 -0
- package/src/PathTracerApp.js +151 -25
- package/src/Processor/ArchiveReader.js +538 -0
- package/src/Processor/AssetLoader.js +265 -23
- package/src/Processor/BVHBuilder.js +48 -32
- package/src/Processor/BVHRefitter.js +244 -99
- package/src/Processor/ChunkedRecords.js +302 -0
- package/src/Processor/EmissiveTriangleBuilder.js +48 -21
- package/src/Processor/GeometryExtractor.js +580 -93
- package/src/Processor/HostMemory.js +222 -0
- package/src/Processor/InstanceTable.js +548 -97
- package/src/Processor/PBRT/EqualAreaOctahedral.js +32 -8
- package/src/Processor/PBRT/PBRTCurves.js +249 -0
- package/src/Processor/PBRT/PBRTMath.js +19 -0
- package/src/Processor/PBRT/PBRTParser.js +187 -41
- package/src/Processor/PBRT/PBRTSceneBuilder.js +586 -42
- package/src/Processor/PBRT/PBRTTokenizer.js +205 -65
- package/src/Processor/PBRT/index.js +257 -50
- package/src/Processor/PackedRayBuffer.js +29 -18
- package/src/Processor/ParallelBVHBuilder.js +9 -8
- package/src/Processor/QueueManager.js +5 -3
- package/src/Processor/SceneProcessor.js +1046 -365
- package/src/Processor/TLASBuilder.js +546 -201
- package/src/Processor/TextureCreator.js +11 -9
- package/src/Processor/VRAMTracker.js +12 -3
- package/src/Processor/Workers/BVHRefitWorker.js +16 -11
- package/src/Processor/Workers/BVHWorker.js +12 -12
- package/src/Processor/Workers/TLASWorker.js +37 -0
- package/src/Processor/utils.js +8 -20
- package/src/Stages/NormalDepth.js +6 -2
- package/src/Stages/PathTracer.js +5 -5
- package/src/Stages/PathTracerStage.js +163 -41
- package/src/TSL/BVHTraversal.js +208 -90
- package/src/TSL/Common.js +135 -2
- package/src/TSL/Displacement.js +21 -11
- package/src/TSL/EmissiveSampling.js +49 -25
- package/src/TSL/ExtendKernel.js +3 -2
- package/src/TSL/LightBVHSampling.js +7 -4
- package/src/TSL/LightsDirect.js +30 -29
- package/src/TSL/ShadeKernel.js +8 -6
- package/src/TSL/SortGlobalKernels.js +3 -4
- package/src/TSL/Struct.js +3 -0
- package/src/TSL/TextureSampling.js +24 -9
- package/src/TSL/patches.js +149 -84
- package/src/index.js +3 -0
- package/src/managers/AnimationManager.js +86 -76
- package/src/managers/MaterialDataManager.js +29 -23
- package/src/managers/TransformManager.js +9 -192
|
@@ -11,19 +11,155 @@
|
|
|
11
11
|
// Inline copy of layout constants (source of truth: EngineDefaults.js).
|
|
12
12
|
// Cannot import because this runs inside Web Workers where window is not defined.
|
|
13
13
|
const TRIANGLE_DATA_LAYOUT = {
|
|
14
|
-
FLOATS_PER_TRIANGLE:
|
|
14
|
+
FLOATS_PER_TRIANGLE: 20,
|
|
15
15
|
POSITION_A_OFFSET: 0,
|
|
16
16
|
POSITION_B_OFFSET: 4,
|
|
17
17
|
POSITION_C_OFFSET: 8,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
NORMAL_A_PACKED_OFFSET: 3,
|
|
19
|
+
NORMAL_B_PACKED_OFFSET: 7,
|
|
20
|
+
NORMAL_C_PACKED_OFFSET: 11,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
|
|
24
|
+
|
|
25
|
+
// The record buffer is uint; positions are f32 in it. Views share the memory, no copy.
|
|
26
|
+
const floatView = ( data ) => data instanceof Float32Array
|
|
27
|
+
? data : new Float32Array( data.buffer, data.byteOffset, data.length );
|
|
28
|
+
const uintView = ( data ) => data instanceof Uint32Array
|
|
29
|
+
? data : new Uint32Array( data.buffer, data.byteOffset, data.length );
|
|
30
|
+
|
|
31
|
+
// Triangles arrive either as one flat record array or, past the ~2 GB array cap, as a
|
|
32
|
+
// ChunkedRecords. Both are handled by resolving the chunk once per triangle; the flat case
|
|
33
|
+
// keeps a constant chunk and plain `index * FPT` arithmetic.
|
|
34
|
+
// BVH nodes get the same treatment: one flat Float32Array, or a ChunkedRecords past the cap.
|
|
35
|
+
const nodeAccess = ( bvhData ) => {
|
|
36
|
+
|
|
37
|
+
const chunked = bvhData && bvhData.chunks ? bvhData : null;
|
|
38
|
+
return {
|
|
39
|
+
chunked,
|
|
40
|
+
f: chunked ? bvhData : bvhData,
|
|
41
|
+
idx: chunked ? bvhData.viewAs( Uint32Array ) : uintView( bvhData ),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Resolve one node: returns nothing, callers use nodeF/nodeIdx/nodeBase below.
|
|
47
|
+
const nodeF = ( acc, n ) => ( acc.chunked ? acc.f.chunkFor( n ) : acc.f );
|
|
48
|
+
const nodeIdx = ( acc, n ) => ( acc.chunked ? acc.idx.chunkFor( n ) : acc.idx );
|
|
49
|
+
const nodeBase = ( acc, n ) => ( acc.chunked ? acc.chunked.baseOf( n ) : n * FLOATS_PER_NODE );
|
|
50
|
+
|
|
51
|
+
const triAccess = ( triangleData ) => {
|
|
52
|
+
|
|
53
|
+
const chunked = triangleData && triangleData.chunks ? triangleData : null;
|
|
54
|
+
return {
|
|
55
|
+
chunked,
|
|
56
|
+
f: chunked ? chunked.viewAs( Float32Array ) : floatView( triangleData ),
|
|
57
|
+
u: chunked ? triangleData : uintView( triangleData ),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Octahedral snorm16 pair, matching packNormalOct in EngineDefaults (not importable here).
|
|
63
|
+
function packNormalOct( x, y, z ) {
|
|
64
|
+
|
|
65
|
+
const len = Math.sqrt( x * x + y * y + z * z );
|
|
66
|
+
if ( len > 0 ) {
|
|
67
|
+
|
|
68
|
+
x /= len; y /= len; z /= len;
|
|
69
|
+
|
|
70
|
+
} else {
|
|
71
|
+
|
|
72
|
+
x = 0; y = 0; z = 1;
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const sum = Math.abs( x ) + Math.abs( y ) + Math.abs( z );
|
|
77
|
+
let u = x / sum, v = y / sum;
|
|
78
|
+
if ( z < 0 ) {
|
|
79
|
+
|
|
80
|
+
const au = u, av = v;
|
|
81
|
+
u = ( 1 - Math.abs( av ) ) * ( au >= 0 ? 1 : - 1 );
|
|
82
|
+
v = ( 1 - Math.abs( au ) ) * ( av >= 0 ? 1 : - 1 );
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const qu = Math.round( Math.min( 1, Math.max( - 1, u ) ) * 32767 ) & 0xffff;
|
|
87
|
+
const qv = Math.round( Math.min( 1, Math.max( - 1, v ) ) * 32767 ) & 0xffff;
|
|
88
|
+
return ( ( qv << 16 ) | qu ) >>> 0;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
24
92
|
const FLOATS_PER_NODE = 16; // 4 vec4s per BVH node
|
|
25
|
-
|
|
26
|
-
|
|
93
|
+
// Inline copies of EngineDefaults.BVH_LEAF_MARKERS — this module also runs inside a worker.
|
|
94
|
+
// Index fields are u32 BIT PATTERNS (exact past 2^24), read through `indexView`.
|
|
95
|
+
const LEAF_MARKER = 0x40000000;
|
|
96
|
+
const BLAS_POINTER_MARKER = 0x40000001;
|
|
97
|
+
|
|
98
|
+
// Relative to the matrix's own magnitude, never an absolute floor — see transformBoundsToWorld.
|
|
99
|
+
const SINGULAR_REL_EPS = 1e-12;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Object-space bounds at `srcOff` through the instance transform stored on TLAS leaf `nodeOff`,
|
|
103
|
+
* written to `dstOff`. Inlined rather than imported: this file also runs inside a worker.
|
|
104
|
+
*/
|
|
105
|
+
function transformBoundsToWorld( bvhData, nodeOff, src, srcOff, dst, dstOff ) {
|
|
106
|
+
|
|
107
|
+
const a0 = bvhData[ nodeOff + 4 ], a1 = bvhData[ nodeOff + 5 ], a2 = bvhData[ nodeOff + 6 ], tx = bvhData[ nodeOff + 7 ];
|
|
108
|
+
const a3 = bvhData[ nodeOff + 8 ], a4 = bvhData[ nodeOff + 9 ], a5 = bvhData[ nodeOff + 10 ], ty = bvhData[ nodeOff + 11 ];
|
|
109
|
+
const a6 = bvhData[ nodeOff + 12 ], a7 = bvhData[ nodeOff + 13 ], a8 = bvhData[ nodeOff + 14 ], tz = bvhData[ nodeOff + 15 ];
|
|
110
|
+
|
|
111
|
+
// Inverse of the 3x3 whose rows are (a0..a2), (a3..a5), (a6..a8).
|
|
112
|
+
const c0x = a4 * a8 - a5 * a7, c0y = a5 * a6 - a3 * a8, c0z = a3 * a7 - a4 * a6;
|
|
113
|
+
const det = a0 * c0x + a1 * c0y + a2 * c0z;
|
|
114
|
+
|
|
115
|
+
const identity = a0 === 1 && a4 === 1 && a8 === 1
|
|
116
|
+
&& a1 === 0 && a2 === 0 && a3 === 0 && a5 === 0 && a6 === 0 && a7 === 0
|
|
117
|
+
&& tx === 0 && ty === 0 && tz === 0;
|
|
118
|
+
|
|
119
|
+
// Moana's ocean is a unit quad scaled to ±1,089,735, so its world-to-object determinant is
|
|
120
|
+
// 7.7e-19 — an absolute floor called that singular and copied the unit quad's bounds straight
|
|
121
|
+
// through as world bounds, collapsing the root AABB on every refit.
|
|
122
|
+
const magnitude = ( Math.abs( a0 ) + Math.abs( a1 ) + Math.abs( a2 ) )
|
|
123
|
+
* ( Math.abs( a3 ) + Math.abs( a4 ) + Math.abs( a5 ) )
|
|
124
|
+
* ( Math.abs( a6 ) + Math.abs( a7 ) + Math.abs( a8 ) );
|
|
125
|
+
|
|
126
|
+
if ( identity || ! Number.isFinite( det ) || Math.abs( det ) <= SINGULAR_REL_EPS * magnitude ) {
|
|
127
|
+
|
|
128
|
+
for ( let i = 0; i < 6; i ++ ) dst[ dstOff + i ] = src[ srcOff + i ];
|
|
129
|
+
return;
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const inv = 1 / det;
|
|
134
|
+
// Columns of the inverse: cross(r1,r2), cross(r2,r0), cross(r0,r1).
|
|
135
|
+
const k0x = c0x * inv, k0y = c0y * inv, k0z = c0z * inv;
|
|
136
|
+
const k1x = ( a2 * a7 - a1 * a8 ) * inv, k1y = ( a0 * a8 - a2 * a6 ) * inv, k1z = ( a1 * a6 - a0 * a7 ) * inv;
|
|
137
|
+
const k2x = ( a1 * a5 - a2 * a4 ) * inv, k2y = ( a2 * a3 - a0 * a5 ) * inv, k2z = ( a0 * a4 - a1 * a3 ) * inv;
|
|
138
|
+
|
|
139
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
140
|
+
let maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;
|
|
141
|
+
|
|
142
|
+
for ( let c = 0; c < 8; c ++ ) {
|
|
143
|
+
|
|
144
|
+
const px = ( c & 1 ? src[ srcOff + 3 ] : src[ srcOff ] ) - tx;
|
|
145
|
+
const py = ( c & 2 ? src[ srcOff + 4 ] : src[ srcOff + 1 ] ) - ty;
|
|
146
|
+
const pz = ( c & 4 ? src[ srcOff + 5 ] : src[ srcOff + 2 ] ) - tz;
|
|
147
|
+
|
|
148
|
+
const wx = k0x * px + k1x * py + k2x * pz;
|
|
149
|
+
const wy = k0y * px + k1y * py + k2y * pz;
|
|
150
|
+
const wz = k0z * px + k1z * py + k2z * pz;
|
|
151
|
+
|
|
152
|
+
if ( wx < minX ) minX = wx; if ( wx > maxX ) maxX = wx;
|
|
153
|
+
if ( wy < minY ) minY = wy; if ( wy > maxY ) maxY = wy;
|
|
154
|
+
if ( wz < minZ ) minZ = wz; if ( wz > maxZ ) maxZ = wz;
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
dst[ dstOff ] = minX; dst[ dstOff + 1 ] = minY; dst[ dstOff + 2 ] = minZ;
|
|
159
|
+
dst[ dstOff + 3 ] = maxX; dst[ dstOff + 4 ] = maxY; dst[ dstOff + 5 ] = maxZ;
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
27
163
|
|
|
28
164
|
export class BVHRefitter {
|
|
29
165
|
|
|
@@ -40,18 +176,22 @@ export class BVHRefitter {
|
|
|
40
176
|
* Update triangle positions in the BVH-reordered triangle array.
|
|
41
177
|
* Iterates in BVH order (sequential writes, random reads) for cache efficiency.
|
|
42
178
|
*
|
|
43
|
-
* @param {
|
|
179
|
+
* @param {Uint32Array} triangleData - BVH-reordered triangle records (mutated in place)
|
|
44
180
|
* @param {Float32Array} newPositions - 9 floats per triangle in ORIGINAL mesh order
|
|
45
181
|
* @param {Uint32Array} bvhToOriginal - Map from BVH-order index to original tri index
|
|
46
182
|
*/
|
|
47
183
|
updateTrianglePositions( triangleData, newPositions, bvhToOriginal ) {
|
|
48
184
|
|
|
49
185
|
const triCount = bvhToOriginal.length;
|
|
186
|
+
const acc = triAccess( triangleData );
|
|
50
187
|
|
|
51
188
|
for ( let bvhIdx = 0; bvhIdx < triCount; bvhIdx ++ ) {
|
|
52
189
|
|
|
53
190
|
const orig = bvhToOriginal[ bvhIdx ];
|
|
54
|
-
|
|
191
|
+
// sequential writes
|
|
192
|
+
const f = acc.chunked ? acc.f.chunkFor( bvhIdx ) : acc.f;
|
|
193
|
+
const u = acc.chunked ? acc.u.chunkFor( bvhIdx ) : acc.u;
|
|
194
|
+
const dstOff = acc.chunked ? acc.chunked.baseOf( bvhIdx ) : bvhIdx * FPT;
|
|
55
195
|
const srcOff = orig * 9;
|
|
56
196
|
|
|
57
197
|
const ax = newPositions[ srcOff ];
|
|
@@ -64,37 +204,30 @@ export class BVHRefitter {
|
|
|
64
204
|
const cy = newPositions[ srcOff + 7 ];
|
|
65
205
|
const cz = newPositions[ srcOff + 8 ];
|
|
66
206
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
207
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ] = ax;
|
|
208
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ] = ay;
|
|
209
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ] = az;
|
|
70
210
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
211
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ] = bx;
|
|
212
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ] = by;
|
|
213
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ] = bz;
|
|
74
214
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
215
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ] = cx;
|
|
216
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ] = cy;
|
|
217
|
+
f[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ] = cz;
|
|
78
218
|
|
|
79
|
-
//
|
|
80
|
-
// Skip sqrt normalization — the path tracer shader normalizes during shading.
|
|
219
|
+
// Face normal from the cross product; packNormalOct normalizes it.
|
|
81
220
|
const abx = bx - ax, aby = by - ay, abz = bz - az;
|
|
82
221
|
const acx = cx - ax, acy = cy - ay, acz = cz - az;
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
triangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 1 ] = ny;
|
|
89
|
-
triangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 2 ] = nz;
|
|
222
|
+
const packed = packNormalOct(
|
|
223
|
+
aby * acz - abz * acy,
|
|
224
|
+
abz * acx - abx * acz,
|
|
225
|
+
abx * acy - aby * acx
|
|
226
|
+
);
|
|
90
227
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
triangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET ] = nx;
|
|
96
|
-
triangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 1 ] = ny;
|
|
97
|
-
triangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 2 ] = nz;
|
|
228
|
+
u[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_PACKED_OFFSET ] = packed;
|
|
229
|
+
u[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_PACKED_OFFSET ] = packed;
|
|
230
|
+
u[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_PACKED_OFFSET ] = packed;
|
|
98
231
|
|
|
99
232
|
}
|
|
100
233
|
|
|
@@ -105,12 +238,14 @@ export class BVHRefitter {
|
|
|
105
238
|
* Same algorithm as refit() but scoped to nodes [startNode, startNode + count).
|
|
106
239
|
*
|
|
107
240
|
* @param {Float32Array} bvhData - Combined BVH array (TLAS + all BLASes)
|
|
108
|
-
* @param {
|
|
241
|
+
* @param {Uint32Array} triangleData - Global triangle records
|
|
109
242
|
* @param {number} startNode - First node index of this BLAS in bvhData
|
|
110
243
|
* @param {number} nodeCount - Number of nodes in this BLAS
|
|
111
244
|
*/
|
|
112
245
|
refitRange( bvhData, triangleData, startNode, nodeCount ) {
|
|
113
246
|
|
|
247
|
+
const acc = triAccess( triangleData );
|
|
248
|
+
|
|
114
249
|
// Grow-only bounds buffer to avoid reallocation on mixed-size BLASes
|
|
115
250
|
if ( nodeCount > this._boundsNodeCount ) {
|
|
116
251
|
|
|
@@ -121,32 +256,37 @@ export class BVHRefitter {
|
|
|
121
256
|
|
|
122
257
|
const bounds = this._bounds;
|
|
123
258
|
const endNode = startNode + nodeCount;
|
|
259
|
+
const nodes = nodeAccess( bvhData );
|
|
124
260
|
|
|
125
261
|
for ( let i = endNode - 1; i >= startNode; i -- ) {
|
|
126
262
|
|
|
127
|
-
const
|
|
263
|
+
const bvhF = nodeF( nodes, i );
|
|
264
|
+
const idx = nodeIdx( nodes, i );
|
|
265
|
+
const o = nodeBase( nodes, i );
|
|
128
266
|
const b = ( i - startNode ) * 6; // bounds indexed relative to BLAS start
|
|
129
267
|
|
|
130
|
-
if (
|
|
268
|
+
if ( idx[ o + 3 ] === LEAF_MARKER ) {
|
|
131
269
|
|
|
132
|
-
const triOffset =
|
|
133
|
-
const triCount =
|
|
270
|
+
const triOffset = idx[ o ];
|
|
271
|
+
const triCount = idx[ o + 1 ];
|
|
134
272
|
|
|
135
273
|
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
136
274
|
let maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;
|
|
137
275
|
|
|
138
276
|
for ( let t = 0; t < triCount; t ++ ) {
|
|
139
277
|
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
278
|
+
const gi = triOffset + t;
|
|
279
|
+
const triFloats = acc.chunked ? acc.f.chunkFor( gi ) : acc.f;
|
|
280
|
+
const tOff = acc.chunked ? acc.chunked.baseOf( gi ) : gi * FPT;
|
|
281
|
+
const ax = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];
|
|
282
|
+
const ay = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];
|
|
283
|
+
const az = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];
|
|
284
|
+
const bx = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];
|
|
285
|
+
const by = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];
|
|
286
|
+
const bz = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];
|
|
287
|
+
const cx = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];
|
|
288
|
+
const cy = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];
|
|
289
|
+
const cz = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];
|
|
150
290
|
|
|
151
291
|
minX = Math.min( minX, ax, bx, cx );
|
|
152
292
|
minY = Math.min( minY, ay, by, cy );
|
|
@@ -167,8 +307,8 @@ export class BVHRefitter {
|
|
|
167
307
|
} else {
|
|
168
308
|
|
|
169
309
|
// Inner node — child indices are absolute, but bounds index relative to startNode
|
|
170
|
-
const leftIdx =
|
|
171
|
-
const rightIdx =
|
|
310
|
+
const leftIdx = idx[ o + 3 ];
|
|
311
|
+
const rightIdx = idx[ o + 7 ];
|
|
172
312
|
const lb = ( leftIdx - startNode ) * 6;
|
|
173
313
|
const rb = ( rightIdx - startNode ) * 6;
|
|
174
314
|
|
|
@@ -186,19 +326,19 @@ export class BVHRefitter {
|
|
|
186
326
|
const rMaxY = bounds[ rb + 4 ];
|
|
187
327
|
const rMaxZ = bounds[ rb + 5 ];
|
|
188
328
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
329
|
+
bvhF[ o ] = lMinX;
|
|
330
|
+
bvhF[ o + 1 ] = lMinY;
|
|
331
|
+
bvhF[ o + 2 ] = lMinZ;
|
|
332
|
+
bvhF[ o + 4 ] = lMaxX;
|
|
333
|
+
bvhF[ o + 5 ] = lMaxY;
|
|
334
|
+
bvhF[ o + 6 ] = lMaxZ;
|
|
195
335
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
336
|
+
bvhF[ o + 8 ] = rMinX;
|
|
337
|
+
bvhF[ o + 9 ] = rMinY;
|
|
338
|
+
bvhF[ o + 10 ] = rMinZ;
|
|
339
|
+
bvhF[ o + 12 ] = rMaxX;
|
|
340
|
+
bvhF[ o + 13 ] = rMaxY;
|
|
341
|
+
bvhF[ o + 14 ] = rMaxZ;
|
|
202
342
|
|
|
203
343
|
bounds[ b ] = Math.min( lMinX, rMinX );
|
|
204
344
|
bounds[ b + 1 ] = Math.min( lMinY, rMinY );
|
|
@@ -219,11 +359,13 @@ export class BVHRefitter {
|
|
|
219
359
|
* indices than parents in pre-order, so reversing processes children first).
|
|
220
360
|
*
|
|
221
361
|
* @param {Float32Array} bvhData - Flat BVH array (mutated in place)
|
|
222
|
-
* @param {
|
|
362
|
+
* @param {Uint32Array} triangleData - Updated triangle records
|
|
223
363
|
* @param {number} nodeCount - Total number of BVH nodes
|
|
224
364
|
*/
|
|
225
365
|
refit( bvhData, triangleData, nodeCount ) {
|
|
226
366
|
|
|
367
|
+
const acc = triAccess( triangleData );
|
|
368
|
+
|
|
227
369
|
// Reuse bounds buffer across frames (reallocate only on scene change)
|
|
228
370
|
if ( nodeCount !== this._boundsNodeCount ) {
|
|
229
371
|
|
|
@@ -233,40 +375,45 @@ export class BVHRefitter {
|
|
|
233
375
|
}
|
|
234
376
|
|
|
235
377
|
const bounds = this._bounds;
|
|
378
|
+
const nodes = nodeAccess( bvhData );
|
|
236
379
|
|
|
237
380
|
// Reverse iteration: bottom-up in pre-order layout
|
|
238
381
|
for ( let i = nodeCount - 1; i >= 0; i -- ) {
|
|
239
382
|
|
|
240
|
-
const
|
|
383
|
+
const bvhF = nodeF( nodes, i );
|
|
384
|
+
const idx = nodeIdx( nodes, i );
|
|
385
|
+
const o = nodeBase( nodes, i );
|
|
241
386
|
const b = i * 6;
|
|
242
387
|
|
|
243
|
-
const marker =
|
|
388
|
+
const marker = idx[ o + 3 ];
|
|
244
389
|
|
|
245
390
|
if ( marker === LEAF_MARKER ) {
|
|
246
391
|
|
|
247
392
|
// Triangle leaf: compute AABB from triangles
|
|
248
|
-
const triOffset =
|
|
249
|
-
const triCount =
|
|
393
|
+
const triOffset = idx[ o ];
|
|
394
|
+
const triCount = idx[ o + 1 ];
|
|
250
395
|
|
|
251
396
|
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
252
397
|
let maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;
|
|
253
398
|
|
|
254
399
|
for ( let t = 0; t < triCount; t ++ ) {
|
|
255
400
|
|
|
256
|
-
const
|
|
401
|
+
const gi = triOffset + t;
|
|
402
|
+
const triFloats = acc.chunked ? acc.f.chunkFor( gi ) : acc.f;
|
|
403
|
+
const tOff = acc.chunked ? acc.chunked.baseOf( gi ) : gi * FPT;
|
|
257
404
|
|
|
258
405
|
// Position A
|
|
259
|
-
const ax =
|
|
260
|
-
const ay =
|
|
261
|
-
const az =
|
|
406
|
+
const ax = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];
|
|
407
|
+
const ay = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];
|
|
408
|
+
const az = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];
|
|
262
409
|
// Position B
|
|
263
|
-
const bx =
|
|
264
|
-
const by =
|
|
265
|
-
const bz =
|
|
410
|
+
const bx = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];
|
|
411
|
+
const by = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];
|
|
412
|
+
const bz = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];
|
|
266
413
|
// Position C
|
|
267
|
-
const cx =
|
|
268
|
-
const cy =
|
|
269
|
-
const cz =
|
|
414
|
+
const cx = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];
|
|
415
|
+
const cy = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];
|
|
416
|
+
const cz = triFloats[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];
|
|
270
417
|
|
|
271
418
|
minX = Math.min( minX, ax, bx, cx );
|
|
272
419
|
minY = Math.min( minY, ay, by, cy );
|
|
@@ -286,21 +433,19 @@ export class BVHRefitter {
|
|
|
286
433
|
|
|
287
434
|
} else if ( marker === BLAS_POINTER_MARKER ) {
|
|
288
435
|
|
|
289
|
-
// BLAS-pointer leaf (TLAS):
|
|
290
|
-
|
|
436
|
+
// BLAS-pointer leaf (TLAS): the BLAS root's bounds are in the instance's own
|
|
437
|
+
// space, and the TLAS sorts on world bounds — so carry the box out through the
|
|
438
|
+
// leaf's transform. Slots 4..15 are the rows of world-to-object; the eight
|
|
439
|
+
// corners go back the other way through its inverse.
|
|
440
|
+
const blasRoot = idx[ o ];
|
|
291
441
|
const br = blasRoot * 6;
|
|
292
|
-
|
|
293
|
-
bounds[ b + 1 ] = bounds[ br + 1 ];
|
|
294
|
-
bounds[ b + 2 ] = bounds[ br + 2 ];
|
|
295
|
-
bounds[ b + 3 ] = bounds[ br + 3 ];
|
|
296
|
-
bounds[ b + 4 ] = bounds[ br + 4 ];
|
|
297
|
-
bounds[ b + 5 ] = bounds[ br + 5 ];
|
|
442
|
+
transformBoundsToWorld( bvhF, o, bounds, br, bounds, b );
|
|
298
443
|
|
|
299
444
|
} else {
|
|
300
445
|
|
|
301
446
|
// Inner node: union children bounds (already computed since we iterate in reverse)
|
|
302
|
-
const leftIdx =
|
|
303
|
-
const rightIdx =
|
|
447
|
+
const leftIdx = idx[ o + 3 ];
|
|
448
|
+
const rightIdx = idx[ o + 7 ];
|
|
304
449
|
const lb = leftIdx * 6;
|
|
305
450
|
const rb = rightIdx * 6;
|
|
306
451
|
|
|
@@ -319,23 +464,23 @@ export class BVHRefitter {
|
|
|
319
464
|
const rMaxZ = bounds[ rb + 5 ];
|
|
320
465
|
|
|
321
466
|
// Write left child AABB into bvhData
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
467
|
+
bvhF[ o ] = lMinX;
|
|
468
|
+
bvhF[ o + 1 ] = lMinY;
|
|
469
|
+
bvhF[ o + 2 ] = lMinZ;
|
|
325
470
|
// o+3 = leftChildIdx (preserved)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
471
|
+
bvhF[ o + 4 ] = lMaxX;
|
|
472
|
+
bvhF[ o + 5 ] = lMaxY;
|
|
473
|
+
bvhF[ o + 6 ] = lMaxZ;
|
|
329
474
|
// o+7 = rightChildIdx (preserved)
|
|
330
475
|
|
|
331
476
|
// Write right child AABB into bvhData
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
477
|
+
bvhF[ o + 8 ] = rMinX;
|
|
478
|
+
bvhF[ o + 9 ] = rMinY;
|
|
479
|
+
bvhF[ o + 10 ] = rMinZ;
|
|
335
480
|
// o+11 = 0 padding
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
481
|
+
bvhF[ o + 12 ] = rMaxX;
|
|
482
|
+
bvhF[ o + 13 ] = rMaxY;
|
|
483
|
+
bvhF[ o + 14 ] = rMaxZ;
|
|
339
484
|
// o+15 = 0 padding
|
|
340
485
|
|
|
341
486
|
// Store this node's bounds as union of children
|