rayzee 4.8.3 → 4.8.6
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 +141 -82
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js +2 -0
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +1 -0
- package/dist/assets/BVHRefitWorker-GkmNJYvb.js +2 -0
- package/dist/assets/BVHRefitWorker-GkmNJYvb.js.map +1 -0
- package/dist/assets/BVHSubtreeWorker-C02ZWVeG.js +2 -0
- package/dist/assets/BVHSubtreeWorker-C02ZWVeG.js.map +1 -0
- package/dist/assets/BVHWorker-DobVXMda.js +2 -0
- package/dist/assets/BVHWorker-DobVXMda.js.map +1 -0
- package/dist/assets/CDFWorker-2MoynL4F.js +2 -0
- package/dist/assets/CDFWorker-2MoynL4F.js.map +1 -0
- package/dist/assets/TexturesWorker-DBqGmVdR.js +2 -0
- package/dist/assets/TexturesWorker-DBqGmVdR.js.map +1 -0
- package/dist/rayzee.es.js +922 -871
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +43 -43
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/Passes/AIUpscaler.js +1 -2
- package/src/PathTracerApp.js +36 -3
- package/src/Processor/AssetLoader.js +2 -2
- package/src/Processor/BVHBuilder.js +1 -2
- package/src/Processor/EquirectHDRInfo.js +1 -2
- package/src/Processor/LightSerializer.js +26 -7
- package/src/Processor/ParallelBVHBuilder.js +8 -9
- package/src/Processor/SceneProcessor.js +3 -4
- package/src/Processor/TextureCreator.js +1 -2
- package/src/Processor/Workers/AIUpscalerWorker.js +21 -7
- package/src/README.md +1 -2
- package/src/Stages/PathTracer.js +7 -6
- package/src/TSL/LightsCore.js +12 -2
- package/src/TSL/LightsSampling.js +8 -6
- package/src/managers/LightManager.js +1 -1
- package/src/managers/UniformManager.js +2 -2
- package/src/Processor/createWorker.js +0 -39
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BVHWorker-DobVXMda.js","names":[],"sources":["../../src/Processor/TreeletOptimizer.js","../../src/Processor/ReinsertionOptimizer.js","../../src/Processor/BVHBuilder.js","../../src/Processor/Workers/BVHWorker.js"],"sourcesContent":["// No external dependencies — uses inline float fields for bounds (no Vector3)\n\n/**\n * Treelet optimization for BVH trees (Bittner et al. 2013).\n *\n * For each small subtree (\"treelet\") of N leaves, enumerates all\n * Catalan(N-1) distinct binary tree topologies, tries all leaf\n * permutations (or greedy assignment for N > 5), and replaces the\n * subtree with the arrangement that minimises SAH cost.\n */\nexport class TreeletOptimizer {\n\n\tconstructor( traversalCost, intersectionCost ) {\n\n\t\tthis.traversalCost = traversalCost;\n\t\tthis.intersectionCost = intersectionCost;\n\t\tthis.maxTreeletLeaves = 7;\n\t\tthis.minImprovement = 0.02; // relative improvement threshold\n\n\t\t// Precompute topologies for leaf counts 3..maxTreeletLeaves\n\t\tthis.topologyCache = new Map();\n\t\tfor ( let n = 3; n <= this.maxTreeletLeaves; n ++ ) {\n\n\t\t\tthis.topologyCache.set( n, this.generateTopologies( n ) );\n\n\t\t}\n\n\t\t// Stats\n\t\tthis.stats = {\n\t\t\ttreeletsProcessed: 0,\n\t\t\ttreeletsImproved: 0,\n\t\t\ttotalSAHImprovement: 0,\n\t\t\taverageSAHImprovement: 0,\n\t\t\toptimizationTime: 0\n\t\t};\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Topology generation — nested binary trees via Catalan decomposition\n\t// ------------------------------------------------------------------\n\n\t/**\n\t * Generate all distinct binary tree topologies for `n` leaves.\n\t * Leaves are represented as integers 0..n-1 (slot indices).\n\t * Internal nodes are 2-element arrays [left, right].\n\t *\n\t * Examples for n=3 (Catalan(2)=2):\n\t * [[0,1],2] and [0,[1,2]]\n\t */\n\tgenerateTopologies( n ) {\n\n\t\tif ( n === 1 ) return [ 0 ];\n\t\tif ( n === 2 ) return [[ 0, 1 ]];\n\n\t\tconst results = [];\n\n\t\t// Split n leaves into left (k) and right (n-k) groups.\n\t\t// Base cases return arrays of topologies:\n\t\t// n=1 → [0] (one topology: leaf 0)\n\t\t// n=2 → [[0,1]] (one topology: pair [0,1])\n\t\t// So `for (const t of topos)` yields individual topologies in all cases.\n\t\tfor ( let k = 1; k < n; k ++ ) {\n\n\t\t\tconst leftTopos = this.generateTopologies( k );\n\t\t\tconst rightTopos = this.generateTopologies( n - k );\n\n\t\t\tfor ( const lt of leftTopos ) {\n\n\t\t\t\tfor ( const rt of rightTopos ) {\n\n\t\t\t\t\tresults.push( [ lt, this.offsetTopology( rt, k ) ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn results;\n\n\t}\n\n\t/**\n\t * Offset all leaf indices in a topology by `offset`.\n\t */\n\toffsetTopology( topo, offset ) {\n\n\t\tif ( typeof topo === 'number' ) return topo + offset;\n\t\treturn [ this.offsetTopology( topo[ 0 ], offset ), this.offsetTopology( topo[ 1 ], offset ) ];\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Main entry point\n\t// ------------------------------------------------------------------\n\n\toptimizeBVH( bvhRoot ) {\n\n\t\tconst startTime = performance.now();\n\t\tconst maxTime = 30000; // 30s safety timeout\n\n\t\tthis.stats = {\n\t\t\ttreeletsProcessed: 0,\n\t\t\ttreeletsImproved: 0,\n\t\t\ttotalSAHImprovement: 0,\n\t\t\taverageSAHImprovement: 0,\n\t\t\toptimizationTime: 0\n\t\t};\n\n\t\t// Identify all treelet roots (bottom-up, non-overlapping)\n\t\tconst treeletRoots = this.identifyTreeletRoots( bvhRoot );\n\n\t\tfor ( let i = 0; i < treeletRoots.length; i ++ ) {\n\n\t\t\tif ( performance.now() - startTime > maxTime ) {\n\n\t\t\t\tconsole.warn( `TreeletOptimizer: timeout after ${i}/${treeletRoots.length} treelets` );\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tthis.optimizeTreelet( treeletRoots[ i ] );\n\n\t\t}\n\n\t\tthis.stats.optimizationTime = performance.now() - startTime;\n\t\tthis.stats.averageSAHImprovement = this.stats.treeletsProcessed > 0\n\t\t\t? this.stats.totalSAHImprovement / this.stats.treeletsProcessed\n\t\t\t: 0;\n\n\t\treturn bvhRoot;\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Treelet identification — bottom-up, non-overlapping\n\t// ------------------------------------------------------------------\n\n\tidentifyTreeletRoots( bvhRoot ) {\n\n\t\tconst roots = [];\n\t\tconst processed = new Set();\n\n\t\t// Post-order traversal: children before parents so we process\n\t\t// bottom-up and skip subtrees already covered by a deeper treelet.\n\t\tconst stack = [ { node: bvhRoot, visited: false } ];\n\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst top = stack[ stack.length - 1 ];\n\n\t\t\tif ( top.visited ) {\n\n\t\t\t\tstack.pop();\n\t\t\t\tconst node = top.node;\n\n\t\t\t\t// Skip leaves\n\t\t\t\tif ( node.triangleCount > 0 ) continue;\n\t\t\t\t// Skip if already inside a processed treelet\n\t\t\t\tif ( processed.has( node ) ) continue;\n\n\t\t\t\tconst leafCount = this.countLeaves( node );\n\t\t\t\tif ( leafCount >= 3 && leafCount <= this.maxTreeletLeaves ) {\n\n\t\t\t\t\troots.push( node );\n\t\t\t\t\tthis.markSubtree( node, processed );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\ttop.visited = true;\n\t\t\t\tconst node = top.node;\n\t\t\t\tif ( node.triangleCount > 0 ) continue;\n\t\t\t\tif ( node.rightChild ) stack.push( { node: node.rightChild, visited: false } );\n\t\t\t\tif ( node.leftChild ) stack.push( { node: node.leftChild, visited: false } );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn roots;\n\n\t}\n\n\tcountLeaves( node ) {\n\n\t\tif ( ! node ) return 0;\n\t\tif ( node.triangleCount > 0 ) return 1;\n\t\treturn this.countLeaves( node.leftChild ) + this.countLeaves( node.rightChild );\n\n\t}\n\n\tmarkSubtree( node, set ) {\n\n\t\tif ( ! node ) return;\n\t\tset.add( node );\n\t\tif ( node.triangleCount > 0 ) return;\n\t\tthis.markSubtree( node.leftChild, set );\n\t\tthis.markSubtree( node.rightChild, set );\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Single treelet optimization\n\t// ------------------------------------------------------------------\n\n\toptimizeTreelet( treeletRoot ) {\n\n\t\t// Extract leaves\n\t\tconst leaves = [];\n\t\tthis.extractLeaves( treeletRoot, leaves );\n\t\tconst n = leaves.length;\n\n\t\tif ( n < 3 || n > this.maxTreeletLeaves ) return;\n\n\t\tthis.stats.treeletsProcessed ++;\n\n\t\tconst originalCost = this.evaluateSubtreeSAH( treeletRoot );\n\t\tconst topologies = this.topologyCache.get( n );\n\t\tif ( ! topologies || topologies.length === 0 ) return;\n\n\t\tlet bestCost = originalCost;\n\t\tlet bestTopo = null;\n\t\tlet bestPerm = null;\n\n\t\tif ( n <= 5 ) {\n\n\t\t\t// Full permutation search — feasible for n<=5 (max 120 perms × 14 topos)\n\t\t\tconst perms = this.generatePermutations( n );\n\n\t\t\tfor ( const topo of topologies ) {\n\n\t\t\t\tfor ( const perm of perms ) {\n\n\t\t\t\t\tconst cost = this.evaluateTopology( topo, leaves, perm );\n\t\t\t\t\tif ( cost < bestCost ) {\n\n\t\t\t\t\t\tbestCost = cost;\n\t\t\t\t\t\tbestTopo = topo;\n\t\t\t\t\t\tbestPerm = perm;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// Greedy assignment for n>5: for each topology, use identity perm\n\t\t\t// plus a few SAH-guided swaps\n\t\t\tconst identityPerm = Array.from( { length: n }, ( _, i ) => i );\n\n\t\t\tfor ( const topo of topologies ) {\n\n\t\t\t\t// Try identity permutation\n\t\t\t\tconst cost = this.evaluateTopology( topo, leaves, identityPerm );\n\t\t\t\tif ( cost < bestCost ) {\n\n\t\t\t\t\tbestCost = cost;\n\t\t\t\t\tbestTopo = topo;\n\t\t\t\t\tbestPerm = identityPerm;\n\n\t\t\t\t}\n\n\t\t\t\t// Try greedy swap improvements\n\t\t\t\tconst result = this.greedySwapOptimize( topo, leaves, identityPerm, cost );\n\t\t\t\tif ( result.cost < bestCost ) {\n\n\t\t\t\t\tbestCost = result.cost;\n\t\t\t\t\tbestTopo = topo;\n\t\t\t\t\tbestPerm = result.perm;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Apply if relative improvement exceeds threshold\n\t\tconst relativeImprovement = ( originalCost - bestCost ) / originalCost;\n\t\tif ( bestTopo && relativeImprovement > this.minImprovement ) {\n\n\t\t\tthis.reconstructTreelet( treeletRoot, bestTopo, leaves, bestPerm );\n\t\t\tthis.stats.treeletsImproved ++;\n\t\t\tthis.stats.totalSAHImprovement += relativeImprovement;\n\n\t\t}\n\n\t}\n\n\textractLeaves( node, leaves ) {\n\n\t\tif ( ! node ) return;\n\n\t\tif ( node.triangleCount > 0 ) {\n\n\t\t\t// BVHNode uses inline floats (minX/maxX etc.)\n\t\t\tleaves.push( {\n\t\t\t\tminX: node.minX, minY: node.minY, minZ: node.minZ,\n\t\t\t\tmaxX: node.maxX, maxY: node.maxY, maxZ: node.maxZ,\n\t\t\t\ttriangleOffset: node.triangleOffset,\n\t\t\t\ttriangleCount: node.triangleCount\n\t\t\t} );\n\t\t\treturn;\n\n\t\t}\n\n\t\tthis.extractLeaves( node.leftChild, leaves );\n\t\tthis.extractLeaves( node.rightChild, leaves );\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// SAH evaluation\n\t// ------------------------------------------------------------------\n\n\tevaluateSubtreeSAH( node ) {\n\n\t\tif ( ! node ) return 0;\n\n\t\tif ( node.triangleCount > 0 ) {\n\n\t\t\treturn this.surfaceAreaFlat( node.minX, node.minY, node.minZ, node.maxX, node.maxY, node.maxZ ) * node.triangleCount * this.intersectionCost;\n\n\t\t}\n\n\t\tconst leftCost = this.evaluateSubtreeSAH( node.leftChild );\n\t\tconst rightCost = this.evaluateSubtreeSAH( node.rightChild );\n\t\tconst sa = this.surfaceAreaFlat( node.minX, node.minY, node.minZ, node.maxX, node.maxY, node.maxZ );\n\t\treturn sa * this.traversalCost + leftCost + rightCost;\n\n\t}\n\n\t/**\n\t * Evaluate SAH cost for a topology with a given leaf permutation.\n\t * Returns { cost, boundsMin, boundsMax } for internal use, or just cost number.\n\t */\n\tevaluateTopology( topo, leaves, perm ) {\n\n\t\tconst result = this.evalTopoRecursive( topo, leaves, perm );\n\t\treturn result.cost;\n\n\t}\n\n\tevalTopoRecursive( topo, leaves, perm ) {\n\n\t\tif ( typeof topo === 'number' ) {\n\n\t\t\t// Leaf slot — look up actual leaf via permutation\n\t\t\tconst leaf = leaves[ perm[ topo ] ];\n\t\t\treturn {\n\t\t\t\tcost: this.surfaceAreaFlat( leaf.minX, leaf.minY, leaf.minZ, leaf.maxX, leaf.maxY, leaf.maxZ ) * leaf.triangleCount * this.intersectionCost,\n\t\t\t\tminX: leaf.minX, minY: leaf.minY, minZ: leaf.minZ,\n\t\t\t\tmaxX: leaf.maxX, maxY: leaf.maxY, maxZ: leaf.maxZ\n\t\t\t};\n\n\t\t}\n\n\t\tconst left = this.evalTopoRecursive( topo[ 0 ], leaves, perm );\n\t\tconst right = this.evalTopoRecursive( topo[ 1 ], leaves, perm );\n\n\t\tconst mnX = Math.min( left.minX, right.minX );\n\t\tconst mnY = Math.min( left.minY, right.minY );\n\t\tconst mnZ = Math.min( left.minZ, right.minZ );\n\t\tconst mxX = Math.max( left.maxX, right.maxX );\n\t\tconst mxY = Math.max( left.maxY, right.maxY );\n\t\tconst mxZ = Math.max( left.maxZ, right.maxZ );\n\n\t\tconst sa = this.surfaceAreaFlat( mnX, mnY, mnZ, mxX, mxY, mxZ );\n\t\treturn {\n\t\t\tcost: sa * this.traversalCost + left.cost + right.cost,\n\t\t\tminX: mnX, minY: mnY, minZ: mnZ,\n\t\t\tmaxX: mxX, maxY: mxY, maxZ: mxZ\n\t\t};\n\n\t}\n\n\tsurfaceAreaFlat( minX, minY, minZ, maxX, maxY, maxZ ) {\n\n\t\tconst dx = maxX - minX;\n\t\tconst dy = maxY - minY;\n\t\tconst dz = maxZ - minZ;\n\t\treturn 2 * ( dx * dy + dy * dz + dz * dx );\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Permutation helpers\n\t// ------------------------------------------------------------------\n\n\tgeneratePermutations( n ) {\n\n\t\tconst result = [];\n\t\tconst arr = Array.from( { length: n }, ( _, i ) => i );\n\n\t\tconst permute = ( start ) => {\n\n\t\t\tif ( start === n ) {\n\n\t\t\t\tresult.push( [ ...arr ] );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tfor ( let i = start; i < n; i ++ ) {\n\n\t\t\t\t[ arr[ start ], arr[ i ] ] = [ arr[ i ], arr[ start ] ];\n\t\t\t\tpermute( start + 1 );\n\t\t\t\t[ arr[ start ], arr[ i ] ] = [ arr[ i ], arr[ start ] ];\n\n\t\t\t}\n\n\t\t};\n\n\t\tpermute( 0 );\n\t\treturn result;\n\n\t}\n\n\t/**\n\t * Greedy pairwise swap optimization for large treelet sizes.\n\t * Starting from an initial permutation, try all pairwise swaps\n\t * and accept any that improve cost. Repeat until no improvement.\n\t */\n\tgreedySwapOptimize( topo, leaves, initialPerm, initialCost ) {\n\n\t\tconst perm = [ ...initialPerm ];\n\t\tlet cost = initialCost;\n\t\tlet improved = true;\n\n\t\twhile ( improved ) {\n\n\t\t\timproved = false;\n\n\t\t\tfor ( let i = 0; i < perm.length - 1; i ++ ) {\n\n\t\t\t\tfor ( let j = i + 1; j < perm.length; j ++ ) {\n\n\t\t\t\t\t// Swap\n\t\t\t\t\t[ perm[ i ], perm[ j ] ] = [ perm[ j ], perm[ i ] ];\n\t\t\t\t\tconst newCost = this.evaluateTopology( topo, leaves, perm );\n\n\t\t\t\t\tif ( newCost < cost ) {\n\n\t\t\t\t\t\tcost = newCost;\n\t\t\t\t\t\timproved = true;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Swap back\n\t\t\t\t\t\t[ perm[ i ], perm[ j ] ] = [ perm[ j ], perm[ i ] ];\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn { perm, cost };\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Reconstruction — builds proper BVHNode instances\n\t// ------------------------------------------------------------------\n\n\treconstructTreelet( treeletRoot, topo, leaves, perm ) {\n\n\t\tconst built = this.buildSubtree( topo, leaves, perm );\n\n\t\t// Copy built structure into the existing treelet root node (inline floats)\n\t\ttreeletRoot.minX = built.minX; treeletRoot.minY = built.minY; treeletRoot.minZ = built.minZ;\n\t\ttreeletRoot.maxX = built.maxX; treeletRoot.maxY = built.maxY; treeletRoot.maxZ = built.maxZ;\n\t\ttreeletRoot.leftChild = built.leftChild;\n\t\ttreeletRoot.rightChild = built.rightChild;\n\t\ttreeletRoot.triangleOffset = built.triangleOffset;\n\t\ttreeletRoot.triangleCount = built.triangleCount;\n\n\t}\n\n\t/**\n\t * Recursively build a BVHNode subtree from a topology + leaf permutation.\n\t */\n\tbuildSubtree( topo, leaves, perm ) {\n\n\t\tif ( typeof topo === 'number' ) {\n\n\t\t\t// Leaf — create a lightweight node with inline bounds\n\t\t\tconst leaf = leaves[ perm[ topo ] ];\n\t\t\tconst node = new TreeletBVHNode();\n\t\t\tnode.minX = leaf.minX; node.minY = leaf.minY; node.minZ = leaf.minZ;\n\t\t\tnode.maxX = leaf.maxX; node.maxY = leaf.maxY; node.maxZ = leaf.maxZ;\n\t\t\tnode.triangleOffset = leaf.triangleOffset;\n\t\t\tnode.triangleCount = leaf.triangleCount;\n\t\t\treturn node;\n\n\t\t}\n\n\t\tconst left = this.buildSubtree( topo[ 0 ], leaves, perm );\n\t\tconst right = this.buildSubtree( topo[ 1 ], leaves, perm );\n\n\t\tconst node = new TreeletBVHNode();\n\t\tnode.leftChild = left;\n\t\tnode.rightChild = right;\n\t\tnode.minX = Math.min( left.minX, right.minX );\n\t\tnode.minY = Math.min( left.minY, right.minY );\n\t\tnode.minZ = Math.min( left.minZ, right.minZ );\n\t\tnode.maxX = Math.max( left.maxX, right.maxX );\n\t\tnode.maxY = Math.max( left.maxY, right.maxY );\n\t\tnode.maxZ = Math.max( left.maxZ, right.maxZ );\n\n\t\treturn node;\n\n\t}\n\n\t// ------------------------------------------------------------------\n\t// Configuration\n\t// ------------------------------------------------------------------\n\n\tsetTreeletSize( size ) {\n\n\t\tthis.maxTreeletLeaves = Math.max( 3, Math.min( 7, size ) );\n\n\t\t// Regenerate topology cache if needed\n\t\tfor ( let n = 3; n <= this.maxTreeletLeaves; n ++ ) {\n\n\t\t\tif ( ! this.topologyCache.has( n ) ) {\n\n\t\t\t\tthis.topologyCache.set( n, this.generateTopologies( n ) );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tsetMinImprovement( threshold ) {\n\n\t\tthis.minImprovement = Math.max( 0.001, threshold );\n\n\t}\n\n\tsetMaxTreelets() {\n\n\t\t// No-op — we process all valid treelets now.\n\t\t// Kept for API compatibility with BVHBuilder.\n\n\t}\n\n\tgetStatistics() {\n\n\t\treturn { ...this.stats };\n\n\t}\n\n}\n\n/**\n * Lightweight BVH node used during reconstruction.\n * Duck-type compatible with the main BVHNode class —\n * uses inline float fields (minX/maxX etc.).\n */\nclass TreeletBVHNode {\n\n\tconstructor() {\n\n\t\tthis.minX = 0; this.minY = 0; this.minZ = 0;\n\t\tthis.maxX = 0; this.maxY = 0; this.maxZ = 0;\n\t\tthis.leftChild = null;\n\t\tthis.rightChild = null;\n\t\tthis.triangleOffset = 0;\n\t\tthis.triangleCount = 0;\n\n\t}\n\n}\n","/**\n * ReinsertionOptimizer — BVH quality improvement via node reinsertion.\n *\n * Based on \"Parallel Reinsertion for Bounding Volume Hierarchy Optimization\"\n * by Meister & Bittner, adapted from madmann91/bvh (MIT).\n *\n * Algorithm:\n * 1. Find the top N nodes by surface area (highest traversal cost).\n * 2. For each candidate, search the tree for the optimal reinsertion target\n * using branch-and-bound pruning.\n * 3. Sort reinsertions by area improvement, apply non-conflicting ones greedily.\n * 4. Repeat for a configurable number of iterations.\n *\n * Typically yields 10-20% SAH cost reduction on top of treelet optimization.\n */\nexport class ReinsertionOptimizer {\n\n\tconstructor( traversalCost, intersectionCost ) {\n\n\t\tthis.traversalCost = traversalCost;\n\t\tthis.intersectionCost = intersectionCost;\n\n\t\t// Fraction of total nodes to consider per iteration\n\t\tthis.batchSizeRatio = 0.02;\n\n\t\t// Maximum optimization iterations\n\t\tthis.maxIterations = 2;\n\n\t\t// Time budget (ms) — abort if exceeded\n\t\tthis.timeBudgetMs = 15000;\n\n\t\t// Statistics\n\t\tthis.stats = { reinsertionsApplied: 0, iterations: 0, timeMs: 0 };\n\n\t}\n\n\tsetBatchSizeRatio( ratio ) {\n\n\t\tthis.batchSizeRatio = Math.max( 0.005, Math.min( 0.1, ratio ) );\n\n\t}\n\n\tsetMaxIterations( n ) {\n\n\t\tthis.maxIterations = Math.max( 1, Math.min( 5, n ) );\n\n\t}\n\n\tgetStatistics() {\n\n\t\treturn { ...this.stats };\n\n\t}\n\n\t// --- Surface area (half SA, consistent with TreeletOptimizer) ---\n\n\tsurfaceArea( node ) {\n\n\t\tconst dx = node.maxX - node.minX;\n\t\tconst dy = node.maxY - node.minY;\n\t\tconst dz = node.maxZ - node.minZ;\n\t\treturn dx * dy + dy * dz + dz * dx;\n\n\t}\n\n\t// --- Parent map: node → { parent, isLeft } ---\n\n\tbuildParentMap( root ) {\n\n\t\tconst map = new Map();\n\t\tmap.set( root, { parent: null, isLeft: false } );\n\n\t\tconst stack = [ root ];\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst node = stack.pop();\n\t\t\tif ( node.triangleCount > 0 ) continue;\n\n\t\t\tif ( node.leftChild ) {\n\n\t\t\t\tmap.set( node.leftChild, { parent: node, isLeft: true } );\n\t\t\t\tstack.push( node.leftChild );\n\n\t\t\t}\n\n\t\t\tif ( node.rightChild ) {\n\n\t\t\t\tmap.set( node.rightChild, { parent: node, isLeft: false } );\n\t\t\t\tstack.push( node.rightChild );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn map;\n\n\t}\n\n\t// --- Candidate selection: top N nodes by surface area (min-heap) ---\n\n\tfindCandidates( root, targetCount, parentMap ) {\n\n\t\t// Collect all non-root nodes (skip root itself and its direct children\n\t\t// whose reinsertion would require mutating root identity)\n\t\tconst heap = []; // min-heap by cost (surface area)\n\n\t\tconst stack = [ root ];\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst node = stack.pop();\n\n\t\t\tif ( node !== root ) {\n\n\t\t\t\tconst info = parentMap.get( node );\n\t\t\t\t// Skip direct children of root to avoid root mutation complexity\n\t\t\t\tif ( info.parent !== root ) {\n\n\t\t\t\t\tconst cost = this.surfaceArea( node );\n\n\t\t\t\t\tif ( heap.length < targetCount ) {\n\n\t\t\t\t\t\theap.push( { node, cost } );\n\t\t\t\t\t\tif ( heap.length === targetCount ) this._heapify( heap );\n\n\t\t\t\t\t} else if ( cost > heap[ 0 ].cost ) {\n\n\t\t\t\t\t\theap[ 0 ] = { node, cost };\n\t\t\t\t\t\tthis._siftDown( heap, 0 );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( node.triangleCount === 0 ) {\n\n\t\t\t\tif ( node.leftChild ) stack.push( node.leftChild );\n\t\t\t\tif ( node.rightChild ) stack.push( node.rightChild );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn heap;\n\n\t}\n\n\t// Min-heap helpers (smallest cost at index 0)\n\t_heapify( heap ) {\n\n\t\tfor ( let i = ( heap.length >> 1 ) - 1; i >= 0; i -- ) this._siftDown( heap, i );\n\n\t}\n\n\t_siftDown( heap, i ) {\n\n\t\tconst n = heap.length;\n\t\twhile ( true ) {\n\n\t\t\tlet smallest = i;\n\t\t\tconst l = 2 * i + 1;\n\t\t\tconst r = 2 * i + 2;\n\t\t\tif ( l < n && heap[ l ].cost < heap[ smallest ].cost ) smallest = l;\n\t\t\tif ( r < n && heap[ r ].cost < heap[ smallest ].cost ) smallest = r;\n\t\t\tif ( smallest === i ) break;\n\t\t\tconst tmp = heap[ i ];\n\t\t\theap[ i ] = heap[ smallest ];\n\t\t\theap[ smallest ] = tmp;\n\t\t\ti = smallest;\n\n\t\t}\n\n\t}\n\n\t// --- Find best reinsertion target for a candidate node ---\n\n\tfindReinsertion( nodeA, root, parentMap ) {\n\n\t\tconst aInfo = parentMap.get( nodeA );\n\t\tconst parentA = aInfo.parent;\n\t\tif ( ! parentA ) return null;\n\n\t\tconst siblingA = aInfo.isLeft ? parentA.rightChild : parentA.leftChild;\n\n\t\tconst nodeArea = this.surfaceArea( nodeA );\n\t\tconst parentArea = this.surfaceArea( parentA );\n\n\t\tlet bestTo = null;\n\t\tlet bestAreaDiff = 0;\n\n\t\t// areaDiff accumulates the net area savings from removing nodeA\n\t\t// along the path from its parent to the current pivot level\n\t\tlet areaDiff = parentArea;\n\n\t\t// pivotBbox tracks the combined bounds of everything except nodeA\n\t\t// along the path from siblingA upward\n\t\tlet pbMinX = siblingA.minX, pbMinY = siblingA.minY, pbMinZ = siblingA.minZ;\n\t\tlet pbMaxX = siblingA.maxX, pbMaxY = siblingA.maxY, pbMaxZ = siblingA.maxZ;\n\n\t\tlet siblingNode = siblingA;\n\t\tlet pivotNode = parentA;\n\n\t\tconst searchStack = [];\n\n\t\tdo {\n\n\t\t\t// Search sibling subtree at current level\n\t\t\tsearchStack.length = 0;\n\t\t\tsearchStack.push( areaDiff, siblingNode );\n\n\t\t\twhile ( searchStack.length > 0 ) {\n\n\t\t\t\t// Pop node and areaDiff (stored as pairs: [areaDiff, node, areaDiff, node, ...])\n\t\t\t\tconst topNode = searchStack.pop();\n\t\t\t\tconst topAreaDiff = searchStack.pop();\n\n\t\t\t\t// Prune: upper bound on improvement can't beat current best\n\t\t\t\tif ( topAreaDiff - nodeArea <= bestAreaDiff ) continue;\n\n\t\t\t\t// Compute merged area if we insert nodeA next to this target\n\t\t\t\tconst mMinX = Math.min( topNode.minX, nodeA.minX );\n\t\t\t\tconst mMinY = Math.min( topNode.minY, nodeA.minY );\n\t\t\t\tconst mMinZ = Math.min( topNode.minZ, nodeA.minZ );\n\t\t\t\tconst mMaxX = Math.max( topNode.maxX, nodeA.maxX );\n\t\t\t\tconst mMaxY = Math.max( topNode.maxY, nodeA.maxY );\n\t\t\t\tconst mMaxZ = Math.max( topNode.maxZ, nodeA.maxZ );\n\t\t\t\tconst mdx = mMaxX - mMinX, mdy = mMaxY - mMinY, mdz = mMaxZ - mMinZ;\n\t\t\t\tconst mergedArea = mdx * mdy + mdy * mdz + mdz * mdx;\n\n\t\t\t\tconst reinsertArea = topAreaDiff - mergedArea;\n\n\t\t\t\tif ( reinsertArea > bestAreaDiff ) {\n\n\t\t\t\t\tbestTo = topNode;\n\t\t\t\t\tbestAreaDiff = reinsertArea;\n\n\t\t\t\t}\n\n\t\t\t\t// Descend into children if inner node\n\t\t\t\tif ( topNode.triangleCount === 0 && topNode.leftChild && topNode.rightChild ) {\n\n\t\t\t\t\tconst childArea = reinsertArea + this.surfaceArea( topNode );\n\t\t\t\t\tsearchStack.push( childArea, topNode.leftChild );\n\t\t\t\t\tsearchStack.push( childArea, topNode.rightChild );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Move up one level\n\t\t\tconst pivotInfo = parentMap.get( pivotNode );\n\t\t\tif ( ! pivotInfo || pivotInfo.parent === null ) break;\n\n\t\t\t// Update pivot bbox: accumulate sibling bounds at each level above parentA\n\t\t\tif ( pivotNode !== parentA ) {\n\n\t\t\t\tpbMinX = Math.min( pbMinX, siblingNode.minX );\n\t\t\t\tpbMinY = Math.min( pbMinY, siblingNode.minY );\n\t\t\t\tpbMinZ = Math.min( pbMinZ, siblingNode.minZ );\n\t\t\t\tpbMaxX = Math.max( pbMaxX, siblingNode.maxX );\n\t\t\t\tpbMaxY = Math.max( pbMaxY, siblingNode.maxY );\n\t\t\t\tpbMaxZ = Math.max( pbMaxZ, siblingNode.maxZ );\n\n\t\t\t\tconst pdx = pbMaxX - pbMinX, pdy = pbMaxY - pbMinY, pdz = pbMaxZ - pbMinZ;\n\t\t\t\tconst pivotBboxArea = pdx * pdy + pdy * pdz + pdz * pdx;\n\t\t\t\tareaDiff += this.surfaceArea( pivotNode ) - pivotBboxArea;\n\n\t\t\t}\n\n\t\t\t// Get sibling of pivot at next ancestor level\n\t\t\tconst pivotParent = pivotInfo.parent;\n\t\t\tsiblingNode = pivotInfo.isLeft ? pivotParent.rightChild : pivotParent.leftChild;\n\t\t\tpivotNode = pivotParent;\n\n\t\t} while ( parentMap.get( pivotNode ).parent !== null );\n\n\t\t// Reject trivial reinsertions (same position)\n\t\tif ( bestTo === siblingA || bestTo === parentA ) return null;\n\n\t\treturn bestTo ? { from: nodeA, to: bestTo, areaDiff: bestAreaDiff } : null;\n\n\t}\n\n\t// --- Conflict detection: nodes involved in a reinsertion ---\n\n\tgetConflicts( from, to, parentMap ) {\n\n\t\tconst aInfo = parentMap.get( from );\n\t\tconst siblingA = aInfo.isLeft ? aInfo.parent.rightChild : aInfo.parent.leftChild;\n\n\t\treturn [\n\t\t\tto,\n\t\t\tfrom,\n\t\t\tsiblingA,\n\t\t\tparentMap.get( to ).parent,\n\t\t\taInfo.parent\n\t\t];\n\n\t}\n\n\t// --- Perform the actual tree surgery ---\n\n\treinsertNode( nodeA, targetC, parentMap ) {\n\n\t\tconst aInfo = parentMap.get( nodeA );\n\t\tconst parentA = aInfo.parent;\n\t\tconst siblingA = aInfo.isLeft ? parentA.rightChild : parentA.leftChild;\n\n\t\tconst parentAInfo = parentMap.get( parentA );\n\t\tconst grandparent = parentAInfo.parent;\n\n\t\tconst cInfo = parentMap.get( targetC );\n\t\tconst targetParent = cInfo.parent;\n\n\t\t// Step 1: Remove parentA from grandparent, replace with siblingA\n\t\tif ( parentAInfo.isLeft ) {\n\n\t\t\tgrandparent.leftChild = siblingA;\n\n\t\t} else {\n\n\t\t\tgrandparent.rightChild = siblingA;\n\n\t\t}\n\n\t\t// Step 2: Reuse parentA as new parent of (nodeA, targetC)\n\t\tparentA.leftChild = nodeA;\n\t\tparentA.rightChild = targetC;\n\t\tparentA.triangleOffset = 0;\n\t\tparentA.triangleCount = 0;\n\t\tparentA.minX = Math.min( nodeA.minX, targetC.minX );\n\t\tparentA.minY = Math.min( nodeA.minY, targetC.minY );\n\t\tparentA.minZ = Math.min( nodeA.minZ, targetC.minZ );\n\t\tparentA.maxX = Math.max( nodeA.maxX, targetC.maxX );\n\t\tparentA.maxY = Math.max( nodeA.maxY, targetC.maxY );\n\t\tparentA.maxZ = Math.max( nodeA.maxZ, targetC.maxZ );\n\n\t\t// Step 3: Place parentA where targetC was\n\t\tif ( cInfo.isLeft ) {\n\n\t\t\ttargetParent.leftChild = parentA;\n\n\t\t} else {\n\n\t\t\ttargetParent.rightChild = parentA;\n\n\t\t}\n\n\t\t// Step 4: Update parent map\n\t\tparentMap.set( siblingA, { parent: grandparent, isLeft: parentAInfo.isLeft } );\n\t\tparentMap.set( parentA, { parent: targetParent, isLeft: cInfo.isLeft } );\n\t\tparentMap.set( nodeA, { parent: parentA, isLeft: true } );\n\t\tparentMap.set( targetC, { parent: parentA, isLeft: false } );\n\n\t\t// Step 5: Refit bounds from removal point (grandparent) up to root\n\t\tthis.refitFrom( grandparent, parentMap );\n\n\t\t// Step 6: Refit bounds from insertion point (targetParent) up to root\n\t\tthis.refitFrom( targetParent, parentMap );\n\n\t}\n\n\t// --- Refit bounding boxes from a node up to root ---\n\n\trefitFrom( node, parentMap ) {\n\n\t\tlet current = node;\n\t\twhile ( current ) {\n\n\t\t\tif ( current.triangleCount === 0 && current.leftChild && current.rightChild ) {\n\n\t\t\t\tconst L = current.leftChild;\n\t\t\t\tconst R = current.rightChild;\n\t\t\t\tcurrent.minX = Math.min( L.minX, R.minX );\n\t\t\t\tcurrent.minY = Math.min( L.minY, R.minY );\n\t\t\t\tcurrent.minZ = Math.min( L.minZ, R.minZ );\n\t\t\t\tcurrent.maxX = Math.max( L.maxX, R.maxX );\n\t\t\t\tcurrent.maxY = Math.max( L.maxY, R.maxY );\n\t\t\t\tcurrent.maxZ = Math.max( L.maxZ, R.maxZ );\n\n\t\t\t}\n\n\t\t\tconst info = parentMap.get( current );\n\t\t\tcurrent = info ? info.parent : null;\n\n\t\t}\n\n\t}\n\n\t// --- Main entry point ---\n\n\toptimizeBVH( root, progressCallback ) {\n\n\t\tconst startTime = performance.now();\n\t\tthis.stats = { reinsertionsApplied: 0, iterations: 0, timeMs: 0 };\n\n\t\tfor ( let iter = 0; iter < this.maxIterations; iter ++ ) {\n\n\t\t\tif ( performance.now() - startTime > this.timeBudgetMs ) break;\n\n\t\t\t// Rebuild parent map each iteration (tree structure changes)\n\t\t\tconst parentMap = this.buildParentMap( root );\n\t\t\tconst nodeCount = parentMap.size;\n\t\t\tconst targetCount = Math.max( 1, Math.floor( nodeCount * this.batchSizeRatio ) );\n\n\t\t\tif ( progressCallback ) {\n\n\t\t\t\tprogressCallback( `Reinsertion iter ${iter + 1}/${this.maxIterations}: selecting ${targetCount} candidates` );\n\n\t\t\t}\n\n\t\t\t// Find highest-cost candidates\n\t\t\tconst candidates = this.findCandidates( root, targetCount, parentMap );\n\n\t\t\t// Find best reinsertion for each candidate\n\t\t\tconst reinsertions = [];\n\t\t\tfor ( let i = 0; i < candidates.length; i ++ ) {\n\n\t\t\t\tif ( performance.now() - startTime > this.timeBudgetMs ) break;\n\n\t\t\t\tconst r = this.findReinsertion( candidates[ i ].node, root, parentMap );\n\t\t\t\tif ( r && r.areaDiff > 0 ) {\n\n\t\t\t\t\treinsertions.push( r );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Sort by improvement (largest first) and apply greedily\n\t\t\treinsertions.sort( ( a, b ) => b.areaDiff - a.areaDiff );\n\n\t\t\tconst touched = new Set();\n\t\t\tlet applied = 0;\n\n\t\t\tfor ( const r of reinsertions ) {\n\n\t\t\t\tconst conflicts = this.getConflicts( r.from, r.to, parentMap );\n\t\t\t\tif ( conflicts.some( n => touched.has( n ) ) ) continue;\n\n\t\t\t\tfor ( const n of conflicts ) touched.add( n );\n\n\t\t\t\tthis.reinsertNode( r.from, r.to, parentMap );\n\t\t\t\tapplied ++;\n\n\t\t\t}\n\n\t\t\tthis.stats.reinsertionsApplied += applied;\n\t\t\tthis.stats.iterations = iter + 1;\n\n\t\t\tif ( progressCallback ) {\n\n\t\t\t\tprogressCallback( `Reinsertion iter ${iter + 1}: applied ${applied} reinsertions` );\n\n\t\t\t}\n\n\t\t\t// No improvements found — tree is already optimal\n\t\t\tif ( applied === 0 ) break;\n\n\t\t}\n\n\t\tthis.stats.timeMs = performance.now() - startTime;\n\t\treturn this.stats;\n\n\t}\n\n}\n","import { TreeletOptimizer } from './TreeletOptimizer.js';\nimport { ReinsertionOptimizer } from './ReinsertionOptimizer.js';\n\n// Inline copy of TRIANGLE_DATA_LAYOUT (mirrors Constants.js).\n// Cannot import Constants.js because BVHBuilder runs inside BVHWorker\n// where `window` (used elsewhere in Constants.js) is not defined.\nconst TRIANGLE_DATA_LAYOUT = {\n\tFLOATS_PER_TRIANGLE: 32,\n\tPOSITION_A_OFFSET: 0,\n\tPOSITION_B_OFFSET: 4,\n\tPOSITION_C_OFFSET: 8,\n\tNORMAL_A_OFFSET: 12,\n\tNORMAL_B_OFFSET: 16,\n\tNORMAL_C_OFFSET: 20,\n\tUV_AB_OFFSET: 24,\n\tUV_C_MAT_OFFSET: 28 // vec4: uvC.x, uvC.y, materialIndex, meshIndex\n};\n\nconst FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;\n\nclass BVHNode {\n\n\tconstructor() {\n\n\t\t// Inline floats instead of Vector3 to avoid 2M+ object allocations\n\t\tthis.minX = 0; this.minY = 0; this.minZ = 0;\n\t\tthis.maxX = 0; this.maxY = 0; this.maxZ = 0;\n\t\tthis.leftChild = null;\n\t\tthis.rightChild = null;\n\t\tthis.triangleOffset = 0;\n\t\tthis.triangleCount = 0;\n\n\t}\n\n}\n\nexport class BVHBuilder {\n\n\tconstructor() {\n\n\t\tthis.useWorker = true;\n\t\tthis.maxLeafSize = 8;\n\t\tthis.numBins = 32;\n\t\tthis.minBins = 8;\n\t\tthis.maxBins = 64;\n\t\tthis.totalNodes = 0;\n\t\tthis.processedTriangles = 0;\n\t\tthis.totalTriangles = 0;\n\t\tthis.lastProgressUpdate = 0;\n\t\tthis.progressUpdateInterval = 100;\n\n\t\t// SAH constants — GPU intersection is ~2.5x more expensive than traversal\n\t\t// (8 storage buffer fetches + Möller-Trumbore vs 4 vec4 reads + slab test)\n\t\tthis.traversalCost = 1.0;\n\t\tthis.intersectionCost = 2.5;\n\n\t\t// Morton code clustering settings\n\t\tthis.useMortonCodes = true;\n\t\tthis.mortonBits = 10;\n\t\tthis.mortonClusterThreshold = 128;\n\n\t\t// Fallback method configuration\n\t\tthis.enableObjectMedianFallback = true;\n\t\tthis.enableSpatialMedianFallback = true;\n\n\t\t// Split statistics\n\t\tthis.splitStats = {\n\t\t\tsahSplits: 0,\n\t\t\tobjectMedianSplits: 0,\n\t\t\tspatialMedianSplits: 0,\n\t\t\tfailedSplits: 0,\n\t\t\tavgBinsUsed: 0,\n\t\t\ttotalSplitAttempts: 0,\n\t\t\tmortonSortTime: 0,\n\t\t\ttotalBuildTime: 0,\n\t\t\ttreeletOptimizationTime: 0,\n\t\t\ttreeletsProcessed: 0,\n\t\t\ttreeletsImproved: 0,\n\t\t\taverageSAHImprovement: 0,\n\t\t\treinsertionOptimizationTime: 0,\n\t\t\treinsertionsApplied: 0,\n\t\t\treinsertionIterations: 0\n\t\t};\n\n\t\t// Treelet optimization configuration\n\t\tthis.enableTreeletOptimization = true;\n\t\tthis.treeletSize = 5;\n\t\tthis.treeletOptimizationPasses = 1;\n\t\tthis.treeletMinImprovement = 0.02;\n\t\tthis.maxTreeletDepth = 3;\n\t\tthis.maxTreeletsPerScene = 20;\n\t\tthis.treeletComplexityThreshold = 50000;\n\n\t\t// Reinsertion optimization configuration\n\t\tthis.enableReinsertionOptimization = true;\n\t\tthis.reinsertionBatchSizeRatio = 0.02;\n\t\tthis.reinsertionMaxIterations = 2;\n\n\t\t// Pre-allocate bin arrays\n\t\tthis.initializeBinArrays();\n\n\t\t// Reusable partition result (avoids per-node object allocation)\n\t\tthis._partResult = {\n\t\t\tmid: 0,\n\t\t\tlMinX: 0, lMinY: 0, lMinZ: 0, lMaxX: 0, lMaxY: 0, lMaxZ: 0,\n\t\t\trMinX: 0, rMinY: 0, rMinZ: 0, rMaxX: 0, rMaxY: 0, rMaxZ: 0\n\t\t};\n\n\t\t// Flat per-triangle arrays (allocated in buildSync)\n\t\tthis.centroids = null;\n\t\tthis.bMin = null;\n\t\tthis.bMax = null;\n\t\tthis.indices = null;\n\t\tthis.mortonCodes = null;\n\t\tthis.triangles = null;\n\n\t\t// Reordered output (produced by buildSync)\n\t\tthis.reorderedTriangleData = null;\n\n\t}\n\n\tinitializeBinArrays() {\n\n\t\tconst mb = this.maxBins;\n\t\t// Flat bin bounds: 3 floats per bin (x, y, z)\n\t\tthis.binBoundsMin = new Float32Array( mb * 3 );\n\t\tthis.binBoundsMax = new Float32Array( mb * 3 );\n\t\tthis.binCounts = new Uint32Array( mb );\n\n\t\t// Prefix-sum arrays for SAH evaluation\n\t\tthis.leftPrefixMin = new Float32Array( mb * 3 );\n\t\tthis.leftPrefixMax = new Float32Array( mb * 3 );\n\t\tthis.leftPrefixCount = new Uint32Array( mb );\n\t\tthis.rightPrefixMin = new Float32Array( mb * 3 );\n\t\tthis.rightPrefixMax = new Float32Array( mb * 3 );\n\t\tthis.rightPrefixCount = new Uint32Array( mb );\n\n\t}\n\n\tgetOptimalBinCount( triangleCount ) {\n\n\t\tif ( triangleCount <= 16 ) return this.minBins;\n\t\tif ( triangleCount <= 64 ) return 16;\n\t\tif ( triangleCount <= 256 ) return 32;\n\t\tif ( triangleCount <= 1024 ) return 48;\n\t\treturn this.maxBins;\n\n\t}\n\n\tsetAdaptiveBinConfig( config ) {\n\n\t\tif ( config.minBins !== undefined ) this.minBins = Math.max( 4, config.minBins );\n\t\tif ( config.maxBins !== undefined ) this.maxBins = Math.min( 128, config.maxBins );\n\t\tif ( config.baseBins !== undefined ) this.numBins = config.baseBins;\n\t\tif ( config.maxBins !== undefined ) this.initializeBinArrays();\n\n\t}\n\n\tsetMortonConfig( config ) {\n\n\t\tif ( config.enabled !== undefined ) this.useMortonCodes = config.enabled;\n\t\tif ( config.bits !== undefined ) this.mortonBits = Math.max( 6, Math.min( 10, config.bits ) );\n\t\tif ( config.threshold !== undefined ) this.mortonClusterThreshold = Math.max( 16, config.threshold );\n\n\t}\n\n\tsetFallbackConfig( config ) {\n\n\t\tif ( config.objectMedian !== undefined ) this.enableObjectMedianFallback = config.objectMedian;\n\t\tif ( config.spatialMedian !== undefined ) this.enableSpatialMedianFallback = config.spatialMedian;\n\n\t}\n\n\tsetTreeletConfig( config ) {\n\n\t\tif ( config.enabled !== undefined ) this.enableTreeletOptimization = config.enabled;\n\t\tif ( config.size !== undefined ) this.treeletSize = Math.max( 3, Math.min( 12, config.size ) );\n\t\tif ( config.passes !== undefined ) this.treeletOptimizationPasses = Math.max( 1, Math.min( 3, config.passes ) );\n\t\tif ( config.minImprovement !== undefined ) this.treeletMinImprovement = Math.max( 0.001, config.minImprovement );\n\n\t}\n\n\tdisableTreeletOptimization() {\n\n\t\tthis.enableTreeletOptimization = false;\n\n\t}\n\n\tsetReinsertionConfig( config ) {\n\n\t\tif ( config.enabled !== undefined ) this.enableReinsertionOptimization = config.enabled;\n\t\tif ( config.batchSizeRatio !== undefined ) this.reinsertionBatchSizeRatio = Math.max( 0.005, Math.min( 0.1, config.batchSizeRatio ) );\n\t\tif ( config.maxIterations !== undefined ) this.reinsertionMaxIterations = Math.max( 1, Math.min( 5, config.maxIterations ) );\n\n\t}\n\n\t/**\n\t * Fill per-triangle arrays (centroids, bMin, bMax, indices) from triangle data.\n\t * Arrays must already be allocated on `this` before calling.\n\t */\n\tinitializeTriangleArrays() {\n\n\t\tconst n = this.totalTriangles;\n\t\tconst src = this.triangles;\n\t\tconst PA = TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET;\n\t\tconst PB = TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET;\n\t\tconst PC = TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET;\n\n\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\tconst base = i * FPT;\n\t\t\tconst ax = src[ base + PA ], ay = src[ base + PA + 1 ], az = src[ base + PA + 2 ];\n\t\t\tconst bx = src[ base + PB ], by = src[ base + PB + 1 ], bz = src[ base + PB + 2 ];\n\t\t\tconst cx = src[ base + PC ], cy = src[ base + PC + 1 ], cz = src[ base + PC + 2 ];\n\n\t\t\tconst o3 = i * 3;\n\t\t\tthis.centroids[ o3 ] = ( ax + bx + cx ) / 3;\n\t\t\tthis.centroids[ o3 + 1 ] = ( ay + by + cy ) / 3;\n\t\t\tthis.centroids[ o3 + 2 ] = ( az + bz + cz ) / 3;\n\n\t\t\tthis.bMin[ o3 ] = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx );\n\t\t\tthis.bMin[ o3 + 1 ] = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy );\n\t\t\tthis.bMin[ o3 + 2 ] = az < bz ? ( az < cz ? az : cz ) : ( bz < cz ? bz : cz );\n\n\t\t\tthis.bMax[ o3 ] = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx );\n\t\t\tthis.bMax[ o3 + 1 ] = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy );\n\t\t\tthis.bMax[ o3 + 2 ] = az > bz ? ( az > cz ? az : cz ) : ( bz > cz ? bz : cz );\n\n\t\t\tthis.indices[ i ] = i;\n\n\t\t}\n\n\t}\n\n\t// --- Morton code helpers ---\n\n\texpandBits( value ) {\n\n\t\tvalue = ( value * 0x00010001 ) & 0xFF0000FF;\n\t\tvalue = ( value * 0x00000101 ) & 0x0F00F00F;\n\t\tvalue = ( value * 0x00000011 ) & 0xC30C30C3;\n\t\tvalue = ( value * 0x00000005 ) & 0x49249249;\n\t\treturn value;\n\n\t}\n\n\tmorton3D( x, y, z ) {\n\n\t\treturn ( this.expandBits( z ) << 2 ) + ( this.expandBits( y ) << 1 ) + this.expandBits( x );\n\n\t}\n\n\tcomputeMortonCodeForIndex( idx, sceneMinX, sceneMinY, sceneMinZ, rangeX, rangeY, rangeZ ) {\n\n\t\tconst c = this.centroids;\n\t\tconst o = idx * 3;\n\t\tconst mortonScale = ( 1 << this.mortonBits ) - 1;\n\n\t\tlet nx = rangeX > 0 ? ( c[ o ] - sceneMinX ) / rangeX : 0;\n\t\tlet ny = rangeY > 0 ? ( c[ o + 1 ] - sceneMinY ) / rangeY : 0;\n\t\tlet nz = rangeZ > 0 ? ( c[ o + 2 ] - sceneMinZ ) / rangeZ : 0;\n\n\t\tconst x = Math.max( 0, Math.min( mortonScale, Math.floor( nx * mortonScale ) ) );\n\t\tconst y = Math.max( 0, Math.min( mortonScale, Math.floor( ny * mortonScale ) ) );\n\t\tconst z = Math.max( 0, Math.min( mortonScale, Math.floor( nz * mortonScale ) ) );\n\n\t\treturn this.morton3D( x, y, z );\n\n\t}\n\n\tsortTrianglesByMortonCode() {\n\n\t\tconst n = this.totalTriangles;\n\t\tif ( ! this.useMortonCodes || n < this.mortonClusterThreshold ) return;\n\n\t\tconst startTime = performance.now();\n\t\tconst c = this.centroids;\n\t\tconst indices = this.indices;\n\n\t\t// Compute scene bounds from centroids\n\t\tlet sMinX = Infinity, sMinY = Infinity, sMinZ = Infinity;\n\t\tlet sMaxX = - Infinity, sMaxY = - Infinity, sMaxZ = - Infinity;\n\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\tconst idx = indices[ i ];\n\t\t\tconst o = idx * 3;\n\t\t\tconst cx = c[ o ], cy = c[ o + 1 ], cz = c[ o + 2 ];\n\t\t\tif ( cx < sMinX ) sMinX = cx;\n\t\t\tif ( cy < sMinY ) sMinY = cy;\n\t\t\tif ( cz < sMinZ ) sMinZ = cz;\n\t\t\tif ( cx > sMaxX ) sMaxX = cx;\n\t\t\tif ( cy > sMaxY ) sMaxY = cy;\n\t\t\tif ( cz > sMaxZ ) sMaxZ = cz;\n\n\t\t}\n\n\t\tconst rX = sMaxX - sMinX, rY = sMaxY - sMinY, rZ = sMaxZ - sMinZ;\n\n\t\t// Compute morton codes (inlined to avoid per-triangle method dispatch)\n\t\tconst mc = this.mortonCodes;\n\t\tconst mortonScale = ( 1 << this.mortonBits ) - 1;\n\t\tconst invRX = rX > 0 ? mortonScale / rX : 0;\n\t\tconst invRY = rY > 0 ? mortonScale / rY : 0;\n\t\tconst invRZ = rZ > 0 ? mortonScale / rZ : 0;\n\n\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\tconst triIdx = indices[ i ];\n\t\t\tconst o = triIdx * 3;\n\n\t\t\tlet mx = ( c[ o ] - sMinX ) * invRX;\n\t\t\tlet my = ( c[ o + 1 ] - sMinY ) * invRY;\n\t\t\tlet mz = ( c[ o + 2 ] - sMinZ ) * invRZ;\n\n\t\t\t// Clamp and truncate to integer\n\t\t\tmx = mx < 0 ? 0 : ( mx > mortonScale ? mortonScale : mx ) | 0;\n\t\t\tmy = my < 0 ? 0 : ( my > mortonScale ? mortonScale : my ) | 0;\n\t\t\tmz = mz < 0 ? 0 : ( mz > mortonScale ? mortonScale : mz ) | 0;\n\n\t\t\t// Inline expandBits + morton3D\n\t\t\tmx = ( mx * 0x00010001 ) & 0xFF0000FF;\n\t\t\tmx = ( mx * 0x00000101 ) & 0x0F00F00F;\n\t\t\tmx = ( mx * 0x00000011 ) & 0xC30C30C3;\n\t\t\tmx = ( mx * 0x00000005 ) & 0x49249249;\n\n\t\t\tmy = ( my * 0x00010001 ) & 0xFF0000FF;\n\t\t\tmy = ( my * 0x00000101 ) & 0x0F00F00F;\n\t\t\tmy = ( my * 0x00000011 ) & 0xC30C30C3;\n\t\t\tmy = ( my * 0x00000005 ) & 0x49249249;\n\n\t\t\tmz = ( mz * 0x00010001 ) & 0xFF0000FF;\n\t\t\tmz = ( mz * 0x00000101 ) & 0x0F00F00F;\n\t\t\tmz = ( mz * 0x00000011 ) & 0xC30C30C3;\n\t\t\tmz = ( mz * 0x00000005 ) & 0x49249249;\n\n\t\t\tmc[ triIdx ] = ( mz << 2 ) + ( my << 1 ) + mx;\n\n\t\t}\n\n\t\t// Radix sort indices by morton code (O(N), 4 passes of 8-bit digits)\n\t\tconst temp = new Uint32Array( n );\n\t\tconst counts = new Uint32Array( 256 );\n\n\t\tfor ( let shift = 0; shift < 32; shift += 8 ) {\n\n\t\t\tcounts.fill( 0 );\n\n\t\t\t// Count digit occurrences\n\t\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\t\tcounts[ ( mc[ indices[ i ] ] >>> shift ) & 0xFF ] ++;\n\n\t\t\t}\n\n\t\t\t// Prefix sum\n\t\t\tlet total = 0;\n\t\t\tfor ( let i = 0; i < 256; i ++ ) {\n\n\t\t\t\tconst c = counts[ i ];\n\t\t\t\tcounts[ i ] = total;\n\t\t\t\ttotal += c;\n\n\t\t\t}\n\n\t\t\t// Scatter to temp\n\t\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\t\tconst digit = ( mc[ indices[ i ] ] >>> shift ) & 0xFF;\n\t\t\t\ttemp[ counts[ digit ] ++ ] = indices[ i ];\n\n\t\t\t}\n\n\t\t\tindices.set( temp );\n\n\t\t}\n\n\t\tthis.splitStats.mortonSortTime += performance.now() - startTime;\n\n\t}\n\n\t// --- Build entry points ---\n\n\t/**\n\t * Build BVH from triangle data.\n\t * Returns { bvhData: Float32Array, bvhRoot: true, reorderedTriangles: Float32Array }\n\t * where bvhData is the GPU-ready flat array (12 floats/node) and reorderedTriangles\n\t * is the BVH-ordered triangle data. Caller must use reorderedTriangles instead of\n\t * the original input (which is neutered after transfer to the worker).\n\t */\n\tbuild( triangles, depth = 30, progressCallback = null ) {\n\n\t\tthis.totalTriangles = triangles.byteLength / ( FPT * 4 );\n\t\tthis.processedTriangles = 0;\n\t\tthis.lastProgressUpdate = performance.now();\n\n\t\tif ( this.useWorker && typeof Worker !== 'undefined' ) {\n\n\t\t\treturn new Promise( ( resolve, reject ) => {\n\n\t\t\t\ttry {\n\n\t\t\t\t\tconst worker = new Worker(\n\t\t\t\t\t\tnew URL( './Workers/BVHWorker.js', import.meta.url ),\n\t\t\t\t\t\t{ type: 'module' }\n\t\t\t\t\t);\n\n\t\t\t\t\tconst triangleCount = this.totalTriangles;\n\t\t\t\t\tconst useShared = typeof SharedArrayBuffer !== 'undefined';\n\t\t\t\t\tconsole.log( `[BVHBuilder] SharedArrayBuffer: ${useShared ? 'enabled' : 'unavailable (using transfer fallback)'}` );\n\n\t\t\t\t\t// Pre-allocate SharedArrayBuffer for reordered output so worker\n\t\t\t\t\t// writes directly to shared memory (no transfer needed on return).\n\t\t\t\t\tconst sharedReorderBuffer = useShared\n\t\t\t\t\t\t? new SharedArrayBuffer( triangleCount * FPT * 4 )\n\t\t\t\t\t\t: null;\n\n\t\t\t\t\tworker.onmessage = ( e ) => {\n\n\t\t\t\t\t\tconst { bvhData, triangles: transferredTriangles, originalToBvh, error, progress, treeletStats } = e.data;\n\n\t\t\t\t\t\tif ( error ) {\n\n\t\t\t\t\t\t\tworker.terminate();\n\t\t\t\t\t\t\treject( new Error( error ) );\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( progress !== undefined && progressCallback ) {\n\n\t\t\t\t\t\t\tprogressCallback( progress );\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( treeletStats ) {\n\n\t\t\t\t\t\t\tthis.splitStats = treeletStats;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tworker.terminate();\n\n\t\t\t\t\t\t// Reordered triangles: from shared memory or fallback transfer\n\t\t\t\t\t\tconst reorderedTriangles = sharedReorderBuffer\n\t\t\t\t\t\t\t? new Float32Array( sharedReorderBuffer )\n\t\t\t\t\t\t\t: transferredTriangles;\n\n\t\t\t\t\t\tresolve( { bvhData, bvhRoot: true, reorderedTriangles, originalToBvh: originalToBvh || null } );\n\n\t\t\t\t\t};\n\n\t\t\t\t\tworker.onerror = ( error ) => {\n\n\t\t\t\t\t\tworker.terminate();\n\t\t\t\t\t\treject( error );\n\n\t\t\t\t\t};\n\n\t\t\t\t\t// Transfer the original buffer directly — avoids 362MB copy.\n\t\t\t\t\tconst transferBuffer = triangles.buffer;\n\t\t\t\t\tconst workerData = {\n\t\t\t\t\t\ttriangleData: transferBuffer,\n\t\t\t\t\t\ttriangleByteOffset: triangles.byteOffset,\n\t\t\t\t\t\ttriangleByteLength: triangles.byteLength,\n\t\t\t\t\t\ttriangleCount,\n\t\t\t\t\t\tdepth,\n\t\t\t\t\t\treportProgress: !! progressCallback,\n\t\t\t\t\t\tsharedReorderBuffer,\n\t\t\t\t\t\ttreeletOptimization: {\n\t\t\t\t\t\t\tenabled: this.enableTreeletOptimization,\n\t\t\t\t\t\t\tsize: this.treeletSize,\n\t\t\t\t\t\t\tpasses: this.treeletOptimizationPasses,\n\t\t\t\t\t\t\tminImprovement: this.treeletMinImprovement\n\t\t\t\t\t\t},\n\t\t\t\t\t\treinsertionOptimization: {\n\t\t\t\t\t\t\tenabled: this.enableReinsertionOptimization,\n\t\t\t\t\t\t\tbatchSizeRatio: this.reinsertionBatchSizeRatio,\n\t\t\t\t\t\t\tmaxIterations: this.reinsertionMaxIterations\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tworker.postMessage( workerData, [ transferBuffer ] );\n\n\t\t\t\t} catch ( error ) {\n\n\t\t\t\t\tconsole.warn( 'Worker creation failed, falling back to synchronous build:', error );\n\t\t\t\t\tresolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t} else {\n\n\t\t\treturn new Promise( ( resolve ) => {\n\n\t\t\t\tresolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );\n\n\t\t\t} );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Synchronous build + flatten helper for non-worker path.\n\t * @private\n\t */\n\t_buildSyncAndFlatten( triangles, depth, progressCallback ) {\n\n\t\tconst root = this.buildSync( triangles, depth, progressCallback );\n\t\tconst bvhData = this.flattenBVH( root );\n\t\t// Return reordered triangles if available (avoids 362MB copy)\n\t\tconst reorderedTriangles = this.reorderedTriangleData || null;\n\t\tconst originalToBvh = this.originalToBvhMap || null;\n\t\treturn { bvhData, bvhRoot: true, reorderedTriangles, originalToBvh };\n\n\t}\n\n\tbuildSync( triangles, depth = 30, progressCallback = null, reorderTarget = null ) {\n\n\t\tconst buildStartTime = performance.now();\n\n\t\t// Reset state\n\t\tthis.totalNodes = 0;\n\t\tthis.processedTriangles = 0;\n\t\tthis.triangles = triangles;\n\t\tthis.totalTriangles = triangles.byteLength / ( FPT * 4 );\n\t\tthis.lastProgressUpdate = performance.now();\n\n\t\tthis.splitStats = {\n\t\t\tsahSplits: 0,\n\t\t\tobjectMedianSplits: 0,\n\t\t\tspatialMedianSplits: 0,\n\t\t\tfailedSplits: 0,\n\t\t\tavgBinsUsed: 0,\n\t\t\ttotalSplitAttempts: 0,\n\t\t\tmortonSortTime: 0,\n\t\t\ttotalBuildTime: 0,\n\t\t\ttreeletOptimizationTime: 0,\n\t\t\ttreeletsProcessed: 0,\n\t\t\ttreeletsImproved: 0,\n\t\t\taverageSAHImprovement: 0,\n\t\t\treinsertionOptimizationTime: 0,\n\t\t\treinsertionsApplied: 0,\n\t\t\treinsertionIterations: 0,\n\t\t\tsaOrderTime: 0,\n\t\t\t// Granular phase timings\n\t\t\tinitTime: 0,\n\t\t\tsahBuildTime: 0,\n\t\t\treorderTime: 0\n\t\t};\n\n\t\tconst n = this.totalTriangles;\n\n\t\t// Phase 1: Allocate and initialize per-triangle arrays\n\t\tconst initStart = performance.now();\n\n\t\tthis.centroids = new Float32Array( n * 3 );\n\t\tthis.bMin = new Float32Array( n * 3 );\n\t\tthis.bMax = new Float32Array( n * 3 );\n\t\tthis.indices = new Uint32Array( n );\n\t\tthis.mortonCodes = new Uint32Array( n );\n\n\t\tthis.initializeTriangleArrays();\n\n\t\tthis.splitStats.initTime = performance.now() - initStart;\n\n\t\t// Phase 2: Morton code spatial clustering\n\t\tthis.sortTrianglesByMortonCode();\n\n\t\t// Phase 3: Recursive SAH build\n\t\tconst sahStart = performance.now();\n\t\tconst root = this.buildNodeRecursive( 0, n, depth, progressCallback );\n\t\tthis.splitStats.sahBuildTime = performance.now() - sahStart;\n\n\t\t// Phase 4: Treelet optimization\n\t\tif ( this.enableTreeletOptimization && this.totalTriangles > 1000 ) {\n\n\t\t\tconst isLargeScene = this.totalTriangles > this.treeletComplexityThreshold;\n\t\t\tconst adaptiveTreeletSize = isLargeScene ? 3 : this.treeletSize;\n\t\t\tconst adaptiveMaxTreelets = isLargeScene ? 10 : this.maxTreeletsPerScene;\n\n\t\t\tconst optimizer = new TreeletOptimizer( this.traversalCost, this.intersectionCost );\n\t\t\toptimizer.setTreeletSize( adaptiveTreeletSize );\n\t\t\toptimizer.setMinImprovement( this.treeletMinImprovement );\n\t\t\toptimizer.setMaxTreelets( adaptiveMaxTreelets );\n\n\t\t\tconst optimizationStartTime = performance.now();\n\n\t\t\tfor ( let pass = 0; pass < this.treeletOptimizationPasses; pass ++ ) {\n\n\t\t\t\tconst passCallback = progressCallback ? ( status ) => {\n\n\t\t\t\t\tprogressCallback( `Treelet optimization pass ${pass + 1}/${this.treeletOptimizationPasses}: ${status}` );\n\n\t\t\t\t} : null;\n\n\t\t\t\ttry {\n\n\t\t\t\t\toptimizer.optimizeBVH( root, passCallback );\n\n\t\t\t\t} catch ( error ) {\n\n\t\t\t\t\tconsole.error( `TreeletOptimizer: Error in pass ${pass + 1}:`, error );\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t\t// optimizeBVH resets stats internally, so afterStats reflects this pass only\n\t\t\t\tconst afterStats = optimizer.getStatistics();\n\t\t\t\tconst passTime = performance.now() - optimizationStartTime;\n\t\t\t\tif ( ( afterStats.treeletsImproved === 0 && pass > 0 ) || passTime > 15000 ) {\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst treeletTime = performance.now() - optimizationStartTime;\n\t\t\tthis.splitStats.treeletOptimizationTime = treeletTime;\n\t\t\tconst treeletStats = optimizer.getStatistics();\n\t\t\tthis.splitStats.treeletsProcessed = treeletStats.treeletsProcessed;\n\t\t\tthis.splitStats.treeletsImproved = treeletStats.treeletsImproved;\n\t\t\tthis.splitStats.averageSAHImprovement = treeletStats.averageSAHImprovement;\n\n\t\t}\n\n\t\t// Phase 4b: Reinsertion optimization (Meister & Bittner)\n\t\tif ( this.enableReinsertionOptimization && this.totalTriangles > 1000 ) {\n\n\t\t\tconst reinsertionOptimizer = new ReinsertionOptimizer( this.traversalCost, this.intersectionCost );\n\t\t\treinsertionOptimizer.setBatchSizeRatio( this.reinsertionBatchSizeRatio );\n\t\t\treinsertionOptimizer.setMaxIterations( this.reinsertionMaxIterations );\n\n\t\t\tconst reinsertCallback = progressCallback ? ( status ) => {\n\n\t\t\t\tprogressCallback( status );\n\n\t\t\t} : null;\n\n\t\t\ttry {\n\n\t\t\t\treinsertionOptimizer.optimizeBVH( root, reinsertCallback );\n\n\t\t\t} catch ( error ) {\n\n\t\t\t\tconsole.error( 'ReinsertionOptimizer: Error:', error );\n\n\t\t\t}\n\n\t\t\tconst reinsertStats = reinsertionOptimizer.getStatistics();\n\t\t\tthis.splitStats.reinsertionOptimizationTime = reinsertStats.timeMs;\n\t\t\tthis.splitStats.reinsertionsApplied = reinsertStats.reinsertionsApplied;\n\t\t\tthis.splitStats.reinsertionIterations = reinsertStats.iterations;\n\n\t\t}\n\n\t\t// Phase 5: Surface-area child ordering (DFS cache locality)\n\t\tconst saOrderStart = performance.now();\n\t\tthis.applySAOrdering( root );\n\t\tthis.splitStats.saOrderTime = performance.now() - saOrderStart;\n\n\t\t// Phase 6: Create reordered triangle data from final index order\n\t\tconst reorderStart = performance.now();\n\t\tconst triSrc = this.triangles;\n\t\tconst reordered = reorderTarget || new Float32Array( n * FPT );\n\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\tconst srcOff = this.indices[ i ] * FPT;\n\t\t\tconst dstOff = i * FPT;\n\t\t\treordered.set( triSrc.subarray( srcOff, srcOff + FPT ), dstOff );\n\n\t\t}\n\n\t\tthis.reorderedTriangleData = reordered;\n\n\t\t// Phase 6b: Build inverse index map for BVH refit\n\t\t// originalToBvh[originalTriIdx] = bvhOrderIdx\n\t\tconst originalToBvh = new Uint32Array( n );\n\t\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\t\toriginalToBvh[ this.indices[ i ] ] = i;\n\n\t\t}\n\n\t\tthis.originalToBvhMap = originalToBvh;\n\n\t\tthis.splitStats.reorderTime = performance.now() - reorderStart;\n\n\t\tthis.splitStats.totalBuildTime = performance.now() - buildStartTime;\n\n\t\t// Print phase-level timing breakdown\n\t\tconst total = this.splitStats.totalBuildTime;\n\t\tconst phasePct = ( ms ) => total > 0 ? ( ms / total * 100 ).toFixed( 1 ) + '%' : '0%';\n\n\t\tconst timingRows = [\n\t\t\t{ Phase: 'Init + bounds', 'Time (ms)': Math.round( this.splitStats.initTime ), '%': phasePct( this.splitStats.initTime ) },\n\t\t\t{ Phase: 'Morton sort', 'Time (ms)': Math.round( this.splitStats.mortonSortTime ), '%': phasePct( this.splitStats.mortonSortTime ) },\n\t\t\t{ Phase: 'SAH recursive build', 'Time (ms)': Math.round( this.splitStats.sahBuildTime ), '%': phasePct( this.splitStats.sahBuildTime ) },\n\t\t\t{ Phase: 'Treelet optimization', 'Time (ms)': Math.round( this.splitStats.treeletOptimizationTime ), '%': phasePct( this.splitStats.treeletOptimizationTime ) },\n\t\t\t{ Phase: 'Reinsertion optimization', 'Time (ms)': Math.round( this.splitStats.reinsertionOptimizationTime ), '%': phasePct( this.splitStats.reinsertionOptimizationTime ) },\n\t\t\t{ Phase: 'SA ordering', 'Time (ms)': Math.round( this.splitStats.saOrderTime ), '%': phasePct( this.splitStats.saOrderTime ) },\n\t\t\t{ Phase: 'Triangle reorder', 'Time (ms)': Math.round( this.splitStats.reorderTime ), '%': phasePct( this.splitStats.reorderTime ) },\n\t\t\t{ Phase: 'TOTAL', 'Time (ms)': Math.round( total ), '%': '100%' }\n\t\t];\n\n\t\tconsole.groupCollapsed( `⏱ BVH Build (${n.toLocaleString()} triangles, ${Math.round( total )}ms)` );\n\t\tconsole.table( timingRows );\n\t\tconsole.table( {\n\t\t\t'Total Nodes': this.totalNodes,\n\t\t\t'Max Leaf Size': this.maxLeafSize,\n\t\t\t'SAH Splits': this.splitStats.sahSplits,\n\t\t\t'Object Median Splits': this.splitStats.objectMedianSplits,\n\t\t\t'Spatial Median Splits': this.splitStats.spatialMedianSplits,\n\t\t\t'Failed Splits': this.splitStats.failedSplits,\n\t\t\t'Treelets Processed': this.splitStats.treeletsProcessed,\n\t\t\t'Treelets Improved': this.splitStats.treeletsImproved,\n\t\t\t'Avg SAH Improvement': ( this.splitStats.averageSAHImprovement * 100 ).toFixed( 2 ) + '%',\n\t\t\t'Reinsertions Applied': this.splitStats.reinsertionsApplied,\n\t\t\t'Reinsertion Iterations': this.splitStats.reinsertionIterations,\n\t\t} );\n\t\tconsole.groupEnd();\n\n\t\tprogressCallback && progressCallback( 100 );\n\n\t\t// Free flat arrays (no longer needed)\n\t\tthis.centroids = null;\n\t\tthis.bMin = null;\n\t\tthis.bMax = null;\n\t\tthis.mortonCodes = null;\n\n\t\treturn root;\n\n\t}\n\n\tupdateProgress( trianglesProcessed, progressCallback ) {\n\n\t\tif ( ! progressCallback ) return;\n\n\t\tthis.processedTriangles += trianglesProcessed;\n\t\tconst now = performance.now();\n\t\tif ( now - this.lastProgressUpdate < this.progressUpdateInterval ) return;\n\n\t\tthis.lastProgressUpdate = now;\n\t\tconst progress = Math.min( Math.floor( ( this.processedTriangles / this.totalTriangles ) * 100 ), 99 );\n\t\tprogressCallback( progress );\n\n\t}\n\n\t// --- Top-level BVH build for parallel construction ---\n\n\t/**\n\t * Build top levels of BVH, creating frontier leaves for parallel subtree construction.\n\t * Frontier leaves are marked with `isFrontier = true` and recorded in `this.frontierTasks`.\n\t * @param {number} start - Start index in indices array\n\t * @param {number} end - End index in indices array\n\t * @param {number} depth - Remaining depth budget for full tree\n\t * @param {number} frontierDepthRemaining - Levels still to build before creating frontier leaves\n\t * @param {Function} progressCallback - Optional progress callback\n\t * @param {number} preMinX - Precomputed bounds (optional)\n\t */\n\tbuildNodeRecursiveToDepth( start, end, depth, frontierDepthRemaining, progressCallback, preMinX, preMinY, preMinZ, preMaxX, preMaxY, preMaxZ ) {\n\n\t\tconst node = new BVHNode();\n\t\tthis.totalNodes ++;\n\n\t\tconst count = end - start;\n\n\t\t// Use precomputed bounds from parent's partition, or compute for root\n\t\tif ( preMinX !== undefined ) {\n\n\t\t\tnode.minX = preMinX; node.minY = preMinY; node.minZ = preMinZ;\n\t\t\tnode.maxX = preMaxX; node.maxY = preMaxY; node.maxZ = preMaxZ;\n\n\t\t} else {\n\n\t\t\tthis.updateNodeBounds( node, start, end );\n\n\t\t}\n\n\t\t// Normal leaf condition (small enough to not need a subtree)\n\t\tif ( count <= this.maxLeafSize || depth <= 0 ) {\n\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\treturn node;\n\n\t\t}\n\n\t\t// Frontier condition: stop recursion and record as parallel task\n\t\tif ( frontierDepthRemaining <= 0 && count > this.maxLeafSize * 16 ) {\n\n\t\t\tconst taskId = this.frontierTasks.length;\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tnode.isFrontier = true;\n\t\t\tnode.frontierTaskId = taskId;\n\t\t\tthis.frontierTasks.push( {\n\t\t\t\ttaskId,\n\t\t\t\tstart,\n\t\t\t\tend,\n\t\t\t\tdepth,\n\t\t\t\tpreMinX: node.minX, preMinY: node.minY, preMinZ: node.minZ,\n\t\t\t\tpreMaxX: node.maxX, preMaxY: node.maxY, preMaxZ: node.maxZ\n\t\t\t} );\n\t\t\treturn node;\n\n\t\t}\n\n\t\t// Find best split using SAH\n\t\tconst splitInfo = this.findBestSplitPositionSAH( start, end, node );\n\n\t\tif ( ! splitInfo.success ) {\n\n\t\t\tthis.splitStats.failedSplits ++;\n\n\t\t\t// If we haven't reached frontier depth yet, make it a frontier task anyway\n\t\t\tif ( frontierDepthRemaining > 0 || count <= this.maxLeafSize * 16 ) {\n\n\t\t\t\tnode.triangleOffset = start;\n\t\t\t\tnode.triangleCount = count;\n\t\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\t\treturn node;\n\n\t\t\t}\n\n\t\t\tconst taskId = this.frontierTasks.length;\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tnode.isFrontier = true;\n\t\t\tnode.frontierTaskId = taskId;\n\t\t\tthis.frontierTasks.push( {\n\t\t\t\ttaskId,\n\t\t\t\tstart,\n\t\t\t\tend,\n\t\t\t\tdepth,\n\t\t\t\tpreMinX: node.minX, preMinY: node.minY, preMinZ: node.minZ,\n\t\t\t\tpreMaxX: node.maxX, preMaxY: node.maxY, preMaxZ: node.maxZ\n\t\t\t} );\n\t\t\treturn node;\n\n\t\t}\n\n\t\t// Track split method\n\t\tif ( splitInfo.method === 'SAH' ) this.splitStats.sahSplits ++;\n\t\telse if ( splitInfo.method === 'object_median' ) this.splitStats.objectMedianSplits ++;\n\t\telse if ( splitInfo.method === 'spatial_median' ) this.splitStats.spatialMedianSplits ++;\n\n\t\t// Partition and compute child bounds in one pass\n\t\tthis.partitionWithBounds( start, end, splitInfo.axis, splitInfo.pos );\n\n\t\tconst p = this._partResult;\n\t\tconst mid = p.mid;\n\t\tconst lMnX = p.lMinX, lMnY = p.lMinY, lMnZ = p.lMinZ;\n\t\tconst lMxX = p.lMaxX, lMxY = p.lMaxY, lMxZ = p.lMaxZ;\n\t\tconst rMnX = p.rMinX, rMnY = p.rMinY, rMnZ = p.rMinZ;\n\t\tconst rMxX = p.rMaxX, rMxY = p.rMaxY, rMxZ = p.rMaxZ;\n\n\t\t// Degenerate partition fallback\n\t\tif ( mid === start || mid === end ) {\n\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\treturn node;\n\n\t\t}\n\n\t\tnode.leftChild = this.buildNodeRecursiveToDepth(\n\t\t\tstart, mid, depth - 1, frontierDepthRemaining - 1, progressCallback,\n\t\t\tlMnX, lMnY, lMnZ, lMxX, lMxY, lMxZ\n\t\t);\n\t\tnode.rightChild = this.buildNodeRecursiveToDepth(\n\t\t\tmid, end, depth - 1, frontierDepthRemaining - 1, progressCallback,\n\t\t\trMnX, rMnY, rMnZ, rMxX, rMxY, rMxZ\n\t\t);\n\n\t\treturn node;\n\n\t}\n\n\t// --- Recursive BVH build (operates on index range) ---\n\n\tbuildNodeRecursive( start, end, depth, progressCallback, preMinX, preMinY, preMinZ, preMaxX, preMaxY, preMaxZ ) {\n\n\t\tconst node = new BVHNode();\n\t\tthis.totalNodes ++;\n\n\t\tconst count = end - start;\n\n\t\t// Use precomputed bounds from parent's partition, or compute for root\n\t\tif ( preMinX !== undefined ) {\n\n\t\t\tnode.minX = preMinX; node.minY = preMinY; node.minZ = preMinZ;\n\t\t\tnode.maxX = preMaxX; node.maxY = preMaxY; node.maxZ = preMaxZ;\n\n\t\t} else {\n\n\t\t\tthis.updateNodeBounds( node, start, end );\n\n\t\t}\n\n\t\t// Leaf condition\n\t\tif ( count <= this.maxLeafSize || depth <= 0 ) {\n\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\treturn node;\n\n\t\t}\n\n\t\t// Find best split using SAH\n\t\tconst splitInfo = this.findBestSplitPositionSAH( start, end, node );\n\n\t\tif ( ! splitInfo.success ) {\n\n\t\t\tthis.splitStats.failedSplits ++;\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\treturn node;\n\n\t\t}\n\n\t\t// Track split method\n\t\tif ( splitInfo.method === 'SAH' ) this.splitStats.sahSplits ++;\n\t\telse if ( splitInfo.method === 'object_median' ) this.splitStats.objectMedianSplits ++;\n\t\telse if ( splitInfo.method === 'spatial_median' ) this.splitStats.spatialMedianSplits ++;\n\n\t\t// Partition and compute child bounds in one pass\n\t\tthis.partitionWithBounds( start, end, splitInfo.axis, splitInfo.pos );\n\n\t\t// Snapshot into locals — _partResult is reused and will be overwritten by child recursion\n\t\tconst p = this._partResult;\n\t\tconst mid = p.mid;\n\t\tconst lMnX = p.lMinX, lMnY = p.lMinY, lMnZ = p.lMinZ;\n\t\tconst lMxX = p.lMaxX, lMxY = p.lMaxY, lMxZ = p.lMaxZ;\n\t\tconst rMnX = p.rMinX, rMnY = p.rMinY, rMnZ = p.rMinZ;\n\t\tconst rMxX = p.rMaxX, rMxY = p.rMaxY, rMxZ = p.rMaxZ;\n\n\t\t// Degenerate partition fallback\n\t\tif ( mid === start || mid === end ) {\n\n\t\t\tnode.triangleOffset = start;\n\t\t\tnode.triangleCount = count;\n\t\t\tthis.updateProgress( count, progressCallback );\n\t\t\treturn node;\n\n\t\t}\n\n\t\tnode.leftChild = this.buildNodeRecursive(\n\t\t\tstart, mid, depth - 1, progressCallback,\n\t\t\tlMnX, lMnY, lMnZ, lMxX, lMxY, lMxZ\n\t\t);\n\t\tnode.rightChild = this.buildNodeRecursive(\n\t\t\tmid, end, depth - 1, progressCallback,\n\t\t\trMnX, rMnY, rMnZ, rMxX, rMxY, rMxZ\n\t\t);\n\n\t\treturn node;\n\n\t}\n\n\t// Partition indices and accumulate child bounds in a single pass\n\tpartitionWithBounds( start, end, axis, splitPos ) {\n\n\t\tconst idx = this.indices;\n\t\tconst c = this.centroids;\n\t\tconst bMn = this.bMin;\n\t\tconst bMx = this.bMax;\n\n\t\tlet lo = start;\n\t\tlet hi = end - 1;\n\n\t\tlet lMinX = Infinity, lMinY = Infinity, lMinZ = Infinity;\n\t\tlet lMaxX = - Infinity, lMaxY = - Infinity, lMaxZ = - Infinity;\n\t\tlet rMinX = Infinity, rMinY = Infinity, rMinZ = Infinity;\n\t\tlet rMaxX = - Infinity, rMaxY = - Infinity, rMaxZ = - Infinity;\n\n\t\twhile ( lo <= hi ) {\n\n\t\t\tconst triIdx = idx[ lo ];\n\t\t\tconst o = triIdx * 3;\n\n\t\t\tif ( c[ o + axis ] <= splitPos ) {\n\n\t\t\t\t// Left partition — accumulate bounds\n\t\t\t\tif ( bMn[ o ] < lMinX ) lMinX = bMn[ o ];\n\t\t\t\tif ( bMn[ o + 1 ] < lMinY ) lMinY = bMn[ o + 1 ];\n\t\t\t\tif ( bMn[ o + 2 ] < lMinZ ) lMinZ = bMn[ o + 2 ];\n\t\t\t\tif ( bMx[ o ] > lMaxX ) lMaxX = bMx[ o ];\n\t\t\t\tif ( bMx[ o + 1 ] > lMaxY ) lMaxY = bMx[ o + 1 ];\n\t\t\t\tif ( bMx[ o + 2 ] > lMaxZ ) lMaxZ = bMx[ o + 2 ];\n\t\t\t\tlo ++;\n\n\t\t\t} else {\n\n\t\t\t\t// Right partition — accumulate bounds\n\t\t\t\tif ( bMn[ o ] < rMinX ) rMinX = bMn[ o ];\n\t\t\t\tif ( bMn[ o + 1 ] < rMinY ) rMinY = bMn[ o + 1 ];\n\t\t\t\tif ( bMn[ o + 2 ] < rMinZ ) rMinZ = bMn[ o + 2 ];\n\t\t\t\tif ( bMx[ o ] > rMaxX ) rMaxX = bMx[ o ];\n\t\t\t\tif ( bMx[ o + 1 ] > rMaxY ) rMaxY = bMx[ o + 1 ];\n\t\t\t\tif ( bMx[ o + 2 ] > rMaxZ ) rMaxZ = bMx[ o + 2 ];\n\n\t\t\t\t// Swap indices[lo] and indices[hi]\n\t\t\t\tidx[ lo ] = idx[ hi ];\n\t\t\t\tidx[ hi ] = triIdx;\n\t\t\t\thi --;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst r = this._partResult;\n\t\tr.mid = lo;\n\t\tr.lMinX = lMinX; r.lMinY = lMinY; r.lMinZ = lMinZ;\n\t\tr.lMaxX = lMaxX; r.lMaxY = lMaxY; r.lMaxZ = lMaxZ;\n\t\tr.rMinX = rMinX; r.rMinY = rMinY; r.rMinZ = rMinZ;\n\t\tr.rMaxX = rMaxX; r.rMaxY = rMaxY; r.rMaxZ = rMaxZ;\n\t\treturn r;\n\n\t}\n\n\tupdateNodeBounds( node, start, end ) {\n\n\t\tlet minX = Infinity, minY = Infinity, minZ = Infinity;\n\t\tlet maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;\n\n\t\tconst idx = this.indices;\n\t\tconst bMin = this.bMin;\n\t\tconst bMax = this.bMax;\n\n\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\tconst o = idx[ i ] * 3;\n\t\t\tif ( bMin[ o ] < minX ) minX = bMin[ o ];\n\t\t\tif ( bMin[ o + 1 ] < minY ) minY = bMin[ o + 1 ];\n\t\t\tif ( bMin[ o + 2 ] < minZ ) minZ = bMin[ o + 2 ];\n\t\t\tif ( bMax[ o ] > maxX ) maxX = bMax[ o ];\n\t\t\tif ( bMax[ o + 1 ] > maxY ) maxY = bMax[ o + 1 ];\n\t\t\tif ( bMax[ o + 2 ] > maxZ ) maxZ = bMax[ o + 2 ];\n\n\t\t}\n\n\t\tnode.minX = minX; node.minY = minY; node.minZ = minZ;\n\t\tnode.maxX = maxX; node.maxY = maxY; node.maxZ = maxZ;\n\n\t}\n\n\t// --- SAH with prefix-sum (O(bins) per axis instead of O(bins²)) ---\n\n\tfindBestSplitPositionSAH( start, end, parentNode ) {\n\n\t\tlet bestCost = Infinity;\n\t\tlet bestAxis = - 1;\n\t\tlet bestPos = 0;\n\n\t\tconst parentSA = this.computeSurfaceAreaFlat( parentNode.minX, parentNode.minY, parentNode.minZ, parentNode.maxX, parentNode.maxY, parentNode.maxZ );\n\t\tconst count = end - start;\n\t\tconst leafCost = this.intersectionCost * count;\n\t\tconst currentBinCount = this.getOptimalBinCount( count );\n\n\t\tthis.splitStats.totalSplitAttempts ++;\n\t\tthis.splitStats.avgBinsUsed = ( ( this.splitStats.avgBinsUsed * ( this.splitStats.totalSplitAttempts - 1 ) ) + currentBinCount ) / this.splitStats.totalSplitAttempts;\n\n\t\tconst idx = this.indices;\n\t\tconst c = this.centroids;\n\t\tconst bMn = this.bMin;\n\t\tconst bMx = this.bMax;\n\t\tconst bbMin = this.binBoundsMin;\n\t\tconst bbMax = this.binBoundsMax;\n\t\tconst bc = this.binCounts;\n\t\tconst lpMin = this.leftPrefixMin;\n\t\tconst lpMax = this.leftPrefixMax;\n\t\tconst lpc = this.leftPrefixCount;\n\t\tconst rpMin = this.rightPrefixMin;\n\t\tconst rpMax = this.rightPrefixMax;\n\t\tconst rpc = this.rightPrefixCount;\n\n\t\t// Single pass: find centroid bounds for all 3 axes\n\t\tlet cMin0 = Infinity, cMax0 = - Infinity;\n\t\tlet cMin1 = Infinity, cMax1 = - Infinity;\n\t\tlet cMin2 = Infinity, cMax2 = - Infinity;\n\n\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\tconst o = idx[ i ] * 3;\n\t\t\tconst c0 = c[ o ], c1 = c[ o + 1 ], c2 = c[ o + 2 ];\n\t\t\tif ( c0 < cMin0 ) cMin0 = c0; if ( c0 > cMax0 ) cMax0 = c0;\n\t\t\tif ( c1 < cMin1 ) cMin1 = c1; if ( c1 > cMax1 ) cMax1 = c1;\n\t\t\tif ( c2 < cMin2 ) cMin2 = c2; if ( c2 > cMax2 ) cMax2 = c2;\n\n\t\t}\n\n\t\tconst centroidMin = [ cMin0, cMin1, cMin2 ];\n\t\tconst centroidMax = [ cMax0, cMax1, cMax2 ];\n\n\t\tfor ( let axis = 0; axis < 3; axis ++ ) {\n\n\t\t\tconst minCentroid = centroidMin[ axis ];\n\t\t\tconst maxCentroid = centroidMax[ axis ];\n\n\t\t\tif ( maxCentroid - minCentroid < 1e-6 ) continue;\n\n\t\t\t// Reset bins\n\t\t\tfor ( let b = 0; b < currentBinCount; b ++ ) {\n\n\t\t\t\tbc[ b ] = 0;\n\t\t\t\tconst b3 = b * 3;\n\t\t\t\tbbMin[ b3 ] = Infinity; bbMin[ b3 + 1 ] = Infinity; bbMin[ b3 + 2 ] = Infinity;\n\t\t\t\tbbMax[ b3 ] = - Infinity; bbMax[ b3 + 1 ] = - Infinity; bbMax[ b3 + 2 ] = - Infinity;\n\n\t\t\t}\n\n\t\t\t// Place triangles into bins\n\t\t\tconst binScale = currentBinCount / ( maxCentroid - minCentroid );\n\t\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\t\tconst triIdx = idx[ i ];\n\t\t\t\tconst cv = c[ triIdx * 3 + axis ];\n\t\t\t\tlet bi = Math.floor( ( cv - minCentroid ) * binScale );\n\t\t\t\tif ( bi >= currentBinCount ) bi = currentBinCount - 1;\n\n\t\t\t\tbc[ bi ] ++;\n\t\t\t\tconst b3 = bi * 3;\n\t\t\t\tconst t3 = triIdx * 3;\n\n\t\t\t\tif ( bMn[ t3 ] < bbMin[ b3 ] ) bbMin[ b3 ] = bMn[ t3 ];\n\t\t\t\tif ( bMn[ t3 + 1 ] < bbMin[ b3 + 1 ] ) bbMin[ b3 + 1 ] = bMn[ t3 + 1 ];\n\t\t\t\tif ( bMn[ t3 + 2 ] < bbMin[ b3 + 2 ] ) bbMin[ b3 + 2 ] = bMn[ t3 + 2 ];\n\t\t\t\tif ( bMx[ t3 ] > bbMax[ b3 ] ) bbMax[ b3 ] = bMx[ t3 ];\n\t\t\t\tif ( bMx[ t3 + 1 ] > bbMax[ b3 + 1 ] ) bbMax[ b3 + 1 ] = bMx[ t3 + 1 ];\n\t\t\t\tif ( bMx[ t3 + 2 ] > bbMax[ b3 + 2 ] ) bbMax[ b3 + 2 ] = bMx[ t3 + 2 ];\n\n\t\t\t}\n\n\t\t\t// Build left prefix sums\n\t\t\tlpc[ 0 ] = bc[ 0 ];\n\t\t\tlpMin[ 0 ] = bbMin[ 0 ]; lpMin[ 1 ] = bbMin[ 1 ]; lpMin[ 2 ] = bbMin[ 2 ];\n\t\t\tlpMax[ 0 ] = bbMax[ 0 ]; lpMax[ 1 ] = bbMax[ 1 ]; lpMax[ 2 ] = bbMax[ 2 ];\n\n\t\t\tfor ( let b = 1; b < currentBinCount; b ++ ) {\n\n\t\t\t\tconst b3 = b * 3;\n\t\t\t\tconst p3 = ( b - 1 ) * 3;\n\t\t\t\tlpc[ b ] = lpc[ b - 1 ] + bc[ b ];\n\t\t\t\tconst lp0 = lpMin[ p3 ], lb0 = bbMin[ b3 ];\n\t\t\t\tconst lp1 = lpMin[ p3 + 1 ], lb1 = bbMin[ b3 + 1 ];\n\t\t\t\tconst lp2 = lpMin[ p3 + 2 ], lb2 = bbMin[ b3 + 2 ];\n\t\t\t\tlpMin[ b3 ] = lp0 < lb0 ? lp0 : lb0;\n\t\t\t\tlpMin[ b3 + 1 ] = lp1 < lb1 ? lp1 : lb1;\n\t\t\t\tlpMin[ b3 + 2 ] = lp2 < lb2 ? lp2 : lb2;\n\t\t\t\tconst lxp0 = lpMax[ p3 ], lxb0 = bbMax[ b3 ];\n\t\t\t\tconst lxp1 = lpMax[ p3 + 1 ], lxb1 = bbMax[ b3 + 1 ];\n\t\t\t\tconst lxp2 = lpMax[ p3 + 2 ], lxb2 = bbMax[ b3 + 2 ];\n\t\t\t\tlpMax[ b3 ] = lxp0 > lxb0 ? lxp0 : lxb0;\n\t\t\t\tlpMax[ b3 + 1 ] = lxp1 > lxb1 ? lxp1 : lxb1;\n\t\t\t\tlpMax[ b3 + 2 ] = lxp2 > lxb2 ? lxp2 : lxb2;\n\n\t\t\t}\n\n\t\t\t// Build right prefix sums\n\t\t\tconst last = currentBinCount - 1;\n\t\t\tconst l3 = last * 3;\n\t\t\trpc[ last ] = bc[ last ];\n\t\t\trpMin[ l3 ] = bbMin[ l3 ]; rpMin[ l3 + 1 ] = bbMin[ l3 + 1 ]; rpMin[ l3 + 2 ] = bbMin[ l3 + 2 ];\n\t\t\trpMax[ l3 ] = bbMax[ l3 ]; rpMax[ l3 + 1 ] = bbMax[ l3 + 1 ]; rpMax[ l3 + 2 ] = bbMax[ l3 + 2 ];\n\n\t\t\tfor ( let b = last - 1; b >= 0; b -- ) {\n\n\t\t\t\tconst b3 = b * 3;\n\t\t\t\tconst n3 = ( b + 1 ) * 3;\n\t\t\t\trpc[ b ] = rpc[ b + 1 ] + bc[ b ];\n\t\t\t\tconst rn0 = rpMin[ n3 ], rb0 = bbMin[ b3 ];\n\t\t\t\tconst rn1 = rpMin[ n3 + 1 ], rb1 = bbMin[ b3 + 1 ];\n\t\t\t\tconst rn2 = rpMin[ n3 + 2 ], rb2 = bbMin[ b3 + 2 ];\n\t\t\t\trpMin[ b3 ] = rn0 < rb0 ? rn0 : rb0;\n\t\t\t\trpMin[ b3 + 1 ] = rn1 < rb1 ? rn1 : rb1;\n\t\t\t\trpMin[ b3 + 2 ] = rn2 < rb2 ? rn2 : rb2;\n\t\t\t\tconst rxn0 = rpMax[ n3 ], rxb0 = bbMax[ b3 ];\n\t\t\t\tconst rxn1 = rpMax[ n3 + 1 ], rxb1 = bbMax[ b3 + 1 ];\n\t\t\t\tconst rxn2 = rpMax[ n3 + 2 ], rxb2 = bbMax[ b3 + 2 ];\n\t\t\t\trpMax[ b3 ] = rxn0 > rxb0 ? rxn0 : rxb0;\n\t\t\t\trpMax[ b3 + 1 ] = rxn1 > rxb1 ? rxn1 : rxb1;\n\t\t\t\trpMax[ b3 + 2 ] = rxn2 > rxb2 ? rxn2 : rxb2;\n\n\t\t\t}\n\n\t\t\t// Evaluate splits using prefix sums (O(bins) instead of O(bins²))\n\t\t\tfor ( let i = 1; i < currentBinCount; i ++ ) {\n\n\t\t\t\tconst leftIdx = ( i - 1 ) * 3;\n\t\t\t\tconst rightIdx = i * 3;\n\t\t\t\tconst leftCount = lpc[ i - 1 ];\n\t\t\t\tconst rightCount = rpc[ i ];\n\n\t\t\t\tif ( leftCount === 0 || rightCount === 0 ) continue;\n\n\t\t\t\t// Inlined surface area: 2*(dx*dy + dy*dz + dz*dx)\n\t\t\t\tconst ldx = lpMax[ leftIdx ] - lpMin[ leftIdx ];\n\t\t\t\tconst ldy = lpMax[ leftIdx + 1 ] - lpMin[ leftIdx + 1 ];\n\t\t\t\tconst ldz = lpMax[ leftIdx + 2 ] - lpMin[ leftIdx + 2 ];\n\t\t\t\tconst leftSA = 2 * ( ldx * ldy + ldy * ldz + ldz * ldx );\n\n\t\t\t\tconst rdx = rpMax[ rightIdx ] - rpMin[ rightIdx ];\n\t\t\t\tconst rdy = rpMax[ rightIdx + 1 ] - rpMin[ rightIdx + 1 ];\n\t\t\t\tconst rdz = rpMax[ rightIdx + 2 ] - rpMin[ rightIdx + 2 ];\n\t\t\t\tconst rightSA = 2 * ( rdx * rdy + rdy * rdz + rdz * rdx );\n\n\t\t\t\tconst cost = this.traversalCost +\n\t\t\t\t\t( leftSA / parentSA ) * leftCount * this.intersectionCost +\n\t\t\t\t\t( rightSA / parentSA ) * rightCount * this.intersectionCost;\n\n\t\t\t\tif ( cost < bestCost && cost < leafCost ) {\n\n\t\t\t\t\tbestCost = cost;\n\t\t\t\t\tbestAxis = axis;\n\t\t\t\t\tbestPos = minCentroid + ( maxCentroid - minCentroid ) * i / currentBinCount;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Fallbacks\n\t\tif ( bestAxis === - 1 ) {\n\n\t\t\tif ( this.enableObjectMedianFallback ) return this.findObjectMedianSplit( start, end );\n\t\t\tif ( this.enableSpatialMedianFallback ) return this.findSpatialMedianSplit( start, end );\n\t\t\treturn { success: false, method: 'fallbacks_disabled' };\n\n\t\t}\n\n\t\treturn { success: true, axis: bestAxis, pos: bestPos, method: 'SAH', binsUsed: currentBinCount };\n\n\t}\n\n\tfindObjectMedianSplit( start, end ) {\n\n\t\tconst idx = this.indices;\n\t\tconst c = this.centroids;\n\t\tlet bestAxis = - 1;\n\t\tlet bestSpread = - 1;\n\n\t\tfor ( let axis = 0; axis < 3; axis ++ ) {\n\n\t\t\tlet minC = Infinity, maxC = - Infinity;\n\t\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\t\tconst v = c[ idx[ i ] * 3 + axis ];\n\t\t\t\tif ( v < minC ) minC = v;\n\t\t\t\tif ( v > maxC ) maxC = v;\n\n\t\t\t}\n\n\t\t\tconst spread = maxC - minC;\n\t\t\tif ( spread > bestSpread ) {\n\n\t\t\t\tbestSpread = spread;\n\t\t\t\tbestAxis = axis;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( bestAxis === - 1 || bestSpread < 1e-10 ) {\n\n\t\t\tif ( this.enableSpatialMedianFallback ) return this.findSpatialMedianSplit( start, end );\n\t\t\treturn { success: false, method: 'object_median_failed' };\n\n\t\t}\n\n\t\t// Quickselect to find median centroid value in O(N) average\n\t\tconst count = end - start;\n\t\tconst k = start + Math.floor( count / 2 );\n\t\tthis.quickselect( start, end, k, bestAxis );\n\n\t\tlet splitPos = c[ idx[ k ] * 3 + bestAxis ];\n\n\t\t// Quickselect guarantees [start,k) <= splitPos, so leftCount >= k-start > 0.\n\t\t// Check if degenerate: all elements at [k+1,end) also <= splitPos (all same value).\n\t\tlet degenerate = true;\n\t\tfor ( let i = k + 1; i < end; i ++ ) {\n\n\t\t\tif ( c[ idx[ i ] * 3 + bestAxis ] > splitPos ) {\n\n\t\t\t\tdegenerate = false;\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( degenerate ) {\n\n\t\t\t// Nudge split between median and its left neighbor\n\t\t\tlet leftMax = - Infinity;\n\t\t\tfor ( let i = start; i < k; i ++ ) {\n\n\t\t\t\tconst v = c[ idx[ i ] * 3 + bestAxis ];\n\t\t\t\tif ( v > leftMax ) leftMax = v;\n\n\t\t\t}\n\n\t\t\tif ( leftMax < splitPos ) {\n\n\t\t\t\tsplitPos = ( leftMax + splitPos ) * 0.5;\n\n\t\t\t} else {\n\n\t\t\t\tif ( this.enableSpatialMedianFallback ) return this.findSpatialMedianSplit( start, end );\n\t\t\t\treturn { success: false, method: 'object_median_degenerate' };\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn { success: true, axis: bestAxis, pos: splitPos, method: 'object_median' };\n\n\t}\n\n\tfindSpatialMedianSplit( start, end ) {\n\n\t\tconst idx = this.indices;\n\t\tconst c = this.centroids;\n\t\tconst bMn = this.bMin;\n\t\tconst bMx = this.bMax;\n\t\tlet bestAxis = - 1;\n\t\tlet bestSpread = - 1;\n\t\tlet bestMin = 0, bestMax = 0;\n\n\t\tfor ( let axis = 0; axis < 3; axis ++ ) {\n\n\t\t\tlet minB = Infinity, maxB = - Infinity;\n\t\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\t\tconst o = idx[ i ] * 3 + axis;\n\t\t\t\tif ( bMn[ o ] < minB ) minB = bMn[ o ];\n\t\t\t\tif ( bMx[ o ] > maxB ) maxB = bMx[ o ];\n\n\t\t\t}\n\n\t\t\tconst spread = maxB - minB;\n\t\t\tif ( spread > bestSpread ) {\n\n\t\t\t\tbestSpread = spread;\n\t\t\t\tbestAxis = axis;\n\t\t\t\tbestMin = minB;\n\t\t\t\tbestMax = maxB;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( bestAxis === - 1 || bestSpread < 1e-12 ) {\n\n\t\t\treturn { success: false, method: 'spatial_median_failed' };\n\n\t\t}\n\n\t\tlet splitPos = ( bestMin + bestMax ) * 0.5;\n\n\t\t// Verify split quality\n\t\tconst count = end - start;\n\t\tlet leftCount = 0;\n\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\tif ( c[ idx[ i ] * 3 + bestAxis ] <= splitPos ) leftCount ++;\n\n\t\t}\n\n\t\tif ( leftCount === 0 || leftCount === count ) {\n\n\t\t\t// Force balanced via quickselect median (O(N) instead of O(N log N) sort)\n\t\t\tconst k = start + Math.floor( count / 2 );\n\t\t\tthis.quickselect( start, end, k, bestAxis );\n\n\t\t\tconst medianVal = c[ idx[ k ] * 3 + bestAxis ];\n\n\t\t\t// Check if all centroids are identical on this axis\n\t\t\tlet allSame = true;\n\t\t\tfor ( let i = start; i < end; i ++ ) {\n\n\t\t\t\tif ( c[ idx[ i ] * 3 + bestAxis ] !== medianVal ) {\n\n\t\t\t\t\tallSame = false;\n\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( allSame ) {\n\n\t\t\t\treturn { success: false, method: 'spatial_median_degenerate' };\n\n\t\t\t}\n\n\t\t\t// Nudge split between median and its neighbor to guarantee a non-empty partition\n\t\t\tlet leftMax = - Infinity;\n\t\t\tfor ( let i = start; i < k; i ++ ) {\n\n\t\t\t\tconst v = c[ idx[ i ] * 3 + bestAxis ];\n\t\t\t\tif ( v > leftMax ) leftMax = v;\n\n\t\t\t}\n\n\t\t\tif ( leftMax < medianVal ) {\n\n\t\t\t\tsplitPos = ( leftMax + medianVal ) * 0.5;\n\n\t\t\t} else {\n\n\t\t\t\t// leftMax == medianVal; find first element > medianVal in right half\n\t\t\t\tlet rightMin = Infinity;\n\t\t\t\tfor ( let i = k + 1; i < end; i ++ ) {\n\n\t\t\t\t\tconst v = c[ idx[ i ] * 3 + bestAxis ];\n\t\t\t\t\tif ( v < rightMin ) rightMin = v;\n\n\t\t\t\t}\n\n\t\t\t\tsplitPos = ( medianVal + rightMin ) * 0.5;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn { success: true, axis: bestAxis, pos: splitPos, method: 'spatial_median' };\n\n\t}\n\n\t// --- Quickselect (Hoare's selection algorithm) ---\n\n\tquickselect( start, end, k, axis ) {\n\n\t\tconst idx = this.indices;\n\t\tconst c = this.centroids;\n\n\t\tlet lo = start;\n\t\tlet hi = end - 1;\n\n\t\twhile ( lo < hi ) {\n\n\t\t\t// Median-of-three pivot selection\n\t\t\tconst mid = ( lo + hi ) >>> 1;\n\t\t\tconst vLo = c[ idx[ lo ] * 3 + axis ];\n\t\t\tconst vMid = c[ idx[ mid ] * 3 + axis ];\n\t\t\tconst vHi = c[ idx[ hi ] * 3 + axis ];\n\n\t\t\t// Sort lo, mid, hi and use mid as pivot\n\t\t\tif ( vLo > vMid ) {\n\n\t\t\t\tconst t = idx[ lo ];\n\t\t\t\tidx[ lo ] = idx[ mid ];\n\t\t\t\tidx[ mid ] = t;\n\n\t\t\t}\n\n\t\t\tif ( vLo > vHi ) {\n\n\t\t\t\tconst t = idx[ lo ];\n\t\t\t\tidx[ lo ] = idx[ hi ];\n\t\t\t\tidx[ hi ] = t;\n\n\t\t\t}\n\n\t\t\tif ( vMid > vHi ) {\n\n\t\t\t\tconst t = idx[ mid ];\n\t\t\t\tidx[ mid ] = idx[ hi ];\n\t\t\t\tidx[ hi ] = t;\n\n\t\t\t}\n\n\t\t\tconst pivot = c[ idx[ mid ] * 3 + axis ];\n\n\t\t\t// Partition around pivot\n\t\t\tlet i = lo;\n\t\t\tlet j = hi;\n\n\t\t\twhile ( i <= j ) {\n\n\t\t\t\twhile ( c[ idx[ i ] * 3 + axis ] < pivot ) i ++;\n\t\t\t\twhile ( c[ idx[ j ] * 3 + axis ] > pivot ) j --;\n\n\t\t\t\tif ( i <= j ) {\n\n\t\t\t\t\tconst t = idx[ i ]; idx[ i ] = idx[ j ]; idx[ j ] = t;\n\t\t\t\t\ti ++;\n\t\t\t\t\tj --;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( j < k ) lo = i;\n\t\t\tif ( i > k ) hi = j;\n\n\t\t}\n\n\t}\n\n\t// --- Surface-area child ordering (DFS cache locality) ---\n\n\t/**\n\t * Ensure left child always has >= surface area of right child.\n\t * This places the larger subtree first in the DFS flat layout,\n\t * improving cache locality during traversal.\n\t * Iterative post-order to avoid stack overflow on deep trees.\n\t */\n\tapplySAOrdering( root ) {\n\n\t\tif ( ! root || ! root.leftChild ) return;\n\n\t\t// Iterative post-order traversal — swap after both children are processed\n\t\tconst stack = [ root ];\n\t\tconst order = [];\n\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst node = stack.pop();\n\t\t\tif ( ! node.leftChild || ! node.rightChild ) continue;\n\n\t\t\torder.push( node );\n\t\t\tstack.push( node.leftChild );\n\t\t\tstack.push( node.rightChild );\n\n\t\t}\n\n\t\t// Process in reverse (bottom-up)\n\t\tfor ( let i = order.length - 1; i >= 0; i -- ) {\n\n\t\t\tconst node = order[ i ];\n\t\t\tconst L = node.leftChild;\n\t\t\tconst R = node.rightChild;\n\n\t\t\tconst ldx = L.maxX - L.minX, ldy = L.maxY - L.minY, ldz = L.maxZ - L.minZ;\n\t\t\tconst rdx = R.maxX - R.minX, rdy = R.maxY - R.minY, rdz = R.maxZ - R.minZ;\n\n\t\t\tif ( rdx * rdy + rdy * rdz + rdz * rdx > ldx * ldy + ldy * ldz + ldz * ldx ) {\n\n\t\t\t\tnode.leftChild = R;\n\t\t\t\tnode.rightChild = L;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t// --- BVH flattening (GPU-ready format) ---\n\n\t/**\n\t * Flatten BVH tree into a Float32Array (16 floats per node).\n\t * Layout per node (4 × vec4):\n\t * Inner: vec4( leftMin.xyz, leftChildIdx ) vec4( leftMax.xyz, rightChildIdx )\n\t * vec4( rightMin.xyz, 0 ) vec4( rightMax.xyz, 0 )\n\t * Leaf: vec4( triOffset, triCount, 0, -1 ) [zeros × 12]\n\t *\n\t * This is the same format as TextureCreator.createBVHRawData,\n\t * but runs inside the worker to avoid structured-clone overhead\n\t * of transferring 1M+ BVHNode objects.\n\t */\n\tflattenBVH( root ) {\n\n\t\t// First pass: assign indices via pre-order traversal\n\t\tconst nodes = [];\n\t\tconst stack = [ root ];\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst node = stack.pop();\n\t\t\tnode._flatIndex = nodes.length;\n\t\t\tnodes.push( node );\n\t\t\t// Push right first so left is processed first (pre-order)\n\t\t\tif ( node.rightChild ) stack.push( node.rightChild );\n\t\t\tif ( node.leftChild ) stack.push( node.leftChild );\n\n\t\t}\n\n\t\t// Second pass: write flat data\n\t\t// Layout: 4 vec4 per node (16 floats)\n\t\t// Inner: [leftMin.xyz, leftChild] [leftMax.xyz, rightChild] [rightMin.xyz, 0] [rightMax.xyz, 0]\n\t\t// Leaf: [triOffset, triCount, 0, -1] [0,0,0,0] [0,0,0,0] [0,0,0,0]\n\t\tconst FLOATS_PER_NODE = 16;\n\t\tconst data = new Float32Array( nodes.length * FLOATS_PER_NODE );\n\n\t\tfor ( let i = 0; i < nodes.length; i ++ ) {\n\n\t\t\tconst node = nodes[ i ];\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\n\t\t\tif ( node.leftChild ) {\n\n\t\t\t\t// Inner node: store children's AABBs\n\t\t\t\tconst left = node.leftChild;\n\t\t\t\tconst right = node.rightChild;\n\n\t\t\t\tdata[ o ] = left.minX;\n\t\t\t\tdata[ o + 1 ] = left.minY;\n\t\t\t\tdata[ o + 2 ] = left.minZ;\n\t\t\t\tdata[ o + 3 ] = left._flatIndex;\n\n\t\t\t\tdata[ o + 4 ] = left.maxX;\n\t\t\t\tdata[ o + 5 ] = left.maxY;\n\t\t\t\tdata[ o + 6 ] = left.maxZ;\n\t\t\t\tdata[ o + 7 ] = right._flatIndex;\n\n\t\t\t\tdata[ o + 8 ] = right.minX;\n\t\t\t\tdata[ o + 9 ] = right.minY;\n\t\t\t\tdata[ o + 10 ] = right.minZ;\n\t\t\t\t// data[o+11] = 0 (padding)\n\n\t\t\t\tdata[ o + 12 ] = right.maxX;\n\t\t\t\tdata[ o + 13 ] = right.maxY;\n\t\t\t\tdata[ o + 14 ] = right.maxZ;\n\t\t\t\t// data[o+15] = 0 (padding)\n\n\t\t\t} else {\n\n\t\t\t\t// Leaf node: triOffset, triCount in vec4(0), marked by leftChild = -1\n\t\t\t\tdata[ o ] = node.triangleOffset;\n\t\t\t\tdata[ o + 1 ] = node.triangleCount;\n\t\t\t\t// data[o+2] = 0 (padding)\n\t\t\t\tdata[ o + 3 ] = - 1; // Leaf marker\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\t/**\n\t * Flatten BVH tree marking frontier leaves with -2 sentinel.\n\t * Returns the flat data and a frontier map for assembly.\n\t * @param {BVHNode} root\n\t * @returns {{ flatData: Float32Array, frontierMap: Array<{taskId: number, flatIndex: number}> }}\n\t */\n\tflattenBVHWithFrontier( root ) {\n\n\t\tconst FLOATS_PER_NODE = 16;\n\n\t\t// First pass: assign indices via pre-order traversal\n\t\tconst nodes = [];\n\t\tconst stack = [ root ];\n\t\twhile ( stack.length > 0 ) {\n\n\t\t\tconst node = stack.pop();\n\t\t\tnode._flatIndex = nodes.length;\n\t\t\tnodes.push( node );\n\t\t\tif ( node.rightChild ) stack.push( node.rightChild );\n\t\t\tif ( node.leftChild ) stack.push( node.leftChild );\n\n\t\t}\n\n\t\t// Second pass: write flat data\n\t\tconst data = new Float32Array( nodes.length * FLOATS_PER_NODE );\n\t\tconst frontierMap = [];\n\n\t\tfor ( let i = 0; i < nodes.length; i ++ ) {\n\n\t\t\tconst node = nodes[ i ];\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\n\t\t\tif ( node.leftChild ) {\n\n\t\t\t\t// Inner node\n\t\t\t\tconst left = node.leftChild;\n\t\t\t\tconst right = node.rightChild;\n\n\t\t\t\tdata[ o ] = left.minX;\n\t\t\t\tdata[ o + 1 ] = left.minY;\n\t\t\t\tdata[ o + 2 ] = left.minZ;\n\t\t\t\tdata[ o + 3 ] = left._flatIndex;\n\n\t\t\t\tdata[ o + 4 ] = left.maxX;\n\t\t\t\tdata[ o + 5 ] = left.maxY;\n\t\t\t\tdata[ o + 6 ] = left.maxZ;\n\t\t\t\tdata[ o + 7 ] = right._flatIndex;\n\n\t\t\t\tdata[ o + 8 ] = right.minX;\n\t\t\t\tdata[ o + 9 ] = right.minY;\n\t\t\t\tdata[ o + 10 ] = right.minZ;\n\n\t\t\t\tdata[ o + 12 ] = right.maxX;\n\t\t\t\tdata[ o + 13 ] = right.maxY;\n\t\t\t\tdata[ o + 14 ] = right.maxZ;\n\n\t\t\t} else if ( node.isFrontier ) {\n\n\t\t\t\t// Frontier leaf: mark with -2 sentinel, use the taskId stored on the node\n\t\t\t\tconst taskId = node.frontierTaskId;\n\t\t\t\tdata[ o ] = node.triangleOffset;\n\t\t\t\tdata[ o + 1 ] = node.triangleCount;\n\t\t\t\tdata[ o + 2 ] = taskId;\n\t\t\t\tdata[ o + 3 ] = - 2; // Frontier sentinel\n\n\t\t\t\tfrontierMap.push( { taskId, flatIndex: i } );\n\n\t\t\t} else {\n\n\t\t\t\t// Regular leaf\n\t\t\t\tdata[ o ] = node.triangleOffset;\n\t\t\t\tdata[ o + 1 ] = node.triangleCount;\n\t\t\t\tdata[ o + 3 ] = - 1; // Leaf marker\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn { flatData: data, frontierMap, nodeCount: nodes.length };\n\n\t}\n\n\t/**\n\t * Assemble the final BVH from top-level flat data and parallel-built subtrees.\n\t * @param {Float32Array} topFlatData - Flattened top-level tree with frontier sentinels\n\t * @param {number} topNodeCount - Number of nodes in top-level tree\n\t * @param {Array} frontierMap - Array of {taskId, flatIndex} from flattenBVHWithFrontier\n\t * @param {Array<{taskId: number, flatData: Float32Array, nodeCount: number}>} subtreeResults\n\t * @returns {Float32Array} Final GPU-ready BVH flat data\n\t */\n\tassembleParallelBVH( topFlatData, topNodeCount, frontierMap, subtreeResults ) {\n\n\t\tconst FLOATS_PER_NODE = 16;\n\n\t\t// Sort subtreeResults by taskId for consistent ordering\n\t\tconst sortedResults = [ ...subtreeResults ].sort( ( a, b ) => a.taskId - b.taskId );\n\n\t\t// Calculate total node count\n\t\tlet totalNodes = topNodeCount;\n\t\tfor ( let i = 0; i < sortedResults.length; i ++ ) {\n\n\t\t\ttotalNodes += sortedResults[ i ].nodeCount;\n\n\t\t}\n\n\t\t// Allocate final array\n\t\tconst finalData = new Float32Array( totalNodes * FLOATS_PER_NODE );\n\n\t\t// Copy top-level data\n\t\tfinalData.set( topFlatData );\n\n\t\t// Build taskId → frontierMap lookup\n\t\tconst frontierByTaskId = new Map();\n\t\tfor ( const entry of frontierMap ) {\n\n\t\t\tfrontierByTaskId.set( entry.taskId, entry.flatIndex );\n\n\t\t}\n\n\t\t// Append each subtree and patch references\n\t\tlet globalOffset = topNodeCount;\n\n\t\tfor ( let i = 0; i < sortedResults.length; i ++ ) {\n\n\t\t\tconst result = sortedResults[ i ];\n\t\t\tconst subtreeData = result.flatData;\n\t\t\tconst subtreeNodeCount = result.nodeCount;\n\t\t\tconst destOffset = globalOffset * FLOATS_PER_NODE;\n\n\t\t\t// Copy subtree data into final array\n\t\t\tfinalData.set( subtreeData, destOffset );\n\n\t\t\t// Adjust child indices within the subtree by adding globalOffset\n\t\t\tfor ( let j = 0; j < subtreeNodeCount; j ++ ) {\n\n\t\t\t\tconst o = destOffset + j * FLOATS_PER_NODE;\n\n\t\t\t\t// Check if inner node (not a leaf: leaf has -1 at o+3)\n\t\t\t\tif ( finalData[ o + 3 ] !== - 1 ) {\n\n\t\t\t\t\t// Adjust leftChildIndex and rightChildIndex\n\t\t\t\t\tfinalData[ o + 3 ] += globalOffset;\n\t\t\t\t\tfinalData[ o + 7 ] += globalOffset;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Overwrite frontier leaf with subtree root data\n\t\t\tconst frontierFlatIndex = frontierByTaskId.get( result.taskId );\n\t\t\tif ( frontierFlatIndex !== undefined ) {\n\n\t\t\t\tconst frontierOffset = frontierFlatIndex * FLOATS_PER_NODE;\n\t\t\t\tconst subtreeRootOffset = destOffset;\n\n\t\t\t\t// Copy subtree root's 16 floats over the frontier leaf\n\t\t\t\tfor ( let k = 0; k < FLOATS_PER_NODE; k ++ ) {\n\n\t\t\t\t\tfinalData[ frontierOffset + k ] = finalData[ subtreeRootOffset + k ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tglobalOffset += subtreeNodeCount;\n\n\t\t}\n\n\t\treturn finalData;\n\n\t}\n\n\t// --- Surface area helpers ---\n\n\tcomputeSurfaceAreaFlat( minX, minY, minZ, maxX, maxY, maxZ ) {\n\n\t\tconst dx = maxX - minX;\n\t\tconst dy = maxY - minY;\n\t\tconst dz = maxZ - minZ;\n\t\treturn 2 * ( dx * dy + dy * dz + dz * dx );\n\n\t}\n\n}\n","import { BVHBuilder } from '../BVHBuilder.js';\n\nconst FPT = 32; // FLOATS_PER_TRIANGLE\n\n// --- Message dispatcher ---\n\nself.onmessage = function ( e ) {\n\n\tconst data = e.data;\n\tconst type = data.type;\n\n\tif ( type === 'buildPhase1' ) {\n\n\t\thandlePhase1( data );\n\n\t} else if ( type === 'assemble' ) {\n\n\t\thandleAssemble( data );\n\n\t} else {\n\n\t\t// Legacy: full single-worker build (backward compatible)\n\t\thandleFullBuild( data );\n\n\t}\n\n};\n\n// --- Phase 1: Init + Morton sort + top-level SAH build ---\n\nfunction handlePhase1( data ) {\n\n\tconst {\n\t\tsharedTriangleData, sharedCentroids, sharedBMin, sharedBMax,\n\t\tsharedIndices, sharedMortonCodes,\n\t\ttriangleCount, depth, parallelDepth,\n\t\treportProgress, treeletOptimization\n\t} = data;\n\n\ttry {\n\n\t\tconst builder = new BVHBuilder();\n\n\t\tif ( treeletOptimization ) {\n\n\t\t\tbuilder.setTreeletConfig( treeletOptimization );\n\n\t\t}\n\n\t\tconst progressCallback = reportProgress ? ( progress ) => {\n\n\t\t\tself.postMessage( { type: 'progress', progress } );\n\n\t\t} : null;\n\n\t\t// Attach shared buffer views\n\t\tbuilder.triangles = new Float32Array( sharedTriangleData );\n\t\tbuilder.centroids = new Float32Array( sharedCentroids );\n\t\tbuilder.bMin = new Float32Array( sharedBMin );\n\t\tbuilder.bMax = new Float32Array( sharedBMax );\n\t\tbuilder.indices = new Uint32Array( sharedIndices );\n\t\tbuilder.mortonCodes = new Uint32Array( sharedMortonCodes );\n\t\tbuilder.totalTriangles = triangleCount;\n\n\t\t// Reset state\n\t\tbuilder.totalNodes = 0;\n\t\tbuilder.processedTriangles = 0;\n\t\tbuilder.lastProgressUpdate = performance.now();\n\n\t\tbuilder.splitStats = {\n\t\t\tsahSplits: 0, objectMedianSplits: 0, spatialMedianSplits: 0,\n\t\t\tfailedSplits: 0, avgBinsUsed: 0, totalSplitAttempts: 0,\n\t\t\tmortonSortTime: 0, totalBuildTime: 0, treeletOptimizationTime: 0,\n\t\t\ttreeletsProcessed: 0, treeletsImproved: 0, averageSAHImprovement: 0,\n\t\t\tinitTime: 0, sahBuildTime: 0, reorderTime: 0\n\t\t};\n\n\t\tconst startTime = performance.now();\n\n\t\t// Phase 1a: Initialize per-triangle arrays (writes into shared buffers)\n\t\tconst initStart = performance.now();\n\t\tbuilder.initializeTriangleArrays();\n\t\tbuilder.splitStats.initTime = performance.now() - initStart;\n\n\t\t// Phase 1b: Morton code spatial clustering\n\t\tbuilder.sortTrianglesByMortonCode();\n\n\t\t// Phase 1c: Build top-level tree to parallelDepth\n\t\tbuilder.frontierTasks = [];\n\t\tconst sahStart = performance.now();\n\t\tconst root = builder.buildNodeRecursiveToDepth( 0, triangleCount, depth, parallelDepth, progressCallback );\n\t\tbuilder.splitStats.sahBuildTime = performance.now() - sahStart;\n\n\t\t// Phase 1d: Surface-area child ordering (DFS cache locality)\n\t\tbuilder.applySAOrdering( root );\n\n\t\t// Phase 1e: Flatten top-level tree with frontier sentinels\n\t\tconst flattenStart = performance.now();\n\t\tconst { flatData, frontierMap, nodeCount } = builder.flattenBVHWithFrontier( root );\n\t\tconst flattenTime = performance.now() - flattenStart;\n\n\t\tconst totalTime = performance.now() - startTime;\n\t\tconsole.log( `[BVHWorker] Phase 1: ${Math.round( totalTime )}ms (init: ${Math.round( builder.splitStats.initTime )}ms, morton: ${Math.round( builder.splitStats.mortonSortTime )}ms, SAH: ${Math.round( builder.splitStats.sahBuildTime )}ms, flatten: ${Math.round( flattenTime )}ms), ${builder.frontierTasks.length} frontier tasks` );\n\n\t\tself.postMessage( {\n\t\t\ttype: 'phase1Result',\n\t\t\ttopFlatData: flatData,\n\t\t\ttopNodeCount: nodeCount,\n\t\t\tfrontierTasks: builder.frontierTasks,\n\t\t\tfrontierMap,\n\t\t\tsplitStats: builder.splitStats\n\t\t}, [ flatData.buffer ] );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( '[BVHWorker] Phase 1 error:', error );\n\t\tself.postMessage( { type: 'error', error: error.message } );\n\n\t}\n\n}\n\n// --- Phase 3: Assemble final BVH + reorder triangles ---\n\nfunction handleAssemble( data ) {\n\n\tconst {\n\t\ttopFlatData, topNodeCount, frontierMap, subtreeResults,\n\t\tsharedTriangleData, sharedIndices, sharedReorderBuffer,\n\t\ttriangleCount\n\t} = data;\n\n\ttry {\n\n\t\tconst startTime = performance.now();\n\t\tconst builder = new BVHBuilder();\n\n\t\t// Assemble the final BVH\n\t\tconst bvhData = builder.assembleParallelBVH(\n\t\t\ttopFlatData, topNodeCount, frontierMap, subtreeResults\n\t\t);\n\n\t\t// Reorder triangles using final indices from SharedArrayBuffer\n\t\tconst indices = new Uint32Array( sharedIndices );\n\t\tconst src = new Float32Array( sharedTriangleData );\n\t\tconst dst = new Float32Array( sharedReorderBuffer );\n\n\t\tfor ( let i = 0; i < triangleCount; i ++ ) {\n\n\t\t\tconst srcOff = indices[ i ] * FPT;\n\t\t\tconst dstOff = i * FPT;\n\t\t\tdst.set( src.subarray( srcOff, srcOff + FPT ), dstOff );\n\n\t\t}\n\n\t\t// Build inverse index map for BVH refit\n\t\tconst originalToBvh = new Uint32Array( triangleCount );\n\t\tfor ( let i = 0; i < triangleCount; i ++ ) {\n\n\t\t\toriginalToBvh[ indices[ i ] ] = i;\n\n\t\t}\n\n\t\tconst totalTime = performance.now() - startTime;\n\t\tconsole.log( `[BVHWorker] Phase 3 (assemble + reorder): ${Math.round( totalTime )}ms (${( bvhData.byteLength / 1024 / 1024 ).toFixed( 1 )}MB BVH)` );\n\n\t\tself.postMessage( {\n\t\t\ttype: 'assembleResult',\n\t\t\tbvhData,\n\t\t\toriginalToBvh,\n\t\t\ttriangleCount\n\t\t}, [ bvhData.buffer, originalToBvh.buffer ] );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( '[BVHWorker] Assembly error:', error );\n\t\tself.postMessage( { type: 'error', error: error.message } );\n\n\t}\n\n}\n\n// --- Legacy: full single-worker build ---\n\nfunction handleFullBuild( data ) {\n\n\tconst { triangleData, triangleByteOffset, triangleByteLength, depth, reportProgress, treeletOptimization, reinsertionOptimization, sharedReorderBuffer } = data;\n\tconst builder = new BVHBuilder();\n\n\ttry {\n\n\t\tif ( treeletOptimization ) {\n\n\t\t\tbuilder.setTreeletConfig( treeletOptimization );\n\n\t\t}\n\n\t\tif ( reinsertionOptimization ) {\n\n\t\t\tbuilder.setReinsertionConfig( reinsertionOptimization );\n\n\t\t}\n\n\t\tconst progressCallback = reportProgress ? ( progress ) => {\n\n\t\t\tself.postMessage( { progress } );\n\n\t\t} : null;\n\n\t\tconst inputTriangles = triangleByteOffset !== undefined\n\t\t\t? new Float32Array( triangleData, triangleByteOffset, triangleByteLength / 4 )\n\t\t\t: new Float32Array( triangleData );\n\n\t\tconst reorderTarget = sharedReorderBuffer\n\t\t\t? new Float32Array( sharedReorderBuffer )\n\t\t\t: null;\n\n\t\tconst bvhRoot = builder.buildSync( inputTriangles, depth, progressCallback, reorderTarget );\n\n\t\tconst flattenStart = performance.now();\n\t\tconst bvhData = builder.flattenBVH( bvhRoot );\n\t\tconst flattenTime = performance.now() - flattenStart;\n\t\tconsole.log( `[BVHWorker] Flatten BVH: ${Math.round( flattenTime )}ms (${( bvhData.byteLength / 1024 / 1024 ).toFixed( 1 )}MB)` );\n\n\t\tconst originalToBvh = builder.originalToBvhMap || null;\n\n\t\tif ( sharedReorderBuffer ) {\n\n\t\t\tconst transferables = [ bvhData.buffer ];\n\t\t\tif ( originalToBvh ) transferables.push( originalToBvh.buffer );\n\n\t\t\tself.postMessage( {\n\t\t\t\tbvhData,\n\t\t\t\toriginalToBvh,\n\t\t\t\ttriangleCount: inputTriangles.length / 32,\n\t\t\t\ttreeletStats: builder.splitStats\n\t\t\t}, transferables );\n\n\t\t} else {\n\n\t\t\tconst reorderedFloat32Array = builder.reorderedTriangleData;\n\t\t\tconst triangleCount = reorderedFloat32Array.byteLength / ( 32 * 4 );\n\n\t\t\tconst transferables = [ bvhData.buffer, reorderedFloat32Array.buffer ];\n\t\t\tif ( originalToBvh ) transferables.push( originalToBvh.buffer );\n\n\t\t\tself.postMessage( {\n\t\t\t\tbvhData,\n\t\t\t\ttriangles: reorderedFloat32Array,\n\t\t\t\toriginalToBvh,\n\t\t\t\ttriangleCount,\n\t\t\t\ttreeletStats: builder.splitStats\n\t\t\t}, transferables );\n\n\t\t}\n\n\t} catch ( error ) {\n\n\t\tconsole.error( '[BVHWorker] Error:', error );\n\t\tself.postMessage( { error: error.message } );\n\n\t}\n\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){function e(e,t,n,r){let i=n,a=n+r-1;for(;i<a;){let n=i+a>>1;e[n]<t?i=n+1:a=n}return i-n}function t(t,n,r){let i=new Float32Array(n*r),a=new Float32Array(r),o=0,s=0;for(let e=0;e<r;e++){let r=0;for(let a=0;a<n;a++){let s=e*n+a,c=t[4*s],l=t[4*s+1],u=t[4*s+2],d=.2126*c+.7152*l+.0722*u;r+=d,o+=d,i[s]=r}if(r!==0)for(let t=e*n,a=e*n+n;t<a;t++)i[t]/=r;s+=r,a[e]=s}if(s!==0)for(let e=0,t=a.length;e<t;e++)a[e]/=s;let c=new Float32Array(r);for(let t=0;t<r;t++)c[t]=(e(a,(t+1)/r,0,r)+.5)/r;let l=new Float32Array(n*r);for(let t=0;t<r;t++)for(let r=0;r<n;r++){let a=t*n+r;l[a]=(e(i,(r+1)/n,t*n,n)+.5)/n}return{marginalData:c,conditionalData:l,totalSum:o}}self.onmessage=function(e){let{floatData:n,width:r,height:i}=e.data;try{let e=t(n,r,i);self.postMessage({marginalData:e.marginalData,conditionalData:e.conditionalData,totalSum:e.totalSum,width:r,height:i},[e.marginalData.buffer,e.conditionalData.buffer])}catch(e){self.postMessage({error:e.message})}}})();
|
|
2
|
+
//# sourceMappingURL=CDFWorker-2MoynL4F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CDFWorker-2MoynL4F.js","names":[],"sources":["../../src/Processor/Workers/CDFWorker.js"],"sourcesContent":["/**\n * Web Worker for computing environment map CDF (Cumulative Distribution Function)\n * for importance sampling. Pure math — no Three.js dependencies.\n *\n * Input: { floatData: Float32Array, width, height }\n * Output: { marginalData: Float32Array, conditionalData: Float32Array, totalSum, width, height }\n */\n\nfunction binarySearchFindClosestIndexOf( array, targetValue, offset, count ) {\n\n\tlet lower = offset;\n\tlet upper = offset + count - 1;\n\n\twhile ( lower < upper ) {\n\n\t\tconst mid = ( lower + upper ) >> 1;\n\n\t\tif ( array[ mid ] < targetValue ) {\n\n\t\t\tlower = mid + 1;\n\n\t\t} else {\n\n\t\t\tupper = mid;\n\n\t\t}\n\n\t}\n\n\treturn lower - offset;\n\n}\n\nfunction buildCDF( floatData, width, height ) {\n\n\tconst cdfConditional = new Float32Array( width * height );\n\tconst cdfMarginal = new Float32Array( height );\n\n\tlet totalSumValue = 0.0;\n\tlet cumulativeWeightMarginal = 0.0;\n\n\t// Build conditional CDFs (per-row distribution)\n\tfor ( let y = 0; y < height; y ++ ) {\n\n\t\tlet cumulativeRowWeight = 0.0;\n\t\tfor ( let x = 0; x < width; x ++ ) {\n\n\t\t\tconst i = y * width + x;\n\t\t\tconst r = floatData[ 4 * i ];\n\t\t\tconst g = floatData[ 4 * i + 1 ];\n\t\t\tconst b = floatData[ 4 * i + 2 ];\n\n\t\t\t// Luminance (Rec. 709)\n\t\t\tconst weight = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n\t\t\tcumulativeRowWeight += weight;\n\t\t\ttotalSumValue += weight;\n\n\t\t\tcdfConditional[ i ] = cumulativeRowWeight;\n\n\t\t}\n\n\t\t// Normalize row CDF to [0, 1]\n\t\tif ( cumulativeRowWeight !== 0 ) {\n\n\t\t\tfor ( let i = y * width, l = y * width + width; i < l; i ++ ) {\n\n\t\t\t\tcdfConditional[ i ] /= cumulativeRowWeight;\n\n\t\t\t}\n\n\t\t}\n\n\t\tcumulativeWeightMarginal += cumulativeRowWeight;\n\t\tcdfMarginal[ y ] = cumulativeWeightMarginal;\n\n\t}\n\n\t// Normalize marginal CDF to [0, 1]\n\tif ( cumulativeWeightMarginal !== 0 ) {\n\n\t\tfor ( let i = 0, l = cdfMarginal.length; i < l; i ++ ) {\n\n\t\t\tcdfMarginal[ i ] /= cumulativeWeightMarginal;\n\n\t\t}\n\n\t}\n\n\t// Invert marginal CDF\n\tconst marginalData = new Float32Array( height );\n\tfor ( let i = 0; i < height; i ++ ) {\n\n\t\tconst dist = ( i + 1 ) / height;\n\t\tconst row = binarySearchFindClosestIndexOf( cdfMarginal, dist, 0, height );\n\t\tmarginalData[ i ] = ( row + 0.5 ) / height;\n\n\t}\n\n\t// Invert conditional CDFs\n\tconst conditionalData = new Float32Array( width * height );\n\tfor ( let y = 0; y < height; y ++ ) {\n\n\t\tfor ( let x = 0; x < width; x ++ ) {\n\n\t\t\tconst i = y * width + x;\n\t\t\tconst dist = ( x + 1 ) / width;\n\t\t\tconst col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );\n\t\t\tconditionalData[ i ] = ( col + 0.5 ) / width;\n\n\t\t}\n\n\t}\n\n\treturn { marginalData, conditionalData, totalSum: totalSumValue };\n\n}\n\nself.onmessage = function ( e ) {\n\n\tconst { floatData, width, height } = e.data;\n\n\ttry {\n\n\t\tconst result = buildCDF( floatData, width, height );\n\n\t\t// Transfer arrays back zero-copy\n\t\tself.postMessage(\n\t\t\t{\n\t\t\t\tmarginalData: result.marginalData,\n\t\t\t\tconditionalData: result.conditionalData,\n\t\t\t\ttotalSum: result.totalSum,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t},\n\t\t\t[ result.marginalData.buffer, result.conditionalData.buffer ]\n\t\t);\n\n\t} catch ( error ) {\n\n\t\tself.postMessage( { error: error.message } );\n\n\t}\n\n};\n"],"mappings":"YAQA,SAAS,EAAgC,EAAO,EAAa,EAAQ,EAAQ,CAE5E,IAAI,EAAQ,EACR,EAAQ,EAAS,EAAQ,EAE7B,KAAQ,EAAQ,GAAQ,CAEvB,IAAM,EAAQ,EAAQ,GAAW,EAE5B,EAAO,GAAQ,EAEnB,EAAQ,EAAM,EAId,EAAQ,EAMV,OAAO,EAAQ,EAIhB,SAAS,EAAU,EAAW,EAAO,EAAS,CAE7C,IAAM,EAAiB,IAAI,aAAc,EAAQ,EAAQ,CACnD,EAAc,IAAI,aAAc,EAAQ,CAE1C,EAAgB,EAChB,EAA2B,EAG/B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAAO,CAEnC,IAAI,EAAsB,EAC1B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAO,IAAO,CAElC,IAAM,EAAI,EAAI,EAAQ,EAChB,EAAI,EAAW,EAAI,GACnB,EAAI,EAAW,EAAI,EAAI,GACvB,EAAI,EAAW,EAAI,EAAI,GAGvB,EAAS,MAAS,EAAI,MAAS,EAAI,MAAS,EAClD,GAAuB,EACvB,GAAiB,EAEjB,EAAgB,GAAM,EAKvB,GAAK,IAAwB,EAE5B,IAAM,IAAI,EAAI,EAAI,EAAO,EAAI,EAAI,EAAQ,EAAO,EAAI,EAAG,IAEtD,EAAgB,IAAO,EAMzB,GAA4B,EAC5B,EAAa,GAAM,EAKpB,GAAK,IAA6B,EAEjC,IAAM,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,EAAI,EAAG,IAE/C,EAAa,IAAO,EAOtB,IAAM,EAAe,IAAI,aAAc,EAAQ,CAC/C,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAI5B,EAAc,IADF,EAAgC,GAD7B,EAAI,GAAM,EACsC,EAAG,EAAQ,CAC9C,IAAQ,EAKrC,IAAM,EAAkB,IAAI,aAAc,EAAQ,EAAQ,CAC1D,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAE5B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAO,IAAO,CAElC,IAAM,EAAI,EAAI,EAAQ,EAGtB,EAAiB,IADL,EAAgC,GAD7B,EAAI,GAAM,EACyC,EAAI,EAAO,EAAO,CACrD,IAAQ,EAMzC,MAAO,CAAE,eAAc,kBAAiB,SAAU,EAAe,CAIlE,KAAK,UAAY,SAAW,EAAI,CAE/B,GAAM,CAAE,YAAW,QAAO,UAAW,EAAE,KAEvC,GAAI,CAEH,IAAM,EAAS,EAAU,EAAW,EAAO,EAAQ,CAGnD,KAAK,YACJ,CACC,aAAc,EAAO,aACrB,gBAAiB,EAAO,gBACxB,SAAU,EAAO,SACjB,QACA,SACA,CACD,CAAE,EAAO,aAAa,OAAQ,EAAO,gBAAgB,OAAQ,CAC7D,OAEQ,EAAQ,CAEjB,KAAK,YAAa,CAAE,MAAO,EAAM,QAAS,CAAE"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){let e,t,n={MAX_BYTES_PER_TEXTURE:256*1024*1024,MAX_TEXTURE_DIMENSION:4096,CHUNK_SIZE:8,ADAPTIVE_CHUNK_SIZE:!0,MEMORY_SAFETY_FACTOR:.8};self.onmessage=async function(t){let{textures:n,maxTextureSize:a,method:o=`direct-transfer`}=t.data;try{e||r(a);let t=await i(n,a,o);self.postMessage(t,[t.data])}catch(e){console.error(`Worker processing failed:`,e),self.postMessage({error:e.message})}};function r(r){let i=Math.min(r,n.MAX_TEXTURE_DIMENSION);e=new OffscreenCanvas(i,i),t=e.getContext(`2d`,{willReadFrequently:!0,alpha:!0,desynchronized:!0})}async function i(e,t,r){let i=h(e,t),o=i.maxWidth*i.maxHeight*e.length*4;if(o>n.MAX_BYTES_PER_TEXTURE)return console.log(`Large texture array detected (${(o/1024/1024).toFixed(2)}MB), using chunked processing`),await a(e,t,r);switch(r){case`direct-transfer`:return await d(e,t);case`offscreen-optimized`:return await p(e,t);case`imageBitmap-batch`:return await m(e,t);default:return await d(e,t)}}async function a(e,t,n){let{maxWidth:r,maxHeight:i}=h(e,t),a=e.length,c=o(r,i,a),l=Math.ceil(a/c);console.log(`Processing ${a} textures in ${l} chunks of up to ${c} textures each`),console.log(`Texture dimensions: ${r}x${i}, Est. memory per chunk: ${(r*i*c*4/1024/1024).toFixed(2)}MB`);let u;try{u=new Uint8Array(r*i*a*4)}catch{return console.warn(`Failed to allocate full texture array, reducing dimensions`),await f(e,g(e,t),n)}let d=r*i*c*4,p;try{p=new Uint8Array(d)}catch{return console.warn(`Failed to allocate chunk buffer, reducing dimensions`),await f(e,g(e,t),n)}for(let t=0;t<l;t++){let n=t*c,o=Math.min(n+c,a),l=o-n,d=await s(e.slice(n,o),r,i,p.subarray(0,r*i*l*4)),f=n*r*i*4,m=l*r*i*4;u.set(new Uint8Array(d.data.slice(0,m)),f),t%2==1&&await new Promise(e=>setTimeout(e,0))}return{data:u.buffer,width:r,height:i,depth:a}}function o(e,t,r){if(!n.ADAPTIVE_CHUNK_SIZE)return Math.min(n.CHUNK_SIZE,r);let i=e*t*4/(1024*1024),a;return a=i<=1?16:i<=4?8:i<=16?4:2,Math.min(a,r,n.CHUNK_SIZE*2)}async function s(n,r,i,a){(e.width!==r||e.height!==i)&&(e.width=r,e.height=i);let o=r*i*4;t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`;for(let e=0;e<n.length;e++){let t=n[e];try{await c(t,a,e*o,r,i)}catch(t){console.warn(`Failed to process texture ${e}:`,t);let n=e*o;a.fill(0,n,n+o)}}return{data:a.buffer,width:r,height:i,depth:n.length}}async function c(e,n,r,i,a){let o;if(e.isDirect&&e.bitmap)o=e.bitmap;else if(e.isImageData&&e.data){let t=new ImageData(new Uint8ClampedArray(e.data),e.width,e.height);o=await createImageBitmap(t,{resizeWidth:i,resizeHeight:a,resizeQuality:`high`})}else if(e.isBlob){let t=new Blob([e.data]);o=await createImageBitmap(t,{resizeWidth:i,resizeHeight:a,resizeQuality:`high`})}else throw Error(`Unknown texture data format`);t.clearRect(0,0,i,a),t.drawImage(o,0,0,i,a);let s=t.getImageData(0,0,i,a);n.set(s.data,r),(e.isImageData||e.isBlob)&&o.close()}async function l(e,t,n){let r;try{r=new Uint8Array(t*n*e.length*4)}catch(r){console.warn(`Failed to allocate texture array in processTextureChunk, reducing dimensions`);let i=Math.max(1,Math.floor(t/2)),a=Math.max(1,Math.floor(n/2));if(i===t&&a===n)throw r;return await l(e,i,a)}return await s(e,t,n,r)}async function u(e,t,n,r,i){await c(e,n,r*i*4*t,r,i)}async function d(n,r){let{maxWidth:i,maxHeight:o}=h(n,r);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);let s=n.length,c;try{c=new Uint8Array(i*o*s*4)}catch(e){return console.error(`Failed to allocate texture array:`,e),await a(n,r,`direct-transfer`)}t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`;for(let e=0;e<n.length;e++){let t=n[e];try{await u(t,e,c,i,o)}catch(t){console.warn(`Failed to process texture ${e}:`,t);let n=i*o*4*e;c.fill(0,n,n+i*o*4)}}return{data:c.buffer,width:i,height:o,depth:s}}async function f(e,t,n){let{maxWidth:r,maxHeight:i}=t;return console.log(`Using reduced dimensions: ${r}x${i}`),await l(e,r,i,n)}async function p(n,r){let{maxWidth:i,maxHeight:o}=h(n,r);(e.width!==i||e.height!==o)&&(e.width=i,e.height=o);let s=n.length,c;try{c=new Uint8Array(i*o*s*4)}catch(e){return console.error(`Failed to allocate texture array:`,e),await a(n,r,`offscreen-optimized`)}t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`;for(let e=0;e<n.length;e++){let r=n[e];try{let n;if(r.isBlob){let e=new Blob([r.data]);n=await createImageBitmap(e,{resizeWidth:i,resizeHeight:o,resizeQuality:`high`})}else{let e=new ImageData(new Uint8ClampedArray(r.data),r.width,r.height);n=await createImageBitmap(e,{resizeWidth:i,resizeHeight:o,resizeQuality:`high`})}t.clearRect(0,0,i,o),t.drawImage(n,0,0);let a=t.getImageData(0,0,i,o),s=i*o*4*e;c.set(a.data,s),n.close()}catch(t){console.warn(`Failed to process texture ${e}:`,t);let n=i*o*4*e;c.fill(0,n,n+i*o*4)}}return{data:c.buffer,width:i,height:o,depth:s}}async function m(n,r){let{maxWidth:i,maxHeight:s}=h(n,r),c=n.length,l;try{l=new Uint8Array(i*s*c*4)}catch(e){return console.error(`Failed to allocate texture array:`,e),await a(n,r,`imageBitmap-batch`)}let u=Math.min(o(i,s,n.length),n.length);for(let r=0;r<n.length;r+=u){let a=Math.min(r+u,n.length),o=[];for(let e=r;e<a;e++){let t=n[e],r;if(t.isBlob){let e=new Blob([t.data]);r=createImageBitmap(e,{resizeWidth:i,resizeHeight:s,resizeQuality:`high`})}else{let e=new ImageData(new Uint8ClampedArray(t.data),t.width,t.height);r=createImageBitmap(e,{resizeWidth:i,resizeHeight:s,resizeQuality:`high`})}o.push(r.then(t=>({bitmap:t,index:e})))}let c=await Promise.all(o);e.width=i,e.height=s,t.imageSmoothingEnabled=!1;for(let{bitmap:e,index:n}of c){t.clearRect(0,0,i,s),t.drawImage(e,0,0);let r=t.getImageData(0,0,i,s),a=i*s*4*n;l.set(r.data,a),e.close()}}return{data:l.buffer,width:i,height:s,depth:c}}function h(e,t){let r=0,i=0;for(let t of e)r=Math.max(r,t.width||0),i=Math.max(i,t.height||0);for(r=2**Math.ceil(Math.log2(r)),i=2**Math.ceil(Math.log2(i)),r=Math.min(r,t,n.MAX_TEXTURE_DIMENSION),i=Math.min(i,t,n.MAX_TEXTURE_DIMENSION);r>=t/2||i>=t/2;)r=Math.max(1,Math.floor(r/2)),i=Math.max(1,Math.floor(i/2));return{maxWidth:r,maxHeight:i}}function g(e,t){let n=h(e,t);return{maxWidth:Math.max(1,Math.floor(n.maxWidth/2)),maxHeight:Math.max(1,Math.floor(n.maxHeight/2))}}})();
|
|
2
|
+
//# sourceMappingURL=TexturesWorker-DBqGmVdR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TexturesWorker-DBqGmVdR.js","names":[],"sources":["../../src/Processor/Workers/TexturesWorker.js"],"sourcesContent":["let canvas, ctx;\n\n// Memory limits and chunking configuration\nconst MEMORY_LIMITS = {\n\tMAX_BYTES_PER_TEXTURE: 256 * 1024 * 1024, // 256MB per texture array\n\tMAX_TEXTURE_DIMENSION: 4096, // Maximum dimension for a single texture\n\tCHUNK_SIZE: 8, // Optimized: Process textures in chunks of 8 for better memory locality\n\tADAPTIVE_CHUNK_SIZE: true, // Enable adaptive chunk sizing based on texture dimensions\n\tMEMORY_SAFETY_FACTOR: 0.8 // Use only 80% of estimated available memory\n};\n\nself.onmessage = async function ( e ) {\n\n\tconst { textures, maxTextureSize, method = 'direct-transfer' } = e.data;\n\n\ttry {\n\n\t\t// Initialize on first use\n\t\tif ( ! canvas ) {\n\n\t\t\tinitializeWorker( maxTextureSize );\n\n\t\t}\n\n\t\tconst result = await processTextures( textures, maxTextureSize, method );\n\n\t\t// Transfer ownership for zero-copy\n\t\tself.postMessage( result, [ result.data ] );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( 'Worker processing failed:', error );\n\t\tself.postMessage( { error: error.message } );\n\n\t}\n\n};\n\nfunction initializeWorker( maxTextureSize ) {\n\n\t// Initialize OffscreenCanvas with optimal settings\n\tconst size = Math.min( maxTextureSize, MEMORY_LIMITS.MAX_TEXTURE_DIMENSION );\n\tcanvas = new OffscreenCanvas( size, size );\n\tctx = canvas.getContext( '2d', {\n\t\twillReadFrequently: true,\n\t\talpha: true,\n\t\tdesynchronized: true\n\t} );\n\n}\n\nasync function processTextures( textures, maxTextureSize, method ) {\n\n\t// Check if we need to use chunked processing\n\tconst dimensions = calculateOptimalDimensions( textures, maxTextureSize );\n\tconst estimatedBytes = dimensions.maxWidth * dimensions.maxHeight * textures.length * 4;\n\n\tif ( estimatedBytes > MEMORY_LIMITS.MAX_BYTES_PER_TEXTURE ) {\n\n\t\tconsole.log( `Large texture array detected (${( estimatedBytes / 1024 / 1024 ).toFixed( 2 )}MB), using chunked processing` );\n\t\treturn await processTexturesInChunks( textures, maxTextureSize, method );\n\n\t}\n\n\tswitch ( method ) {\n\n\t\tcase 'direct-transfer':\n\t\t\treturn await processWithDirectTransfer( textures, maxTextureSize );\n\t\tcase 'offscreen-optimized':\n\t\t\treturn await processWithOffscreenOptimized( textures, maxTextureSize );\n\t\tcase 'imageBitmap-batch':\n\t\t\treturn await processWithImageBitmapBatch( textures, maxTextureSize );\n\t\tdefault:\n\t\t\treturn await processWithDirectTransfer( textures, maxTextureSize );\n\n\t}\n\n}\n\nasync function processTexturesInChunks( textures, maxTextureSize, method ) {\n\n\tconst dimensions = calculateOptimalDimensions( textures, maxTextureSize );\n\tconst { maxWidth, maxHeight } = dimensions;\n\tconst depth = textures.length;\n\n\t// Optimize chunk size based on texture dimensions and memory constraints\n\tconst chunkSize = calculateOptimalChunkSize( maxWidth, maxHeight, depth );\n\tconst numChunks = Math.ceil( depth / chunkSize );\n\n\tconsole.log( `Processing ${depth} textures in ${numChunks} chunks of up to ${chunkSize} textures each` );\n\tconsole.log( `Texture dimensions: ${maxWidth}x${maxHeight}, Est. memory per chunk: ${( maxWidth * maxHeight * chunkSize * 4 / 1024 / 1024 ).toFixed( 2 )}MB` );\n\n\t// Allocate the full output array\n\tlet data;\n\ttry {\n\n\t\tdata = new Uint8Array( maxWidth * maxHeight * depth * 4 );\n\n\t} catch {\n\n\t\t// If full allocation fails, fall back to reduced dimensions\n\t\tconsole.warn( 'Failed to allocate full texture array, reducing dimensions' );\n\t\tconst reducedDimensions = calculateReducedDimensions( textures, maxTextureSize );\n\t\treturn await processWithReducedDimensions( textures, reducedDimensions, method );\n\n\t}\n\n\t// Pre-allocate chunk buffer for reuse\n\tconst chunkBufferSize = maxWidth * maxHeight * chunkSize * 4;\n\tlet chunkBuffer;\n\n\ttry {\n\n\t\tchunkBuffer = new Uint8Array( chunkBufferSize );\n\n\t} catch ( error ) {\n\n\t\tconsole.warn( 'Failed to allocate chunk buffer, reducing dimensions' );\n\t\tconst reducedDimensions = calculateReducedDimensions( textures, maxTextureSize );\n\t\treturn await processWithReducedDimensions( textures, reducedDimensions, method );\n\n\t}\n\n\t// Process each chunk with optimized memory reuse\n\tfor ( let chunkIndex = 0; chunkIndex < numChunks; chunkIndex ++ ) {\n\n\t\tconst startIdx = chunkIndex * chunkSize;\n\t\tconst endIdx = Math.min( startIdx + chunkSize, depth );\n\t\tconst actualChunkSize = endIdx - startIdx;\n\t\tconst chunkTextures = textures.slice( startIdx, endIdx );\n\n\t\tconst chunkResult = await processTextureChunkOptimized(\n\t\t\tchunkTextures,\n\t\t\tmaxWidth,\n\t\t\tmaxHeight,\n\t\t\tchunkBuffer.subarray( 0, maxWidth * maxHeight * actualChunkSize * 4 )\n\t\t);\n\n\t\t// Copy chunk data to main array\n\t\tconst offset = startIdx * maxWidth * maxHeight * 4;\n\t\tconst copySize = actualChunkSize * maxWidth * maxHeight * 4;\n\t\tdata.set( new Uint8Array( chunkResult.data.slice( 0, copySize ) ), offset );\n\n\t\t// Micro-yield to prevent blocking the thread\n\n\t\tif ( chunkIndex % 2 === 1 ) { // Yield every 2 chunks instead of every chunk\n\n\t\t\tawait new Promise( resolve => setTimeout( resolve, 0 ) );\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tdata: data.buffer,\n\t\twidth: maxWidth,\n\t\theight: maxHeight,\n\t\tdepth\n\t};\n\n}\n\nfunction calculateOptimalChunkSize( maxWidth, maxHeight, totalTextures ) {\n\n\tif ( ! MEMORY_LIMITS.ADAPTIVE_CHUNK_SIZE ) {\n\n\t\treturn Math.min( MEMORY_LIMITS.CHUNK_SIZE, totalTextures );\n\n\t}\n\n\t// Calculate memory per texture in MB\n\tconst bytesPerTexture = maxWidth * maxHeight * 4;\n\tconst mbPerTexture = bytesPerTexture / ( 1024 * 1024 );\n\n\t// Adaptive chunk sizing based on texture size\n\tlet optimalChunkSize;\n\n\tif ( mbPerTexture <= 1 ) { // Small textures (<=1MB)\n\n\t\toptimalChunkSize = 16; // Process more at once\n\n\t} else if ( mbPerTexture <= 4 ) { // Medium textures (1-4MB)\n\n\t\toptimalChunkSize = 8; // Balanced approach\n\n\t} else if ( mbPerTexture <= 16 ) { // Large textures (4-16MB)\n\n\t\toptimalChunkSize = 4; // Conservative\n\n\t} else { // Very large textures (>16MB)\n\n\t\toptimalChunkSize = 2; // Very conservative\n\n\t}\n\n\t// Don't exceed total texture count or configured limits\n\treturn Math.min( optimalChunkSize, totalTextures, MEMORY_LIMITS.CHUNK_SIZE * 2 );\n\n}\n\nasync function processTextureChunkOptimized( textures, maxWidth, maxHeight, outputBuffer ) {\n\n\t// Resize canvas for this chunk if needed\n\tif ( canvas.width !== maxWidth || canvas.height !== maxHeight ) {\n\n\t\tcanvas.width = maxWidth;\n\t\tcanvas.height = maxHeight;\n\n\t}\n\n\t// Use provided buffer to avoid allocation\n\tconst bytesPerTexture = maxWidth * maxHeight * 4;\n\n\t// Optimize context settings once per chunk\n\tctx.imageSmoothingEnabled = true;\n\tctx.imageSmoothingQuality = 'high';\n\n\t// Process textures with optimized single texture processing\n\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\tconst textureData = textures[ i ];\n\n\t\ttry {\n\n\t\t\tconst offset = i * bytesPerTexture;\n\t\t\tawait processSingleTextureOptimized( textureData, outputBuffer, offset, maxWidth, maxHeight );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.warn( `Failed to process texture ${i}:`, error );\n\t\t\t// Fill with transparent pixels as fallback\n\t\t\tconst offset = i * bytesPerTexture;\n\t\t\toutputBuffer.fill( 0, offset, offset + bytesPerTexture );\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tdata: outputBuffer.buffer,\n\t\twidth: maxWidth,\n\t\theight: maxHeight,\n\t\tdepth: textures.length\n\t};\n\n}\n\nasync function processSingleTextureOptimized( textureData, outputData, offset, maxWidth, maxHeight ) {\n\n\tlet imageBitmap;\n\n\tif ( textureData.isDirect && textureData.bitmap ) {\n\n\t\t// Direct ImageBitmap transfer - no conversion needed!\n\t\timageBitmap = textureData.bitmap;\n\n\t} else if ( textureData.isImageData && textureData.data ) {\n\n\t\t// Direct ImageData transfer - minimal conversion\n\t\tconst imageData = new ImageData(\n\t\t\tnew Uint8ClampedArray( textureData.data ),\n\t\t\ttextureData.width,\n\t\t\ttextureData.height\n\t\t);\n\n\t\timageBitmap = await createImageBitmap( imageData, {\n\t\t\tresizeWidth: maxWidth,\n\t\t\tresizeHeight: maxHeight,\n\t\t\tresizeQuality: 'high'\n\t\t} );\n\n\t} else if ( textureData.isBlob ) {\n\n\t\t// Legacy blob processing (fallback)\n\t\tconst blob = new Blob( [ textureData.data ] );\n\t\timageBitmap = await createImageBitmap( blob, {\n\t\t\tresizeWidth: maxWidth,\n\t\t\tresizeHeight: maxHeight,\n\t\t\tresizeQuality: 'high'\n\t\t} );\n\n\t} else {\n\n\t\tthrow new Error( 'Unknown texture data format' );\n\n\t}\n\n\t// Clear and draw to canvas\n\tctx.clearRect( 0, 0, maxWidth, maxHeight );\n\tctx.drawImage( imageBitmap, 0, 0, maxWidth, maxHeight );\n\n\t// Get image data efficiently\n\tconst imageData = ctx.getImageData( 0, 0, maxWidth, maxHeight );\n\n\t// Copy directly to the specified offset in output buffer\n\toutputData.set( imageData.data, offset );\n\n\t// Clean up ImageBitmap if we created it\n\tif ( textureData.isImageData || textureData.isBlob ) {\n\n\t\timageBitmap.close();\n\n\t}\n\n}\n\nasync function processTextureChunk( textures, maxWidth, maxHeight ) {\n\n\tlet data;\n\ttry {\n\n\t\tdata = new Uint8Array( maxWidth * maxHeight * textures.length * 4 );\n\n\t} catch ( error ) {\n\n\t\tconsole.warn( 'Failed to allocate texture array in processTextureChunk, reducing dimensions' );\n\t\tconst nextWidth = Math.max( 1, Math.floor( maxWidth / 2 ) );\n\t\tconst nextHeight = Math.max( 1, Math.floor( maxHeight / 2 ) );\n\n\t\tif ( nextWidth === maxWidth && nextHeight === maxHeight ) {\n\n\t\t\tthrow error;\n\n\t\t}\n\n\t\treturn await processTextureChunk( textures, nextWidth, nextHeight );\n\n\t}\n\n\treturn await processTextureChunkOptimized( textures, maxWidth, maxHeight, data );\n\n}\n\nasync function processSingleTexture( textureData, index, outputData, maxWidth, maxHeight ) {\n\n\tconst offset = maxWidth * maxHeight * 4 * index;\n\tawait processSingleTextureOptimized( textureData, outputData, offset, maxWidth, maxHeight );\n\n}\n\nasync function processWithDirectTransfer( textures, maxTextureSize ) {\n\n\tconst dimensions = calculateOptimalDimensions( textures, maxTextureSize );\n\tconst { maxWidth, maxHeight } = dimensions;\n\n\t// Resize canvas if needed\n\tif ( canvas.width !== maxWidth || canvas.height !== maxHeight ) {\n\n\t\tcanvas.width = maxWidth;\n\t\tcanvas.height = maxHeight;\n\n\t}\n\n\tconst depth = textures.length;\n\n\t// Try to allocate memory with fallback\n\tlet data;\n\ttry {\n\n\t\tdata = new Uint8Array( maxWidth * maxHeight * depth * 4 );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( 'Failed to allocate texture array:', error );\n\t\t// Fall back to chunked processing\n\t\treturn await processTexturesInChunks( textures, maxTextureSize, 'direct-transfer' );\n\n\t}\n\n\t// Optimize context settings for batch processing\n\tctx.imageSmoothingEnabled = true;\n\tctx.imageSmoothingQuality = 'high';\n\n\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\tconst textureData = textures[ i ];\n\n\t\ttry {\n\n\t\t\tawait processSingleTexture( textureData, i, data, maxWidth, maxHeight );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.warn( `Failed to process texture ${i}:`, error );\n\t\t\t// Fill with transparent pixels as fallback\n\t\t\tconst offset = maxWidth * maxHeight * 4 * i;\n\t\t\tdata.fill( 0, offset, offset + maxWidth * maxHeight * 4 );\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tdata: data.buffer,\n\t\twidth: maxWidth,\n\t\theight: maxHeight,\n\t\tdepth\n\t};\n\n}\n\nasync function processWithReducedDimensions( textures, dimensions, method ) {\n\n\tconst { maxWidth, maxHeight } = dimensions;\n\tconsole.log( `Using reduced dimensions: ${maxWidth}x${maxHeight}` );\n\n\t// Process with reduced dimensions\n\treturn await processTextureChunk( textures, maxWidth, maxHeight, method );\n\n}\n\nasync function processWithOffscreenOptimized( textures, maxTextureSize ) {\n\n\tconst dimensions = calculateOptimalDimensions( textures, maxTextureSize );\n\tconst { maxWidth, maxHeight } = dimensions;\n\n\t// Resize canvas if needed\n\tif ( canvas.width !== maxWidth || canvas.height !== maxHeight ) {\n\n\t\tcanvas.width = maxWidth;\n\t\tcanvas.height = maxHeight;\n\n\t}\n\n\tconst depth = textures.length;\n\n\t// Try to allocate memory with fallback\n\tlet data;\n\ttry {\n\n\t\tdata = new Uint8Array( maxWidth * maxHeight * depth * 4 );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( 'Failed to allocate texture array:', error );\n\t\t// Fall back to chunked processing\n\t\treturn await processTexturesInChunks( textures, maxTextureSize, 'offscreen-optimized' );\n\n\t}\n\n\t// Optimize context settings for batch processing\n\tctx.imageSmoothingEnabled = true;\n\tctx.imageSmoothingQuality = 'high';\n\n\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\tconst textureData = textures[ i ];\n\n\t\ttry {\n\n\t\t\tlet imageBitmap;\n\n\t\t\tif ( textureData.isBlob ) {\n\n\t\t\t\t// Create ImageBitmap from blob data\n\t\t\t\tconst blob = new Blob( [ textureData.data ] );\n\t\t\t\timageBitmap = await createImageBitmap( blob, {\n\t\t\t\t\tresizeWidth: maxWidth,\n\t\t\t\t\tresizeHeight: maxHeight,\n\t\t\t\t\tresizeQuality: 'high'\n\t\t\t\t} );\n\n\t\t\t} else {\n\n\t\t\t\t// Handle direct image data\n\t\t\t\tconst imageData = new ImageData(\n\t\t\t\t\tnew Uint8ClampedArray( textureData.data ),\n\t\t\t\t\ttextureData.width,\n\t\t\t\t\ttextureData.height\n\t\t\t\t);\n\t\t\t\timageBitmap = await createImageBitmap( imageData, {\n\t\t\t\t\tresizeWidth: maxWidth,\n\t\t\t\t\tresizeHeight: maxHeight,\n\t\t\t\t\tresizeQuality: 'high'\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\t// Clear and draw to canvas\n\t\t\tctx.clearRect( 0, 0, maxWidth, maxHeight );\n\t\t\tctx.drawImage( imageBitmap, 0, 0 );\n\n\t\t\t// Get image data efficiently\n\t\t\tconst imageData = ctx.getImageData( 0, 0, maxWidth, maxHeight );\n\n\t\t\t// Copy to output array\n\t\t\tconst offset = maxWidth * maxHeight * 4 * i;\n\t\t\tdata.set( imageData.data, offset );\n\n\t\t\t// Clean up ImageBitmap\n\t\t\timageBitmap.close();\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.warn( `Failed to process texture ${i}:`, error );\n\t\t\t// Fill with transparent pixels as fallback\n\t\t\tconst offset = maxWidth * maxHeight * 4 * i;\n\t\t\tdata.fill( 0, offset, offset + maxWidth * maxHeight * 4 );\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tdata: data.buffer,\n\t\twidth: maxWidth,\n\t\theight: maxHeight,\n\t\tdepth\n\t};\n\n}\n\nasync function processWithImageBitmapBatch( textures, maxTextureSize ) {\n\n\tconst dimensions = calculateOptimalDimensions( textures, maxTextureSize );\n\tconst { maxWidth, maxHeight } = dimensions;\n\n\tconst depth = textures.length;\n\n\t// Try to allocate memory with fallback\n\tlet data;\n\ttry {\n\n\t\tdata = new Uint8Array( maxWidth * maxHeight * depth * 4 );\n\n\t} catch ( error ) {\n\n\t\tconsole.error( 'Failed to allocate texture array:', error );\n\t\t// Fall back to chunked processing\n\t\treturn await processTexturesInChunks( textures, maxTextureSize, 'imageBitmap-batch' );\n\n\t}\n\n\t// Process in batches for memory efficiency - optimized batch size\n\tconst batchSize = Math.min( calculateOptimalChunkSize( maxWidth, maxHeight, textures.length ), textures.length );\n\n\tfor ( let batchStart = 0; batchStart < textures.length; batchStart += batchSize ) {\n\n\t\tconst batchEnd = Math.min( batchStart + batchSize, textures.length );\n\t\tconst batchPromises = [];\n\n\t\t// Create all ImageBitmaps for this batch in parallel\n\t\tfor ( let i = batchStart; i < batchEnd; i ++ ) {\n\n\t\t\tconst textureData = textures[ i ];\n\n\t\t\tlet bitmapPromise;\n\t\t\tif ( textureData.isBlob ) {\n\n\t\t\t\tconst blob = new Blob( [ textureData.data ] );\n\t\t\t\tbitmapPromise = createImageBitmap( blob, {\n\t\t\t\t\tresizeWidth: maxWidth,\n\t\t\t\t\tresizeHeight: maxHeight,\n\t\t\t\t\tresizeQuality: 'high'\n\t\t\t\t} );\n\n\t\t\t} else {\n\n\t\t\t\tconst imageData = new ImageData(\n\t\t\t\t\tnew Uint8ClampedArray( textureData.data ),\n\t\t\t\t\ttextureData.width,\n\t\t\t\t\ttextureData.height\n\t\t\t\t);\n\t\t\t\tbitmapPromise = createImageBitmap( imageData, {\n\t\t\t\t\tresizeWidth: maxWidth,\n\t\t\t\t\tresizeHeight: maxHeight,\n\t\t\t\t\tresizeQuality: 'high'\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\tbatchPromises.push(\n\t\t\t\tbitmapPromise.then( bitmap => ( { bitmap, index: i } ) )\n\t\t\t);\n\n\t\t}\n\n\t\t// Wait for all bitmaps in this batch\n\t\tconst bitmaps = await Promise.all( batchPromises );\n\n\t\t// Process each bitmap\n\t\tcanvas.width = maxWidth;\n\t\tcanvas.height = maxHeight;\n\t\tctx.imageSmoothingEnabled = false; // Fast processing for batches\n\n\t\tfor ( const { bitmap, index } of bitmaps ) {\n\n\t\t\tctx.clearRect( 0, 0, maxWidth, maxHeight );\n\t\t\tctx.drawImage( bitmap, 0, 0 );\n\n\t\t\tconst imageData = ctx.getImageData( 0, 0, maxWidth, maxHeight );\n\t\t\tconst offset = maxWidth * maxHeight * 4 * index;\n\t\t\tdata.set( imageData.data, offset );\n\n\t\t\tbitmap.close();\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tdata: data.buffer,\n\t\twidth: maxWidth,\n\t\theight: maxHeight,\n\t\tdepth\n\t};\n\n}\n\nfunction calculateOptimalDimensions( textures, maxTextureSize ) {\n\n\tlet maxWidth = 0;\n\tlet maxHeight = 0;\n\n\tfor ( let texture of textures ) {\n\n\t\tmaxWidth = Math.max( maxWidth, texture.width || 0 );\n\t\tmaxHeight = Math.max( maxHeight, texture.height || 0 );\n\n\t}\n\n\t// Round to power of 2 for optimal GPU performance\n\tmaxWidth = Math.pow( 2, Math.ceil( Math.log2( maxWidth ) ) );\n\tmaxHeight = Math.pow( 2, Math.ceil( Math.log2( maxHeight ) ) );\n\n\t// Respect texture size limits\n\tmaxWidth = Math.min( maxWidth, maxTextureSize, MEMORY_LIMITS.MAX_TEXTURE_DIMENSION );\n\tmaxHeight = Math.min( maxHeight, maxTextureSize, MEMORY_LIMITS.MAX_TEXTURE_DIMENSION );\n\n\t// Additional safety check\n\twhile ( maxWidth >= maxTextureSize / 2 || maxHeight >= maxTextureSize / 2 ) {\n\n\t\tmaxWidth = Math.max( 1, Math.floor( maxWidth / 2 ) );\n\t\tmaxHeight = Math.max( 1, Math.floor( maxHeight / 2 ) );\n\n\t}\n\n\treturn { maxWidth, maxHeight };\n\n}\n\nfunction calculateReducedDimensions( textures, maxTextureSize ) {\n\n\t// Calculate dimensions but reduce by factor of 2 for memory safety\n\tconst original = calculateOptimalDimensions( textures, maxTextureSize );\n\n\treturn {\n\t\tmaxWidth: Math.max( 1, Math.floor( original.maxWidth / 2 ) ),\n\t\tmaxHeight: Math.max( 1, Math.floor( original.maxHeight / 2 ) )\n\t};\n\n}\n\n"],"mappings":"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"}
|