@openusd-wasm/three-loader 0.0.2 → 0.0.3

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.
@@ -1,506 +1,7 @@
1
1
  import { createPxr } from "@openusd-wasm/pxr";
2
2
  import * as THREE from "three";
3
3
  import { FileLoader, Loader } from "three";
4
- //#region src/asset-resolver.ts
5
- const DEFAULT_ASSET_SEARCH_EXTENSIONS = [
6
- ".usd",
7
- ".usda",
8
- ".usdc",
9
- ".usdz",
10
- ".png",
11
- ".jpg",
12
- ".jpeg",
13
- ".webp",
14
- ".ktx2",
15
- ".exr",
16
- ".hdr",
17
- ".mdl"
18
- ];
19
- const DEFAULT_ASSET_SEARCH_ROOTS = [
20
- "resource",
21
- "resources",
22
- "asset",
23
- "assets",
24
- "img",
25
- "image",
26
- "images",
27
- "texture",
28
- "textures",
29
- "material",
30
- "materials"
31
- ];
32
- const DEFAULT_MAX_ASSET_REFERENCES = 80;
33
- const DEFAULT_MAX_ASSET_REFERENCE_DEPTH = 4;
34
- const DEFAULT_PACKAGE_REMAP_ALIAS_COUNT = 16;
35
- const USD_LAYER_EXTENSIONS = new Set([
36
- ".usd",
37
- ".usda",
38
- ".usdc",
39
- ".usdz"
40
- ]);
41
- const TEXTURE_DIRECTORY_NAMES = new Set([
42
- "img",
43
- "image",
44
- "images",
45
- "texture",
46
- "textures"
47
- ]);
48
- function toFetchableUrl(value) {
49
- try {
50
- const url = new URL(value, globalThis.location?.href);
51
- return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "file:" ? url.href : null;
52
- } catch {
53
- return null;
54
- }
55
- }
56
- function normalizeFsRelativePath(path) {
57
- const parts = [];
58
- for (const part of path.replaceAll("\\", "/").split("/")) {
59
- if (!part || part === ".") continue;
60
- if (part === "..") {
61
- parts.pop();
62
- continue;
63
- }
64
- parts.push(part);
65
- }
66
- return parts.join("/");
67
- }
68
- function dirname(path) {
69
- const normalized = normalizeFsRelativePath(path);
70
- const index = normalized.lastIndexOf("/");
71
- return index === -1 ? "" : normalized.slice(0, index);
72
- }
73
- function joinRelativePath(base, path) {
74
- return normalizeFsRelativePath(base ? `${base}/${path}` : path);
75
- }
76
- function extensionOf(path) {
77
- const match = /\.([A-Za-z0-9]+)$/.exec(path.split(/[?#]/)[0] ?? path);
78
- return match ? `.${match[1]?.toLowerCase()}` : "";
79
- }
80
- function contentTypeForPath(path) {
81
- switch (extensionOf(path)) {
82
- case ".png": return "image/png";
83
- case ".jpg":
84
- case ".jpeg": return "image/jpeg";
85
- case ".webp": return "image/webp";
86
- default: return "application/octet-stream";
87
- }
88
- }
89
- function baseName(path) {
90
- return normalizeFsRelativePath(path).split("/").pop() ?? "";
91
- }
92
- function fileNameForSource$1(sourcePath) {
93
- const name = (sourcePath.split(/[?#]/)[0] ?? sourcePath).split("/").filter(Boolean).pop();
94
- return name && /\.[a-z0-9]+$/i.test(name) ? name : "scene.usda";
95
- }
96
- function toArrayBuffer(data) {
97
- const buffer = new ArrayBuffer(data.byteLength);
98
- new Uint8Array(buffer).set(data);
99
- return buffer;
100
- }
101
- function trimAssetReference(value) {
102
- let ref = value.trim();
103
- if (!ref || ref.length > 220) return null;
104
- ref = ref.replace(/^[@<"']+/, "").replace(/[@>"')\]},;]+$/g, "");
105
- if (!ref || ref.startsWith("#") || ref.startsWith("$")) return null;
106
- if (/^[A-Za-z]:[\\/]/.test(ref)) return null;
107
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(ref)) return null;
108
- if (ref.startsWith("/")) return null;
109
- if (/[\0\r\n\t\f\v]/.test(ref)) return null;
110
- if (!/^[ A-Za-z0-9._~!$&'()+,;=@/-]+$/.test(ref)) return null;
111
- return ref;
112
- }
113
- function isUsdCrate(data) {
114
- return data[0] === 80 && data[1] === 88 && data[2] === 82 && data[3] === 45 && data[4] === 85 && data[5] === 83 && data[6] === 68 && data[7] === 67;
115
- }
116
- function startsWithAssetSearchRoot(value) {
117
- return /^(resource|resources|asset|assets|img|image|images|texture|textures|material|materials)(\/|$)/i.test(value);
118
- }
119
- function startsWithAssetSearchRootPath(value) {
120
- const normalized = normalizeFsRelativePath(value);
121
- return normalized.includes("/") && startsWithAssetSearchRoot(normalized);
122
- }
123
- function looksLikeExtensionlessAssetPath(value) {
124
- const parts = normalizeFsRelativePath(value).split("/");
125
- if (parts.length < 2 || parts.length > 3) return false;
126
- if (parts.some((part) => part.length < 2 || part.length > 80)) return false;
127
- if (parts.some((part) => part.startsWith("."))) return false;
128
- if (parts.some((part) => !/^[A-Za-z0-9._-]+$/.test(part))) return false;
129
- if (!/_/.test(value) || !/\d/.test(value)) return false;
130
- return true;
131
- }
132
- function looksLikeKnownExtensionlessAsset(value) {
133
- const normalized = normalizeFsRelativePath(value);
134
- if (!normalized.includes("/")) return false;
135
- return [
136
- "material",
137
- "model",
138
- "scene"
139
- ].includes(baseName(normalized).toLowerCase());
140
- }
141
- function looksLikeAssetReference(value, searchExtensions, includeExtensionlessAssetPaths, wholeString) {
142
- const extension = extensionOf(value);
143
- if (extension) {
144
- if (!searchExtensions.includes(extension)) return false;
145
- const name = baseName(value);
146
- if (name === extension) return false;
147
- if (wholeString && !value.startsWith("./") && !value.startsWith("../") && !value.includes("/")) return name.length >= 8;
148
- return true;
149
- }
150
- if (value.startsWith("./") || value.startsWith("../")) {
151
- const relative = normalizeFsRelativePath(value);
152
- if (startsWithAssetSearchRootPath(relative)) return looksLikeExtensionlessAssetPath(relative) || looksLikeKnownExtensionlessAsset(relative);
153
- return looksLikeExtensionlessAssetPath(relative);
154
- }
155
- if (startsWithAssetSearchRootPath(value)) return looksLikeExtensionlessAssetPath(value) || looksLikeKnownExtensionlessAsset(value);
156
- return includeExtensionlessAssetPaths && looksLikeExtensionlessAssetPath(value);
157
- }
158
- function extractAsciiStrings(data) {
159
- const strings = [];
160
- let current = "";
161
- for (const byte of data) if (byte >= 32 && byte <= 126) current += String.fromCharCode(byte);
162
- else {
163
- if (current.length >= 4) strings.push(current);
164
- current = "";
165
- }
166
- if (current.length >= 4) strings.push(current);
167
- return strings;
168
- }
169
- function discoverAssetReferences(data, searchExtensions, includeExtensionlessAssetPaths = false) {
170
- const out = /* @__PURE__ */ new Set();
171
- for (const text of extractAsciiStrings(data)) {
172
- const whole = trimAssetReference(text);
173
- if (whole && looksLikeAssetReference(whole, searchExtensions, includeExtensionlessAssetPaths, true)) out.add(whole);
174
- for (const match of text.matchAll(/@([^@\n\r]+)@/g)) {
175
- const ref = trimAssetReference(match[1] ?? "");
176
- if (ref && looksLikeAssetReference(ref, searchExtensions, includeExtensionlessAssetPaths, false)) out.add(ref);
177
- }
178
- for (const match of text.matchAll(/["']([^"'\n\r]+\.(?:usd|usda|usdc|usdz|png|jpe?g|webp|ktx2|exr|hdr|mdl))["']/gi)) {
179
- const ref = trimAssetReference(match[1] ?? "");
180
- if (ref && looksLikeAssetReference(ref, searchExtensions, includeExtensionlessAssetPaths, false)) out.add(ref);
181
- }
182
- }
183
- return [...out];
184
- }
185
- function discoverAssetSearchRoots(data, searchRoots) {
186
- const out = /* @__PURE__ */ new Set();
187
- const rootSet = new Set(searchRoots);
188
- for (const text of extractAsciiStrings(data)) {
189
- const ref = trimAssetReference(text);
190
- if (!ref) continue;
191
- const segment = firstPathSegment(ref);
192
- if (rootSet.has(segment) && normalizeFsRelativePath(ref).includes("/")) out.add(segment);
193
- }
194
- return [...out];
195
- }
196
- function buildExtensionVariants(path, searchExtensions) {
197
- const normalized = normalizeFsRelativePath(path);
198
- if (!normalized) return [];
199
- if (extensionOf(normalized)) return [normalized];
200
- return [...searchExtensions.map((extension) => `${normalized}${extension}`), normalized];
201
- }
202
- function buildReferencePathVariants(relativePath) {
203
- const normalized = normalizeFsRelativePath(relativePath);
204
- if (!normalized) return [];
205
- return [{
206
- path: normalized,
207
- aliases: [normalized]
208
- }];
209
- }
210
- function buildPackageRemapAliases(sourceRelativePath, assetPath) {
211
- const extension = extensionOf(assetPath);
212
- if (!extension || USD_LAYER_EXTENSIONS.has(extension)) return [];
213
- const name = baseName(assetPath);
214
- if (!name) return [];
215
- const aliasDirs = new Set([dirname(sourceRelativePath)]);
216
- const assetDir = dirname(assetPath);
217
- const assetParentDir = dirname(assetDir);
218
- const assetTextureDir = baseName(assetDir).toLowerCase();
219
- if (TEXTURE_DIRECTORY_NAMES.has(assetTextureDir)) aliasDirs.add(assetParentDir);
220
- return unique([...aliasDirs].flatMap((aliasDir) => Array.from({ length: DEFAULT_PACKAGE_REMAP_ALIAS_COUNT }, (_, index) => joinRelativePath(aliasDir, `${index}/${name}`))));
221
- }
222
- function addPackageRemapAliases(files, aliases, data, ownedAliases, aliasConflicts, existingFiles = {}) {
223
- for (const alias of aliases) {
224
- if (aliasConflicts.has(alias)) continue;
225
- const existingInput = existingFiles[alias];
226
- if (existingInput && existingInput !== data) {
227
- aliasConflicts.add(alias);
228
- continue;
229
- }
230
- const existing = files[alias];
231
- if (existing && existing !== data) {
232
- if (ownedAliases.has(alias)) {
233
- delete files[alias];
234
- ownedAliases.delete(alias);
235
- }
236
- aliasConflicts.add(alias);
237
- continue;
238
- }
239
- if (!existing) {
240
- files[alias] = data;
241
- ownedAliases.add(alias);
242
- }
243
- }
244
- }
245
- function unique(values) {
246
- return [...new Set(values)];
247
- }
248
- function uniqueFileCount(files) {
249
- return new Set(Object.values(files)).size;
250
- }
251
- function firstPathSegment(path) {
252
- return normalizeFsRelativePath(path).split("/")[0] ?? "";
253
- }
254
- function normalizeSearchRoots(roots) {
255
- return unique((roots ?? DEFAULT_ASSET_SEARCH_ROOTS).map((root) => normalizeFsRelativePath(root)).filter(Boolean));
256
- }
257
- function inferReferencedSearchRoots(refs, searchRoots) {
258
- const rootSet = new Set(searchRoots);
259
- return unique(refs.map((ref) => firstPathSegment(ref)).filter((segment) => rootSet.has(segment)));
260
- }
261
- function buildFetchAttempts(ref, sourceUrl, sourceRelativePath, searchExtensions, rootSourceUrl, searchRoots) {
262
- const sourceDir = dirname(sourceRelativePath);
263
- const attempts = [];
264
- const seen = /* @__PURE__ */ new Set();
265
- const normalizedRef = normalizeFsRelativePath(ref);
266
- const isDotRelative = /^\.{1,2}\//.test(ref);
267
- const candidates = [];
268
- function addCandidate(fsPath, fetchPath, baseUrl, aliases = []) {
269
- const normalizedFsPath = normalizeFsRelativePath(fsPath);
270
- if (!normalizedFsPath) return;
271
- candidates.push({
272
- fsPath: normalizedFsPath,
273
- fetchPath,
274
- baseUrl,
275
- aliases: unique([
276
- normalizedRef,
277
- normalizedFsPath,
278
- ...aliases
279
- ].filter(Boolean))
280
- });
281
- }
282
- if (!isDotRelative) {
283
- for (const root of searchRoots) if (normalizedRef !== root && !normalizedRef.startsWith(`${root}/`)) addCandidate(`${root}/${normalizedRef}`, `${root}/${normalizedRef}`, rootSourceUrl);
284
- }
285
- addCandidate(joinRelativePath(sourceDir, ref), ref, sourceUrl);
286
- if (!isDotRelative && sourceDir) addCandidate(normalizedRef, normalizedRef, rootSourceUrl);
287
- for (const candidate of candidates) for (const variant of buildReferencePathVariants(candidate.fsPath)) {
288
- const fsAliases = new Set([...candidate.aliases, ...variant.aliases]);
289
- for (const pathWithExtension of buildExtensionVariants(variant.path, searchExtensions)) {
290
- const extension = extensionOf(pathWithExtension);
291
- fsAliases.add(pathWithExtension);
292
- const fetchPath = extensionOf(candidate.fetchPath) ? candidate.fetchPath : `${candidate.fetchPath}${extension}`;
293
- try {
294
- const url = new URL(fetchPath, candidate.baseUrl).href;
295
- if (seen.has(url)) continue;
296
- seen.add(url);
297
- attempts.push({
298
- url,
299
- fsPaths: [...fsAliases],
300
- sourceRelativePath: pathWithExtension,
301
- packageRemapAliases: buildPackageRemapAliases(sourceRelativePath, pathWithExtension)
302
- });
303
- } catch {}
304
- }
305
- }
306
- return attempts;
307
- }
308
- async function fetchBinary(url) {
309
- try {
310
- const response = await fetch(url);
311
- if (!response.ok) return null;
312
- return new Uint8Array(await response.arrayBuffer());
313
- } catch {
314
- return null;
315
- }
316
- }
317
- async function autoResolveAssetFiles(rootData, options) {
318
- const sourcePath = options.sourcePath ?? "";
319
- if (options.autoResolveAssets === false || !sourcePath) return {};
320
- const sourceUrl = toFetchableUrl(sourcePath);
321
- if (!sourceUrl) return {};
322
- const searchExtensions = options.assetSearchExtensions ?? DEFAULT_ASSET_SEARCH_EXTENSIONS;
323
- const configuredSearchRoots = normalizeSearchRoots(options.assetSearchRoots);
324
- const maxFiles = options.maxAssetReferences ?? DEFAULT_MAX_ASSET_REFERENCES;
325
- const maxDepth = options.maxAssetReferenceDepth ?? DEFAULT_MAX_ASSET_REFERENCE_DEPTH;
326
- const rootRelativePath = normalizeFsRelativePath(options.fileName ?? fileNameForSource$1(sourcePath));
327
- const files = {};
328
- const ownedPackageAliases = /* @__PURE__ */ new Set();
329
- const packageAliasConflicts = /* @__PURE__ */ new Set();
330
- const fetchedUrls = /* @__PURE__ */ new Set();
331
- const queued = /* @__PURE__ */ new Set();
332
- const rootRefs = discoverAssetReferences(rootData, searchExtensions, true);
333
- const inferredSearchRoots = unique([...inferReferencedSearchRoots(rootRefs, configuredSearchRoots), ...discoverAssetSearchRoots(rootData, configuredSearchRoots)]);
334
- const queue = rootRefs.map((ref) => ({
335
- ref,
336
- sourceUrl,
337
- rootSourceUrl: sourceUrl,
338
- sourceRelativePath: rootRelativePath,
339
- searchRoots: inferredSearchRoots,
340
- depth: 0
341
- }));
342
- for (const item of queue) queued.add(`${item.sourceUrl}\n${item.ref}`);
343
- while (queue.length > 0 && uniqueFileCount(files) < maxFiles) {
344
- const item = queue.shift();
345
- if (!item || item.depth > maxDepth) continue;
346
- for (const attempt of buildFetchAttempts(item.ref, item.sourceUrl, item.sourceRelativePath, searchExtensions, item.rootSourceUrl, item.searchRoots)) {
347
- if (fetchedUrls.has(attempt.url)) continue;
348
- fetchedUrls.add(attempt.url);
349
- const data = await fetchBinary(attempt.url);
350
- if (!data) continue;
351
- for (const fsPath of attempt.fsPaths) {
352
- ownedPackageAliases.delete(fsPath);
353
- files[fsPath] = data;
354
- }
355
- addPackageRemapAliases(files, attempt.packageRemapAliases, data, ownedPackageAliases, packageAliasConflicts);
356
- if (item.depth < maxDepth) {
357
- const sourceRelativePath = attempt.sourceRelativePath;
358
- if (sourceRelativePath) for (const ref of discoverAssetReferences(data, searchExtensions, !isUsdCrate(data))) {
359
- const queueKey = `${attempt.url}\n${ref}`;
360
- if (queued.has(queueKey)) continue;
361
- queued.add(queueKey);
362
- const nestedSearchRoots = unique([...item.searchRoots, ...inferReferencedSearchRoots([sourceRelativePath], configuredSearchRoots)]);
363
- queue.push({
364
- ref,
365
- sourceUrl: attempt.url,
366
- rootSourceUrl: item.rootSourceUrl,
367
- sourceRelativePath,
368
- searchRoots: nestedSearchRoots,
369
- depth: item.depth + 1
370
- });
371
- }
372
- }
373
- break;
374
- }
375
- }
376
- return files;
377
- }
378
- function isMetadataAssetReference(value, searchExtensions) {
379
- if (/^(blob|data):/i.test(value)) return false;
380
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(value)) return false;
381
- if (value.startsWith("/")) return false;
382
- return searchExtensions.includes(extensionOf(value));
383
- }
384
- function collectMetadataAssetReferences(value, searchExtensions, refs = /* @__PURE__ */ new Set()) {
385
- if (!value) return refs;
386
- if (Array.isArray(value)) {
387
- for (const item of value) collectMetadataAssetReferences(item, searchExtensions, refs);
388
- return refs;
389
- }
390
- if (typeof value !== "object") return refs;
391
- const asset = value;
392
- for (const item of [asset.path, asset.url]) if (typeof item === "string" && isMetadataAssetReference(item, searchExtensions)) refs.add(item);
393
- for (const item of Object.values(value)) collectMetadataAssetReferences(item, searchExtensions, refs);
394
- return refs;
395
- }
396
- async function autoResolveTextureFiles(metadata, existingFiles, options) {
397
- const sourcePath = options.sourcePath ?? "";
398
- if (options.autoResolveAssets === false || !sourcePath) return {};
399
- const sourceUrl = toFetchableUrl(sourcePath);
400
- if (!sourceUrl) return {};
401
- const searchExtensions = options.assetSearchExtensions ?? DEFAULT_ASSET_SEARCH_EXTENSIONS;
402
- const configuredSearchRoots = normalizeSearchRoots(options.assetSearchRoots);
403
- const textureRefs = [...collectMetadataAssetReferences(metadata, searchExtensions)];
404
- const searchRoots = unique([...inferReferencedSearchRoots(Object.keys(existingFiles), configuredSearchRoots), ...inferReferencedSearchRoots(textureRefs, configuredSearchRoots)]);
405
- const maxFiles = options.maxAssetReferences ?? DEFAULT_MAX_ASSET_REFERENCES;
406
- const remainingFiles = Math.max(maxFiles - uniqueFileCount(existingFiles), 0);
407
- if (remainingFiles === 0) return {};
408
- const rootRelativePath = normalizeFsRelativePath(options.fileName ?? fileNameForSource$1(sourcePath));
409
- const files = {};
410
- const ownedPackageAliases = /* @__PURE__ */ new Set();
411
- const packageAliasConflicts = /* @__PURE__ */ new Set();
412
- const fetchedUrls = /* @__PURE__ */ new Set();
413
- let resolvedFiles = 0;
414
- for (const ref of textureRefs) {
415
- if (resolvedFiles >= remainingFiles) break;
416
- for (const attempt of buildFetchAttempts(ref, sourceUrl, rootRelativePath, searchExtensions, sourceUrl, searchRoots)) {
417
- if (attempt.fsPaths.some((path) => existingFiles[path] || files[path])) break;
418
- if (fetchedUrls.has(attempt.url)) continue;
419
- fetchedUrls.add(attempt.url);
420
- const data = await fetchBinary(attempt.url);
421
- if (!data) continue;
422
- for (const fsPath of attempt.fsPaths) {
423
- ownedPackageAliases.delete(fsPath);
424
- files[fsPath] = data;
425
- }
426
- addPackageRemapAliases(files, attempt.packageRemapAliases, data, ownedPackageAliases, packageAliasConflicts, existingFiles);
427
- resolvedFiles += 1;
428
- break;
429
- }
430
- }
431
- return files;
432
- }
433
- function readZipEntryName(data, start, length) {
434
- return new TextDecoder().decode(data.subarray(start, start + length));
435
- }
436
- function extractStoredZipEntries(data) {
437
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
438
- const entries = /* @__PURE__ */ new Map();
439
- let offset = 0;
440
- while (offset + 30 <= data.byteLength) {
441
- if (view.getUint32(offset, true) !== 67324752) break;
442
- const method = view.getUint16(offset + 8, true);
443
- const compressedSize = view.getUint32(offset + 18, true);
444
- const fileNameLength = view.getUint16(offset + 26, true);
445
- const extraLength = view.getUint16(offset + 28, true);
446
- const nameStart = offset + 30;
447
- const dataStart = nameStart + fileNameLength + extraLength;
448
- const dataEnd = dataStart + compressedSize;
449
- if (dataEnd > data.byteLength) break;
450
- const name = normalizeFsRelativePath(readZipEntryName(data, nameStart, fileNameLength));
451
- if (name && method === 0) entries.set(name, data.slice(dataStart, dataEnd));
452
- offset = dataEnd;
453
- }
454
- return entries;
455
- }
456
- function createTextureResolverFromEntries(entries) {
457
- if (typeof Blob === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return null;
458
- const imageEntries = new Map([...entries].filter(([path]) => [
459
- ".png",
460
- ".jpg",
461
- ".jpeg",
462
- ".webp"
463
- ].includes(extensionOf(path))));
464
- if (imageEntries.size === 0) return null;
465
- const byBaseName = /* @__PURE__ */ new Map();
466
- for (const path of imageEntries.keys()) {
467
- const name = baseName(path);
468
- byBaseName.set(name, byBaseName.has(name) ? null : path);
469
- }
470
- const urls = /* @__PURE__ */ new Map();
471
- const urlForEntry = (path) => {
472
- const entry = imageEntries.get(path);
473
- if (!entry) return null;
474
- let url = urls.get(path);
475
- if (!url) {
476
- url = URL.createObjectURL(new Blob([toArrayBuffer(entry)], { type: contentTypeForPath(path) }));
477
- urls.set(path, url);
478
- }
479
- return url;
480
- };
481
- const resolvePath = (value) => {
482
- if (!value) return null;
483
- const normalized = normalizeFsRelativePath(value.split(/[?#]/)[0] ?? value);
484
- if (!normalized) return null;
485
- if (imageEntries.has(normalized)) return urlForEntry(normalized);
486
- const suffixMatches = [...imageEntries.keys()].filter((path) => path.endsWith(`/${normalized}`));
487
- if (suffixMatches.length === 1 && suffixMatches[0]) return urlForEntry(suffixMatches[0]);
488
- const virtualFsMatches = [...imageEntries.keys()].filter((path) => normalized.endsWith(`/${path}`));
489
- if (virtualFsMatches.length === 1 && virtualFsMatches[0]) return urlForEntry(virtualFsMatches[0]);
490
- const uniqueBaseName = byBaseName.get(baseName(normalized));
491
- return uniqueBaseName ? urlForEntry(uniqueBaseName) : null;
492
- };
493
- return {
494
- urls,
495
- resolve(asset) {
496
- return resolvePath(asset.resolvedPath) ?? resolvePath(asset.path) ?? resolvePath(asset.url) ?? null;
497
- }
498
- };
499
- }
500
- function createPackageTextureResolver(data) {
501
- return createTextureResolverFromEntries(extractStoredZipEntries(data));
502
- }
503
- //#endregion
4
+ import { autoResolveAssetFiles, autoResolveTextureFiles, createPackageTextureResolver, createTextureResolverFromEntries, findPackageRootLayer } from "@openusd-wasm/utils";
504
5
  //#region src/metadata.ts
505
6
  const MATERIAL_INPUT_SUFFIXES = [
506
7
  "diffuseColor",
@@ -517,11 +18,13 @@ const MATERIAL_INPUT_SUFFIXES = [
517
18
  "file",
518
19
  "wrapS",
519
20
  "wrapT",
21
+ "st",
22
+ "in",
23
+ "varname",
520
24
  "scale",
521
25
  "translation",
522
26
  "rotation"
523
27
  ];
524
- const MATERIAL_ASSET_ATTRIBUTE_NAMES = new Set(["info:mdl:sourceAsset"]);
525
28
  const JOINT_TYPES = {
526
29
  PhysicsFixedJoint: "fixed",
527
30
  PhysicsRevoluteJoint: "revolute",
@@ -536,6 +39,13 @@ function dispose$1(value) {
536
39
  function disposeAll(values) {
537
40
  for (let i = values.length - 1; i >= 0; --i) dispose$1(values[i]);
538
41
  }
42
+ function pathToString(path) {
43
+ if (path && typeof path === "object") {
44
+ const maybePath = path;
45
+ if (typeof maybePath.GetString === "function") return String(maybePath.GetString());
46
+ }
47
+ return String(path);
48
+ }
539
49
  function isValid$1(value) {
540
50
  if (!value) return false;
541
51
  if (typeof value.IsValid === "function") return Boolean(value.IsValid());
@@ -593,170 +103,24 @@ function toVector4(value) {
593
103
  }
594
104
  return null;
595
105
  }
596
- function identityMatrix() {
597
- return [
598
- 1,
599
- 0,
600
- 0,
601
- 0,
602
- 0,
603
- 1,
604
- 0,
605
- 0,
606
- 0,
607
- 0,
608
- 1,
609
- 0,
610
- 0,
611
- 0,
612
- 0,
613
- 1
614
- ];
615
- }
616
- function multiplyMatrices(a, b) {
617
- const out = new Array(16).fill(0);
618
- for (let row = 0; row < 4; row += 1) for (let column = 0; column < 4; column += 1) out[row * 4 + column] = (a[row * 4] ?? 0) * (b[column] ?? 0) + (a[row * 4 + 1] ?? 0) * (b[4 + column] ?? 0) + (a[row * 4 + 2] ?? 0) * (b[8 + column] ?? 0) + (a[row * 4 + 3] ?? 0) * (b[12 + column] ?? 0);
619
- return out;
620
- }
621
106
  function matricesNearlyEqual(a, b) {
622
107
  if (!a || !b || a.length < 16 || b.length < 16) return false;
623
108
  return a.every((value, index) => Math.abs(value - (b[index] ?? 0)) < 1e-8);
624
109
  }
625
- function matrixFromXformOp(opName, value) {
626
- const numbers = toNumberArray(value);
627
- if (numbers.length === 0) return null;
628
- if (opName.startsWith("xformOp:transform")) return numbers.length >= 16 ? numbers.slice(0, 16) : null;
629
- if (opName.startsWith("xformOp:translate")) return [
630
- 1,
631
- 0,
632
- 0,
633
- 0,
634
- 0,
635
- 1,
636
- 0,
637
- 0,
638
- 0,
639
- 0,
640
- 1,
641
- 0,
642
- numbers[0] ?? 0,
643
- numbers[1] ?? 0,
644
- numbers[2] ?? 0,
645
- 1
646
- ];
647
- if (opName.startsWith("xformOp:scale")) return [
648
- numbers[0] ?? 1,
649
- 0,
650
- 0,
651
- 0,
652
- 0,
653
- numbers[1] ?? numbers[0] ?? 1,
654
- 0,
655
- 0,
656
- 0,
657
- 0,
658
- numbers[2] ?? numbers[0] ?? 1,
659
- 0,
660
- 0,
661
- 0,
662
- 0,
663
- 1
664
- ];
665
- if (opName.startsWith("xformOp:rotate")) return rotationMatrixFromXformOp(opName, numbers);
666
- return null;
667
- }
668
- function rotationMatrixFromXformOp(opName, degrees) {
669
- const rotateX = (degree) => {
670
- const radians = degree * Math.PI / 180;
671
- const cos = Math.cos(radians);
672
- const sin = Math.sin(radians);
673
- return [
674
- 1,
675
- 0,
676
- 0,
677
- 0,
678
- 0,
679
- cos,
680
- sin,
681
- 0,
682
- 0,
683
- -sin,
684
- cos,
685
- 0,
686
- 0,
687
- 0,
688
- 0,
689
- 1
690
- ];
691
- };
692
- const rotateY = (degree) => {
693
- const radians = degree * Math.PI / 180;
694
- const cos = Math.cos(radians);
695
- const sin = Math.sin(radians);
696
- return [
697
- cos,
698
- 0,
699
- -sin,
700
- 0,
701
- 0,
702
- 1,
703
- 0,
704
- 0,
705
- sin,
706
- 0,
707
- cos,
708
- 0,
709
- 0,
710
- 0,
711
- 0,
712
- 1
713
- ];
714
- };
715
- const rotateZ = (degree) => {
716
- const radians = degree * Math.PI / 180;
717
- const cos = Math.cos(radians);
718
- const sin = Math.sin(radians);
719
- return [
720
- cos,
721
- sin,
722
- 0,
723
- 0,
724
- -sin,
725
- cos,
726
- 0,
727
- 0,
728
- 0,
729
- 0,
730
- 1,
731
- 0,
732
- 0,
733
- 0,
734
- 0,
735
- 1
736
- ];
737
- };
738
- const suffix = opName.split(":")[1] ?? "";
739
- if (suffix === "rotateX") return rotateX(degrees[0] ?? 0);
740
- if (suffix === "rotateY") return rotateY(degrees[0] ?? 0);
741
- if (suffix === "rotateZ") return rotateZ(degrees[0] ?? 0);
742
- const axes = suffix.replace(/^rotate/, "").split("");
743
- if (axes.length === 0) return null;
744
- return axes.reduce((matrix, axis, index) => {
745
- const degree = degrees[index] ?? 0;
746
- const next = axis === "X" ? rotateX(degree) : axis === "Y" ? rotateY(degree) : axis === "Z" ? rotateZ(degree) : null;
747
- return next ? multiplyMatrices(matrix, next) : matrix;
748
- }, identityMatrix());
749
- }
750
110
  function attr(prim, name, time, defaultValue = null) {
751
111
  const attribute = prim.GetAttribute(name);
752
112
  try {
753
- if (!isValid$1(attribute)) return defaultValue;
754
- const value = attribute.Get(time);
755
- return value === null || value === void 0 ? defaultValue : normalizeValue(value);
113
+ return attributeValue(attribute, time, defaultValue);
756
114
  } finally {
757
115
  dispose$1(attribute);
758
116
  }
759
117
  }
118
+ function attributeValue(attribute, time, defaultValue = null, authoredOnly = false) {
119
+ if (!isValid$1(attribute)) return defaultValue;
120
+ if (authoredOnly && typeof attribute.HasAuthoredValue === "function" && !attribute.HasAuthoredValue()) return defaultValue;
121
+ const value = attribute.Get(time);
122
+ return value === null || value === void 0 ? defaultValue : normalizeValue(value);
123
+ }
760
124
  function attrMetadata(prim, name, key, defaultValue = null) {
761
125
  const attribute = prim.GetAttribute(name);
762
126
  try {
@@ -767,20 +131,64 @@ function attrMetadata(prim, name, key, defaultValue = null) {
767
131
  dispose$1(attribute);
768
132
  }
769
133
  }
770
- function relationshipTargets(prim, name) {
771
- const relationship = prim.GetRelationship(name);
134
+ function relationshipTargetPaths(relationship) {
135
+ if (!isValid$1(relationship)) return [];
136
+ const targets = relationship.GetTargets();
137
+ if (!Array.isArray(targets)) return [];
138
+ try {
139
+ return targets.map(pathToString);
140
+ } finally {
141
+ disposeAll(targets);
142
+ }
143
+ }
144
+ function schemaAttributeValue(schema, methodName, time, defaultValue = null, authoredOnly = false) {
145
+ const attribute = schema[methodName]();
146
+ try {
147
+ return attributeValue(attribute, time, defaultValue, authoredOnly);
148
+ } finally {
149
+ dispose$1(attribute);
150
+ }
151
+ }
152
+ function schemaRelationshipTargets(schema, methodName) {
153
+ const relationship = schema[methodName]();
772
154
  try {
773
- if (!isValid$1(relationship)) return [];
774
- const targets = relationship.GetTargets();
775
- return Array.isArray(targets) ? targets.map(String) : [];
155
+ return relationshipTargetPaths(relationship);
776
156
  } finally {
777
157
  dispose$1(relationship);
778
158
  }
779
159
  }
780
- function connectionSourcePath(attribute) {
781
- const connections = attribute.GetConnections();
782
- if (!Array.isArray(connections) || connections.length === 0) return null;
783
- return String(connections[0]).split(".outputs:")[0] ?? null;
160
+ function assetPathValue(assetPath) {
161
+ const path = typeof assetPath.GetAssetPath === "function" ? String(assetPath.GetAssetPath()) : "";
162
+ if (!path) return null;
163
+ const resolvedPath = typeof assetPath.GetResolvedPath === "function" ? String(assetPath.GetResolvedPath()) : "";
164
+ return resolvedPath ? {
165
+ path,
166
+ resolvedPath
167
+ } : { path };
168
+ }
169
+ function propertyPathPrimPath(path) {
170
+ if (path && typeof path === "object") {
171
+ const maybePath = path;
172
+ if (typeof maybePath.GetPrimPath === "function") {
173
+ const primPath = maybePath.GetPrimPath();
174
+ try {
175
+ return pathToString(primPath) || null;
176
+ } finally {
177
+ dispose$1(primPath);
178
+ }
179
+ }
180
+ }
181
+ return pathToString(path).split(/\.(?:outputs|inputs):/)[0] || null;
182
+ }
183
+ function inputSourcePrimPath(input) {
184
+ const connections = input.GetRawConnectedSourcePaths();
185
+ if (!Array.isArray(connections)) return null;
186
+ try {
187
+ if (connections.length === 0) return null;
188
+ return propertyPathPrimPath(connections[0]);
189
+ } finally {
190
+ disposeAll(connections);
191
+ }
784
192
  }
785
193
  function primChildren(prim) {
786
194
  const children = prim.GetChildren();
@@ -794,24 +202,37 @@ function primAtPath(pxr, stage, path) {
794
202
  dispose$1(sdfPath);
795
203
  }
796
204
  }
797
- function materialBinding(prim) {
798
- let current = prim;
799
- let ownsCurrent = false;
205
+ function materialBinding(pxr, prim) {
206
+ const binding = new pxr.UsdShade.MaterialBindingAPI(prim);
800
207
  try {
801
- while (current && !current.IsPseudoRoot()) {
802
- const targets = relationshipTargets(current, "material:binding");
803
- if (targets.length > 0) return targets[0] ?? null;
804
- const parent = current.GetParent();
805
- if (ownsCurrent) dispose$1(current);
806
- current = parent;
807
- ownsCurrent = true;
208
+ if (!isValid$1(binding)) return null;
209
+ const computed = binding.ComputeBoundMaterial("", false);
210
+ if (!Array.isArray(computed)) return null;
211
+ const [material, bindingRel] = computed;
212
+ try {
213
+ if (!isValid$1(bindingRel)) return null;
214
+ const resolvedPath = pxr.UsdShade.MaterialBindingAPI.GetResolvedTargetPathFromBindingRel(bindingRel);
215
+ try {
216
+ const path = pathToString(resolvedPath);
217
+ if (path) return path;
218
+ } finally {
219
+ dispose$1(resolvedPath);
220
+ }
221
+ if (!material || !isValid$1(material)) return null;
222
+ const materialPrim = material.GetPrim();
223
+ try {
224
+ return isValid$1(materialPrim) ? String(materialPrim.GetPath()) : null;
225
+ } finally {
226
+ dispose$1(materialPrim);
227
+ }
228
+ } finally {
229
+ disposeAll(computed);
808
230
  }
809
- return null;
810
231
  } finally {
811
- if (ownsCurrent) dispose$1(current);
232
+ dispose$1(binding);
812
233
  }
813
234
  }
814
- function localTransformForPrim(pxr, prim) {
235
+ function localTransformForPrim(pxr, prim, time) {
815
236
  if (!prim.IsA("Xformable")) return {
816
237
  localMatrix: null,
817
238
  resetsXformStack: false
@@ -822,7 +243,7 @@ function localTransformForPrim(pxr, prim) {
822
243
  localMatrix: null,
823
244
  resetsXformStack: false
824
245
  };
825
- const transform = xformable.GetLocalTransformation();
246
+ const transform = xformable.GetLocalTransformation(time);
826
247
  return {
827
248
  localMatrix: toNumberArray(transform?.matrix),
828
249
  resetsXformStack: Boolean(transform?.resetsXformStack)
@@ -842,7 +263,7 @@ function parentPathForPrim(prim) {
842
263
  }
843
264
  function getModelView(pxr, prims, time) {
844
265
  return { prims: prims.map((prim) => {
845
- const transform = localTransformForPrim(pxr, prim);
266
+ const transform = localTransformForPrim(pxr, prim, time);
846
267
  return {
847
268
  path: String(prim.GetPath()),
848
269
  parentPath: parentPathForPrim(prim),
@@ -891,13 +312,27 @@ function asFloat(value, fallback = 0) {
891
312
  const number = asFiniteNumber(value);
892
313
  return number === null ? fallback : number;
893
314
  }
315
+ function shaderId(shader) {
316
+ const id = shader.GetShaderId();
317
+ return typeof id === "string" && id ? id : null;
318
+ }
319
+ function shaderInputValue(shader, inputName, time, defaultValue = null) {
320
+ const input = shader.GetInput(inputName);
321
+ try {
322
+ if (!isValid$1(input)) return defaultValue;
323
+ const value = input.Get(time);
324
+ return value === null || value === void 0 ? defaultValue : normalizeValue(value);
325
+ } finally {
326
+ dispose$1(input);
327
+ }
328
+ }
894
329
  function shaderInputConnectionPath(shader, inputName) {
895
- const attribute = shader.GetAttribute(inputName);
330
+ const input = shader.GetInput(inputName);
896
331
  try {
897
- if (!isValid$1(attribute)) return null;
898
- return connectionSourcePath(attribute);
332
+ if (!isValid$1(input)) return null;
333
+ return inputSourcePrimPath(input);
899
334
  } finally {
900
- dispose$1(attribute);
335
+ dispose$1(input);
901
336
  }
902
337
  }
903
338
  function shaderChildByPath(material, path) {
@@ -910,29 +345,41 @@ function shaderChildByPath(material, path) {
910
345
  }
911
346
  }
912
347
  function primvarNameForTextureShader(pxr, stage, material, textureShader, time) {
913
- const stSourcePath = shaderInputConnectionPath(textureShader, "inputs:st");
348
+ const stSourcePath = shaderInputConnectionPath(textureShader, "st");
914
349
  if (!stSourcePath) return null;
915
350
  const stSource = shaderChildByPath(material, stSourcePath) ?? primAtPath(pxr, stage, stSourcePath);
916
351
  try {
917
352
  if (!isValid$1(stSource)) return null;
918
- const id = attr(stSource, "info:id", time);
919
- if (id === "UsdPrimvarReader_float2") {
920
- const varname = attr(stSource, "inputs:varname", time);
921
- return typeof varname === "string" && varname ? varname : null;
922
- }
923
- if (id === "UsdTransform2d") {
924
- const nestedSourcePath = shaderInputConnectionPath(stSource, "inputs:in") ?? shaderInputConnectionPath(stSource, "inputs:st");
925
- if (!nestedSourcePath) return null;
926
- const nestedSource = shaderChildByPath(material, nestedSourcePath) ?? primAtPath(pxr, stage, nestedSourcePath);
927
- try {
928
- if (!isValid$1(nestedSource) || attr(nestedSource, "info:id", time) !== "UsdPrimvarReader_float2") return null;
929
- const varname = attr(nestedSource, "inputs:varname", time);
353
+ const stShader = new pxr.UsdShade.Shader(stSource);
354
+ try {
355
+ if (!isValid$1(stShader)) return null;
356
+ const id = shaderId(stShader);
357
+ if (id === "UsdPrimvarReader_float2") {
358
+ const varname = shaderInputValue(stShader, "varname", time);
930
359
  return typeof varname === "string" && varname ? varname : null;
931
- } finally {
932
- dispose$1(nestedSource);
933
360
  }
361
+ if (id === "UsdTransform2d") {
362
+ const nestedSourcePath = shaderInputConnectionPath(stShader, "in") ?? shaderInputConnectionPath(stShader, "st");
363
+ if (!nestedSourcePath) return null;
364
+ const nestedSource = shaderChildByPath(material, nestedSourcePath) ?? primAtPath(pxr, stage, nestedSourcePath);
365
+ try {
366
+ if (!isValid$1(nestedSource)) return null;
367
+ const nestedShader = new pxr.UsdShade.Shader(nestedSource);
368
+ try {
369
+ if (!isValid$1(nestedShader) || shaderId(nestedShader) !== "UsdPrimvarReader_float2") return null;
370
+ const varname = shaderInputValue(nestedShader, "varname", time);
371
+ return typeof varname === "string" && varname ? varname : null;
372
+ } finally {
373
+ dispose$1(nestedShader);
374
+ }
375
+ } finally {
376
+ dispose$1(nestedSource);
377
+ }
378
+ }
379
+ return null;
380
+ } finally {
381
+ dispose$1(stShader);
934
382
  }
935
- return null;
936
383
  } finally {
937
384
  dispose$1(stSource);
938
385
  }
@@ -944,10 +391,16 @@ function texturePrimvarForMaterial(pxr, stage, materialPath, time) {
944
391
  if (!isValid$1(material)) return "primvars:st";
945
392
  const children = primChildren(material);
946
393
  try {
947
- for (const shader of children) {
948
- if (shader.GetTypeName() !== "Shader" || attr(shader, "info:id", time) !== "UsdUVTexture") continue;
949
- const varname = primvarNameForTextureShader(pxr, stage, material, shader, time);
950
- if (varname) return `primvars:${varname}`;
394
+ for (const child of children) {
395
+ if (child.GetTypeName() !== "Shader") continue;
396
+ const shader = new pxr.UsdShade.Shader(child);
397
+ try {
398
+ if (!isValid$1(shader) || shaderId(shader) !== "UsdUVTexture") continue;
399
+ const varname = primvarNameForTextureShader(pxr, stage, material, shader, time);
400
+ if (varname) return `primvars:${varname}`;
401
+ } finally {
402
+ dispose$1(shader);
403
+ }
951
404
  }
952
405
  } finally {
953
406
  disposeAll(children);
@@ -964,30 +417,36 @@ function textureTransformForMaterial(pxr, stage, materialPath, time) {
964
417
  if (!isValid$1(material)) return null;
965
418
  const children = primChildren(material);
966
419
  try {
967
- for (const shader of children) {
968
- if (shader.GetTypeName() !== "Shader" || attr(shader, "info:id", time) !== "UsdUVTexture") continue;
969
- const stAttribute = shader.GetAttribute("inputs:st");
420
+ for (const child of children) {
421
+ if (child.GetTypeName() !== "Shader") continue;
422
+ const shader = new pxr.UsdShade.Shader(child);
970
423
  try {
971
- if (!isValid$1(stAttribute)) continue;
972
- const sourcePath = connectionSourcePath(stAttribute);
424
+ if (!isValid$1(shader) || shaderId(shader) !== "UsdUVTexture") continue;
425
+ const sourcePath = shaderInputConnectionPath(shader, "st");
973
426
  if (!sourcePath) return null;
974
427
  const transform = primAtPath(pxr, stage, sourcePath);
975
428
  try {
976
- if (!isValid$1(transform) || attr(transform, "info:id", time) !== "UsdTransform2d") return null;
977
- const scale = asVec2(attr(transform, "inputs:scale", time), [1, 1]);
978
- const translation = asVec2(attr(transform, "inputs:translation", time), [0, 0]);
979
- return {
980
- scaleX: scale[0],
981
- scaleY: scale[1],
982
- translateX: translation[0],
983
- translateY: translation[1],
984
- rotation: asFloat(attr(transform, "inputs:rotation", time))
985
- };
429
+ if (!isValid$1(transform)) return null;
430
+ const transformShader = new pxr.UsdShade.Shader(transform);
431
+ try {
432
+ if (!isValid$1(transformShader) || shaderId(transformShader) !== "UsdTransform2d") return null;
433
+ const scale = asVec2(shaderInputValue(transformShader, "scale", time), [1, 1]);
434
+ const translation = asVec2(shaderInputValue(transformShader, "translation", time), [0, 0]);
435
+ return {
436
+ scaleX: scale[0],
437
+ scaleY: scale[1],
438
+ translateX: translation[0],
439
+ translateY: translation[1],
440
+ rotation: asFloat(shaderInputValue(transformShader, "rotation", time))
441
+ };
442
+ } finally {
443
+ dispose$1(transformShader);
444
+ }
986
445
  } finally {
987
446
  dispose$1(transform);
988
447
  }
989
448
  } finally {
990
- dispose$1(stAttribute);
449
+ dispose$1(shader);
991
450
  }
992
451
  }
993
452
  } finally {
@@ -1023,7 +482,7 @@ function triangulateMesh(pxr, stage, prim, time) {
1023
482
  const faceIndices = toNumberArray(attr(prim, "faceVertexIndices", time, []));
1024
483
  const normals = toArray(attr(prim, "normals", time, []));
1025
484
  const normalInterpolation = String(attrMetadata(prim, "normals", "interpolation", attr(prim, "normals:interpolation", time, "")) || "") || null;
1026
- const boundMaterial = materialBinding(prim);
485
+ const boundMaterial = materialBinding(pxr, prim);
1027
486
  const uv = meshPrimvar(prim, texturePrimvarForMaterial(pxr, stage, boundMaterial, time), time);
1028
487
  const colors = toArray(attr(prim, "primvars:displayColor", time, []));
1029
488
  const opacities = toArray(attr(prim, "primvars:displayOpacity", time, []));
@@ -1082,177 +541,244 @@ function getModelElements(pxr, stage, prims, time) {
1082
541
  elements.push({
1083
542
  path: String(prim.GetPath()),
1084
543
  doubleSided: Boolean(attr(prim, "doubleSided", time, false)),
1085
- material: materialBinding(prim),
544
+ material: materialBinding(pxr, prim),
1086
545
  geometry: triangulateMesh(pxr, stage, prim, time)
1087
546
  });
1088
547
  }
1089
548
  return elements;
1090
549
  }
1091
- function collectShaderInputs(prim, time) {
550
+ function collectShadeInputs(connectable, time) {
1092
551
  const inputs = {};
1093
- const attributes = prim.GetAttributes();
552
+ const shadeInputs = connectable.GetInputs();
1094
553
  try {
1095
- for (const attribute of attributes) {
1096
- const name = String(attribute.GetName());
1097
- if (!(name.includes("inputs:") && MATERIAL_INPUT_SUFFIXES.some((suffix) => name.endsWith(suffix))) && !MATERIAL_ASSET_ATTRIBUTE_NAMES.has(name)) continue;
1098
- const value = attribute.Get(time);
1099
- const connections = attribute.GetConnections();
554
+ for (const input of shadeInputs) {
555
+ const baseName = String(input.GetBaseName());
556
+ if (!MATERIAL_INPUT_SUFFIXES.some((suffix) => baseName.endsWith(suffix))) continue;
557
+ const name = String(input.GetFullName());
558
+ const value = input.Get(time);
559
+ const connections = input.GetRawConnectedSourcePaths();
1100
560
  if (value !== null && value !== void 0) inputs[name] = normalizeValue(value);
1101
- if (Array.isArray(connections) && connections.length > 0) inputs[`${name}.connect`] = connections.map(String);
561
+ if (Array.isArray(connections)) try {
562
+ if (connections.length > 0) inputs[`${name}.connect`] = connections.map(pathToString);
563
+ } finally {
564
+ disposeAll(connections);
565
+ }
1102
566
  }
1103
567
  } finally {
1104
- disposeAll(attributes);
568
+ disposeAll(shadeInputs);
1105
569
  }
1106
570
  return inputs;
1107
571
  }
1108
- function getModelMaterials(prims, time) {
572
+ function collectShaderSourceAssets(shader) {
573
+ const inputs = {};
574
+ const sourceTypes = shader.GetSourceTypes();
575
+ if (!Array.isArray(sourceTypes)) return inputs;
576
+ for (const sourceType of sourceTypes) {
577
+ const sourceTypeName = String(sourceType);
578
+ const sourceAsset = shader.GetSourceAsset(sourceTypeName);
579
+ try {
580
+ const value = assetPathValue(sourceAsset);
581
+ if (!value) continue;
582
+ const name = sourceTypeName ? `info:${sourceTypeName}:sourceAsset` : "info:sourceAsset";
583
+ inputs[name] = value;
584
+ } finally {
585
+ dispose$1(sourceAsset);
586
+ }
587
+ }
588
+ return inputs;
589
+ }
590
+ function getModelMaterials(pxr, prims, time) {
1109
591
  const materials = [];
1110
592
  for (const prim of prims) {
1111
593
  if (prim.GetTypeName() !== "Material") continue;
594
+ const material = new pxr.UsdShade.Material(prim);
1112
595
  const children = primChildren(prim);
1113
596
  const shaders = [];
1114
597
  try {
598
+ if (!isValid$1(material)) continue;
1115
599
  for (const child of children) {
1116
600
  if (child.GetTypeName() !== "Shader") continue;
1117
- const idValue = attr(child, "info:id", time);
1118
- const id = typeof idValue === "string" ? idValue : null;
1119
- const inputs = collectShaderInputs(child, time);
1120
- if (Object.keys(inputs).length === 0 && id !== "UsdPreviewSurface" && id !== "UsdUVTexture") continue;
1121
- shaders.push({
1122
- path: String(child.GetPath()),
1123
- id,
1124
- inputs
1125
- });
601
+ const shader = new pxr.UsdShade.Shader(child);
602
+ try {
603
+ if (!isValid$1(shader)) continue;
604
+ const idValue = shader.GetShaderId();
605
+ const id = typeof idValue === "string" && idValue ? idValue : null;
606
+ const inputs = {
607
+ ...collectShadeInputs(shader, time),
608
+ ...collectShaderSourceAssets(shader)
609
+ };
610
+ if (Object.keys(inputs).length === 0 && id !== "UsdPreviewSurface" && id !== "UsdUVTexture") continue;
611
+ shaders.push({
612
+ path: String(child.GetPath()),
613
+ id,
614
+ inputs
615
+ });
616
+ } finally {
617
+ dispose$1(shader);
618
+ }
1126
619
  }
620
+ materials.push({
621
+ path: String(prim.GetPath()),
622
+ inputs: collectShadeInputs(material, time),
623
+ shaders
624
+ });
1127
625
  } finally {
1128
626
  disposeAll(children);
627
+ dispose$1(material);
1129
628
  }
1130
- materials.push({
1131
- path: String(prim.GetPath()),
1132
- inputs: collectShaderInputs(prim, time),
1133
- shaders
1134
- });
1135
629
  }
1136
630
  return materials;
1137
631
  }
1138
- function driveForNamespace(prim, namespace, time) {
632
+ function driveForNamespace(pxr, prim, namespace, time) {
633
+ const driveAPI = new pxr.UsdPhysics.DriveAPI(prim, namespace);
1139
634
  const drive = {
1140
- targetPosition: asFiniteNumber(attr(prim, `drive:${namespace}:physics:targetPosition`, time)),
1141
- targetVelocity: asFiniteNumber(attr(prim, `drive:${namespace}:physics:targetVelocity`, time)),
1142
- stiffness: asFiniteNumber(attr(prim, `drive:${namespace}:physics:stiffness`, time)),
1143
- damping: asFiniteNumber(attr(prim, `drive:${namespace}:physics:damping`, time)),
1144
- maxForce: asFiniteNumber(attr(prim, `drive:${namespace}:physics:maxForce`, time))
635
+ targetPosition: null,
636
+ targetVelocity: null,
637
+ stiffness: null,
638
+ damping: null,
639
+ maxForce: null
1145
640
  };
1146
- return Object.values(drive).some((value) => value !== null) ? drive : null;
641
+ try {
642
+ if (!isValid$1(driveAPI)) return null;
643
+ drive.targetPosition = asFiniteNumber(schemaAttributeValue(driveAPI, "GetTargetPositionAttr", time, null, true));
644
+ drive.targetVelocity = asFiniteNumber(schemaAttributeValue(driveAPI, "GetTargetVelocityAttr", time, null, true));
645
+ drive.stiffness = asFiniteNumber(schemaAttributeValue(driveAPI, "GetStiffnessAttr", time, null, true));
646
+ drive.damping = asFiniteNumber(schemaAttributeValue(driveAPI, "GetDampingAttr", time, null, true));
647
+ drive.maxForce = asFiniteNumber(schemaAttributeValue(driveAPI, "GetMaxForceAttr", time, null, true));
648
+ return Object.values(drive).some((value) => value !== null) ? drive : null;
649
+ } finally {
650
+ dispose$1(driveAPI);
651
+ }
1147
652
  }
1148
653
  function jointTypeForPrim(typeName) {
1149
654
  if (JOINT_TYPES[typeName]) return JOINT_TYPES[typeName];
1150
655
  if (typeName.startsWith("Physics") && typeName.endsWith("Joint")) return typeName.slice(7, -5).toLowerCase() || "joint";
1151
656
  return null;
1152
657
  }
1153
- function getModelJoints(prims, time) {
658
+ function jointLimitSchema(pxr, prim, typeName) {
659
+ switch (typeName) {
660
+ case "PhysicsRevoluteJoint": return new pxr.UsdPhysics.RevoluteJoint(prim);
661
+ case "PhysicsPrismaticJoint": return new pxr.UsdPhysics.PrismaticJoint(prim);
662
+ case "PhysicsSphericalJoint": return new pxr.UsdPhysics.SphericalJoint(prim);
663
+ case "PhysicsDistanceJoint": return new pxr.UsdPhysics.DistanceJoint(prim);
664
+ default: return null;
665
+ }
666
+ }
667
+ function jointAxisAndLimits(pxr, prim, typeName, time) {
668
+ const schema = jointLimitSchema(pxr, prim, typeName);
669
+ if (!schema) return {
670
+ axis: null,
671
+ lowerLimit: null,
672
+ upperLimit: null
673
+ };
674
+ try {
675
+ if (!isValid$1(schema)) return {
676
+ axis: null,
677
+ lowerLimit: null,
678
+ upperLimit: null
679
+ };
680
+ if (typeName === "PhysicsSphericalJoint") {
681
+ const axis = schemaAttributeValue(schema, "GetAxisAttr", time);
682
+ return {
683
+ axis: typeof axis === "string" ? axis : null,
684
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetConeAngle0LimitAttr", time, null, true)),
685
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetConeAngle1LimitAttr", time, null, true))
686
+ };
687
+ }
688
+ if (typeName === "PhysicsDistanceJoint") return {
689
+ axis: null,
690
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetMinDistanceAttr", time, null, true)),
691
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetMaxDistanceAttr", time, null, true))
692
+ };
693
+ const axis = schemaAttributeValue(schema, "GetAxisAttr", time);
694
+ return {
695
+ axis: typeof axis === "string" ? axis : null,
696
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetLowerLimitAttr", time)),
697
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetUpperLimitAttr", time))
698
+ };
699
+ } finally {
700
+ dispose$1(schema);
701
+ }
702
+ }
703
+ function getModelJoints(pxr, prims, time) {
1154
704
  const joints = [];
1155
705
  for (const prim of prims) {
1156
706
  const typeName = String(prim.GetTypeName());
1157
707
  const jointType = jointTypeForPrim(typeName);
1158
708
  if (!jointType) continue;
1159
- const angularDrive = driveForNamespace(prim, "angular", time);
1160
- const linearDrive = driveForNamespace(prim, "linear", time);
709
+ const joint = new pxr.UsdPhysics.Joint(prim);
710
+ if (!isValid$1(joint)) {
711
+ dispose$1(joint);
712
+ continue;
713
+ }
714
+ const angularDrive = driveForNamespace(pxr, prim, "angular", time);
715
+ const linearDrive = driveForNamespace(pxr, prim, "linear", time);
1161
716
  const drives = {};
1162
717
  if (angularDrive) drives.angular = angularDrive;
1163
718
  if (linearDrive) drives.linear = linearDrive;
1164
- const body0 = relationshipTargets(prim, "physics:body0");
1165
- const body1 = relationshipTargets(prim, "physics:body1");
719
+ const body0 = schemaRelationshipTargets(joint, "GetBody0Rel");
720
+ const body1 = schemaRelationshipTargets(joint, "GetBody1Rel");
1166
721
  const drive = jointType === "prismatic" ? linearDrive ?? angularDrive : angularDrive ?? linearDrive;
1167
- joints.push({
1168
- path: String(prim.GetPath()),
1169
- name: String(prim.GetName()),
1170
- typeName,
1171
- jointType,
1172
- body0: body0[0] ?? null,
1173
- body1: body1[0] ?? null,
1174
- axis: typeof attr(prim, "physics:axis", time) === "string" ? String(attr(prim, "physics:axis", time)) : null,
1175
- localPos0: toVector3(attr(prim, "physics:localPos0", time, [
1176
- 0,
1177
- 0,
1178
- 0
1179
- ]), [
1180
- 0,
1181
- 0,
1182
- 0
1183
- ]),
1184
- localPos1: toVector3(attr(prim, "physics:localPos1", time, [
1185
- 0,
1186
- 0,
1187
- 0
1188
- ]), [
1189
- 0,
1190
- 0,
1191
- 0
1192
- ]),
1193
- localRot0: toVector4(attr(prim, "physics:localRot0", time)),
1194
- localRot1: toVector4(attr(prim, "physics:localRot1", time)),
1195
- lowerLimit: asFiniteNumber(attr(prim, "physics:lowerLimit", time)),
1196
- upperLimit: asFiniteNumber(attr(prim, "physics:upperLimit", time)),
1197
- enabled: Boolean(attr(prim, "physics:jointEnabled", time, true)),
1198
- drive,
1199
- drives
1200
- });
722
+ const axisAndLimits = jointAxisAndLimits(pxr, prim, typeName, time);
723
+ try {
724
+ joints.push({
725
+ path: String(prim.GetPath()),
726
+ name: String(prim.GetName()),
727
+ typeName,
728
+ jointType,
729
+ body0: body0[0] ?? null,
730
+ body1: body1[0] ?? null,
731
+ ...axisAndLimits,
732
+ localPos0: toVector3(schemaAttributeValue(joint, "GetLocalPos0Attr", time, [
733
+ 0,
734
+ 0,
735
+ 0
736
+ ]), [
737
+ 0,
738
+ 0,
739
+ 0
740
+ ]),
741
+ localPos1: toVector3(schemaAttributeValue(joint, "GetLocalPos1Attr", time, [
742
+ 0,
743
+ 0,
744
+ 0
745
+ ]), [
746
+ 0,
747
+ 0,
748
+ 0
749
+ ]),
750
+ localRot0: toVector4(schemaAttributeValue(joint, "GetLocalRot0Attr", time)),
751
+ localRot1: toVector4(schemaAttributeValue(joint, "GetLocalRot1Attr", time)),
752
+ enabled: Boolean(schemaAttributeValue(joint, "GetJointEnabledAttr", time, true)),
753
+ drive,
754
+ drives
755
+ });
756
+ } finally {
757
+ dispose$1(joint);
758
+ }
1201
759
  }
1202
760
  return joints;
1203
761
  }
1204
- function stageTimingFromLayer(rootLayer) {
1205
- const text = String(rootLayer.ExportToString?.() ?? "");
1206
- const numberFor = (name) => {
1207
- const match = new RegExp(`${name}\\s*=\\s*(-?\\d+(?:\\.\\d+)?)`).exec(text);
1208
- if (!match?.[1]) return null;
1209
- const value = Number(match[1]);
1210
- return Number.isFinite(value) ? value : null;
1211
- };
1212
- return {
1213
- startTimeCode: numberFor("startTimeCode"),
1214
- endTimeCode: numberFor("endTimeCode"),
1215
- timeCodesPerSecond: numberFor("timeCodesPerSecond") ?? 24
1216
- };
1217
- }
1218
762
  function getStageInfo(pxr, stage, options) {
1219
763
  const defaultPrim = stage.GetDefaultPrim();
1220
- const rootLayer = stage.GetRootLayer();
1221
764
  try {
1222
- const timing = stageTimingFromLayer(rootLayer);
1223
765
  return {
1224
766
  sourcePath: options.sourcePath,
1225
767
  rootLayerIdentifier: options.rootLayerIdentifier,
1226
768
  defaultPrim: isValid$1(defaultPrim) ? String(defaultPrim.GetPath()) : null,
1227
769
  upAxis: String(pxr.UsdGeom.GetStageUpAxis(stage)),
1228
- ...timing
770
+ startTimeCode: stage.GetStartTimeCode(),
771
+ endTimeCode: stage.GetEndTimeCode(),
772
+ timeCodesPerSecond: stage.GetTimeCodesPerSecond()
1229
773
  };
1230
774
  } finally {
1231
- dispose$1(rootLayer);
1232
775
  dispose$1(defaultPrim);
1233
776
  }
1234
777
  }
1235
- function localMatrixFromXformOps(prim, time) {
1236
- const opOrder = toArray(attr(prim, "xformOpOrder", time, [])).map(String);
1237
- if (opOrder.length === 0) return null;
1238
- let matrix = identityMatrix();
1239
- let foundOp = false;
1240
- for (const opName of opOrder) {
1241
- if (opName.startsWith("!")) {
1242
- matrix = identityMatrix();
1243
- continue;
1244
- }
1245
- const opMatrix = matrixFromXformOp(opName, attr(prim, opName, time));
1246
- if (!opMatrix) continue;
1247
- matrix = multiplyMatrices(matrix, opMatrix);
1248
- foundOp = true;
1249
- }
1250
- return foundOp ? matrix : null;
1251
- }
1252
778
  function sampleLocalMatrix(pxr, prim, frame) {
1253
779
  const time = new pxr.Usd.TimeCode(frame);
1254
780
  try {
1255
- return localMatrixFromXformOps(prim, time);
781
+ return localTransformForPrim(pxr, prim, time).localMatrix;
1256
782
  } finally {
1257
783
  dispose$1(time);
1258
784
  }
@@ -1269,10 +795,10 @@ function animationSampleFrames(start, end) {
1269
795
  });
1270
796
  }
1271
797
  function getModelAnimations(pxr, prims, stageInfo) {
1272
- const start = stageInfo.startTimeCode ?? null;
1273
- const end = stageInfo.endTimeCode ?? null;
1274
- const timeCodesPerSecond = stageInfo.timeCodesPerSecond ?? 24;
1275
- if (start === null || end === null || end <= start) return [];
798
+ const start = stageInfo.startTimeCode;
799
+ const end = stageInfo.endTimeCode;
800
+ const timeCodesPerSecond = stageInfo.timeCodesPerSecond;
801
+ if (end <= start) return [];
1276
802
  const frames = animationSampleFrames(start, end);
1277
803
  if (frames.length < 2) return [];
1278
804
  const transforms = [];
@@ -1319,8 +845,8 @@ function extractUSDModelData(pxr, stage, options = {}) {
1319
845
  stage: stageInfo,
1320
846
  view: getModelView(pxr, prims, time),
1321
847
  elements: getModelElements(pxr, stage, prims, time),
1322
- materials: getModelMaterials(prims, time),
1323
- joints: getModelJoints(prims, time),
848
+ materials: getModelMaterials(pxr, prims, time),
849
+ joints: getModelJoints(pxr, prims, time),
1324
850
  animations: getModelAnimations(pxr, prims, stageInfo)
1325
851
  };
1326
852
  } finally {
@@ -1625,13 +1151,19 @@ function deleteFileIfExists(pxr, path) {
1625
1151
  if (pxr.FS.exists(path)) pxr.FS.deleteFile(path);
1626
1152
  } catch {}
1627
1153
  }
1628
- async function writeUSDInput(pxr, input, options) {
1629
- const directory = joinFsPath(options.workingDirectory, String(++nextLoadId));
1154
+ function createUSDInputDirectory(pxr, workingDirectory) {
1155
+ const directory = joinFsPath(workingDirectory, String(++nextLoadId));
1630
1156
  pxr.FS.createDir(directory);
1631
- for (const [relativePath, file] of Object.entries(options.files ?? {})) {
1157
+ return directory;
1158
+ }
1159
+ async function writeUSDFiles(pxr, directory, files) {
1160
+ for (const [relativePath, file] of Object.entries(files ?? {})) {
1632
1161
  const filePath = joinFsPath(directory, relativePath);
1633
1162
  pxr.FS.writeFile(filePath, await sourceToWritableData(file));
1634
1163
  }
1164
+ }
1165
+ async function writeUSDInput(pxr, directory, input, options) {
1166
+ await writeUSDFiles(pxr, directory, options.files);
1635
1167
  const path = joinFsPath(directory, sanitizeFileName(options.fileName));
1636
1168
  pxr.FS.writeFile(path, input);
1637
1169
  return path;
@@ -1714,15 +1246,19 @@ var USDLoader = class extends Loader {
1714
1246
  const extension = extensionForPath(mergedOptions.fileName);
1715
1247
  const writableData = await sourceToWritableData(input);
1716
1248
  const packageTextureResolver = extension === "usdz" && writableData instanceof Uint8Array ? createPackageTextureResolver(writableData) : null;
1717
- let autoFiles = writableData instanceof Uint8Array && extension !== "usdz" ? await autoResolveAssetFiles(writableData, mergedOptions) : {};
1718
- const filePath = await writeUSDInput(pxr, writableData, {
1719
- ...mergedOptions,
1720
- files: {
1721
- ...autoFiles,
1722
- ...mergedOptions.files
1723
- }
1249
+ const packageRootLayer = extension === "usdz" && writableData instanceof Uint8Array ? mergedOptions.usdzLayer ?? findPackageRootLayer(writableData) ?? void 0 : mergedOptions.usdzLayer;
1250
+ const rootFileName = sanitizeFileName(mergedOptions.fileName);
1251
+ const directory = createUSDInputDirectory(pxr, mergedOptions.workingDirectory);
1252
+ const filePath = await writeUSDInput(pxr, directory, writableData, {
1253
+ fileName: rootFileName,
1254
+ files: mergedOptions.files
1724
1255
  });
1725
- const { stage, identifier } = openStage(pxr, filePath, extension, mergedOptions.usdzLayer);
1256
+ let autoFiles = extension !== "usdz" ? await autoResolveAssetFiles(pxr, filePath, {
1257
+ ...mergedOptions,
1258
+ fileName: rootFileName
1259
+ }) : {};
1260
+ await writeUSDFiles(pxr, directory, autoFiles);
1261
+ const { stage, identifier } = openStage(pxr, filePath, extension, packageRootLayer);
1726
1262
  try {
1727
1263
  const metadata = extractUSDModelData(pxr, stage, {
1728
1264
  sourcePath: mergedOptions.sourcePath,
@@ -1778,7 +1314,7 @@ function limitsForJoint(joint) {
1778
1314
  }
1779
1315
  function initialValueForJoint(joint) {
1780
1316
  const { lower, upper } = limitsForJoint(joint);
1781
- return clamp(joint.drive?.targetPosition ?? lower, lower, upper);
1317
+ return clamp(joint.drive?.targetPosition ?? 0, lower, upper);
1782
1318
  }
1783
1319
  function isManipulableJoint(joint) {
1784
1320
  return joint.enabled && (joint.jointType === "revolute" || joint.jointType === "prismatic");
@@ -1844,7 +1380,12 @@ function jointValueKey(model, joint) {
1844
1380
  return `${model.root.uuid}:${joint.path}`;
1845
1381
  }
1846
1382
  function localAxisForJoint(joint) {
1847
- return axisVectors[joint.axis ?? ""]?.clone() ?? axisVectors.X.clone();
1383
+ const axis = axisVectors[joint.axis ?? ""]?.clone() ?? axisVectors.X.clone();
1384
+ const rotation = joint.localRot0;
1385
+ if (!rotation) return axis;
1386
+ const quaternion = new THREE.Quaternion(rotation[1] ?? 0, rotation[2] ?? 0, rotation[3] ?? 0, rotation[0] ?? 1);
1387
+ if (quaternion.lengthSq() < 1e-10) return axis;
1388
+ return axis.applyQuaternion(quaternion.normalize());
1848
1389
  }
1849
1390
  function localPivotForJoint(joint) {
1850
1391
  return new THREE.Vector3(joint.localPos0[0] ?? 0, joint.localPos0[1] ?? 0, joint.localPos0[2] ?? 0);
@@ -2204,4 +1745,4 @@ var USDManipulationControls = class {
2204
1745
  }
2205
1746
  };
2206
1747
  //#endregion
2207
- export { USDLoader, USDManipulationControls, autoResolveAssetFiles, autoResolveTextureFiles, buildUSDObject, createPackageTextureResolver, createTextureResolverFromEntries, createUSDLoadedModel, extractUSDModelData };
1748
+ export { USDLoader, USDManipulationControls, buildUSDObject, createUSDLoadedModel, extractUSDModelData };