rayzee 11.8.0 → 11.9.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 +18 -6
- package/dist/{ArchiveImporter-_sJm5uaK.js → ArchiveImporter-BJxszU_k.js} +1549 -1232
- package/dist/{BidirectionalIntegrator-Dzt4Z97x.js → BidirectionalIntegrator-DWg66vAd.js} +240 -240
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-30MfpHwQ.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-xljoXM0E.js → EngineIssues-CfJiBaNj.js} +1 -0
- package/dist/{NeuralRetouch-BKQCsmEs.js → NeuralRetouch-BD9E-Yht.js} +1 -1
- package/dist/{NeuralSuperRes-C4ymq68e.js → NeuralSuperRes-C8Mf3tJX.js} +1 -1
- package/dist/{PathTracer-C_to_i40.js → PathTracer-DeE9UJ1n.js} +3433 -3363
- package/dist/{PhysicalSky-bMHbm_7H.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-B_aB6VTJ.js → RayzeeRenderer-CzLyBA3G.js} +13 -8
- package/dist/{SceneMetadata-CSmlJW4c.js → SceneMetadata-C17UfOEp.js} +1 -1
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/color.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{openStorage-B085D9FR.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +567 -559
- package/dist/rayzee.umd.js +106 -101
- package/dist/{sceneCachePolicy-D_uBSbQF.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +1 -0
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/Processor/ArchiveImporter.js +83 -21
- package/src/Processor/AssetLoader.js +7 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +47 -12
- package/src/Processor/PBRT/PBRTParser.js +144 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +287 -67
- package/src/Processor/PBRT/PBRTTextureBake.js +191 -9
- package/src/Processor/PBRT/index.js +19 -4
- package/src/Processor/SceneProcessor.js +42 -26
- package/src/RayzeeRenderer.js +4 -2
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +5 -3
- package/src/Stages/PathTracer.js +2 -0
- package/src/Stages/PathTracerStage.js +5 -1
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/Common.js +25 -8
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/SceneResources.js +8 -0
- package/src/TSL/ShadeKernel.js +2 -2
- package/src/TSL/Struct.js +1 -0
- package/src/managers/MaterialDataManager.js +11 -0
|
@@ -5,10 +5,15 @@
|
|
|
5
5
|
* A layer is an image as pbrt evaluates it: linear values, top row first, `channels` 1 (a float texture reads
|
|
6
6
|
* an image's alpha where it has one, else the mean of its colour) or 3, already scaled and inverted, and the uv
|
|
7
7
|
* mapping pbrt applies before the lookup (st = scale · uv + delta, image row from the top = 1 − t).
|
|
8
|
+
*
|
|
9
|
+
* pbrt's position mappings (`planar`, `spherical`, `cylindrical`) read no uv: their (s, t) becomes a uv set generated per
|
|
10
|
+
* shape (`generateUV`), and a layer in one carries it as `uv` (`{ type, key, … }`; null for the mesh's own uv).
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
13
|
const MAX_BAKE_SIZE = 4096;
|
|
11
14
|
|
|
15
|
+
export const uvKey = ( uv ) => uv?.key ?? '';
|
|
16
|
+
|
|
12
17
|
export const srgbToLinear = ( v ) => ( v <= 0.04045 ? v / 12.92 : ( ( v + 0.055 ) / 1.055 ) ** 2.4 );
|
|
13
18
|
export const linearToSRGB = ( v ) => ( v <= 0.0031308 ? v * 12.92 : 1.055 * v ** ( 1 / 2.4 ) - 0.055 );
|
|
14
19
|
|
|
@@ -20,7 +25,7 @@ export const linearToSRGB = ( v ) => ( v <= 0.0031308 ? v * 12.92 : 1.055 * v **
|
|
|
20
25
|
* @param {(v: number) => number} [options.decode] - 8-bit value / 255 → linear; omitted for float data
|
|
21
26
|
* @param {number} [options.scale=1]
|
|
22
27
|
* @param {boolean} [options.invert=false]
|
|
23
|
-
* @param {{ su: number, sv: number, du: number, dv: number }} [options.mapping]
|
|
28
|
+
* @param {{ su: number, sv: number, du: number, dv: number, uv?: object }} [options.mapping]
|
|
24
29
|
*/
|
|
25
30
|
export function makeLayer( pixels, { channels, decode = null, scale = 1, invert = false, mapping = null } ) {
|
|
26
31
|
|
|
@@ -29,7 +34,9 @@ export function makeLayer( pixels, { channels, decode = null, scale = 1, invert
|
|
|
29
34
|
const data = new Float32Array( width * height * channels );
|
|
30
35
|
const topDown = pixels.topDown !== false;
|
|
31
36
|
// pbrt's encoding applies to every channel of an 8-bit image, alpha included.
|
|
32
|
-
const
|
|
37
|
+
const bytes = decode && ( pixels.data instanceof Uint8Array || pixels.data instanceof Uint8ClampedArray );
|
|
38
|
+
const lut = bytes ? Float64Array.from( { length: 256 }, ( _, v ) => decode( v / 255 ) ) : null;
|
|
39
|
+
const read = lut ? ( i ) => lut[ pixels.data[ i ] ] : ( i ) => ( decode ? decode( pixels.data[ i ] / 255 ) : pixels.data[ i ] );
|
|
33
40
|
const fromAlpha = channels === 1 && stride === 4 && hasAlpha( pixels );
|
|
34
41
|
const mean = channels === 1 && stride >= 3 && ! fromAlpha;
|
|
35
42
|
|
|
@@ -53,7 +60,7 @@ export function makeLayer( pixels, { channels, decode = null, scale = 1, invert
|
|
|
53
60
|
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
return { width, height, channels, data, su: 1, sv: 1, du: 0, dv: 0, ...mapping };
|
|
63
|
+
return { width, height, channels, data, su: 1, sv: 1, du: 0, dv: 0, uv: null, ...mapping };
|
|
57
64
|
|
|
58
65
|
}
|
|
59
66
|
|
|
@@ -76,6 +83,15 @@ export function layerMean( layer ) {
|
|
|
76
83
|
|
|
77
84
|
}
|
|
78
85
|
|
|
86
|
+
function layerMeanRGB( { data, channels } ) {
|
|
87
|
+
|
|
88
|
+
const sum = [ 0, 0, 0 ];
|
|
89
|
+
for ( let i = 0; i < data.length; i ++ ) sum[ i % channels ] += data[ i ];
|
|
90
|
+
const texels = data.length / channels || 1;
|
|
91
|
+
return [ 0, 1, 2 ].map( ( c ) => sum[ Math.min( c, channels - 1 ) ] / texels );
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
// Bilinear, repeating, at pbrt's (s, t).
|
|
80
96
|
function sample( layer, s, t, out ) {
|
|
81
97
|
|
|
@@ -102,20 +118,36 @@ function sample( layer, s, t, out ) {
|
|
|
102
118
|
/**
|
|
103
119
|
* Each term is `{ rgb }` (a constant) or `{ layer, tint? }` (an image, times `tint`); `clamp` clamps it to [0, 1]
|
|
104
120
|
* first, as pbrt clamps a colour texture read as an albedo, before anything mixes it. Evaluates
|
|
105
|
-
* `combine( values )` over the texel grid of the largest image among the terms
|
|
106
|
-
* a term with another mapping is sampled through it
|
|
121
|
+
* `combine( values )` over the texel grid of the largest image among the terms in the first image's uv set, in that
|
|
122
|
+
* image's uv mapping; a term with another mapping is sampled through it, and one in another uv set counts as its mean
|
|
123
|
+
* (`averaged` says how many). The result is sRGB bytes laid out for a DataTexture
|
|
107
124
|
* (bottom row first), clamped to [0, 1] as pbrt clamps an albedo. Null when every term is a constant.
|
|
108
125
|
* @param {Array<{ rgb?: number[], layer?: object, tint?: number[] }>} terms
|
|
109
126
|
* @param {(values: number[][]) => number[]} combine - linear RGB from each term's linear RGB
|
|
110
127
|
* @param {object} [options]
|
|
111
128
|
* @param {(values: number[][]) => number} [options.alphaOf] - the alpha channel, linear; opaque without it
|
|
112
129
|
* @param {boolean} [options.linear=false] - linear bytes, for a data map
|
|
113
|
-
* @returns {{ data: Uint8Array, width: number, height: number, mapping: { su: number, sv: number, du: number, dv: number } } | null}
|
|
130
|
+
* @returns {{ data: Uint8Array, width: number, height: number, mapping: { su: number, sv: number, du: number, dv: number, uv: object|null }, averaged: number } | null}
|
|
114
131
|
*/
|
|
115
132
|
export function bake( terms, combine, { alphaOf = null, linear = false } = {} ) {
|
|
116
133
|
|
|
117
|
-
|
|
118
|
-
|
|
134
|
+
const set = uvKey( terms.find( ( term ) => term.layer )?.layer.uv );
|
|
135
|
+
let driver = null, averaged = 0;
|
|
136
|
+
terms = terms.map( ( term ) => {
|
|
137
|
+
|
|
138
|
+
if ( ! term.layer ) return term;
|
|
139
|
+
if ( uvKey( term.layer.uv ) !== set ) {
|
|
140
|
+
|
|
141
|
+
averaged ++;
|
|
142
|
+
const mean = layerMeanRGB( term.layer );
|
|
143
|
+
return { rgb: mean.map( ( v, c ) => v * ( term.tint?.[ c ] ?? 1 ) ), clamp: term.clamp };
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if ( ! driver || term.layer.width * term.layer.height > driver.width * driver.height ) driver = term.layer;
|
|
148
|
+
return term;
|
|
149
|
+
|
|
150
|
+
} );
|
|
119
151
|
if ( ! driver ) return null;
|
|
120
152
|
|
|
121
153
|
const width = Math.min( driver.width, MAX_BAKE_SIZE ), height = Math.min( driver.height, MAX_BAKE_SIZE );
|
|
@@ -166,7 +198,7 @@ export function bake( terms, combine, { alphaOf = null, linear = false } = {} )
|
|
|
166
198
|
|
|
167
199
|
}
|
|
168
200
|
|
|
169
|
-
return { data: out, width, height, mapping: { su: driver.su, sv: driver.sv, du: driver.du, dv: driver.dv } };
|
|
201
|
+
return { data: out, width, height, mapping: { su: driver.su, sv: driver.sv, du: driver.du, dv: driver.dv, uv: driver.uv ?? null }, averaged };
|
|
170
202
|
|
|
171
203
|
}
|
|
172
204
|
|
|
@@ -177,3 +209,153 @@ export const mixOf = ( [ a, b, amount ] ) => {
|
|
|
177
209
|
return [ a[ 0 ] + ( b[ 0 ] - a[ 0 ] ) * t, a[ 1 ] + ( b[ 1 ] - a[ 1 ] ) * t, a[ 2 ] + ( b[ 2 ] - a[ 2 ] ) * t ];
|
|
178
210
|
|
|
179
211
|
};
|
|
212
|
+
|
|
213
|
+
// The coordinate that goes once around the axis: spherical φ is t, cylindrical φ is s.
|
|
214
|
+
const WRAPPED = { spherical: 1, cylindrical: 0 };
|
|
215
|
+
|
|
216
|
+
/** pbrt-v4's (s, t) of a point in texture space (textures.h: PlanarMapping, SphericalMapping, CylindricalMapping). */
|
|
217
|
+
export function mapPoint( mapping, x, y, z, out ) {
|
|
218
|
+
|
|
219
|
+
if ( mapping.type === 'planar' ) {
|
|
220
|
+
|
|
221
|
+
const { vs, vt } = mapping;
|
|
222
|
+
out[ 0 ] = mapping.ds + x * vs[ 0 ] + y * vs[ 1 ] + z * vs[ 2 ];
|
|
223
|
+
out[ 1 ] = mapping.dt + x * vt[ 0 ] + y * vt[ 1 ] + z * vt[ 2 ];
|
|
224
|
+
|
|
225
|
+
} else if ( mapping.type === 'spherical' ) {
|
|
226
|
+
|
|
227
|
+
const length = Math.hypot( x, y, z ) || 1;
|
|
228
|
+
const phi = Math.atan2( y, x );
|
|
229
|
+
out[ 0 ] = Math.acos( Math.min( 1, Math.max( - 1, z / length ) ) ) / Math.PI;
|
|
230
|
+
out[ 1 ] = ( phi < 0 ? phi + 2 * Math.PI : phi ) / ( 2 * Math.PI );
|
|
231
|
+
|
|
232
|
+
} else {
|
|
233
|
+
|
|
234
|
+
out[ 0 ] = ( Math.PI + Math.atan2( y, x ) ) / ( 2 * Math.PI );
|
|
235
|
+
out[ 1 ] = z;
|
|
236
|
+
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return out;
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* pbrt's (s, t) at each vertex as a uv set, `toTexture` (column-major) taking `positions` to texture space. A triangle
|
|
245
|
+
* across the seam gets copies of its vertices a turn on (`extra` names their sources, `index` the rewritten triangles,
|
|
246
|
+
* null when none); a vertex on the axis takes its triangle's mean angle.
|
|
247
|
+
* @param {Float32Array} positions
|
|
248
|
+
* @param {ArrayLike<number>|null} index
|
|
249
|
+
* @param {number[]} toTexture
|
|
250
|
+
* @param {{ type: string }} mapping
|
|
251
|
+
* @returns {{ uv: Float32Array, index: Uint32Array|null, extra: Uint32Array }}
|
|
252
|
+
*/
|
|
253
|
+
export function generateUV( positions, index, toTexture, mapping ) {
|
|
254
|
+
|
|
255
|
+
const count = positions.length / 3;
|
|
256
|
+
const m = toTexture;
|
|
257
|
+
const k = WRAPPED[ mapping.type ];
|
|
258
|
+
const st = new Float64Array( count * 2 );
|
|
259
|
+
const radius = k === undefined ? null : new Float64Array( count );
|
|
260
|
+
const out = [ 0, 0 ];
|
|
261
|
+
let maxRadius = 0;
|
|
262
|
+
|
|
263
|
+
for ( let i = 0; i < count; i ++ ) {
|
|
264
|
+
|
|
265
|
+
const px = positions[ i * 3 ], py = positions[ i * 3 + 1 ], pz = positions[ i * 3 + 2 ];
|
|
266
|
+
const x = m[ 0 ] * px + m[ 4 ] * py + m[ 8 ] * pz + m[ 12 ];
|
|
267
|
+
const y = m[ 1 ] * px + m[ 5 ] * py + m[ 9 ] * pz + m[ 13 ];
|
|
268
|
+
const z = m[ 2 ] * px + m[ 6 ] * py + m[ 10 ] * pz + m[ 14 ];
|
|
269
|
+
mapPoint( mapping, x, y, z, out );
|
|
270
|
+
st[ i * 2 ] = out[ 0 ];
|
|
271
|
+
st[ i * 2 + 1 ] = out[ 1 ];
|
|
272
|
+
if ( ! radius ) continue;
|
|
273
|
+
radius[ i ] = Math.hypot( x, y );
|
|
274
|
+
if ( radius[ i ] > maxRadius ) maxRadius = radius[ i ];
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const extra = [];
|
|
279
|
+
let triangles = index;
|
|
280
|
+
|
|
281
|
+
if ( radius ) {
|
|
282
|
+
|
|
283
|
+
const onAxis = maxRadius * 1e-6;
|
|
284
|
+
const copies = new Map();
|
|
285
|
+
const corner = [ 0, 0, 0 ], value = [ 0, 0, 0 ];
|
|
286
|
+
const triangleCount = ( index ? index.length : count ) / 3;
|
|
287
|
+
|
|
288
|
+
for ( let t = 0; t < triangleCount; t ++ ) {
|
|
289
|
+
|
|
290
|
+
let lo = Infinity, hi = - Infinity, sum = 0, around = 0;
|
|
291
|
+
for ( let c = 0; c < 3; c ++ ) {
|
|
292
|
+
|
|
293
|
+
const v = corner[ c ] = index ? index[ t * 3 + c ] : t * 3 + c;
|
|
294
|
+
if ( radius[ v ] <= onAxis ) continue;
|
|
295
|
+
lo = Math.min( lo, st[ v * 2 + k ] );
|
|
296
|
+
hi = Math.max( hi, st[ v * 2 + k ] );
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const across = hi - lo > 0.5;
|
|
301
|
+
for ( let c = 0; c < 3; c ++ ) {
|
|
302
|
+
|
|
303
|
+
const w = st[ corner[ c ] * 2 + k ];
|
|
304
|
+
if ( radius[ corner[ c ] ] <= onAxis ) value[ c ] = NaN;
|
|
305
|
+
else {
|
|
306
|
+
|
|
307
|
+
value[ c ] = across && w < 0.5 ? w + 1 : w;
|
|
308
|
+
sum += value[ c ];
|
|
309
|
+
around ++;
|
|
310
|
+
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for ( let c = 0; c < 3; c ++ ) {
|
|
316
|
+
|
|
317
|
+
const v = corner[ c ];
|
|
318
|
+
const w = Number.isNaN( value[ c ] ) ? ( around > 0 ? sum / around : st[ v * 2 + k ] ) : value[ c ];
|
|
319
|
+
if ( w === st[ v * 2 + k ] ) continue;
|
|
320
|
+
|
|
321
|
+
if ( ! index ) {
|
|
322
|
+
|
|
323
|
+
st[ v * 2 + k ] = w;
|
|
324
|
+
continue;
|
|
325
|
+
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const key = `${v}|${w}`;
|
|
329
|
+
let copy = copies.get( key );
|
|
330
|
+
if ( copy === undefined ) {
|
|
331
|
+
|
|
332
|
+
copies.set( key, copy = count + extra.length / 2 );
|
|
333
|
+
extra.push( v, w );
|
|
334
|
+
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if ( triangles === index ) triangles = Uint32Array.from( index );
|
|
338
|
+
triangles[ t * 3 + c ] = copy;
|
|
339
|
+
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const added = extra.length / 2;
|
|
347
|
+
const uv = new Float32Array( ( count + added ) * 2 );
|
|
348
|
+
for ( let i = 0; i < count * 2; i ++ ) uv[ i ] = st[ i ];
|
|
349
|
+
const sources = new Uint32Array( added );
|
|
350
|
+
for ( let i = 0; i < added; i ++ ) {
|
|
351
|
+
|
|
352
|
+
const v = sources[ i ] = extra[ i * 2 ];
|
|
353
|
+
uv[ ( count + i ) * 2 ] = st[ v * 2 ];
|
|
354
|
+
uv[ ( count + i ) * 2 + 1 ] = st[ v * 2 + 1 ];
|
|
355
|
+
uv[ ( count + i ) * 2 + k ] = extra[ i * 2 + 1 ];
|
|
356
|
+
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { uv, index: triangles === index ? null : triangles, extra: sources };
|
|
360
|
+
|
|
361
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { freeNow } from './buffers.js';
|
|
18
|
-
import { PBRTParser } from './PBRTParser.js';
|
|
18
|
+
import { PBRTParser, ParamUsage } from './PBRTParser.js';
|
|
19
19
|
import { PBRTSceneBuilder } from './PBRTSceneBuilder.js';
|
|
20
20
|
import { findFrameSequence, FrameSequenceMerger, motionFromShutter, SEQUENCE_FPS } from './PBRTAnimation.js';
|
|
21
21
|
|
|
@@ -380,7 +380,8 @@ export async function listEntryPathsFrom( vfs ) {
|
|
|
380
380
|
* @param {boolean} [args.convertHandedness=true]
|
|
381
381
|
* @param {boolean} [args.animation=true] - load a frame sequence (`frame25.pbrt`, `frame35.pbrt`, …)
|
|
382
382
|
* as one animated scene instead of its first frame alone. Ignored when `entryPath` names a file.
|
|
383
|
-
* @returns {Promise<{group, camera, environment, animations, warnings, entryPath, frames}>}
|
|
383
|
+
* @returns {Promise<{group, camera, environment, animations, warnings, entryPath, frames, parameters}>} - `parameters`: what
|
|
384
|
+
* ParamUsage.finish() reports, the parameters this load ignored or approximated
|
|
384
385
|
*/
|
|
385
386
|
export async function loadPBRTScene( args ) {
|
|
386
387
|
|
|
@@ -401,6 +402,7 @@ export async function loadPBRTScene( args ) {
|
|
|
401
402
|
}
|
|
402
403
|
|
|
403
404
|
const entryPath = requested || candidates[ 0 ];
|
|
405
|
+
const usage = new ParamUsage();
|
|
404
406
|
|
|
405
407
|
// Parse (with Include resolution). Bytes go straight to the lexer — a scene file can
|
|
406
408
|
// be larger than the longest string JavaScript will build.
|
|
@@ -415,7 +417,8 @@ export async function loadPBRTScene( args ) {
|
|
|
415
417
|
// scene file at once — the difference between 5.7 GB resident and a few MB.
|
|
416
418
|
releaseInclude: ( include, currentDir, bytes ) => vfs.releasePath( include, currentDir, bytes ),
|
|
417
419
|
maxPlacements: args.maxPlacements,
|
|
418
|
-
instanceIncludes: args.instanceIncludes
|
|
420
|
+
instanceIncludes: args.instanceIncludes,
|
|
421
|
+
usage
|
|
419
422
|
} );
|
|
420
423
|
|
|
421
424
|
const ir = await parser.parse( bytes, path.includes( '/' ) ? path.slice( 0, path.lastIndexOf( '/' ) ) : '' );
|
|
@@ -490,6 +493,7 @@ export async function loadPBRTScene( args ) {
|
|
|
490
493
|
curveSteps: args.curveSteps,
|
|
491
494
|
curveSides: args.curveSides,
|
|
492
495
|
curveTolerance: args.curveTolerance,
|
|
496
|
+
usage,
|
|
493
497
|
resolvePLY: async ( filename ) => {
|
|
494
498
|
|
|
495
499
|
const rec = vfs.findRecord( filename );
|
|
@@ -515,9 +519,20 @@ export async function loadPBRTScene( args ) {
|
|
|
515
519
|
} );
|
|
516
520
|
|
|
517
521
|
const buildStart = performance.now();
|
|
518
|
-
|
|
522
|
+
let result, parameters;
|
|
523
|
+
try {
|
|
524
|
+
|
|
525
|
+
result = await builder.build( ir );
|
|
526
|
+
|
|
527
|
+
} finally {
|
|
528
|
+
|
|
529
|
+
parameters = usage.finish();
|
|
530
|
+
|
|
531
|
+
}
|
|
532
|
+
|
|
519
533
|
return {
|
|
520
534
|
...result,
|
|
535
|
+
parameters,
|
|
521
536
|
entryPath: sequence ? sequence[ 0 ].path : entryPath,
|
|
522
537
|
frames: sequence ? sequence.map( f => f.path ) : null,
|
|
523
538
|
candidates,
|
|
@@ -63,6 +63,14 @@ const GEOMETRY_QUEUE_BYTES = 256 * 1024 * 1024;
|
|
|
63
63
|
|
|
64
64
|
const log = createLogger( 'scene' );
|
|
65
65
|
|
|
66
|
+
// The per-type texture lists an extraction hands over (GeometryExtractor.getExtractedData).
|
|
67
|
+
const TEXTURE_LISTS = Object.freeze( [
|
|
68
|
+
'maps', 'normalMaps', 'bumpMaps', 'roughnessMaps', 'metalnessMaps', 'emissiveMaps', 'displacementMaps',
|
|
69
|
+
'anisotropyMaps', 'transmissionMaps', 'clearcoatMaps', 'clearcoatRoughnessMaps', 'sheenColorMaps',
|
|
70
|
+
'sheenRoughnessMaps', 'iridescenceMaps', 'iridescenceThicknessMaps', 'specularIntensityMaps', 'specularColorMaps',
|
|
71
|
+
'diffuseTransmissionMaps', 'diffuseTransmissionColorMaps',
|
|
72
|
+
] );
|
|
73
|
+
|
|
66
74
|
// A BLAS's nodes: one array, or a split mesh's parts in order — never one array that large.
|
|
67
75
|
const nodeParts = nodes => ( Array.isArray( nodes ) ? nodes : [ nodes ] );
|
|
68
76
|
const nodeBytes = nodes => nodeParts( nodes ).reduce( ( sum, part ) => sum + part.byteLength, 0 );
|
|
@@ -913,25 +921,7 @@ export class SceneProcessor {
|
|
|
913
921
|
this.materialTriangleCounts = extractedData.materialTriangleCounts; // Per-material tri count for sort-bin remap
|
|
914
922
|
this.meshes = extractedData.meshes;
|
|
915
923
|
this.meshTriangleRanges = extractedData.meshTriangleRanges; // Per-mesh { start, count } for TLAS/BLAS
|
|
916
|
-
this.
|
|
917
|
-
this.normalMaps = extractedData.normalMaps;
|
|
918
|
-
this.bumpMaps = extractedData.bumpMaps;
|
|
919
|
-
this.roughnessMaps = extractedData.roughnessMaps;
|
|
920
|
-
this.metalnessMaps = extractedData.metalnessMaps;
|
|
921
|
-
this.emissiveMaps = extractedData.emissiveMaps;
|
|
922
|
-
this.displacementMaps = extractedData.displacementMaps;
|
|
923
|
-
this.anisotropyMaps = extractedData.anisotropyMaps;
|
|
924
|
-
this.transmissionMaps = extractedData.transmissionMaps;
|
|
925
|
-
this.clearcoatMaps = extractedData.clearcoatMaps;
|
|
926
|
-
this.clearcoatRoughnessMaps = extractedData.clearcoatRoughnessMaps;
|
|
927
|
-
this.sheenColorMaps = extractedData.sheenColorMaps;
|
|
928
|
-
this.sheenRoughnessMaps = extractedData.sheenRoughnessMaps;
|
|
929
|
-
this.iridescenceMaps = extractedData.iridescenceMaps;
|
|
930
|
-
this.iridescenceThicknessMaps = extractedData.iridescenceThicknessMaps;
|
|
931
|
-
this.specularIntensityMaps = extractedData.specularIntensityMaps;
|
|
932
|
-
this.specularColorMaps = extractedData.specularColorMaps;
|
|
933
|
-
this.diffuseTransmissionMaps = extractedData.diffuseTransmissionMaps ?? [];
|
|
934
|
-
this.diffuseTransmissionColorMaps = extractedData.diffuseTransmissionColorMaps ?? [];
|
|
924
|
+
this._adoptTextureLists( extractedData );
|
|
935
925
|
this.directionalLights = extractedData.directionalLights;
|
|
936
926
|
this.cameras = extractedData.cameras;
|
|
937
927
|
|
|
@@ -2453,13 +2443,7 @@ export class SceneProcessor {
|
|
|
2453
2443
|
// Update material arrays (but keep existing triangle data)
|
|
2454
2444
|
this.materials = extractedData.materials;
|
|
2455
2445
|
this.meshes = extractedData.meshes; // Update mesh data
|
|
2456
|
-
this.
|
|
2457
|
-
this.normalMaps = extractedData.normalMaps;
|
|
2458
|
-
this.bumpMaps = extractedData.bumpMaps;
|
|
2459
|
-
this.roughnessMaps = extractedData.roughnessMaps;
|
|
2460
|
-
this.metalnessMaps = extractedData.metalnessMaps;
|
|
2461
|
-
this.emissiveMaps = extractedData.emissiveMaps;
|
|
2462
|
-
this.displacementMaps = extractedData.displacementMaps;
|
|
2446
|
+
this._adoptTextureLists( extractedData );
|
|
2463
2447
|
|
|
2464
2448
|
// Bucket textures, remap material indices, regenerate raw material data, and
|
|
2465
2449
|
// build the consolidated bucket arrays — same path as the initial build.
|
|
@@ -2487,6 +2471,13 @@ export class SceneProcessor {
|
|
|
2487
2471
|
|
|
2488
2472
|
}
|
|
2489
2473
|
|
|
2474
|
+
/** @private The extraction's material indices point into these lists, so every one is taken from it. */
|
|
2475
|
+
_adoptTextureLists( extractedData ) {
|
|
2476
|
+
|
|
2477
|
+
for ( const name of TEXTURE_LISTS ) this[ name ] = extractedData[ name ] ?? [];
|
|
2478
|
+
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2490
2481
|
/**
|
|
2491
2482
|
* Dispose only material-related textures
|
|
2492
2483
|
* @private
|
|
@@ -2726,6 +2717,7 @@ export class SceneProcessor {
|
|
|
2726
2717
|
const FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
|
|
2727
2718
|
const triRanges = [];
|
|
2728
2719
|
const blasRanges = [];
|
|
2720
|
+
const refit = [];
|
|
2729
2721
|
let owners = 0;
|
|
2730
2722
|
for ( let i = 0; i < table.count; i ++ ) {
|
|
2731
2723
|
|
|
@@ -2736,6 +2728,7 @@ export class SceneProcessor {
|
|
|
2736
2728
|
if ( ! p ) continue;
|
|
2737
2729
|
this._updateMeshTrianglePositions( i, p );
|
|
2738
2730
|
blasRanges.push( table.blasOffsetOf( i ), table.blasNodeCountOf( i ) );
|
|
2731
|
+
refit.push( i );
|
|
2739
2732
|
|
|
2740
2733
|
// Smooth normals overwrite the face normals just computed, so they follow per mesh.
|
|
2741
2734
|
if ( normalsFor ) {
|
|
@@ -2764,6 +2757,12 @@ export class SceneProcessor {
|
|
|
2764
2757
|
const msg = e.data;
|
|
2765
2758
|
if ( msg.type === 'refitComplete' ) {
|
|
2766
2759
|
|
|
2760
|
+
// Later moves and main-thread refits box placements by these.
|
|
2761
|
+
for ( const i of refit ) table.recomputeAABB( i, this.bvh, this.triangles );
|
|
2762
|
+
table.computeGroupAABBs();
|
|
2763
|
+
// The worker boxes a group copy by its shown members only.
|
|
2764
|
+
const leaves = this._hiddenGroupLeaves();
|
|
2765
|
+
if ( leaves.length ) this._refitTLAS( leaves );
|
|
2767
2766
|
resolve( { refitTimeMs: msg.refitTimeMs, triRanges, bvhRanges } );
|
|
2768
2767
|
|
|
2769
2768
|
} else if ( msg.type === 'error' ) {
|
|
@@ -2979,6 +2978,22 @@ export class SceneProcessor {
|
|
|
2979
2978
|
|
|
2980
2979
|
}
|
|
2981
2980
|
|
|
2981
|
+
/** The TLAS leaves of every copy of a group with a hidden member. @private */
|
|
2982
|
+
_hiddenGroupLeaves() {
|
|
2983
|
+
|
|
2984
|
+
const table = this.instanceTable;
|
|
2985
|
+
const leaves = [];
|
|
2986
|
+
for ( const g of table.groups ) {
|
|
2987
|
+
|
|
2988
|
+
if ( g.members.every( ( _, j ) => table.visible[ g.starts[ j ] ] ) ) continue;
|
|
2989
|
+
for ( let c = 0; c < g.count; c ++ ) leaves.push( table.leafOfPlacement( g.starts[ 0 ] + c ) );
|
|
2990
|
+
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
return leaves;
|
|
2994
|
+
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2982
2997
|
/**
|
|
2983
2998
|
* A grouped mesh's parts are traced as one object, so a part moved alone takes its siblings with it: each sibling
|
|
2984
2999
|
* is given the first moved member's world transform, and all of them are moved.
|
|
@@ -4276,6 +4291,7 @@ export class SceneProcessor {
|
|
|
4276
4291
|
// scene's 4), producing a tree of a different size that the swap below rejects —
|
|
4277
4292
|
// so every rebuild was thrown away.
|
|
4278
4293
|
maxLeafSize: this.bvhBuilder.maxLeafSize,
|
|
4294
|
+
foldLeaves: this._foldLeaves === true,
|
|
4279
4295
|
numBins: this.bvhBuilder.numBins,
|
|
4280
4296
|
maxBins: this.bvhBuilder.maxBins,
|
|
4281
4297
|
minBins: this.bvhBuilder.minBins,
|
package/src/RayzeeRenderer.js
CHANGED
|
@@ -1233,7 +1233,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1233
1233
|
this._modelReplaced();
|
|
1234
1234
|
// Before the scene's own environment starts loading: a spilling scene's is narrowed as it decodes.
|
|
1235
1235
|
this._planSpill();
|
|
1236
|
-
await this.loadSceneData( { pendingEnvironment: this._beginSceneMetadataEnvironment() } );
|
|
1236
|
+
const built = await this.loadSceneData( { pendingEnvironment: this._beginSceneMetadataEnvironment() } );
|
|
1237
1237
|
this._maybeSpill();
|
|
1238
1238
|
this.pipeline?.eventBus.emit( 'pipeline:lightingChanged' );
|
|
1239
1239
|
// Not held: the first denoise of a new scene lands ~1.1 s after the load (upload and
|
|
@@ -1242,6 +1242,7 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
1242
1242
|
this.reset();
|
|
1243
1243
|
this.dispatchEvent( eventPayload );
|
|
1244
1244
|
this._modelAnnounced();
|
|
1245
|
+
if ( built ) this.assetLoader?.commitPendingGraph( this.sceneModel );
|
|
1245
1246
|
|
|
1246
1247
|
} catch ( error ) {
|
|
1247
1248
|
|
|
@@ -3912,7 +3913,8 @@ export class RayzeeRenderer extends EventDispatcher {
|
|
|
3912
3913
|
|
|
3913
3914
|
// Drag-drop / file load is a replace: clear any appended models first.
|
|
3914
3915
|
this._clearAppendedModels();
|
|
3915
|
-
await this.loadSceneData( { pendingEnvironment: this._beginSceneMetadataEnvironment() } );
|
|
3916
|
+
const built = await this.loadSceneData( { pendingEnvironment: this._beginSceneMetadataEnvironment() } );
|
|
3917
|
+
if ( built ) this.assetLoader?.commitPendingGraph( event.model );
|
|
3916
3918
|
|
|
3917
3919
|
} else if ( event.texture ) {
|
|
3918
3920
|
|
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
export const GBUFFER_MISS_DEPTH = 65504.0;
|
|
5
5
|
export const GBUFFER_MISS_THRESHOLD = 6e4;
|
|
6
6
|
|
|
7
|
-
// Albedo demodulation safety floor. Demod (`color / safeAlbedo`) and remod (`lighting * safeAlbedo`)
|
|
8
|
-
// only round-trip exactly when both sides use the same value.
|
|
9
|
-
export const ALBEDO_EPS = 0.01;
|
|
10
|
-
|
|
11
7
|
// The live view's denoisers, for setDenoiserStrategy.
|
|
12
8
|
export const DENOISER_STRATEGIES = Object.freeze( [ 'none', 'nrd', 'oidn' ] );
|
|
13
9
|
|