@loaders.gl/scene 5.0.0-alpha.1

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.
Files changed (42) hide show
  1. package/LICENSE +41 -0
  2. package/README.md +23 -0
  3. package/dist/dist.dev.js +831 -0
  4. package/dist/dist.min.js +14 -0
  5. package/dist/index.cjs +65 -0
  6. package/dist/index.cjs.map +7 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +5 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/parse-usd.d.ts +5 -0
  12. package/dist/lib/parse-usd.d.ts.map +1 -0
  13. package/dist/lib/parse-usd.js +276 -0
  14. package/dist/lib/parse-usd.js.map +1 -0
  15. package/dist/lib/parse-usda.d.ts +4 -0
  16. package/dist/lib/parse-usda.d.ts.map +1 -0
  17. package/dist/lib/parse-usda.js +395 -0
  18. package/dist/lib/parse-usda.js.map +1 -0
  19. package/dist/lib/parse-usdz.d.ts +3 -0
  20. package/dist/lib/parse-usdz.d.ts.map +1 -0
  21. package/dist/lib/parse-usdz.js +50 -0
  22. package/dist/lib/parse-usdz.js.map +1 -0
  23. package/dist/lib/usd-types.d.ts +86 -0
  24. package/dist/lib/usd-types.d.ts.map +1 -0
  25. package/dist/lib/usd-types.js +5 -0
  26. package/dist/lib/usd-types.js.map +1 -0
  27. package/dist/usd-loader-types.d.ts +50 -0
  28. package/dist/usd-loader-types.d.ts.map +1 -0
  29. package/dist/usd-loader-types.js +35 -0
  30. package/dist/usd-loader-types.js.map +1 -0
  31. package/dist/usd-loader.d.ts +30 -0
  32. package/dist/usd-loader.d.ts.map +1 -0
  33. package/dist/usd-loader.js +17 -0
  34. package/dist/usd-loader.js.map +1 -0
  35. package/package.json +55 -0
  36. package/src/index.ts +15 -0
  37. package/src/lib/parse-usd.ts +349 -0
  38. package/src/lib/parse-usda.ts +458 -0
  39. package/src/lib/parse-usdz.ts +59 -0
  40. package/src/lib/usd-types.ts +103 -0
  41. package/src/usd-loader-types.ts +40 -0
  42. package/src/usd-loader.ts +26 -0
