rayzee 10.0.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +252 -44
- package/dist/ActiveColor-YvuQq44O.js +13 -0
- package/dist/ArchiveImporter-Hn35o6Z5.js +5145 -0
- package/dist/AssetConfig-DSEntE65.js +30 -0
- package/dist/BidirectionalIntegrator-CBw8hkeu.js +946 -0
- package/dist/BufferLayout-D_IBo2ci.js +150 -0
- package/dist/ColorManagement-oeEk7oSu.js +1488 -0
- package/dist/EngineEvents-Ci8dVWCP.js +73 -0
- package/dist/EngineIssues-CIZnXHsF.js +72 -0
- package/dist/FileFormats-sPGJeCRc.js +102 -0
- package/dist/Logger-dDDGj9Av.js +97 -0
- package/dist/{NeuralRetouch-CYgkQY8Z.js → NeuralRetouch-BzNpiVHZ.js} +8 -6
- package/dist/{NeuralSuperRes-PkgDLQcD.js → NeuralSuperRes-Bk7bHg2B.js} +55 -120
- package/dist/PathTracer-72XufKAr.js +29976 -0
- package/dist/PhysicalSky-BgjdV88k.js +1219 -0
- package/dist/RayzeeRenderer-1QRO97eq.js +3551 -0
- package/dist/SolarLimb-CPaeZVwz.js +721 -0
- package/dist/ToneMapGPU-CjU3osDd.js +177 -0
- package/dist/ViewTransforms-CYN7bq2w.js +255 -0
- package/dist/WorkingMatrix-C6y233hT.js +27 -0
- package/dist/ZipReader-gDSWr0I5.js +98 -0
- package/dist/addons/archives.es.js +3 -0
- package/dist/addons/bidirectional.es.js +2 -0
- package/dist/addons/color.es.js +3 -0
- package/dist/addons/formats.es.js +2 -0
- package/dist/addons/physical-sky.es.js +2 -0
- package/dist/addons/storage.es.js +3 -0
- package/dist/archiveFormats-tnAoUL1j.js +94 -0
- package/dist/identity-DrQsGT2o.js +39 -0
- package/dist/openStorage-DwcBRUj7.js +820 -0
- package/dist/rayzee-core.es.js +12 -0
- package/dist/rayzee.es.js +2551 -44024
- package/dist/rayzee.umd.js +528 -487
- package/dist/sceneCachePolicy-C67jbci-.js +443 -0
- package/dist/shared-BGBcSxBQ.js +17 -0
- package/package.json +23 -2
- package/src/Color/ActiveColor.js +34 -0
- package/src/Color/BasicColor.js +147 -0
- package/src/Color/ColorManagement.js +7 -31
- package/src/Color/ViewTransforms.js +5 -1
- package/src/EngineDefaults.js +62 -1029
- package/src/EngineEvents.js +2 -0
- package/src/EngineIssues.js +2 -0
- package/src/Headless.js +6 -5
- package/src/Passes/AIUpscaler.js +58 -141
- package/src/Passes/OIDNDenoiser.js +92 -65
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/PathTracerApp.js +620 -4026
- package/src/Pipeline/PipelineContext.js +0 -4
- package/src/Platform.js +36 -0
- package/src/Processor/ArchiveCache.js +20 -7
- package/src/Processor/ArchiveImporter.js +1265 -0
- package/src/Processor/ArchiveReader.js +22 -4
- package/src/Processor/AssetLoader.js +327 -1763
- package/src/Processor/AtmosphereModel.js +1 -2
- package/src/Processor/BVHBuilder.js +14 -126
- package/src/Processor/BVHLeafFold.js +4 -6
- package/src/Processor/BVHRefitter.js +67 -46
- package/src/Processor/BufferLayout.js +291 -0
- package/src/Processor/EmissiveTriangleBuilder.js +1 -1
- package/src/Processor/EnvironmentExactTable.js +187 -0
- package/src/Processor/EquirectHDRInfo.js +30 -223
- package/src/Processor/FileFormats.js +116 -0
- package/src/Processor/GLTFDecoders.js +85 -0
- package/src/Processor/GLTFDiffuseTransmission.js +63 -0
- package/src/Processor/GeometryExtractor.js +15 -5
- package/src/Processor/HostMemory.js +7 -1
- package/src/Processor/IESParser.js +47 -35
- package/src/Processor/InstanceTable.js +1 -1
- package/src/Processor/KernelManager.js +35 -2
- package/src/Processor/MaterialPacking.js +10 -8
- package/src/Processor/PBRT/PBRTMaterials.js +254 -42
- package/src/Processor/PBRT/PBRTParser.js +49 -19
- package/src/Processor/PBRT/PBRTScattering.js +93 -0
- package/src/Processor/PBRT/PBRTSceneBuilder.js +1050 -75
- package/src/Processor/PBRT/PBRTTextureBake.js +179 -0
- package/src/Processor/PBRT/PFM.js +59 -0
- package/src/Processor/PackedRayBuffer.js +116 -9
- package/src/Processor/ParallelBVHBuilder.js +3 -6
- package/src/Processor/PhysicalSky.js +5 -4
- package/src/Processor/PlatformImageLoader.js +50 -6
- package/src/Processor/QueueManager.js +39 -5
- package/src/Processor/ReinsertionOptimizer.js +2 -2
- package/src/Processor/SceneMetadata.js +30 -5
- package/src/Processor/SceneProcessor.js +53 -62
- package/src/Processor/ShaderBuilder.js +6 -16
- package/src/Processor/SimpleSky.js +1 -62
- package/src/Processor/SolarLimb.js +3 -0
- package/src/Processor/SplitBLAS.js +1 -1
- package/src/Processor/StorageTexturePool.js +22 -1
- package/src/Processor/TLASBuilder.js +1 -3
- package/src/Processor/TextureBuckets.js +237 -0
- package/src/Processor/TextureCreator.js +181 -8
- package/src/Processor/ToneMapGPU.js +64 -15
- package/src/Processor/Workers/BVHRefitWorker.js +5 -2
- package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
- package/src/Processor/Workers/BVHWorker.js +5 -18
- package/src/Processor/Workers/CDFWorker.js +6 -183
- package/src/Processor/Workers/TexturesWorker.js +103 -18
- package/src/Processor/archiveFormats.js +7 -0
- package/src/Processor/utils.js +2 -1
- package/src/RayzeeRenderer.js +3900 -0
- package/src/RenderSettings.js +68 -62
- package/src/Stages/ASVGF.js +13 -14
- package/src/Stages/AutoExposure.js +17 -17
- package/src/Stages/BilateralFilter.js +2 -1
- package/src/Stages/Compositor.js +8 -23
- package/src/Stages/DenoiserSettings.js +182 -0
- package/src/Stages/EdgeFilter.js +2 -1
- package/src/Stages/MotionVector.js +1 -1
- package/src/Stages/NRD.js +11 -5
- package/src/Stages/NormalDepth.js +12 -8
- package/src/Stages/PathTracer.js +336 -43
- package/src/Stages/PathTracerStage.js +69 -66
- package/src/Stages/Variance.js +1 -1
- package/src/Storage/BLASCache.js +1 -1
- package/src/Storage/CDFCache.js +18 -17
- package/src/Storage/DownloadCache.js +1 -1
- package/src/Storage/SceneGraphCodec.js +46 -3
- package/src/Storage/SpillStore.js +1 -1
- package/src/Storage/StorageManager.js +45 -9
- package/src/Storage/areas.js +4 -0
- package/src/TSL/BVHTraversal.js +50 -25
- package/src/TSL/Bidirectional.js +208 -0
- package/src/TSL/BidirectionalLamps.js +132 -0
- package/src/TSL/Clearcoat.js +15 -6
- package/src/TSL/Common.js +20 -15
- package/src/TSL/ConnectKernel.js +167 -0
- package/src/TSL/Debugger.js +1 -1
- package/src/TSL/Displacement.js +4 -3
- package/src/TSL/EmissiveSampling.js +65 -18
- package/src/TSL/Environment.js +77 -106
- package/src/TSL/EnvironmentCDF.js +85 -39
- package/src/TSL/ExtendKernel.js +8 -3
- package/src/TSL/FinalWriteKernel.js +4 -4
- package/src/TSL/GenerateKernel.js +18 -4
- package/src/TSL/HitFacet.js +35 -5
- package/src/TSL/LightBVHSampling.js +40 -9
- package/src/TSL/LightGenerateKernel.js +293 -0
- package/src/TSL/LightGuide.js +198 -0
- package/src/TSL/LightSplatKernel.js +180 -0
- package/src/TSL/LightVisibility.js +111 -0
- package/src/TSL/LightsCore.js +13 -38
- package/src/TSL/LightsDirect.js +28 -29
- package/src/TSL/LightsIndirect.js +3 -1
- package/src/TSL/LightsSampling.js +239 -51
- package/src/TSL/MaterialEvaluation.js +113 -31
- package/src/TSL/MaterialProperties.js +44 -17
- package/src/TSL/MaterialTransmission.js +52 -47
- package/src/TSL/MergeKernel.js +224 -0
- package/src/TSL/PathTracerCore.js +65 -29
- package/src/TSL/Random.js +5 -1
- package/src/TSL/SceneResources.js +51 -0
- package/src/TSL/ShadeKernel.js +985 -101
- package/src/TSL/Struct.js +10 -0
- package/src/TSL/Sun.js +1 -1
- package/src/TSL/TextureSampling.js +66 -61
- package/src/addons/archives.js +14 -0
- package/src/addons/bidirectional.js +13 -0
- package/src/addons/color.js +32 -0
- package/src/addons/formats.js +16 -0
- package/src/addons/physicalSky.js +14 -0
- package/src/addons/storage.js +19 -0
- package/src/core.js +31 -0
- package/src/index.js +13 -23
- package/src/integrators/BidirectionalIntegrator.js +838 -0
- package/src/managers/AnimationManager.js +88 -6
- package/src/managers/CameraManager.js +103 -46
- package/src/managers/DenoisingManager.js +14 -10
- package/src/managers/EnvironmentManager.js +125 -103
- package/src/managers/GoboManager.js +9 -0
- package/src/managers/MaterialDataManager.js +48 -37
- package/src/managers/UniformManager.js +32 -10
- package/dist/ToneMapGPU-B6XIe3DU.js +0 -544
- package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
- package/src/Processor/TreeletOptimizer.js +0 -574
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Texture processing constants
|
|
2
|
+
export const TEXTURE_CONSTANTS = {
|
|
3
|
+
VEC4_PER_BVH_NODE: 4,
|
|
4
|
+
FLOATS_PER_VEC4: 4,
|
|
5
|
+
MIN_TEXTURE_WIDTH: 4,
|
|
6
|
+
MAX_CONCURRENT_WORKERS: Math.min( typeof navigator !== 'undefined' ? navigator.hardwareConcurrency || 4 : 4, 6 ),
|
|
7
|
+
BUFFER_POOL_SIZE: 20,
|
|
8
|
+
CANVAS_POOL_SIZE: 12,
|
|
9
|
+
CACHE_SIZE_LIMIT: 50,
|
|
10
|
+
// Hardware ceiling for a single texture-array dimension (WebGPU maxTextureDimension2D
|
|
11
|
+
// guaranteed minimum). The configurable maxTextureSize setting is clamped to this.
|
|
12
|
+
MAX_TEXTURE_SIZE: 8192,
|
|
13
|
+
// Default cap applied when no maxTextureSize is supplied (engine standalone use).
|
|
14
|
+
DEFAULT_MAX_TEXTURE_SIZE: 4096,
|
|
15
|
+
// Per-map-type ceiling before bucketing; the real cap is per (pool, bucket) in _bucketTextures.
|
|
16
|
+
MAX_TEXTURES_LIMIT: 4 * 256,
|
|
17
|
+
// Array shapes per colorSpace pool. Material maps are grouped into this many
|
|
18
|
+
// (width, height) classes so a small or oddly-shaped map no longer pays a large
|
|
19
|
+
// neighbour's footprint. 4 → ~8 bound material arrays (4 sRGB + 4 linear).
|
|
20
|
+
MATERIAL_BUCKET_COUNT: 4,
|
|
21
|
+
// Packing stride: a map's stored index encodes bucketId * BUCKET_LAYER_STRIDE + layer.
|
|
22
|
+
// Also the per-bucket layer cap. Kept at the WebGPU portable maxTextureArrayLayers floor (256)
|
|
23
|
+
// so a consolidated bucket (which merges several map types of one size) stays portable.
|
|
24
|
+
BUCKET_LAYER_STRIDE: 256,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// An RGBA8 row upload must be a multiple of 256 bytes, so bucket widths land on 64 texels.
|
|
28
|
+
const BUCKET_WIDTH_ALIGN = 64;
|
|
29
|
+
|
|
30
|
+
export function alignBucketWidth( width, maxTextureSize ) {
|
|
31
|
+
|
|
32
|
+
const aligned = Math.ceil( Math.max( 1, width ) / BUCKET_WIDTH_ALIGN ) * BUCKET_WIDTH_ALIGN;
|
|
33
|
+
return Math.min( maxTextureSize, Math.max( TEXTURE_CONSTANTS.MIN_TEXTURE_WIDTH, aligned ) );
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function bucketOf( units ) {
|
|
38
|
+
|
|
39
|
+
let width = 0, height = 0, count = 0;
|
|
40
|
+
for ( const unit of units ) {
|
|
41
|
+
|
|
42
|
+
width = Math.max( width, unit.width );
|
|
43
|
+
height = Math.max( height, unit.height );
|
|
44
|
+
count += unit.count;
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { units, width, height, count };
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Footprint times layer count.
|
|
53
|
+
function bucketCost( bucket, maxTextureSize ) {
|
|
54
|
+
|
|
55
|
+
return alignBucketWidth( bucket.width, maxTextureSize ) * bucket.height * bucket.count * 4;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function totalCost( buckets, maxTextureSize ) {
|
|
60
|
+
|
|
61
|
+
return buckets.reduce( ( sum, b ) => sum + bucketCost( b, maxTextureSize ), 0 );
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Repeatedly fuse the pair of buckets whose union wastes least, until `count` remain.
|
|
66
|
+
function mergeDown( buckets, count, maxTextureSize ) {
|
|
67
|
+
|
|
68
|
+
let current = buckets.slice();
|
|
69
|
+
|
|
70
|
+
while ( current.length > count ) {
|
|
71
|
+
|
|
72
|
+
let best = null;
|
|
73
|
+
|
|
74
|
+
for ( let i = 0; i < current.length; i ++ ) {
|
|
75
|
+
|
|
76
|
+
for ( let j = i + 1; j < current.length; j ++ ) {
|
|
77
|
+
|
|
78
|
+
const fused = bucketOf( current[ i ].units.concat( current[ j ].units ) );
|
|
79
|
+
const delta = bucketCost( fused, maxTextureSize )
|
|
80
|
+
- bucketCost( current[ i ], maxTextureSize ) - bucketCost( current[ j ], maxTextureSize );
|
|
81
|
+
if ( ! best || delta < best.delta ) best = { i, j, delta, fused };
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
current = current.filter( ( _, k ) => k !== best.i && k !== best.j ).concat( [ best.fused ] );
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return current;
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The pre-shape-aware layout, kept as a rival opening: square scenes still do well on it.
|
|
96
|
+
function ladderBuckets( units, count, maxTextureSize ) {
|
|
97
|
+
|
|
98
|
+
const ladder = [];
|
|
99
|
+
for ( let i = count - 1; i >= 0; i -- ) {
|
|
100
|
+
|
|
101
|
+
ladder.push( Math.max( TEXTURE_CONSTANTS.MIN_TEXTURE_WIDTH, Math.round( maxTextureSize / Math.pow( 2, i ) ) ) );
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const bins = ladder.map( () => [] );
|
|
106
|
+
|
|
107
|
+
for ( const unit of units ) {
|
|
108
|
+
|
|
109
|
+
const longest = Math.pow( 2, Math.ceil( Math.log2( Math.max( unit.width, unit.height ) ) ) );
|
|
110
|
+
let slot = ladder.findIndex( size => longest <= size );
|
|
111
|
+
if ( slot < 0 ) slot = ladder.length - 1;
|
|
112
|
+
bins[ slot ].push( unit );
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return bins.filter( bin => bin.length ).map( bucketOf );
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Both openings above are greedy and stop short on mixed aspects; moving one shape at a time
|
|
121
|
+
// to whichever bucket makes the pool cheapest recovers the difference.
|
|
122
|
+
function refine( buckets, maxTextureSize ) {
|
|
123
|
+
|
|
124
|
+
let current = buckets;
|
|
125
|
+
|
|
126
|
+
for ( let pass = 0; pass < 8; pass ++ ) {
|
|
127
|
+
|
|
128
|
+
let improved = false;
|
|
129
|
+
|
|
130
|
+
for ( const unit of current.flatMap( b => b.units ) ) {
|
|
131
|
+
|
|
132
|
+
const from = current.findIndex( b => b.units.includes( unit ) );
|
|
133
|
+
if ( from < 0 ) continue;
|
|
134
|
+
|
|
135
|
+
for ( let to = 0; to < current.length; to ++ ) {
|
|
136
|
+
|
|
137
|
+
if ( to === from ) continue;
|
|
138
|
+
|
|
139
|
+
const moved = [];
|
|
140
|
+
for ( let i = 0; i < current.length; i ++ ) {
|
|
141
|
+
|
|
142
|
+
if ( i === from ) {
|
|
143
|
+
|
|
144
|
+
const rest = current[ i ].units.filter( u => u !== unit );
|
|
145
|
+
if ( rest.length ) moved.push( bucketOf( rest ) );
|
|
146
|
+
|
|
147
|
+
} else if ( i === to ) moved.push( bucketOf( current[ i ].units.concat( [ unit ] ) ) );
|
|
148
|
+
else moved.push( current[ i ] );
|
|
149
|
+
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if ( totalCost( moved, maxTextureSize ) < totalCost( current, maxTextureSize ) ) {
|
|
153
|
+
|
|
154
|
+
current = moved;
|
|
155
|
+
improved = true;
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if ( ! improved ) break;
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return current;
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Choose up to `count` array shapes for one colorSpace pool. Grouping by longest edge alone
|
|
174
|
+
* forces a 2000x453 banner into a 2048x2048 array; grouping on both axes does not.
|
|
175
|
+
*
|
|
176
|
+
* @param {Array<{width: number, height: number}>} sizes - every texture in the pool
|
|
177
|
+
* @param {number} maxTextureSize
|
|
178
|
+
* @param {number} [count]
|
|
179
|
+
* @returns {Array<{width: number, height: number}>} ascending by footprint
|
|
180
|
+
*/
|
|
181
|
+
export function planTextureBuckets( sizes, maxTextureSize, count = TEXTURE_CONSTANTS.MATERIAL_BUCKET_COUNT ) {
|
|
182
|
+
|
|
183
|
+
const distinct = new Map();
|
|
184
|
+
|
|
185
|
+
for ( const { width, height } of sizes ) {
|
|
186
|
+
|
|
187
|
+
const w = Math.min( maxTextureSize, Math.max( 1, width || 1 ) );
|
|
188
|
+
const h = Math.min( maxTextureSize, Math.max( 1, height || 1 ) );
|
|
189
|
+
const key = `${w}x${h}`;
|
|
190
|
+
const seen = distinct.get( key );
|
|
191
|
+
if ( seen ) seen.count ++;
|
|
192
|
+
else distinct.set( key, { width: w, height: h, count: 1 } );
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const units = [ ...distinct.values() ];
|
|
197
|
+
let buckets = units.map( unit => bucketOf( [ unit ] ) );
|
|
198
|
+
|
|
199
|
+
if ( buckets.length > count ) {
|
|
200
|
+
|
|
201
|
+
const ladder = ladderBuckets( units, count, maxTextureSize );
|
|
202
|
+
const merged = mergeDown( buckets, count, maxTextureSize );
|
|
203
|
+
buckets = totalCost( ladder, maxTextureSize ) < totalCost( merged, maxTextureSize ) ? ladder : merged;
|
|
204
|
+
buckets = refine( buckets, maxTextureSize );
|
|
205
|
+
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return buckets
|
|
209
|
+
.map( b => ( { width: alignBucketWidth( b.width, maxTextureSize ), height: b.height } ) )
|
|
210
|
+
.sort( ( a, b ) => a.width * a.height - b.width * b.height );
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Index of the cheapest planned bucket that can hold a texture at its native size.
|
|
216
|
+
* A texture larger than every bucket lands in the largest and is downscaled there.
|
|
217
|
+
*
|
|
218
|
+
* @param {number} width
|
|
219
|
+
* @param {number} height
|
|
220
|
+
* @param {Array<{width: number, height: number}>} shapes - from {@link planTextureBuckets}
|
|
221
|
+
* @returns {number}
|
|
222
|
+
*/
|
|
223
|
+
export function getTextureBucketId( width, height, shapes ) {
|
|
224
|
+
|
|
225
|
+
const w = Math.max( 1, width || 1 );
|
|
226
|
+
const h = Math.max( 1, height || 1 );
|
|
227
|
+
for ( let i = 0; i < shapes.length; i ++ ) if ( shapes[ i ].width >= w && shapes[ i ].height >= h ) return i;
|
|
228
|
+
return Math.max( 0, shapes.length - 1 );
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Pack (bucketId, layer) into the single int slot a material map index occupies.
|
|
233
|
+
export function packTextureIndex( bucketId, layer ) {
|
|
234
|
+
|
|
235
|
+
return bucketId * TEXTURE_CONSTANTS.BUCKET_LAYER_STRIDE + layer;
|
|
236
|
+
|
|
237
|
+
}
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace, LinearSRGBColorSpace, RepeatWrapping, FloatType, HalfFloatType } from "three";
|
|
2
|
-
import {
|
|
2
|
+
import { MATERIAL_DATA_LAYOUT, BVH_LEAF_MARKERS, assertBVHIndexFits, bvhIndexView } from './BufferLayout.js';
|
|
3
|
+
import { alignBucketWidth, TEXTURE_CONSTANTS } from './TextureBuckets.js';
|
|
3
4
|
import { packMaterial } from './MaterialPacking.js';
|
|
4
5
|
import TexturesWorker from './Workers/TexturesWorker.js?worker&inline';
|
|
5
6
|
import PackWorker from './Workers/PackWorker.js?worker&inline';
|
|
6
7
|
import { resampleRGBA8 } from './ResampleRGBA8.js';
|
|
7
8
|
import { ISSUE_CODES, EngineIssueError } from '../EngineIssues.js';
|
|
8
9
|
import { linearToSRGB } from './ToneMapCPU.js';
|
|
9
|
-
import { getActiveColorManagement } from '../Color/
|
|
10
|
+
import { getActiveColorManagement } from '../Color/ActiveColor.js';
|
|
10
11
|
import { createLogger } from '../utils/Logger.js';
|
|
11
12
|
import { createWorker, hardwareThreads, hasWorkers } from '../Platform.js';
|
|
12
13
|
|
|
13
14
|
const log = createLogger( 'textures' );
|
|
14
15
|
|
|
16
|
+
// Memory management constants
|
|
17
|
+
export const MEMORY_CONSTANTS = {
|
|
18
|
+
MAX_BUFFER_MEMORY: 1024 * 1024 * 1024,
|
|
19
|
+
MAX_TEXTURE_MEMORY: 2048 * 1024 * 1024,
|
|
20
|
+
CLEANUP_THRESHOLD: 0.8,
|
|
21
|
+
STREAM_BATCH_SIZE: 4
|
|
22
|
+
};
|
|
23
|
+
|
|
15
24
|
// Below this a pack is cheaper than starting a worker for it.
|
|
16
25
|
const WORKER_PACK_BYTES = 8 * 2 ** 20;
|
|
17
26
|
|
|
27
|
+
// Main-thread work allowed between yields while streaming layers to a worker.
|
|
28
|
+
const STREAM_YIELD_MS = 16;
|
|
29
|
+
|
|
30
|
+
const SHARED_PACK_WORKERS = 4;
|
|
31
|
+
// Below about one 8K source a bucket packs fast enough alone, and extra workers only compete with the BVH build.
|
|
32
|
+
const SHARED_PACK_MIN_PIXELS = 8192 * 8192;
|
|
33
|
+
|
|
18
34
|
// Buckets pack concurrently; this keeps their workers within the cores, beside the BVH pool's.
|
|
19
35
|
const packSlots = { busy: 0, waiting: [] };
|
|
20
36
|
|
|
@@ -835,6 +851,9 @@ export class TextureCreator {
|
|
|
835
851
|
case 'worker-direct':
|
|
836
852
|
result = await this.processWithWorkerDirect( normalized );
|
|
837
853
|
break;
|
|
854
|
+
case 'worker-streaming':
|
|
855
|
+
result = await this.processInWorkerStreaming( normalized );
|
|
856
|
+
break;
|
|
838
857
|
case 'main-batch':
|
|
839
858
|
result = await this.processOnMainThreadBatch( normalized, strategy.batchSize );
|
|
840
859
|
break;
|
|
@@ -892,10 +911,7 @@ export class TextureCreator {
|
|
|
892
911
|
|
|
893
912
|
if ( this.capabilities.workers && estimatedMemory > MEMORY_CONSTANTS.MAX_TEXTURE_MEMORY ) {
|
|
894
913
|
|
|
895
|
-
|
|
896
|
-
// full array correctly). The former 'worker-chunked' path combined its chunks
|
|
897
|
-
// by returning only the first, silently dropping the rest — removed.
|
|
898
|
-
return { method: 'main-streaming' };
|
|
914
|
+
return { method: 'worker-streaming' };
|
|
899
915
|
|
|
900
916
|
} else if ( this.capabilities.workers && totalPixels > 2097152 ) {
|
|
901
917
|
|
|
@@ -931,14 +947,24 @@ export class TextureCreator {
|
|
|
931
947
|
}
|
|
932
948
|
|
|
933
949
|
this.activeWorkers ++;
|
|
934
|
-
|
|
935
|
-
|
|
950
|
+
let slots = 1;
|
|
951
|
+
let worker = null;
|
|
936
952
|
|
|
937
953
|
try {
|
|
938
954
|
|
|
939
955
|
// Prepare textures for worker with direct transfer
|
|
940
956
|
const texturesData = await this.prepareTexturesForWorkerDirect( textures );
|
|
941
957
|
|
|
958
|
+
const extra = this._sharedPackSlots( texturesData );
|
|
959
|
+
if ( extra > 0 ) {
|
|
960
|
+
|
|
961
|
+
slots += extra;
|
|
962
|
+
this.activeWorkers += extra;
|
|
963
|
+
return this.createDataArrayTextureFromResult( await this._packAcrossWorkers( texturesData, slots ) );
|
|
964
|
+
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
worker = new TexturesWorker();
|
|
942
968
|
const result = await new Promise( ( resolve, reject ) => {
|
|
943
969
|
|
|
944
970
|
worker.onmessage = ( e ) => {
|
|
@@ -983,6 +1009,153 @@ export class TextureCreator {
|
|
|
983
1009
|
|
|
984
1010
|
return this.createDataArrayTextureFromResult( result );
|
|
985
1011
|
|
|
1012
|
+
} finally {
|
|
1013
|
+
|
|
1014
|
+
worker?.terminate();
|
|
1015
|
+
this.activeWorkers -= slots;
|
|
1016
|
+
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/** Extra worker slots a bucket may take now, beyond its own. */
|
|
1022
|
+
_sharedPackSlots( texturesData ) {
|
|
1023
|
+
|
|
1024
|
+
if ( typeof SharedArrayBuffer === 'undefined' || ! globalThis.crossOriginIsolated ) return 0;
|
|
1025
|
+
if ( texturesData.reduce( ( sum, t ) => sum + t.width * t.height, 0 ) < SHARED_PACK_MIN_PIXELS ) return 0;
|
|
1026
|
+
const free = this.maxConcurrentWorkers - this.activeWorkers;
|
|
1027
|
+
return Math.max( 0, Math.min( texturesData.length, SHARED_PACK_WORKERS, free + 1 ) - 1 );
|
|
1028
|
+
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* The worker's direct path split across `count` workers, each drawing its layers straight into
|
|
1033
|
+
* one shared array. Large sources dominate (their downscale), so they are dealt out first.
|
|
1034
|
+
*/
|
|
1035
|
+
async _packAcrossWorkers( texturesData, count ) {
|
|
1036
|
+
|
|
1037
|
+
const { maxWidth: width, maxHeight: height } = this.calculateOptimalDimensions( texturesData.map( image => ( { image } ) ) );
|
|
1038
|
+
const depth = texturesData.length;
|
|
1039
|
+
const shared = new SharedArrayBuffer( width * height * depth * 4 );
|
|
1040
|
+
|
|
1041
|
+
const parts = Array.from( { length: count }, () => ( { cost: 0, layers: [], transfer: [] } ) );
|
|
1042
|
+
const order = texturesData.map( ( _, index ) => index );
|
|
1043
|
+
const cost = index => texturesData[ index ].width * texturesData[ index ].height;
|
|
1044
|
+
order.sort( ( a, b ) => cost( b ) - cost( a ) );
|
|
1045
|
+
|
|
1046
|
+
for ( const index of order ) {
|
|
1047
|
+
|
|
1048
|
+
const part = parts.reduce( ( least, p ) => ( p.cost < least.cost ? p : least ) );
|
|
1049
|
+
const texture = texturesData[ index ];
|
|
1050
|
+
part.layers.push( { index, texture } );
|
|
1051
|
+
part.cost += cost( index );
|
|
1052
|
+
if ( texture.data instanceof ArrayBuffer ) part.transfer.push( texture.data );
|
|
1053
|
+
else if ( texture.bitmap ) part.transfer.push( texture.bitmap );
|
|
1054
|
+
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const workers = [];
|
|
1058
|
+
|
|
1059
|
+
try {
|
|
1060
|
+
|
|
1061
|
+
await Promise.all( parts.filter( part => part.layers.length ).map( part => new Promise( ( resolve, reject ) => {
|
|
1062
|
+
|
|
1063
|
+
const worker = createWorker( TexturesWorker );
|
|
1064
|
+
workers.push( worker );
|
|
1065
|
+
worker.onmessage = ( { data } ) => ( data.error ? reject( new Error( data.error ) ) : resolve() );
|
|
1066
|
+
worker.onerror = ( event ) => reject( new Error( event.message || 'texture worker failed' ) );
|
|
1067
|
+
worker.postMessage( { shared, width, height, layers: part.layers, maxTextureSize: this.maxTextureSize }, part.transfer );
|
|
1068
|
+
|
|
1069
|
+
} ) ) );
|
|
1070
|
+
|
|
1071
|
+
} finally {
|
|
1072
|
+
|
|
1073
|
+
for ( const worker of workers ) worker.terminate();
|
|
1074
|
+
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
return { data: shared, width, height, depth };
|
|
1078
|
+
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* `processOnMainThreadStreaming` with the draw and read moved to a worker: each layer is
|
|
1083
|
+
* resized here exactly as there, so the array is byte-for-byte the same.
|
|
1084
|
+
*/
|
|
1085
|
+
async processInWorkerStreaming( textures ) {
|
|
1086
|
+
|
|
1087
|
+
const validTextures = textures.filter( tex => tex?.image );
|
|
1088
|
+
if ( validTextures.length === 0 ) return this.createFallbackTexture();
|
|
1089
|
+
|
|
1090
|
+
const { maxWidth, maxHeight } = this.calculateOptimalDimensions( validTextures );
|
|
1091
|
+
const depth = validTextures.length;
|
|
1092
|
+
|
|
1093
|
+
while ( this.activeWorkers >= this.maxConcurrentWorkers ) {
|
|
1094
|
+
|
|
1095
|
+
await new Promise( resolve => setTimeout( resolve, 10 ) );
|
|
1096
|
+
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
this.activeWorkers ++;
|
|
1100
|
+
const worker = createWorker( TexturesWorker );
|
|
1101
|
+
|
|
1102
|
+
try {
|
|
1103
|
+
|
|
1104
|
+
let acked = 0;
|
|
1105
|
+
let wake = null;
|
|
1106
|
+
let settle;
|
|
1107
|
+
const finished = new Promise( ( resolve, reject ) => settle = { resolve, reject } );
|
|
1108
|
+
finished.catch( () => {} );
|
|
1109
|
+
|
|
1110
|
+
worker.onmessage = ( { data } ) => {
|
|
1111
|
+
|
|
1112
|
+
if ( data.error ) settle.reject( new Error( data.error ) );
|
|
1113
|
+
else if ( data.stream === 'ack' ) acked ++;
|
|
1114
|
+
else settle.resolve( data );
|
|
1115
|
+
wake?.();
|
|
1116
|
+
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
worker.onerror = ( event ) => {
|
|
1120
|
+
|
|
1121
|
+
settle.reject( new Error( event.message || 'texture worker failed' ) );
|
|
1122
|
+
wake?.();
|
|
1123
|
+
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
worker.postMessage( { stream: 'begin', width: maxWidth, height: maxHeight, depth } );
|
|
1127
|
+
let lastYield = performance.now();
|
|
1128
|
+
|
|
1129
|
+
for ( let i = 0; i < depth; i ++ ) {
|
|
1130
|
+
|
|
1131
|
+
while ( i - acked >= MEMORY_CONSTANTS.STREAM_BATCH_SIZE ) {
|
|
1132
|
+
|
|
1133
|
+
await Promise.race( [ new Promise( resolve => wake = resolve ), finished ] );
|
|
1134
|
+
lastYield = performance.now();
|
|
1135
|
+
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const texture = validTextures[ i ];
|
|
1139
|
+
const bitmap = await createImageBitmap( texture.image, {
|
|
1140
|
+
resizeWidth: maxWidth,
|
|
1141
|
+
resizeHeight: maxHeight,
|
|
1142
|
+
resizeQuality: 'high',
|
|
1143
|
+
imageOrientation: texture.flipY !== false ? 'flipY' : 'none',
|
|
1144
|
+
} );
|
|
1145
|
+
worker.postMessage( { stream: 'layer', index: i, bitmap }, [ bitmap ] );
|
|
1146
|
+
|
|
1147
|
+
if ( performance.now() - lastYield > STREAM_YIELD_MS ) {
|
|
1148
|
+
|
|
1149
|
+
await new Promise( resolve => setTimeout( resolve, 0 ) );
|
|
1150
|
+
lastYield = performance.now();
|
|
1151
|
+
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
worker.postMessage( { stream: 'end' } );
|
|
1157
|
+
return this.createDataArrayTextureFromResult( await finished );
|
|
1158
|
+
|
|
986
1159
|
} finally {
|
|
987
1160
|
|
|
988
1161
|
worker.terminate();
|
|
@@ -56,7 +56,39 @@ fn rayzee_source( x: u32, y: u32 ) -> vec4<f32> {
|
|
|
56
56
|
}`,
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// RGBA bytes for a picture, or four float planes (r, g, b, a) for a network that wants the curve
|
|
60
|
+
// unquantized. The planes take a 2.2 power, not the sRGB curve: what the AI upscaler has always fed
|
|
61
|
+
// its network, so its output does not move.
|
|
62
|
+
const OUTPUTS = {
|
|
63
|
+
rgba8: /* wgsl */ `
|
|
64
|
+
@group(0) @binding(1) var<storage, read_write> dst: array<u32>;
|
|
65
|
+
|
|
66
|
+
fn rayzee_store( i: u32, linear: vec4<f32>, mapped: vec3<f32> ) {
|
|
67
|
+
let b8 = rayzee_to_u8( rayzee_encode( mapped, params.mode ) );
|
|
68
|
+
var a8 = 255u;
|
|
69
|
+
if ( params.alpha == 1u ) { a8 = rayzee_to_u8( vec3<f32>( linear.a ) ).x; }
|
|
70
|
+
dst[ i ] = b8.x | ( b8.y << 8u ) | ( b8.z << 16u ) | ( a8 << 24u );
|
|
71
|
+
}`,
|
|
72
|
+
planar: /* wgsl */ `
|
|
73
|
+
@group(0) @binding(1) var<storage, read_write> dst: array<f32>;
|
|
74
|
+
|
|
75
|
+
fn rayzee_store( i: u32, linear: vec4<f32>, mapped: vec3<f32> ) {
|
|
76
|
+
var c = clamp( mapped, vec3<f32>( 0.0 ), vec3<f32>( 1.0 ) );
|
|
77
|
+
if ( ! rayzee_output_encoded( params.mode ) ) {
|
|
78
|
+
let m = max( mapped, vec3<f32>( 0.0 ) );
|
|
79
|
+
c = select( pow( m, vec3<f32>( 1.0 / 2.2 ) ), vec3<f32>( 0.0 ), m <= vec3<f32>( 0.0 ) );
|
|
80
|
+
}
|
|
81
|
+
let n = params.width * params.height;
|
|
82
|
+
dst[ i ] = c.r;
|
|
83
|
+
dst[ n + i ] = c.g;
|
|
84
|
+
dst[ 2u * n + i ] = c.b;
|
|
85
|
+
dst[ 3u * n + i ] = linear.a;
|
|
86
|
+
}`,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const BYTES_PER_PIXEL = { rgba8: 4, planar: 16 };
|
|
90
|
+
|
|
91
|
+
const toneMapPassWGSL = ( transformWGSL, input, output ) => /* wgsl */ `
|
|
60
92
|
struct Params {
|
|
61
93
|
width: u32,
|
|
62
94
|
height: u32,
|
|
@@ -67,11 +99,11 @@ struct Params {
|
|
|
67
99
|
alpha: u32,
|
|
68
100
|
};
|
|
69
101
|
|
|
70
|
-
@group(0) @binding(1) var<storage, read_write> dst: array<u32>;
|
|
71
102
|
@group(0) @binding(2) var<uniform> params: Params;
|
|
72
103
|
${SOURCES[ input ]}
|
|
73
104
|
|
|
74
105
|
${transformWGSL}
|
|
106
|
+
${OUTPUTS[ output ]}
|
|
75
107
|
|
|
76
108
|
@compute @workgroup_size(8, 8)
|
|
77
109
|
fn main( @builtin(global_invocation_id) gid: vec3<u32> ) {
|
|
@@ -81,11 +113,7 @@ fn main( @builtin(global_invocation_id) gid: vec3<u32> ) {
|
|
|
81
113
|
if ( params.flipY == 1u ) { srcY = params.height - 1u - gid.y; }
|
|
82
114
|
|
|
83
115
|
let linear = rayzee_source( gid.x, srcY );
|
|
84
|
-
|
|
85
|
-
let b8 = rayzee_to_u8( rayzee_encode( mapped, params.mode ) );
|
|
86
|
-
var a8 = 255u;
|
|
87
|
-
if ( params.alpha == 1u ) { a8 = rayzee_to_u8( vec3<f32>( linear.a ) ).x; }
|
|
88
|
-
dst[ gid.y * params.width + gid.x ] = b8.x | ( b8.y << 8u ) | ( b8.z << 16u ) | ( a8 << 24u );
|
|
116
|
+
rayzee_store( gid.y * params.width + gid.x, linear, rayzee_tone_map( linear.rgb, params.mode, params.exposure, params.saturation ) );
|
|
89
117
|
}
|
|
90
118
|
`;
|
|
91
119
|
|
|
@@ -94,17 +122,18 @@ const PARAMS_BYTES = 32;
|
|
|
94
122
|
|
|
95
123
|
/**
|
|
96
124
|
* Tone-maps a tightly packed rgba16float buffer (two `u32` per pixel), or with `input: 'texture'` a
|
|
97
|
-
* float texture read by pixel, into RGBA bytes.
|
|
125
|
+
* float texture read by pixel, into RGBA bytes — or with `output: 'planar'` into float planes.
|
|
98
126
|
*
|
|
99
127
|
* Bound to one device and one image size; `ensureSize()` reallocates when the size changes.
|
|
100
128
|
*/
|
|
101
129
|
export class PackedToneMapper {
|
|
102
130
|
|
|
103
|
-
constructor( device, label = 'rayzee:tonemap', { input = 'packed' } = {} ) {
|
|
131
|
+
constructor( device, label = 'rayzee:tonemap', { input = 'packed', output = 'rgba8' } = {} ) {
|
|
104
132
|
|
|
105
133
|
this.device = device;
|
|
106
134
|
this.label = label;
|
|
107
135
|
this.input = input;
|
|
136
|
+
this.output = output;
|
|
108
137
|
this.width = 0;
|
|
109
138
|
this.height = 0;
|
|
110
139
|
this._pipeline = null;
|
|
@@ -139,7 +168,7 @@ export class PackedToneMapper {
|
|
|
139
168
|
label: this.label,
|
|
140
169
|
layout: 'auto',
|
|
141
170
|
compute: {
|
|
142
|
-
module: this.device.createShaderModule( { label: this.label, code: toneMapPassWGSL( wgsl, this.input ) } ),
|
|
171
|
+
module: this.device.createShaderModule( { label: this.label, code: toneMapPassWGSL( wgsl, this.input, this.output ) } ),
|
|
143
172
|
entryPoint: 'main',
|
|
144
173
|
},
|
|
145
174
|
} );
|
|
@@ -202,7 +231,7 @@ export class PackedToneMapper {
|
|
|
202
231
|
|
|
203
232
|
this._releaseBuffers();
|
|
204
233
|
|
|
205
|
-
const bytes = width * height *
|
|
234
|
+
const bytes = width * height * BYTES_PER_PIXEL[ this.output ];
|
|
206
235
|
this._storage = this.device.createBuffer( {
|
|
207
236
|
label: `${this.label}-out`,
|
|
208
237
|
size: bytes,
|
|
@@ -235,7 +264,26 @@ export class PackedToneMapper {
|
|
|
235
264
|
* @param {boolean} [tone.preserveAlpha=false] - the source's alpha, rounded as the CPU rounds it; else 255
|
|
236
265
|
* @returns {Promise<Uint8ClampedArray>} RGBA bytes, `width * height * 4`
|
|
237
266
|
*/
|
|
238
|
-
async toRGBA8( src,
|
|
267
|
+
async toRGBA8( src, tone ) {
|
|
268
|
+
|
|
269
|
+
return new Uint8ClampedArray( await this._run( src, tone ) );
|
|
270
|
+
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* The same tone map into four float planes — r, g, b, then the source's alpha — each `width * height`.
|
|
275
|
+
* Needs `output: 'planar'`.
|
|
276
|
+
* @param {GPUBuffer|GPUTexture} src - as for toRGBA8
|
|
277
|
+
* @param {object} tone - as for toRGBA8; `preserveAlpha` does not apply
|
|
278
|
+
* @returns {Promise<Float32Array>}
|
|
279
|
+
*/
|
|
280
|
+
async toPlanar( src, tone ) {
|
|
281
|
+
|
|
282
|
+
return new Float32Array( await this._run( src, tone ) );
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async _run( src, { exposure = 1, toneMapping = 0, saturation = 1, flipY = false, preserveAlpha = false } = {} ) {
|
|
239
287
|
|
|
240
288
|
if ( this.disposed ) throw new Error( 'PackedToneMapper: disposed' );
|
|
241
289
|
if ( ! this._storage ) throw new Error( 'PackedToneMapper: call ensureSize() first' );
|
|
@@ -246,6 +294,7 @@ export class PackedToneMapper {
|
|
|
246
294
|
this._ensureTables();
|
|
247
295
|
|
|
248
296
|
const { width, height } = this;
|
|
297
|
+
const bytes = width * height * BYTES_PER_PIXEL[ this.output ];
|
|
249
298
|
|
|
250
299
|
this._paramU32[ 0 ] = width;
|
|
251
300
|
this._paramU32[ 1 ] = height;
|
|
@@ -275,13 +324,13 @@ export class PackedToneMapper {
|
|
|
275
324
|
pass.setBindGroup( 0, group );
|
|
276
325
|
pass.dispatchWorkgroups( Math.ceil( width / 8 ), Math.ceil( height / 8 ) );
|
|
277
326
|
pass.end();
|
|
278
|
-
encoder.copyBufferToBuffer( this._storage, 0, this._map, 0,
|
|
327
|
+
encoder.copyBufferToBuffer( this._storage, 0, this._map, 0, bytes );
|
|
279
328
|
this.device.queue.submit( [ encoder.finish() ] );
|
|
280
329
|
|
|
281
330
|
await this._map.mapAsync( GPUMapMode.READ );
|
|
282
|
-
const
|
|
331
|
+
const out = this._map.getMappedRange().slice( 0 );
|
|
283
332
|
this._map.unmap();
|
|
284
|
-
return
|
|
333
|
+
return out;
|
|
285
334
|
|
|
286
335
|
}
|
|
287
336
|
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Protocol:
|
|
5
5
|
* 'init' → receives the shared triangle and node buffers (once per scene)
|
|
6
|
-
* 'refit' → recomputes every node's AABB from the triangles already written there
|
|
6
|
+
* 'refit' → recomputes every node's AABB from the triangles already written there, or with
|
|
7
|
+
* `blasRanges` only those BLASes and the TLAS
|
|
7
8
|
*
|
|
8
9
|
* Positions never reach here: the main thread scatters each mesh into the shared triangle
|
|
9
10
|
* records as it reads it, so no buffer the size of the scene exists on either side.
|
|
@@ -45,7 +46,9 @@ self.onmessage = function ( e ) {
|
|
|
45
46
|
|
|
46
47
|
const startTime = performance.now();
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
const { blasRanges, tlasNodeCount } = e.data;
|
|
50
|
+
if ( blasRanges ) refitter.refitPartial( bvhData, triData, blasRanges, tlasNodeCount );
|
|
51
|
+
else refitter.refit( bvhData, triData, nodeCount );
|
|
49
52
|
|
|
50
53
|
self.postMessage( {
|
|
51
54
|
type: 'refitComplete',
|