rayzee 11.2.1 → 11.4.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 +91 -34
- package/dist/ArchiveImporter-Cgyx5AF7.js +7860 -0
- package/dist/BidirectionalIntegrator-BKF-cwH2.js +954 -0
- package/dist/{BufferLayout-D_IBo2ci.js → BufferLayout-xPRH9jYD.js} +41 -5
- package/dist/{NeuralRetouch-Cr-otSW8.js → NeuralRetouch-C3Q2XqbF.js} +4 -3
- package/dist/{NeuralSuperRes-B7E3_M0t.js → NeuralSuperRes-DV5TtURP.js} +11 -5
- package/dist/{PathTracer-D6nbknj5.js → PathTracer-D3MvEaZK.js} +4384 -4618
- package/dist/{PhysicalSky-Dq5VSiCQ.js → PhysicalSky-CY4jiz6a.js} +2 -2
- package/dist/{RayzeeRenderer-DbjqO6v-.js → RayzeeRenderer-D23m6NqG.js} +486 -391
- package/dist/SolarLimb-bZE6y63S.js +762 -0
- package/dist/{ToneMapGPU-BOjpul7A.js → ToneMapGPU-CTDBvmmS.js} +31 -22
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +1327 -838
- package/dist/rayzee.umd.js +146 -134
- package/dist/sceneCachePolicy-AKOEu2lf.js +1602 -0
- package/package.json +1 -1
- package/src/EngineDefaults.js +2 -2
- package/src/Passes/AIUpscaler.js +2 -0
- package/src/PathTracerApp.js +126 -19
- package/src/Pipeline/CompletionTracker.js +7 -3
- package/src/Processor/ArchiveImporter.js +173 -94
- package/src/Processor/ArchiveReader.js +20 -23
- package/src/Processor/AssetLoader.js +48 -1
- package/src/Processor/BVHRefitter.js +109 -13
- package/src/Processor/BufferLayout.js +115 -0
- package/src/Processor/ChunkedRecords.js +3 -2
- package/src/Processor/EmissiveTriangleBuilder.js +6 -6
- package/src/Processor/GeometryExtractor.js +189 -79
- package/src/Processor/HostMemory.js +39 -9
- package/src/Processor/InstanceTable.js +714 -42
- package/src/Processor/PBRT/PBRTCurves.js +224 -6
- package/src/Processor/PBRT/PBRTMaterials.js +3 -0
- package/src/Processor/PBRT/PBRTSceneBuilder.js +82 -25
- package/src/Processor/PackedRayBuffer.js +5 -2
- package/src/Processor/SceneProcessor.js +747 -141
- package/src/Processor/TLASBuilder.js +32 -17
- package/src/Processor/ToneMapCPU.js +16 -4
- package/src/Processor/ToneMapGPU.js +30 -14
- package/src/Processor/USD/USDLayer.js +1149 -0
- package/src/Processor/USD/USDScene.js +1655 -0
- package/src/Processor/USD/USDStage.js +682 -0
- package/src/Processor/USD/USDText.js +674 -0
- package/src/Processor/USD/index.js +138 -0
- package/src/Processor/Workers/BVHRefitWorker.js +3 -3
- package/src/RayzeeRenderer.js +221 -33
- package/src/RenderSettings.js +4 -2
- package/src/Stages/AutoExposure.js +484 -494
- package/src/Stages/Compositor.js +27 -6
- package/src/Stages/LocalExposure.js +546 -0
- package/src/Stages/NormalDepth.js +7 -12
- package/src/Stages/PathTracer.js +18 -21
- package/src/Stages/PathTracerStage.js +282 -103
- package/src/Storage/GeometrySpill.js +46 -14
- package/src/Storage/SceneGraphCodec.js +17 -3
- package/src/Storage/SpillStore.js +9 -0
- package/src/TSL/BVHTraversal.js +183 -7
- package/src/TSL/Common.js +57 -13
- package/src/TSL/ShadeKernel.js +3 -2
- package/src/TSL/TextureSampling.js +11 -8
- package/src/TSL/patches.js +84 -0
- package/src/index.js +1 -0
- package/src/integrators/BidirectionalIntegrator.js +20 -2
- package/src/managers/DenoisingManager.js +15 -19
- package/src/managers/InteractionManager.js +19 -6
- package/src/managers/MaterialDataManager.js +22 -5
- package/src/managers/UniformManager.js +3 -0
- package/src/managers/VideoRenderManager.js +30 -4
- package/src/neural/NeuralRetouch.js +1 -0
- package/src/neural/NeuralSuperRes.js +6 -1
- package/dist/ArchiveImporter-D_x863yu.js +0 -5198
- package/dist/BidirectionalIntegrator-DmkxxEOR.js +0 -946
- package/dist/SolarLimb-CPaeZVwz.js +0 -721
- package/dist/sceneCachePolicy-C4-4qwt6.js +0 -485
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USD composition over files read on demand: layer stacks (sublayers), references, payloads, inherits, specializes
|
|
3
|
+
* and variant sets, composed one prim at a time. A composed prim keeps the tree of sites its opinions come from —
|
|
4
|
+
* one node per layer stack and arc, strongest first, as OpenUSD's prim index does — and reads values, children and
|
|
5
|
+
* relationship targets through it, each node mapping the paths it authors into the stage's namespace.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { composeListOps, parseUSDC } from './USDLayer.js';
|
|
9
|
+
import { parseUSDA } from './USDText.js';
|
|
10
|
+
|
|
11
|
+
const RANK = { local: 0, inherit: 1, variant: 2, reference: 3, payload: 4, specialize: 5 };
|
|
12
|
+
const MAX_CONNECTION_HOPS = 32;
|
|
13
|
+
|
|
14
|
+
export function parseLayer( bytes, path ) {
|
|
15
|
+
|
|
16
|
+
const crate = bytes.length >= 8 && bytes[ 0 ] === 0x50 && bytes[ 1 ] === 0x58 && bytes[ 2 ] === 0x52 && bytes[ 3 ] === 0x2D
|
|
17
|
+
&& bytes[ 4 ] === 0x55 && bytes[ 5 ] === 0x53 && bytes[ 6 ] === 0x44 && bytes[ 7 ] === 0x43;
|
|
18
|
+
if ( ! crate ) return parseUSDA( new TextDecoder().decode( bytes ), path );
|
|
19
|
+
const whole = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
20
|
+
return parseUSDC( whole ? bytes.buffer : bytes.slice().buffer, path );
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalize( path ) {
|
|
25
|
+
|
|
26
|
+
const out = [];
|
|
27
|
+
for ( const part of path.replace( /\\/g, '/' ).split( '/' ) ) {
|
|
28
|
+
|
|
29
|
+
if ( part === '' || part === '.' ) continue;
|
|
30
|
+
if ( part === '..' ) out.pop();
|
|
31
|
+
else out.push( part );
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return out.join( '/' );
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const dirOf = path => ( path.includes( '/' ) ? path.slice( 0, path.lastIndexOf( '/' ) ) : '' );
|
|
40
|
+
|
|
41
|
+
export class USDFiles {
|
|
42
|
+
|
|
43
|
+
/** `omitted`: files there but not read, those of the parts left out of a partial load. */
|
|
44
|
+
constructor( paths, read, { omitted = [] } = {} ) {
|
|
45
|
+
|
|
46
|
+
this.read = read;
|
|
47
|
+
this.exact = new Map();
|
|
48
|
+
this.lower = new Map();
|
|
49
|
+
this.byName = new Map();
|
|
50
|
+
this.omitted = new Set( omitted.map( path => normalize( path ).toLowerCase() ) );
|
|
51
|
+
for ( const path of paths ) {
|
|
52
|
+
|
|
53
|
+
const key = normalize( path );
|
|
54
|
+
this.exact.set( key, path );
|
|
55
|
+
this.lower.set( key.toLowerCase(), path );
|
|
56
|
+
const name = key.split( '/' ).pop().toLowerCase();
|
|
57
|
+
const bucket = this.byName.get( name );
|
|
58
|
+
if ( bucket ) bucket.push( path );
|
|
59
|
+
else this.byName.set( name, [ path ] );
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
isOmitted( asset, from = '' ) {
|
|
66
|
+
|
|
67
|
+
return this.omitted.has( this.key( asset, from ).toLowerCase() );
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
key( asset, from ) {
|
|
72
|
+
|
|
73
|
+
const clean = asset.replace( /\\/g, '/' );
|
|
74
|
+
return normalize( clean.startsWith( '/' ) ? clean : `${dirOf( from )}/${clean}` );
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
resolve( asset, from = '' ) {
|
|
79
|
+
|
|
80
|
+
if ( ! asset ) return null;
|
|
81
|
+
const clean = asset.replace( /\\/g, '/' );
|
|
82
|
+
const key = this.key( asset, from );
|
|
83
|
+
const hit = this.exact.get( key ) ?? this.lower.get( key.toLowerCase() );
|
|
84
|
+
if ( hit ) return hit;
|
|
85
|
+
const tail = normalize( clean ).toLowerCase();
|
|
86
|
+
const bucket = this.byName.get( tail.split( '/' ).pop() ) ?? [];
|
|
87
|
+
const suffix = bucket.filter( p => normalize( p ).toLowerCase().endsWith( `/${tail}` ) );
|
|
88
|
+
return suffix.length === 1 ? suffix[ 0 ] : null;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const identity = path => path;
|
|
95
|
+
|
|
96
|
+
/** Paths under `src` moved under `dst`, then through `next`; other paths only through `next`. */
|
|
97
|
+
function prefixMap( src, dst, next ) {
|
|
98
|
+
|
|
99
|
+
if ( src === dst ) return next;
|
|
100
|
+
return path => {
|
|
101
|
+
|
|
102
|
+
if ( path === src ) return next( dst );
|
|
103
|
+
const c = path.charCodeAt( src.length );
|
|
104
|
+
if ( path.startsWith( src ) && ( c === 47 || c === 46 || c === 123 ) ) return next( dst + path.slice( src.length ) );
|
|
105
|
+
return next( path );
|
|
106
|
+
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const childPath = ( parent, name ) => ( parent === '/' ? `/${name}` : `${parent}/${name}` );
|
|
112
|
+
|
|
113
|
+
class Node {
|
|
114
|
+
|
|
115
|
+
constructor( stack, path, map, arc, depth ) {
|
|
116
|
+
|
|
117
|
+
this.stack = stack;
|
|
118
|
+
this.path = path;
|
|
119
|
+
this.map = map;
|
|
120
|
+
this.arc = arc;
|
|
121
|
+
this.depth = depth;
|
|
122
|
+
this.specs = [];
|
|
123
|
+
this.layers = [];
|
|
124
|
+
this.children = [];
|
|
125
|
+
this.choice = null;
|
|
126
|
+
this.doneSets = null;
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Children stay in strength order: by arc, then the arc authored deeper in namespace first. */
|
|
131
|
+
add( child ) {
|
|
132
|
+
|
|
133
|
+
const rank = RANK[ child.arc ];
|
|
134
|
+
let i = this.children.length;
|
|
135
|
+
while ( i > 0 ) {
|
|
136
|
+
|
|
137
|
+
const c = this.children[ i - 1 ];
|
|
138
|
+
const r = RANK[ c.arc ];
|
|
139
|
+
if ( r < rank || ( r === rank && c.depth >= child.depth ) ) break;
|
|
140
|
+
i --;
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.children.splice( i, 0, child );
|
|
145
|
+
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function* strength( node ) {
|
|
151
|
+
|
|
152
|
+
yield node;
|
|
153
|
+
for ( const child of node.children ) yield* strength( child );
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function mapOp( op, map ) {
|
|
158
|
+
|
|
159
|
+
const each = list => list.map( map );
|
|
160
|
+
return { explicit: op.explicit ? each( op.explicit ) : null, prepend: each( op.prepend ), append: each( op.append ), add: each( op.add ), delete: each( op.delete ) };
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export class Prim {
|
|
165
|
+
|
|
166
|
+
constructor( stage, path, root ) {
|
|
167
|
+
|
|
168
|
+
this.stage = stage;
|
|
169
|
+
this.path = path;
|
|
170
|
+
this.root = root;
|
|
171
|
+
this._sites = null;
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get name() {
|
|
176
|
+
|
|
177
|
+
return this.path === '/' ? '' : this.path.slice( this.path.lastIndexOf( '/' ) + 1 );
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** `[ node, spec, layer ]`, strongest first. */
|
|
182
|
+
get sites() {
|
|
183
|
+
|
|
184
|
+
if ( this._sites ) return this._sites;
|
|
185
|
+
this._sites = [];
|
|
186
|
+
for ( const node of strength( this.root ) ) for ( let k = 0; k < node.specs.length; k ++ ) this._sites.push( [ node, node.specs[ k ], node.layers[ k ] ] );
|
|
187
|
+
return this._sites;
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
get typeName() {
|
|
192
|
+
|
|
193
|
+
for ( const [ , spec ] of this.sites ) if ( spec.typeName ) return spec.typeName;
|
|
194
|
+
return '';
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Defined by some `def` or `class`, and not abstract (its strongest one a `class`). */
|
|
199
|
+
get isDefined() {
|
|
200
|
+
|
|
201
|
+
for ( const [ , spec ] of this.sites ) {
|
|
202
|
+
|
|
203
|
+
if ( spec.specifier === 'def' ) return true;
|
|
204
|
+
if ( spec.specifier === 'class' ) return false;
|
|
205
|
+
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return false;
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
meta( key ) {
|
|
213
|
+
|
|
214
|
+
for ( const [ , spec ] of this.sites ) if ( spec.meta[ key ] !== undefined ) return spec.meta[ key ];
|
|
215
|
+
return undefined;
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
property( name ) {
|
|
220
|
+
|
|
221
|
+
for ( const [ , spec ] of this.sites ) {
|
|
222
|
+
|
|
223
|
+
const prop = spec.props?.get( name );
|
|
224
|
+
if ( prop && ( prop.hasValue || prop.timeSamples ) ) return prop;
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** The strongest value, or the earliest time sample where there is none; undefined when unauthored or blocked. */
|
|
233
|
+
value( name ) {
|
|
234
|
+
|
|
235
|
+
const prop = this.property( name );
|
|
236
|
+
if ( ! prop ) return undefined;
|
|
237
|
+
if ( prop.hasValue ) return prop.value ?? undefined;
|
|
238
|
+
const first = prop.timeSamples?.values().next();
|
|
239
|
+
return first && ! first.done ? first.value ?? undefined : undefined;
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
propertyMeta( name, key ) {
|
|
244
|
+
|
|
245
|
+
for ( const [ , spec ] of this.sites ) {
|
|
246
|
+
|
|
247
|
+
const value = spec.props?.get( name )?.meta[ key ];
|
|
248
|
+
if ( value !== undefined ) return value;
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return undefined;
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** An asset-valued attribute as the file it names, resolved against the layer that authored the value. */
|
|
257
|
+
assetPath( name ) {
|
|
258
|
+
|
|
259
|
+
for ( const [ , spec, layer ] of this.sites ) {
|
|
260
|
+
|
|
261
|
+
const prop = spec.props?.get( name );
|
|
262
|
+
if ( ! prop?.hasValue ) continue;
|
|
263
|
+
const value = prop.value;
|
|
264
|
+
return typeof value === 'string' && value ? { asset: value, path: this.stage.files.resolve( value, layer.path ) } : null;
|
|
265
|
+
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return null;
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
propertyNames() {
|
|
273
|
+
|
|
274
|
+
const names = new Set();
|
|
275
|
+
for ( const [ , spec ] of this.sites ) for ( const name of spec.props?.keys() ?? [] ) names.add( name );
|
|
276
|
+
return [ ...names ];
|
|
277
|
+
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
_paths( name, field ) {
|
|
281
|
+
|
|
282
|
+
const ops = [];
|
|
283
|
+
for ( const [ node, spec ] of this.sites ) {
|
|
284
|
+
|
|
285
|
+
const op = spec.props?.get( name )?.[ field ];
|
|
286
|
+
if ( op ) ops.push( mapOp( op, node.map ) );
|
|
287
|
+
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return composeListOps( ops ).map( item => item.value );
|
|
291
|
+
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
targets( name ) {
|
|
295
|
+
|
|
296
|
+
return this._paths( name, 'targets' );
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
connections( name ) {
|
|
301
|
+
|
|
302
|
+
return this._paths( name, 'connections' );
|
|
303
|
+
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
childNames() {
|
|
307
|
+
|
|
308
|
+
const names = new Set();
|
|
309
|
+
for ( const [ , spec ] of this.sites ) for ( const name of spec.children?.keys() ?? [] ) names.add( name );
|
|
310
|
+
return [ ...names ];
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* What two instanceable prims must share to share a prototype: the arcs on the prim itself and all they bring in.
|
|
316
|
+
* The sites that only hold the prim's own opinions — where an ancestor's arc brought it in — differ per copy and are
|
|
317
|
+
* left out, as OpenUSD leaves out an instance's local opinions.
|
|
318
|
+
*/
|
|
319
|
+
instanceKey() {
|
|
320
|
+
|
|
321
|
+
const depth = this.path.split( '/' ).length - 1;
|
|
322
|
+
const parts = [];
|
|
323
|
+
const visit = node => {
|
|
324
|
+
|
|
325
|
+
if ( node.arc !== 'local' && node.depth >= depth ) parts.push( `${node.arc}|${node.stack.id}|${node.path}|${node.choice ?? ''}` );
|
|
326
|
+
for ( const child of node.children ) visit( child );
|
|
327
|
+
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
visit( this.root );
|
|
331
|
+
return parts.join( ';' );
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export class USDStage {
|
|
338
|
+
|
|
339
|
+
constructor( files, { warn = () => {} } = {} ) {
|
|
340
|
+
|
|
341
|
+
this.files = files;
|
|
342
|
+
this.warn = warn;
|
|
343
|
+
this._layers = new Map();
|
|
344
|
+
this._stacks = new Map();
|
|
345
|
+
this._prims = new Map();
|
|
346
|
+
this._warned = new Set();
|
|
347
|
+
this.omittedArcs = 0;
|
|
348
|
+
this.root = null;
|
|
349
|
+
this.pseudoRoot = null;
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
warnOnce( key, message ) {
|
|
354
|
+
|
|
355
|
+
if ( this._warned.has( key ) ) return;
|
|
356
|
+
this._warned.add( key );
|
|
357
|
+
this.warn( message );
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async open( path ) {
|
|
362
|
+
|
|
363
|
+
this.root = await this.stack( path );
|
|
364
|
+
if ( ! this.root ) throw new Error( `USD layer "${path}" could not be read` );
|
|
365
|
+
const meta = this.root.layers[ 0 ].meta;
|
|
366
|
+
this.upAxis = meta.upAxis ?? 'Y';
|
|
367
|
+
this.metersPerUnit = meta.metersPerUnit ?? 0.01;
|
|
368
|
+
this.defaultPrim = meta.defaultPrim ?? '';
|
|
369
|
+
this.pseudoRoot = new Prim( this, '/', this.rootNode( this.root, identity, 'local', 0 ) );
|
|
370
|
+
this._prims.set( '/', this.pseudoRoot );
|
|
371
|
+
return this.pseudoRoot;
|
|
372
|
+
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
layer( path ) {
|
|
376
|
+
|
|
377
|
+
let pending = this._layers.get( path );
|
|
378
|
+
if ( ! pending ) {
|
|
379
|
+
|
|
380
|
+
pending = ( async () => {
|
|
381
|
+
|
|
382
|
+
const bytes = await this.files.read( path );
|
|
383
|
+
if ( ! bytes ) return null;
|
|
384
|
+
try {
|
|
385
|
+
|
|
386
|
+
return parseLayer( bytes, path );
|
|
387
|
+
|
|
388
|
+
} catch ( error ) {
|
|
389
|
+
|
|
390
|
+
this.warnOnce( `parse:${path}`, `USD layer "${path}" could not be parsed: ${error.message}` );
|
|
391
|
+
return null;
|
|
392
|
+
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
} )();
|
|
396
|
+
this._layers.set( path, pending );
|
|
397
|
+
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return pending;
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
stack( path, seen = new Set() ) {
|
|
405
|
+
|
|
406
|
+
let pending = this._stacks.get( path );
|
|
407
|
+
if ( ! pending ) {
|
|
408
|
+
|
|
409
|
+
pending = ( async () => {
|
|
410
|
+
|
|
411
|
+
const layers = [];
|
|
412
|
+
const add = async ( p ) => {
|
|
413
|
+
|
|
414
|
+
if ( seen.has( p ) ) return;
|
|
415
|
+
seen.add( p );
|
|
416
|
+
const layer = await this.layer( p );
|
|
417
|
+
if ( ! layer ) return;
|
|
418
|
+
layers.push( layer );
|
|
419
|
+
for ( const sub of layer.meta.subLayers ?? [] ) {
|
|
420
|
+
|
|
421
|
+
const resolved = this.files.resolve( sub, p );
|
|
422
|
+
if ( resolved ) await add( resolved );
|
|
423
|
+
else this.warnOnce( `missing:${sub}`, `USD sublayer "${sub}" of "${p}" not found` );
|
|
424
|
+
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
await add( path );
|
|
430
|
+
if ( layers.length === 0 ) return null;
|
|
431
|
+
return { id: path, layers, defaultPrim: layers[ 0 ].meta.defaultPrim ?? '' };
|
|
432
|
+
|
|
433
|
+
} )();
|
|
434
|
+
this._stacks.set( path, pending );
|
|
435
|
+
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return pending;
|
|
439
|
+
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
rootNode( stack, map, arc, depth ) {
|
|
443
|
+
|
|
444
|
+
const node = new Node( stack, '/', map, arc, depth );
|
|
445
|
+
node.layers = stack.layers.slice();
|
|
446
|
+
node.specs = stack.layers.map( layer => layer.root );
|
|
447
|
+
return node;
|
|
448
|
+
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async child( prim, name ) {
|
|
452
|
+
|
|
453
|
+
const path = childPath( prim.path, name );
|
|
454
|
+
const cached = this._prims.get( path );
|
|
455
|
+
if ( cached ) return cached;
|
|
456
|
+
const depth = path.split( '/' ).length - 1;
|
|
457
|
+
const root = await this.childNode( prim.root, name, depth, new Set() );
|
|
458
|
+
if ( ! root ) return null;
|
|
459
|
+
await this.addVariants( root, depth, new Set() );
|
|
460
|
+
const child = new Prim( this, path, root );
|
|
461
|
+
this._prims.set( path, child );
|
|
462
|
+
return child;
|
|
463
|
+
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async primAt( path ) {
|
|
467
|
+
|
|
468
|
+
if ( ! this.pseudoRoot ) return null;
|
|
469
|
+
const cached = this._prims.get( path );
|
|
470
|
+
if ( cached ) return cached;
|
|
471
|
+
let prim = this.pseudoRoot;
|
|
472
|
+
for ( const name of path.split( '/' ).filter( Boolean ) ) {
|
|
473
|
+
|
|
474
|
+
prim = await this.child( prim, name );
|
|
475
|
+
if ( ! prim ) return null;
|
|
476
|
+
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return prim;
|
|
480
|
+
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/** Forgets composed prims and every layer outside the root layer stack, once a part of the stage is done with. */
|
|
484
|
+
release() {
|
|
485
|
+
|
|
486
|
+
this._prims.clear();
|
|
487
|
+
this._prims.set( '/', this.pseudoRoot );
|
|
488
|
+
const keep = new Set( this.root.layers.map( layer => layer.path ) );
|
|
489
|
+
for ( const path of this._layers.keys() ) if ( ! keep.has( path ) ) this._layers.delete( path );
|
|
490
|
+
for ( const path of this._stacks.keys() ) if ( path !== this.root.id ) this._stacks.delete( path );
|
|
491
|
+
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async childNode( parent, name, depth, chain ) {
|
|
495
|
+
|
|
496
|
+
const node = new Node( parent.stack, childPath( parent.path, name ), parent.map, parent.arc, parent.depth );
|
|
497
|
+
node.choice = parent.choice;
|
|
498
|
+
for ( let k = 0; k < parent.specs.length; k ++ ) {
|
|
499
|
+
|
|
500
|
+
const spec = parent.specs[ k ].child( name );
|
|
501
|
+
if ( spec ) {
|
|
502
|
+
|
|
503
|
+
node.specs.push( spec );
|
|
504
|
+
node.layers.push( parent.layers[ k ] );
|
|
505
|
+
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
for ( const sub of parent.children ) {
|
|
511
|
+
|
|
512
|
+
const child = await this.childNode( sub, name, depth, chain );
|
|
513
|
+
if ( child ) node.children.push( child );
|
|
514
|
+
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if ( node.specs.length ) await this.addArcs( node, depth, chain );
|
|
518
|
+
return node.specs.length || node.children.length ? node : null;
|
|
519
|
+
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async addArcs( node, depth, chain ) {
|
|
523
|
+
|
|
524
|
+
const arcs = key => composeListOps( node.specs.map( spec => spec.arcs?.[ key ] ?? null ), node.layers );
|
|
525
|
+
for ( const { value } of arcs( 'inherits' ) ) await this.addClass( node, value, 'inherit', depth, chain );
|
|
526
|
+
for ( const { value, source } of arcs( 'references' ) ) await this.addReference( node, value, source, 'reference', depth, chain );
|
|
527
|
+
for ( const { value, source } of arcs( 'payload' ) ) await this.addReference( node, value, source, 'payload', depth, chain );
|
|
528
|
+
for ( const { value } of arcs( 'specializes' ) ) await this.addClass( node, value, 'specialize', depth, chain );
|
|
529
|
+
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async addClass( node, path, arc, depth, chain ) {
|
|
533
|
+
|
|
534
|
+
const sub = await this.subtree( node.stack, path, prefixMap( path, node.path, node.map ), arc, depth, chain );
|
|
535
|
+
if ( sub ) node.add( sub );
|
|
536
|
+
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async addReference( node, ref, layer, arc, depth, chain ) {
|
|
540
|
+
|
|
541
|
+
let stack = node.stack;
|
|
542
|
+
if ( ref.asset ) {
|
|
543
|
+
|
|
544
|
+
const path = this.files.resolve( ref.asset, layer?.path ?? '' );
|
|
545
|
+
if ( ! path ) {
|
|
546
|
+
|
|
547
|
+
if ( this.files.isOmitted( ref.asset, layer?.path ?? '' ) ) this.omittedArcs ++;
|
|
548
|
+
else this.warnOnce( `missing:${ref.asset}`, `USD ${arc} "${ref.asset}" (from "${layer?.path}") not found — skipped` );
|
|
549
|
+
return;
|
|
550
|
+
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
stack = await this.stack( path );
|
|
554
|
+
if ( ! stack ) return;
|
|
555
|
+
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const target = ref.path || ( stack.defaultPrim ? `/${stack.defaultPrim}` : '' );
|
|
559
|
+
if ( ! target ) {
|
|
560
|
+
|
|
561
|
+
this.warnOnce( `nodefault:${stack.id}`, `USD ${arc} to "${stack.id}" names no prim and the layer has no defaultPrim — skipped` );
|
|
562
|
+
return;
|
|
563
|
+
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const sub = await this.subtree( stack, target, prefixMap( target, node.path, node.map ), arc, depth, chain );
|
|
567
|
+
if ( sub ) node.add( sub );
|
|
568
|
+
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* The sites of `path` in a layer stack, composed from its root down with the arcs and variants of its ancestors,
|
|
573
|
+
* as the target of an arc. Its own variant sets are left for the prim it is composed into, where a stronger site
|
|
574
|
+
* may select them.
|
|
575
|
+
*/
|
|
576
|
+
async subtree( stack, path, map, arc, depth, chain ) {
|
|
577
|
+
|
|
578
|
+
const key = `${stack.id}\u0000${path}`;
|
|
579
|
+
if ( chain.has( key ) ) {
|
|
580
|
+
|
|
581
|
+
this.warnOnce( `cycle:${key}`, `USD composition cycle through "${path}" in "${stack.id}" — skipped` );
|
|
582
|
+
return null;
|
|
583
|
+
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const inner = new Set( chain ).add( key );
|
|
587
|
+
let node = this.rootNode( stack, map, arc, depth );
|
|
588
|
+
const names = path.split( '/' ).filter( Boolean );
|
|
589
|
+
for ( let i = 0; i < names.length; i ++ ) {
|
|
590
|
+
|
|
591
|
+
if ( i > 0 ) await this.addVariants( node, depth, inner );
|
|
592
|
+
node = await this.childNode( node, names[ i ], depth, inner );
|
|
593
|
+
if ( ! node ) return null;
|
|
594
|
+
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return node;
|
|
598
|
+
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Variant sets, evaluated once the other arcs are in: a selection may be authored by any site, the strongest wins,
|
|
603
|
+
* and a selected variant can bring arcs and variant sets of its own.
|
|
604
|
+
*/
|
|
605
|
+
async addVariants( root, depth, chain ) {
|
|
606
|
+
|
|
607
|
+
for ( let changed = true; changed; ) {
|
|
608
|
+
|
|
609
|
+
changed = false;
|
|
610
|
+
for ( const node of [ ...strength( root ) ] ) {
|
|
611
|
+
|
|
612
|
+
const sets = new Set( composeListOps( node.specs.map( spec => spec.arcs?.variantSetNames ?? null ) ).map( item => item.value ) );
|
|
613
|
+
for ( const spec of node.specs ) for ( const set of spec.variantSets?.keys() ?? [] ) sets.add( set );
|
|
614
|
+
for ( const set of sets ) {
|
|
615
|
+
|
|
616
|
+
node.doneSets ??= new Set();
|
|
617
|
+
if ( node.doneSets.has( set ) ) continue;
|
|
618
|
+
node.doneSets.add( set );
|
|
619
|
+
const choice = this.selection( root, set );
|
|
620
|
+
if ( ! choice ) continue;
|
|
621
|
+
|
|
622
|
+
const variant = new Node( node.stack, node.path, node.map, 'variant', depth );
|
|
623
|
+
variant.choice = `${set}=${choice}`;
|
|
624
|
+
for ( let k = 0; k < node.specs.length; k ++ ) {
|
|
625
|
+
|
|
626
|
+
const spec = node.specs[ k ].variant( set, choice );
|
|
627
|
+
if ( spec ) {
|
|
628
|
+
|
|
629
|
+
variant.specs.push( spec );
|
|
630
|
+
variant.layers.push( node.layers[ k ] );
|
|
631
|
+
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if ( variant.specs.length === 0 ) continue;
|
|
637
|
+
node.add( variant );
|
|
638
|
+
await this.addArcs( variant, depth, chain );
|
|
639
|
+
changed = true;
|
|
640
|
+
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
selection( root, set ) {
|
|
650
|
+
|
|
651
|
+
for ( const node of strength( root ) ) for ( const spec of node.specs ) {
|
|
652
|
+
|
|
653
|
+
const choice = spec.variantSelection?.[ set ];
|
|
654
|
+
if ( choice ) return choice;
|
|
655
|
+
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return null;
|
|
659
|
+
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async connectionSource( prim, attribute ) {
|
|
663
|
+
|
|
664
|
+
let [ target ] = prim.connections( attribute );
|
|
665
|
+
for ( let hop = 0; target && hop < MAX_CONNECTION_HOPS; hop ++ ) {
|
|
666
|
+
|
|
667
|
+
const dot = target.lastIndexOf( '.' );
|
|
668
|
+
const source = await this.primAt( target.slice( 0, dot ) );
|
|
669
|
+
if ( ! source ) return null;
|
|
670
|
+
const name = target.slice( dot + 1 );
|
|
671
|
+
const next = source.connections( name )[ 0 ];
|
|
672
|
+
if ( ! next || source.value( 'info:id' ) ) return { prim: source, name };
|
|
673
|
+
target = next;
|
|
674
|
+
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return null;
|
|
678
|
+
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
}
|
|
682
|
+
|