@@ -0,0 +1,831 @@
1
+ (function webpackUniversalModuleDefinition(root, factory) {
2
+ if (typeof exports === 'object' && typeof module === 'object')
3
+ module.exports = factory();
4
+ else if (typeof define === 'function' && define.amd) define([], factory);
5
+ else if (typeof exports === 'object') exports['loaders'] = factory();
6
+ else root['loaders'] = factory();})(globalThis, function () {
7
+ "use strict";
8
+ var __exports__ = (() => {
9
+ var __create = Object.create;
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __getProtoOf = Object.getPrototypeOf;
14
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
15
+ var __esm = (fn, res) => function __init() {
16
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
+ };
18
+ var __commonJS = (cb, mod) => function __require() {
19
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var __copyProps = (to, from, except, desc) => {
26
+ if (from && typeof from === "object" || typeof from === "function") {
27
+ for (let key of __getOwnPropNames(from))
28
+ if (!__hasOwnProp.call(to, key) && key !== except)
29
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
30
+ }
31
+ return to;
32
+ };
33
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
34
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
35
+ // If the importer is in node compatibility mode or this is not an ESM
36
+ // file that has been converted to a CommonJS file using a Babel-
37
+ // compatible transform (i.e. "__esModule" has not been set), then set
38
+ // "default" to the CommonJS "module.exports" for node compatibility.
39
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
40
+ mod
41
+ ));
42
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
43
+
44
+ // external-global-plugin:@loaders.gl/core
45
+ var require_core = __commonJS({
46
+ "external-global-plugin:@loaders.gl/core"(exports, module) {
47
+ module.exports = globalThis.loaders;
48
+ }
49
+ });
50
+
51
+ // src/lib/parse-usda.ts
52
+ function parseUSDA(source, url) {
53
+ if (!source.trimStart().startsWith("#usda")) {
54
+ throw new Error("OpenUSD ASCII layers must begin with the #usda header.");
55
+ }
56
+ return new USDAParser(source, url).parse();
57
+ }
58
+ var NUMBER_PATTERN, QUALIFIERS, USDAParser, USDTokenizer;
59
+ var init_parse_usda = __esm({
60
+ "src/lib/parse-usda.ts"() {
61
+ "use strict";
62
+ NUMBER_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/;
63
+ QUALIFIERS = /* @__PURE__ */ new Set([
64
+ "uniform",
65
+ "varying",
66
+ "custom",
67
+ "prepend",
68
+ "append",
69
+ "add",
70
+ "delete",
71
+ "reorder"
72
+ ]);
73
+ USDAParser = class {
74
+ /** Token stream for the source layer. */
75
+ tokenizer;
76
+ /** URL of the source layer. */
77
+ url;
78
+ /** Creates a USDA parser. */
79
+ constructor(source, url) {
80
+ this.tokenizer = new USDTokenizer(source);
81
+ this.url = url;
82
+ }
83
+ /** Parses the complete USDA layer. */
84
+ parse() {
85
+ const metadata = this.tokenizer.match("(") ? this.parseMetadata(")") : {};
86
+ const rootPrims = [];
87
+ while (!this.tokenizer.isAtEnd()) {
88
+ if (this.isPrimDeclaration()) {
89
+ rootPrims.push(this.parsePrim(""));
90
+ } else {
91
+ this.tokenizer.read();
92
+ }
93
+ }
94
+ return {
95
+ format: "usda",
96
+ url: this.url,
97
+ metadata,
98
+ rootPrims,
99
+ layers: this.url ? [this.url] : []
100
+ };
101
+ }
102
+ /** Parses a prim and its descendants. */
103
+ parsePrim(parentPath) {
104
+ const declaration = this.tokenizer.read();
105
+ const specifier = declaration.value;
106
+ const firstToken = this.tokenizer.read();
107
+ const hasExplicitType = this.tokenizer.peek().kind === "string";
108
+ const type = hasExplicitType ? firstToken.value : "";
109
+ const name = hasExplicitType ? this.tokenizer.read().value : firstToken.value;
110
+ const path = `${parentPath}/${name}`;
111
+ const metadata = this.tokenizer.match("(") ? this.parseMetadata(")") : {};
112
+ this.tokenizer.expect("{");
113
+ const contents = this.parsePrimContents(path);
114
+ return {
115
+ name,
116
+ path,
117
+ sourceUrl: this.url,
118
+ type,
119
+ specifier,
120
+ attributes: contents.attributes,
121
+ metadata: { ...metadata, ...contents.metadata },
122
+ variants: contents.variants,
123
+ children: contents.children
124
+ };
125
+ }
126
+ /** Parses the attributes, variants, and child prims inside a prim body. */
127
+ parsePrimContents(parentPath) {
128
+ const attributes = {};
129
+ const metadata = {};
130
+ const children = [];
131
+ const variants = {};
132
+ while (!this.tokenizer.isAtEnd() && !this.tokenizer.match("}")) {
133
+ if (this.isPrimDeclaration()) {
134
+ children.push(this.parsePrim(parentPath));
135
+ } else if (this.tokenizer.peek().value === "variantSet") {
136
+ this.parseVariantSet(parentPath, variants);
137
+ } else {
138
+ const attribute = this.parseAttribute();
139
+ if (attribute) {
140
+ attributes[attribute.name] = attribute;
141
+ }
142
+ }
143
+ }
144
+ return { attributes, metadata, children, variants };
145
+ }
146
+ /** Parses one named variant set. */
147
+ parseVariantSet(parentPath, variants) {
148
+ this.tokenizer.expect("variantSet");
149
+ const variantSetName = this.tokenizer.read().value;
150
+ this.tokenizer.expect("=");
151
+ this.tokenizer.expect("{");
152
+ const variantSet = {};
153
+ while (!this.tokenizer.isAtEnd() && !this.tokenizer.match("}")) {
154
+ const variantName = this.tokenizer.read().value;
155
+ this.tokenizer.expect("{");
156
+ const contents = this.parsePrimContents(parentPath);
157
+ variantSet[variantName] = {
158
+ attributes: contents.attributes,
159
+ metadata: contents.metadata,
160
+ children: contents.children
161
+ };
162
+ }
163
+ variants[variantSetName] = variantSet;
164
+ }
165
+ /** Parses an attribute declaration at the current token. */
166
+ parseAttribute() {
167
+ const firstToken = this.tokenizer.read();
168
+ if (firstToken.kind === "end") {
169
+ return null;
170
+ }
171
+ const declarationTokens = [firstToken];
172
+ while (!this.tokenizer.isAtEnd() && this.tokenizer.peek().line === firstToken.line) {
173
+ const nextToken = this.tokenizer.peek();
174
+ if (nextToken.value === "=") {
175
+ this.tokenizer.read();
176
+ break;
177
+ }
178
+ if (nextToken.value === "{" || nextToken.value === "}") {
179
+ return null;
180
+ }
181
+ declarationTokens.push(this.tokenizer.read());
182
+ }
183
+ const hasAssignment = this.tokenizer.previousValue === "=";
184
+ if (!hasAssignment) {
185
+ return null;
186
+ }
187
+ const significantTokens = declarationTokens.filter((token) => !QUALIFIERS.has(token.value));
188
+ if (significantTokens.length === 0) {
189
+ return null;
190
+ }
191
+ const name = significantTokens[significantTokens.length - 1].value;
192
+ const type = significantTokens.slice(0, -1).map((token) => token.value).join("");
193
+ const value = this.parseValue();
194
+ const metadata = this.tokenizer.match("(") ? this.parseMetadata(")") : {};
195
+ return { name, type, value, metadata };
196
+ }
197
+ /** Parses a metadata dictionary through the requested terminator. */
198
+ parseMetadata(terminator) {
199
+ const metadata = {};
200
+ while (!this.tokenizer.isAtEnd() && !this.tokenizer.match(terminator)) {
201
+ this.tokenizer.match(",");
202
+ if (this.tokenizer.peek().value === terminator) {
203
+ this.tokenizer.read();
204
+ break;
205
+ }
206
+ const firstToken = this.tokenizer.read();
207
+ const declarationTokens = [firstToken];
208
+ while (!this.tokenizer.isAtEnd() && this.tokenizer.peek().line === firstToken.line) {
209
+ if (this.tokenizer.peek().value === "=" || this.tokenizer.peek().value === ":") {
210
+ this.tokenizer.read();
211
+ break;
212
+ }
213
+ if (this.tokenizer.peek().value === terminator) {
214
+ break;
215
+ }
216
+ declarationTokens.push(this.tokenizer.read());
217
+ }
218
+ if (this.tokenizer.previousValue !== "=" && this.tokenizer.previousValue !== ":") {
219
+ continue;
220
+ }
221
+ const name = declarationTokens[declarationTokens.length - 1].value;
222
+ metadata[name] = this.parseValue();
223
+ this.tokenizer.match(",");
224
+ }
225
+ return metadata;
226
+ }
227
+ /** Parses one scalar, list, tuple, dictionary, asset path, or scene path value. */
228
+ parseValue() {
229
+ const token = this.tokenizer.read();
230
+ if (token.value === "[") {
231
+ return this.parseList("]");
232
+ }
233
+ if (token.value === "(") {
234
+ return this.parseList(")");
235
+ }
236
+ if (token.value === "{") {
237
+ return this.parseMetadata("}");
238
+ }
239
+ if (token.kind === "asset") {
240
+ const reference = { assetPath: token.value };
241
+ if (this.tokenizer.peek().kind === "path" && this.tokenizer.peek().line === token.line) {
242
+ reference.primPath = this.tokenizer.read().value;
243
+ }
244
+ return reference;
245
+ }
246
+ if (token.kind === "path") {
247
+ return { path: token.value };
248
+ }
249
+ if (token.kind === "number") {
250
+ return Number(token.value);
251
+ }
252
+ if (token.value === "true") {
253
+ return true;
254
+ }
255
+ if (token.value === "false") {
256
+ return false;
257
+ }
258
+ if (token.value === "None" || token.value === "null") {
259
+ return null;
260
+ }
261
+ return token.value;
262
+ }
263
+ /** Parses a comma-separated list or tuple. */
264
+ parseList(terminator) {
265
+ const values = [];
266
+ while (!this.tokenizer.isAtEnd() && !this.tokenizer.match(terminator)) {
267
+ if (this.tokenizer.match(",")) {
268
+ continue;
269
+ }
270
+ values.push(this.parseValue());
271
+ this.tokenizer.match(",");
272
+ }
273
+ return values;
274
+ }
275
+ /** Returns whether the current token starts a prim declaration. */
276
+ isPrimDeclaration() {
277
+ const value = this.tokenizer.peek().value;
278
+ return value === "def" || value === "over" || value === "class";
279
+ }
280
+ };
281
+ USDTokenizer = class {
282
+ /** Complete source text. */
283
+ source;
284
+ /** Current character offset. */
285
+ offset = 0;
286
+ /** Current one-based line number. */
287
+ line = 1;
288
+ /** Lookahead token, when one has been read. */
289
+ bufferedToken = null;
290
+ /** Value of the most recently consumed token. */
291
+ previousValue = "";
292
+ /** Creates a tokenizer for USDA source text. */
293
+ constructor(source) {
294
+ this.source = source;
295
+ }
296
+ /** Returns the next token without consuming it. */
297
+ peek() {
298
+ this.bufferedToken ||= this.readToken();
299
+ return this.bufferedToken;
300
+ }
301
+ /** Consumes and returns the next token. */
302
+ read() {
303
+ const token = this.peek();
304
+ this.bufferedToken = null;
305
+ this.previousValue = token.value;
306
+ return token;
307
+ }
308
+ /** Consumes a token when its value matches. */
309
+ match(value) {
310
+ if (this.peek().value !== value) {
311
+ return false;
312
+ }
313
+ this.read();
314
+ return true;
315
+ }
316
+ /** Consumes an expected token or throws a line-numbered parse error. */
317
+ expect(value) {
318
+ const token = this.read();
319
+ if (token.value !== value) {
320
+ throw new Error(`Expected "${value}" at USDA line ${token.line}, received "${token.value}".`);
321
+ }
322
+ }
323
+ /** Returns whether the token stream is exhausted. */
324
+ isAtEnd() {
325
+ return this.peek().kind === "end";
326
+ }
327
+ /** Reads the next token from the current character offset. */
328
+ readToken() {
329
+ this.skipIgnoredText();
330
+ const line = this.line;
331
+ const character = this.source[this.offset];
332
+ if (character === void 0) {
333
+ return { value: "", line, kind: "end" };
334
+ }
335
+ if ("{}[](),=".includes(character)) {
336
+ this.offset++;
337
+ return { value: character, line, kind: "punctuation" };
338
+ }
339
+ if (character === '"' || character === "'") {
340
+ return { value: this.readQuotedString(character), line, kind: "string" };
341
+ }
342
+ if (character === "@") {
343
+ return { value: this.readDelimitedValue("@"), line, kind: "asset" };
344
+ }
345
+ if (character === "<") {
346
+ return { value: this.readDelimitedValue(">"), line, kind: "path" };
347
+ }
348
+ const numericMatch = this.source.slice(this.offset).match(NUMBER_PATTERN);
349
+ if (numericMatch) {
350
+ this.offset += numericMatch[0].length;
351
+ return { value: numericMatch[0], line, kind: "number" };
352
+ }
353
+ const startOffset = this.offset;
354
+ while (this.offset < this.source.length) {
355
+ const nextCharacter = this.source[this.offset];
356
+ if (/\s/.test(nextCharacter) || "{}[](),=@<>".includes(nextCharacter)) {
357
+ break;
358
+ }
359
+ this.offset++;
360
+ }
361
+ if (this.offset === startOffset) {
362
+ this.offset++;
363
+ }
364
+ return { value: this.source.slice(startOffset, this.offset), line, kind: "word" };
365
+ }
366
+ /** Skips whitespace, comments, and triple-quoted documentation blocks. */
367
+ skipIgnoredText() {
368
+ while (this.offset < this.source.length) {
369
+ const character = this.source[this.offset];
370
+ if (character === "\n") {
371
+ this.line++;
372
+ this.offset++;
373
+ } else if (/\s/.test(character)) {
374
+ this.offset++;
375
+ } else if (character === "#") {
376
+ while (this.offset < this.source.length && this.source[this.offset] !== "\n") {
377
+ this.offset++;
378
+ }
379
+ } else if (character === "/" && this.source[this.offset + 1] === "*") {
380
+ this.offset += 2;
381
+ while (this.offset < this.source.length && !this.source.startsWith("*/", this.offset)) {
382
+ if (this.source[this.offset] === "\n") {
383
+ this.line++;
384
+ }
385
+ this.offset++;
386
+ }
387
+ this.offset += 2;
388
+ } else if (this.source.startsWith('"""', this.offset)) {
389
+ this.offset += 3;
390
+ while (this.offset < this.source.length && !this.source.startsWith('"""', this.offset)) {
391
+ if (this.source[this.offset] === "\n") {
392
+ this.line++;
393
+ }
394
+ this.offset++;
395
+ }
396
+ this.offset += 3;
397
+ } else {
398
+ break;
399
+ }
400
+ }
401
+ }
402
+ /** Reads a quoted string and decodes common escapes. */
403
+ readQuotedString(delimiter) {
404
+ this.offset++;
405
+ let value = "";
406
+ while (this.offset < this.source.length) {
407
+ const character = this.source[this.offset++];
408
+ if (character === delimiter) {
409
+ break;
410
+ }
411
+ if (character === "\\" && this.offset < this.source.length) {
412
+ const escapedCharacter = this.source[this.offset++];
413
+ value += escapedCharacter === "n" ? "\n" : escapedCharacter;
414
+ } else {
415
+ value += character;
416
+ }
417
+ }
418
+ return value;
419
+ }
420
+ /** Reads an asset or scene path through its closing delimiter. */
421
+ readDelimitedValue(delimiter) {
422
+ this.offset++;
423
+ const startOffset = this.offset;
424
+ while (this.offset < this.source.length && this.source[this.offset] !== delimiter) {
425
+ this.offset++;
426
+ }
427
+ const value = this.source.slice(startOffset, this.offset);
428
+ this.offset++;
429
+ return value;
430
+ }
431
+ };
432
+ }
433
+ });
434
+
435
+ // src/lib/parse-usdz.ts
436
+ function parseUSDZArchive(arrayBuffer) {
437
+ const view = new DataView(arrayBuffer);
438
+ const directoryOffset = findCentralDirectory(view);
439
+ const entryCount = view.getUint16(directoryOffset + 10, true);
440
+ let entryOffset = view.getUint32(directoryOffset + 16, true);
441
+ const files = /* @__PURE__ */ new Map();
442
+ const textDecoder = new TextDecoder();
443
+ for (let entryIndex = 0; entryIndex < entryCount; entryIndex++) {
444
+ if (view.getUint32(entryOffset, true) !== CENTRAL_DIRECTORY_ENTRY) {
445
+ throw new Error("USDZ archive contains an invalid ZIP central-directory entry.");
446
+ }
447
+ const compression = view.getUint16(entryOffset + 10, true);
448
+ const compressedLength = view.getUint32(entryOffset + 20, true);
449
+ const filenameLength = view.getUint16(entryOffset + 28, true);
450
+ const extraLength = view.getUint16(entryOffset + 30, true);
451
+ const commentLength = view.getUint16(entryOffset + 32, true);
452
+ const localOffset = view.getUint32(entryOffset + 42, true);
453
+ const filename = textDecoder.decode(
454
+ new Uint8Array(arrayBuffer, entryOffset + 46, filenameLength)
455
+ );
456
+ if (compression !== 0) {
457
+ throw new Error(`USDZ entry "${filename}" is compressed; USDZ requires stored ZIP entries.`);
458
+ }
459
+ if (view.getUint32(localOffset, true) !== LOCAL_FILE_HEADER) {
460
+ throw new Error(`USDZ entry "${filename}" has an invalid local ZIP header.`);
461
+ }
462
+ const localNameLength = view.getUint16(localOffset + 26, true);
463
+ const localExtraLength = view.getUint16(localOffset + 28, true);
464
+ const contentOffset = localOffset + 30 + localNameLength + localExtraLength;
465
+ files.set(filename, arrayBuffer.slice(contentOffset, contentOffset + compressedLength));
466
+ entryOffset += 46 + filenameLength + extraLength + commentLength;
467
+ }
468
+ return files;
469
+ }
470
+ function findCentralDirectory(view) {
471
+ const minimumOffset = Math.max(0, view.byteLength - 65557);
472
+ for (let offset = view.byteLength - 22; offset >= minimumOffset; offset--) {
473
+ if (view.getUint32(offset, true) === END_OF_CENTRAL_DIRECTORY) {
474
+ return offset;
475
+ }
476
+ }
477
+ throw new Error("USDZ archive does not contain a ZIP end-of-central-directory record.");
478
+ }
479
+ var END_OF_CENTRAL_DIRECTORY, CENTRAL_DIRECTORY_ENTRY, LOCAL_FILE_HEADER;
480
+ var init_parse_usdz = __esm({
481
+ "src/lib/parse-usdz.ts"() {
482
+ "use strict";
483
+ END_OF_CENTRAL_DIRECTORY = 101010256;
484
+ CENTRAL_DIRECTORY_ENTRY = 33639248;
485
+ LOCAL_FILE_HEADER = 67324752;
486
+ }
487
+ });
488
+
489
+ // src/lib/parse-usd.ts
490
+ async function parseUSD(data, options = {}, context) {
491
+ const url = getSourceLocation(options.core?.baseUrl, context?.url);
492
+ const environment = {
493
+ fetch: async (resourceUrl) => {
494
+ const response = await (context?.fetch || fetch)(resourceUrl);
495
+ if (!(response instanceof Response)) {
496
+ throw new Error(`OpenUSD reference "${resourceUrl}" did not return an HTTP response.`);
497
+ }
498
+ if (!response.ok) {
499
+ throw new Error(`Unable to load OpenUSD reference "${resourceUrl}": ${response.status}.`);
500
+ }
501
+ return response;
502
+ },
503
+ cache: /* @__PURE__ */ new Map(),
504
+ archiveFiles: /* @__PURE__ */ new Map(),
505
+ layers: /* @__PURE__ */ new Set(),
506
+ options: {
507
+ ...options,
508
+ usd: { ...USD_LOADER_DEFAULT_OPTIONS, ...options.usd }
509
+ }
510
+ };
511
+ let stage;
512
+ if (isZIPArchive(data)) {
513
+ const archive = parseUSDZArchive(data);
514
+ const rootFilename = Array.from(archive.keys()).find((filename) => /\.usda?$/i.test(filename));
515
+ if (!rootFilename) {
516
+ throw new Error("USDZ archives with binary USDC root layers are not implemented yet.");
517
+ }
518
+ const archiveBase = "https://usd.archive/";
519
+ for (const [filename, content] of archive) {
520
+ environment.archiveFiles.set(new URL(filename, archiveBase).href, content);
521
+ }
522
+ const rootUrl = new URL(rootFilename, archiveBase).href;
523
+ stage = parseUSDLayer(archive.get(rootFilename), rootUrl);
524
+ stage.format = "usdz";
525
+ } else {
526
+ stage = parseUSDLayer(data, url);
527
+ }
528
+ if (stage.url) {
529
+ environment.layers.add(stage.url);
530
+ }
531
+ if (environment.options.usd?.compose !== false) {
532
+ stage.rootPrims = await composePrims(stage.rootPrims, stage.url, {}, environment, 0);
533
+ }
534
+ stage.layers = Array.from(environment.layers);
535
+ if (url) {
536
+ stage.url = url;
537
+ }
538
+ return stage;
539
+ }
540
+ function getSourceLocation(configuredLocation, contextLocation) {
541
+ const configuredUrl = getAbsoluteUrl(configuredLocation);
542
+ if (configuredUrl) {
543
+ return configuredUrl;
544
+ }
545
+ const contextUrl = getAbsoluteUrl(contextLocation);
546
+ return contextUrl || configuredLocation || contextLocation;
547
+ }
548
+ function getAbsoluteUrl(candidate) {
549
+ try {
550
+ return candidate ? new URL(candidate).href : void 0;
551
+ } catch {
552
+ return void 0;
553
+ }
554
+ }
555
+ function resolveAssetPath(assetPath, sourceLocation) {
556
+ try {
557
+ return new URL(assetPath, sourceLocation).href;
558
+ } catch {
559
+ const slashIndex = Math.max(sourceLocation.lastIndexOf("/"), sourceLocation.lastIndexOf("\\"));
560
+ return slashIndex >= 0 ? `${sourceLocation.slice(0, slashIndex + 1)}${assetPath}` : assetPath;
561
+ }
562
+ }
563
+ function parseUSDLayer(data, url) {
564
+ const signature = TEXT_DECODER.decode(data.slice(0, 8));
565
+ if (signature === CRATE_SIGNATURE) {
566
+ throw new Error("Binary USDC crate layers are not implemented yet; use ASCII USDA layers.");
567
+ }
568
+ return parseUSDA(TEXT_DECODER.decode(data), url);
569
+ }
570
+ async function composePrims(prims, sourceUrl, inheritedVariants, environment, depth) {
571
+ if (depth > (environment.options.usd?.maxReferenceDepth ?? 12)) {
572
+ throw new Error("OpenUSD reference composition exceeded the configured depth limit.");
573
+ }
574
+ const composedPrims = [];
575
+ for (const sourcePrim of prims) {
576
+ if (sourcePrim.specifier === "class") {
577
+ continue;
578
+ }
579
+ const localSelections = getVariantSelections(sourcePrim.metadata["variants"]);
580
+ const selections = {
581
+ ...localSelections,
582
+ ...inheritedVariants,
583
+ ...environment.options.usd?.variantSelections
584
+ };
585
+ const primSourceUrl = sourcePrim.sourceUrl || sourceUrl;
586
+ let composedPrim = clonePrim(sourcePrim);
587
+ for (const [variantSetName, variantSet] of Object.entries(composedPrim.variants)) {
588
+ const selection = selections[variantSetName] || Object.keys(variantSet)[0];
589
+ const variant = variantSet[selection];
590
+ if (variant) {
591
+ composedPrim = mergePrim(composedPrim, {
592
+ ...composedPrim,
593
+ attributes: variant.attributes,
594
+ metadata: variant.metadata,
595
+ children: variant.children,
596
+ variants: {}
597
+ });
598
+ }
599
+ }
600
+ const references = getPrimReferences(composedPrim);
601
+ if (environment.options.usd?.loadReferences !== false && references.length > 0) {
602
+ for (const reference of references) {
603
+ if (!primSourceUrl) {
604
+ throw new Error("OpenUSD references require a source URL or options.core.baseUrl.");
605
+ }
606
+ const referencedUrl = resolveAssetPath(reference.assetPath, primSourceUrl);
607
+ const stage = await loadReferencedStage(referencedUrl, environment);
608
+ const referencedPrims = selectReferencedPrims(stage, reference.primPath);
609
+ const resolvedPrims = await composePrims(
610
+ referencedPrims,
611
+ referencedUrl,
612
+ selections,
613
+ environment,
614
+ depth + 1
615
+ );
616
+ for (const referencedPrim of resolvedPrims) {
617
+ composedPrim = mergePrim(rebasePrim(referencedPrim, composedPrim.path), composedPrim);
618
+ }
619
+ }
620
+ }
621
+ composedPrim.children = await composePrims(
622
+ composedPrim.children,
623
+ primSourceUrl,
624
+ selections,
625
+ environment,
626
+ depth
627
+ );
628
+ composedPrims.push(composedPrim);
629
+ }
630
+ return composedPrims;
631
+ }
632
+ async function loadReferencedStage(url, environment) {
633
+ let pendingStage = environment.cache.get(url);
634
+ if (!pendingStage) {
635
+ pendingStage = (async () => {
636
+ const archiveContent = environment.archiveFiles.get(url);
637
+ let content = archiveContent;
638
+ if (!content) {
639
+ let response;
640
+ try {
641
+ response = await environment.fetch(url);
642
+ } catch (error) {
643
+ throw new Error(`Unable to fetch USD layer "${url}": ${String(error)}`);
644
+ }
645
+ content = response.arrayBuffer();
646
+ }
647
+ const stage = parseUSDLayer(await content, url);
648
+ environment.layers.add(url);
649
+ return stage;
650
+ })();
651
+ environment.cache.set(url, pendingStage);
652
+ }
653
+ return pendingStage;
654
+ }
655
+ function selectReferencedPrims(stage, primPath) {
656
+ const targetPath = primPath || stage.metadata["defaultPrim"];
657
+ if (typeof targetPath !== "string") {
658
+ return stage.rootPrims;
659
+ }
660
+ const normalizedPath = targetPath.startsWith("/") ? targetPath : `/${targetPath}`;
661
+ const target = findPrim(stage.rootPrims, normalizedPath);
662
+ return target ? [target] : [];
663
+ }
664
+ function findPrim(prims, path) {
665
+ for (const prim of prims) {
666
+ if (prim.path === path) {
667
+ return prim;
668
+ }
669
+ const child = findPrim(prim.children, path);
670
+ if (child) {
671
+ return child;
672
+ }
673
+ }
674
+ return void 0;
675
+ }
676
+ function getPrimReferences(prim) {
677
+ const references = [];
678
+ for (const value of [
679
+ prim.metadata["references"],
680
+ prim.metadata["payload"],
681
+ prim.metadata["payloads"]
682
+ ]) {
683
+ if (Array.isArray(value)) {
684
+ references.push(...value.filter(isAssetPath));
685
+ } else if (isAssetPath(value)) {
686
+ references.push(value);
687
+ }
688
+ }
689
+ return references;
690
+ }
691
+ function getVariantSelections(value) {
692
+ if (!value || typeof value !== "object" || Array.isArray(value) || isAssetPath(value)) {
693
+ return {};
694
+ }
695
+ return Object.fromEntries(
696
+ Object.entries(value).filter((entry) => typeof entry[1] === "string")
697
+ );
698
+ }
699
+ function isAssetPath(value) {
700
+ return Boolean(
701
+ value && typeof value === "object" && !Array.isArray(value) && "assetPath" in value
702
+ );
703
+ }
704
+ function clonePrim(prim) {
705
+ return {
706
+ ...prim,
707
+ attributes: { ...prim.attributes },
708
+ metadata: { ...prim.metadata },
709
+ variants: { ...prim.variants },
710
+ children: prim.children.map(clonePrim)
711
+ };
712
+ }
713
+ function rebasePrim(prim, path) {
714
+ const rebased = clonePrim(prim);
715
+ rebased.path = path;
716
+ rebased.children = rebased.children.map((child) => rebasePrim(child, `${path}/${child.name}`));
717
+ return rebased;
718
+ }
719
+ function mergePrim(reference, override) {
720
+ const children = reference.children.map(clonePrim);
721
+ for (const overridingChild of override.children) {
722
+ const matchingIndex = children.findIndex((child) => child.name === overridingChild.name);
723
+ if (matchingIndex >= 0) {
724
+ children[matchingIndex] = mergePrim(children[matchingIndex], overridingChild);
725
+ } else {
726
+ children.push(clonePrim(overridingChild));
727
+ }
728
+ }
729
+ return {
730
+ ...reference,
731
+ ...override,
732
+ sourceUrl: getPrimReferences(override).length > 0 ? override.sourceUrl : reference.sourceUrl || override.sourceUrl,
733
+ type: override.type || reference.type,
734
+ attributes: { ...reference.attributes, ...override.attributes },
735
+ metadata: { ...reference.metadata, ...override.metadata },
736
+ variants: { ...reference.variants, ...override.variants },
737
+ children
738
+ };
739
+ }
740
+ function isZIPArchive(arrayBuffer) {
741
+ return arrayBuffer.byteLength >= 4 && new DataView(arrayBuffer).getUint32(0, true) === ZIP_SIGNATURE;
742
+ }
743
+ var TEXT_DECODER, CRATE_SIGNATURE, ZIP_SIGNATURE, USD_LOADER_DEFAULT_OPTIONS;
744
+ var init_parse_usd = __esm({
745
+ "src/lib/parse-usd.ts"() {
746
+ "use strict";
747
+ init_parse_usda();
748
+ init_parse_usdz();
749
+ TEXT_DECODER = new TextDecoder();
750
+ CRATE_SIGNATURE = "PXR-USDC";
751
+ ZIP_SIGNATURE = 67324752;
752
+ USD_LOADER_DEFAULT_OPTIONS = {
753
+ compose: true,
754
+ loadReferences: true,
755
+ maxReferenceDepth: 12,
756
+ variantSelections: {}
757
+ };
758
+ }
759
+ });
760
+
761
+ // src/usd-loader.ts
762
+ var usd_loader_exports = {};
763
+ __export(usd_loader_exports, {
764
+ USDLoaderWithParser: () => USDLoaderWithParser
765
+ });
766
+ async function parseText(text, options, context) {
767
+ return parseUSD(new TextEncoder().encode(text).buffer, options, context);
768
+ }
769
+ var _USDLoaderPreload, USDLoaderMetadataWithoutPreload, USDLoaderWithParser;
770
+ var init_usd_loader = __esm({
771
+ "src/usd-loader.ts"() {
772
+ "use strict";
773
+ init_parse_usd();
774
+ init_usd_loader_types();
775
+ ({ preload: _USDLoaderPreload, ...USDLoaderMetadataWithoutPreload } = USDLoader);
776
+ USDLoaderWithParser = {
777
+ ...USDLoaderMetadataWithoutPreload,
778
+ parse: parseUSD,
779
+ parseText
780
+ };
781
+ }
782
+ });
783
+
784
+ // src/usd-loader-types.ts
785
+ async function preload() {
786
+ const { USDLoaderWithParser: USDLoaderWithParser2 } = await Promise.resolve().then(() => (init_usd_loader(), usd_loader_exports));
787
+ return USDLoaderWithParser2;
788
+ }
789
+ var VERSION, USDLoader;
790
+ var init_usd_loader_types = __esm({
791
+ "src/usd-loader-types.ts"() {
792
+ "use strict";
793
+ VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
794
+ USDLoader = {
795
+ dataType: null,
796
+ batchType: null,
797
+ name: "Universal Scene Description",
798
+ id: "usd",
799
+ module: "scene",
800
+ version: VERSION,
801
+ extensions: ["usd", "usda", "usdz"],
802
+ mimeTypes: ["model/vnd.usd", "model/vnd.usda", "model/vnd.usdz+zip"],
803
+ text: true,
804
+ binary: true,
805
+ tests: ["#usda", "PK"],
806
+ options: {
807
+ usd: {
808
+ compose: true,
809
+ loadReferences: true,
810
+ maxReferenceDepth: 12,
811
+ variantSelections: {}
812
+ }
813
+ },
814
+ preload
815
+ };
816
+ }
817
+ });
818
+
819
+ // bundle.ts
820
+ var bundle_exports = {};
821
+ __export(bundle_exports, {
822
+ USDLoader: () => USDLoader
823
+ });
824
+ __reExport(bundle_exports, __toESM(require_core(), 1));
825
+
826
+ // src/index.ts
827
+ init_usd_loader_types();
828
+ return __toCommonJS(bundle_exports);
829
+ })();
830
+ return __exports__;
831
+ });