@openusd-wasm/three-loader 0.0.2 → 0.0.4

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]();
772
146
  try {
773
- if (!isValid$1(relationship)) return [];
774
- const targets = relationship.GetTargets();
775
- return Array.isArray(targets) ? targets.map(String) : [];
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]();
154
+ try {
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,38 @@ 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();
210
+ if (!Array.isArray(computed)) return null;
211
+ const [material, bindingRel] = computed;
212
+ try {
213
+ if (isValid$1(bindingRel)) {
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
+ }
222
+ if (!material || !isValid$1(material)) return null;
223
+ const materialPrim = material.GetPrim();
224
+ try {
225
+ return isValid$1(materialPrim) ? String(materialPrim.GetPath()) : null;
226
+ } finally {
227
+ dispose$1(materialPrim);
228
+ }
229
+ } finally {
230
+ disposeAll(computed);
808
231
  }
809
- return null;
810
232
  } finally {
811
- if (ownsCurrent) dispose$1(current);
233
+ dispose$1(binding);
812
234
  }
813
235
  }
814
- function localTransformForPrim(pxr, prim) {
236
+ function localTransformForPrim(pxr, prim, time) {
815
237
  if (!prim.IsA("Xformable")) return {
816
238
  localMatrix: null,
817
239
  resetsXformStack: false
@@ -822,7 +244,7 @@ function localTransformForPrim(pxr, prim) {
822
244
  localMatrix: null,
823
245
  resetsXformStack: false
824
246
  };
825
- const transform = xformable.GetLocalTransformation();
247
+ const transform = xformable.GetLocalTransformation(time);
826
248
  return {
827
249
  localMatrix: toNumberArray(transform?.matrix),
828
250
  resetsXformStack: Boolean(transform?.resetsXformStack)
@@ -842,7 +264,7 @@ function parentPathForPrim(prim) {
842
264
  }
843
265
  function getModelView(pxr, prims, time) {
844
266
  return { prims: prims.map((prim) => {
845
- const transform = localTransformForPrim(pxr, prim);
267
+ const transform = localTransformForPrim(pxr, prim, time);
846
268
  return {
847
269
  path: String(prim.GetPath()),
848
270
  parentPath: parentPathForPrim(prim),
@@ -891,13 +313,27 @@ function asFloat(value, fallback = 0) {
891
313
  const number = asFiniteNumber(value);
892
314
  return number === null ? fallback : number;
893
315
  }
316
+ function shaderId(shader) {
317
+ const id = shader.GetShaderId();
318
+ return typeof id === "string" && id ? id : null;
319
+ }
320
+ function shaderInputValue(shader, inputName, time, defaultValue = null) {
321
+ const input = shader.GetInput(inputName);
322
+ try {
323
+ if (!isValid$1(input)) return defaultValue;
324
+ const value = input.Get(time);
325
+ return value === null || value === void 0 ? defaultValue : normalizeValue(value);
326
+ } finally {
327
+ dispose$1(input);
328
+ }
329
+ }
894
330
  function shaderInputConnectionPath(shader, inputName) {
895
- const attribute = shader.GetAttribute(inputName);
331
+ const input = shader.GetInput(inputName);
896
332
  try {
897
- if (!isValid$1(attribute)) return null;
898
- return connectionSourcePath(attribute);
333
+ if (!isValid$1(input)) return null;
334
+ return inputSourcePrimPath(input);
899
335
  } finally {
900
- dispose$1(attribute);
336
+ dispose$1(input);
901
337
  }
902
338
  }
903
339
  function shaderChildByPath(material, path) {
@@ -910,29 +346,41 @@ function shaderChildByPath(material, path) {
910
346
  }
911
347
  }
912
348
  function primvarNameForTextureShader(pxr, stage, material, textureShader, time) {
913
- const stSourcePath = shaderInputConnectionPath(textureShader, "inputs:st");
349
+ const stSourcePath = shaderInputConnectionPath(textureShader, "st");
914
350
  if (!stSourcePath) return null;
915
351
  const stSource = shaderChildByPath(material, stSourcePath) ?? primAtPath(pxr, stage, stSourcePath);
916
352
  try {
917
353
  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);
354
+ const stShader = new pxr.UsdShade.Shader(stSource);
355
+ try {
356
+ if (!isValid$1(stShader)) return null;
357
+ const id = shaderId(stShader);
358
+ if (id === "UsdPrimvarReader_float2") {
359
+ const varname = shaderInputValue(stShader, "varname", time);
930
360
  return typeof varname === "string" && varname ? varname : null;
931
- } finally {
932
- dispose$1(nestedSource);
933
361
  }
362
+ if (id === "UsdTransform2d") {
363
+ const nestedSourcePath = shaderInputConnectionPath(stShader, "in") ?? shaderInputConnectionPath(stShader, "st");
364
+ if (!nestedSourcePath) return null;
365
+ const nestedSource = shaderChildByPath(material, nestedSourcePath) ?? primAtPath(pxr, stage, nestedSourcePath);
366
+ try {
367
+ if (!isValid$1(nestedSource)) return null;
368
+ const nestedShader = new pxr.UsdShade.Shader(nestedSource);
369
+ try {
370
+ if (!isValid$1(nestedShader) || shaderId(nestedShader) !== "UsdPrimvarReader_float2") return null;
371
+ const varname = shaderInputValue(nestedShader, "varname", time);
372
+ return typeof varname === "string" && varname ? varname : null;
373
+ } finally {
374
+ dispose$1(nestedShader);
375
+ }
376
+ } finally {
377
+ dispose$1(nestedSource);
378
+ }
379
+ }
380
+ return null;
381
+ } finally {
382
+ dispose$1(stShader);
934
383
  }
935
- return null;
936
384
  } finally {
937
385
  dispose$1(stSource);
938
386
  }
@@ -944,10 +392,16 @@ function texturePrimvarForMaterial(pxr, stage, materialPath, time) {
944
392
  if (!isValid$1(material)) return "primvars:st";
945
393
  const children = primChildren(material);
946
394
  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}`;
395
+ for (const child of children) {
396
+ if (child.GetTypeName() !== "Shader") continue;
397
+ const shader = new pxr.UsdShade.Shader(child);
398
+ try {
399
+ if (!isValid$1(shader) || shaderId(shader) !== "UsdUVTexture") continue;
400
+ const varname = primvarNameForTextureShader(pxr, stage, material, shader, time);
401
+ if (varname) return `primvars:${varname}`;
402
+ } finally {
403
+ dispose$1(shader);
404
+ }
951
405
  }
952
406
  } finally {
953
407
  disposeAll(children);
@@ -964,30 +418,36 @@ function textureTransformForMaterial(pxr, stage, materialPath, time) {
964
418
  if (!isValid$1(material)) return null;
965
419
  const children = primChildren(material);
966
420
  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");
421
+ for (const child of children) {
422
+ if (child.GetTypeName() !== "Shader") continue;
423
+ const shader = new pxr.UsdShade.Shader(child);
970
424
  try {
971
- if (!isValid$1(stAttribute)) continue;
972
- const sourcePath = connectionSourcePath(stAttribute);
425
+ if (!isValid$1(shader) || shaderId(shader) !== "UsdUVTexture") continue;
426
+ const sourcePath = shaderInputConnectionPath(shader, "st");
973
427
  if (!sourcePath) return null;
974
428
  const transform = primAtPath(pxr, stage, sourcePath);
975
429
  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
- };
430
+ if (!isValid$1(transform)) return null;
431
+ const transformShader = new pxr.UsdShade.Shader(transform);
432
+ try {
433
+ if (!isValid$1(transformShader) || shaderId(transformShader) !== "UsdTransform2d") return null;
434
+ const scale = asVec2(shaderInputValue(transformShader, "scale", time), [1, 1]);
435
+ const translation = asVec2(shaderInputValue(transformShader, "translation", time), [0, 0]);
436
+ return {
437
+ scaleX: scale[0],
438
+ scaleY: scale[1],
439
+ translateX: translation[0],
440
+ translateY: translation[1],
441
+ rotation: asFloat(shaderInputValue(transformShader, "rotation", time))
442
+ };
443
+ } finally {
444
+ dispose$1(transformShader);
445
+ }
986
446
  } finally {
987
447
  dispose$1(transform);
988
448
  }
989
449
  } finally {
990
- dispose$1(stAttribute);
450
+ dispose$1(shader);
991
451
  }
992
452
  }
993
453
  } finally {
@@ -1023,7 +483,7 @@ function triangulateMesh(pxr, stage, prim, time) {
1023
483
  const faceIndices = toNumberArray(attr(prim, "faceVertexIndices", time, []));
1024
484
  const normals = toArray(attr(prim, "normals", time, []));
1025
485
  const normalInterpolation = String(attrMetadata(prim, "normals", "interpolation", attr(prim, "normals:interpolation", time, "")) || "") || null;
1026
- const boundMaterial = materialBinding(prim);
486
+ const boundMaterial = materialBinding(pxr, prim);
1027
487
  const uv = meshPrimvar(prim, texturePrimvarForMaterial(pxr, stage, boundMaterial, time), time);
1028
488
  const colors = toArray(attr(prim, "primvars:displayColor", time, []));
1029
489
  const opacities = toArray(attr(prim, "primvars:displayOpacity", time, []));
@@ -1082,177 +542,244 @@ function getModelElements(pxr, stage, prims, time) {
1082
542
  elements.push({
1083
543
  path: String(prim.GetPath()),
1084
544
  doubleSided: Boolean(attr(prim, "doubleSided", time, false)),
1085
- material: materialBinding(prim),
545
+ material: materialBinding(pxr, prim),
1086
546
  geometry: triangulateMesh(pxr, stage, prim, time)
1087
547
  });
1088
548
  }
1089
549
  return elements;
1090
550
  }
1091
- function collectShaderInputs(prim, time) {
551
+ function collectShadeInputs(connectable, time) {
1092
552
  const inputs = {};
1093
- const attributes = prim.GetAttributes();
553
+ const shadeInputs = connectable.GetInputs();
1094
554
  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();
555
+ for (const input of shadeInputs) {
556
+ const baseName = String(input.GetBaseName());
557
+ if (!MATERIAL_INPUT_SUFFIXES.some((suffix) => baseName.endsWith(suffix))) continue;
558
+ const name = String(input.GetFullName());
559
+ const value = input.Get(time);
560
+ const connections = input.GetRawConnectedSourcePaths();
1100
561
  if (value !== null && value !== void 0) inputs[name] = normalizeValue(value);
1101
- if (Array.isArray(connections) && connections.length > 0) inputs[`${name}.connect`] = connections.map(String);
562
+ if (Array.isArray(connections)) try {
563
+ if (connections.length > 0) inputs[`${name}.connect`] = connections.map(pathToString);
564
+ } finally {
565
+ disposeAll(connections);
566
+ }
1102
567
  }
1103
568
  } finally {
1104
- disposeAll(attributes);
569
+ disposeAll(shadeInputs);
1105
570
  }
1106
571
  return inputs;
1107
572
  }
1108
- function getModelMaterials(prims, time) {
573
+ function collectShaderSourceAssets(shader) {
574
+ const inputs = {};
575
+ const sourceTypes = shader.GetSourceTypes();
576
+ if (!Array.isArray(sourceTypes)) return inputs;
577
+ for (const sourceType of sourceTypes) {
578
+ const sourceTypeName = String(sourceType);
579
+ const sourceAsset = shader.GetSourceAsset(sourceTypeName);
580
+ try {
581
+ const value = assetPathValue(sourceAsset);
582
+ if (!value) continue;
583
+ const name = sourceTypeName ? `info:${sourceTypeName}:sourceAsset` : "info:sourceAsset";
584
+ inputs[name] = value;
585
+ } finally {
586
+ dispose$1(sourceAsset);
587
+ }
588
+ }
589
+ return inputs;
590
+ }
591
+ function getModelMaterials(pxr, prims, time) {
1109
592
  const materials = [];
1110
593
  for (const prim of prims) {
1111
594
  if (prim.GetTypeName() !== "Material") continue;
595
+ const material = new pxr.UsdShade.Material(prim);
1112
596
  const children = primChildren(prim);
1113
597
  const shaders = [];
1114
598
  try {
599
+ if (!isValid$1(material)) continue;
1115
600
  for (const child of children) {
1116
601
  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
- });
602
+ const shader = new pxr.UsdShade.Shader(child);
603
+ try {
604
+ if (!isValid$1(shader)) continue;
605
+ const idValue = shader.GetShaderId();
606
+ const id = typeof idValue === "string" && idValue ? idValue : null;
607
+ const inputs = {
608
+ ...collectShadeInputs(shader, time),
609
+ ...collectShaderSourceAssets(shader)
610
+ };
611
+ if (Object.keys(inputs).length === 0 && id !== "UsdPreviewSurface" && id !== "UsdUVTexture") continue;
612
+ shaders.push({
613
+ path: String(child.GetPath()),
614
+ id,
615
+ inputs
616
+ });
617
+ } finally {
618
+ dispose$1(shader);
619
+ }
1126
620
  }
621
+ materials.push({
622
+ path: String(prim.GetPath()),
623
+ inputs: collectShadeInputs(material, time),
624
+ shaders
625
+ });
1127
626
  } finally {
1128
627
  disposeAll(children);
628
+ dispose$1(material);
1129
629
  }
1130
- materials.push({
1131
- path: String(prim.GetPath()),
1132
- inputs: collectShaderInputs(prim, time),
1133
- shaders
1134
- });
1135
630
  }
1136
631
  return materials;
1137
632
  }
1138
- function driveForNamespace(prim, namespace, time) {
633
+ function driveForNamespace(pxr, prim, namespace, time) {
634
+ const driveAPI = new pxr.UsdPhysics.DriveAPI(prim, namespace);
1139
635
  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))
636
+ targetPosition: null,
637
+ targetVelocity: null,
638
+ stiffness: null,
639
+ damping: null,
640
+ maxForce: null
1145
641
  };
1146
- return Object.values(drive).some((value) => value !== null) ? drive : null;
642
+ try {
643
+ if (!isValid$1(driveAPI)) return null;
644
+ drive.targetPosition = asFiniteNumber(schemaAttributeValue(driveAPI, "GetTargetPositionAttr", time, null, true));
645
+ drive.targetVelocity = asFiniteNumber(schemaAttributeValue(driveAPI, "GetTargetVelocityAttr", time, null, true));
646
+ drive.stiffness = asFiniteNumber(schemaAttributeValue(driveAPI, "GetStiffnessAttr", time, null, true));
647
+ drive.damping = asFiniteNumber(schemaAttributeValue(driveAPI, "GetDampingAttr", time, null, true));
648
+ drive.maxForce = asFiniteNumber(schemaAttributeValue(driveAPI, "GetMaxForceAttr", time, null, true));
649
+ return Object.values(drive).some((value) => value !== null) ? drive : null;
650
+ } finally {
651
+ dispose$1(driveAPI);
652
+ }
1147
653
  }
1148
654
  function jointTypeForPrim(typeName) {
1149
655
  if (JOINT_TYPES[typeName]) return JOINT_TYPES[typeName];
1150
656
  if (typeName.startsWith("Physics") && typeName.endsWith("Joint")) return typeName.slice(7, -5).toLowerCase() || "joint";
1151
657
  return null;
1152
658
  }
1153
- function getModelJoints(prims, time) {
659
+ function jointLimitSchema(pxr, prim, typeName) {
660
+ switch (typeName) {
661
+ case "PhysicsRevoluteJoint": return new pxr.UsdPhysics.RevoluteJoint(prim);
662
+ case "PhysicsPrismaticJoint": return new pxr.UsdPhysics.PrismaticJoint(prim);
663
+ case "PhysicsSphericalJoint": return new pxr.UsdPhysics.SphericalJoint(prim);
664
+ case "PhysicsDistanceJoint": return new pxr.UsdPhysics.DistanceJoint(prim);
665
+ default: return null;
666
+ }
667
+ }
668
+ function jointAxisAndLimits(pxr, prim, typeName, time) {
669
+ const schema = jointLimitSchema(pxr, prim, typeName);
670
+ if (!schema) return {
671
+ axis: null,
672
+ lowerLimit: null,
673
+ upperLimit: null
674
+ };
675
+ try {
676
+ if (!isValid$1(schema)) return {
677
+ axis: null,
678
+ lowerLimit: null,
679
+ upperLimit: null
680
+ };
681
+ if (typeName === "PhysicsSphericalJoint") {
682
+ const axis = schemaAttributeValue(schema, "GetAxisAttr", time);
683
+ return {
684
+ axis: typeof axis === "string" ? axis : null,
685
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetConeAngle0LimitAttr", time, null, true)),
686
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetConeAngle1LimitAttr", time, null, true))
687
+ };
688
+ }
689
+ if (typeName === "PhysicsDistanceJoint") return {
690
+ axis: null,
691
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetMinDistanceAttr", time, null, true)),
692
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetMaxDistanceAttr", time, null, true))
693
+ };
694
+ const axis = schemaAttributeValue(schema, "GetAxisAttr", time);
695
+ return {
696
+ axis: typeof axis === "string" ? axis : null,
697
+ lowerLimit: asFiniteNumber(schemaAttributeValue(schema, "GetLowerLimitAttr", time)),
698
+ upperLimit: asFiniteNumber(schemaAttributeValue(schema, "GetUpperLimitAttr", time))
699
+ };
700
+ } finally {
701
+ dispose$1(schema);
702
+ }
703
+ }
704
+ function getModelJoints(pxr, prims, time) {
1154
705
  const joints = [];
1155
706
  for (const prim of prims) {
1156
707
  const typeName = String(prim.GetTypeName());
1157
708
  const jointType = jointTypeForPrim(typeName);
1158
709
  if (!jointType) continue;
1159
- const angularDrive = driveForNamespace(prim, "angular", time);
1160
- const linearDrive = driveForNamespace(prim, "linear", time);
710
+ const joint = new pxr.UsdPhysics.Joint(prim);
711
+ if (!isValid$1(joint)) {
712
+ dispose$1(joint);
713
+ continue;
714
+ }
715
+ const angularDrive = driveForNamespace(pxr, prim, "angular", time);
716
+ const linearDrive = driveForNamespace(pxr, prim, "linear", time);
1161
717
  const drives = {};
1162
718
  if (angularDrive) drives.angular = angularDrive;
1163
719
  if (linearDrive) drives.linear = linearDrive;
1164
- const body0 = relationshipTargets(prim, "physics:body0");
1165
- const body1 = relationshipTargets(prim, "physics:body1");
720
+ const body0 = schemaRelationshipTargets(joint, "GetBody0Rel");
721
+ const body1 = schemaRelationshipTargets(joint, "GetBody1Rel");
1166
722
  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
- });
723
+ const axisAndLimits = jointAxisAndLimits(pxr, prim, typeName, time);
724
+ try {
725
+ joints.push({
726
+ path: String(prim.GetPath()),
727
+ name: String(prim.GetName()),
728
+ typeName,
729
+ jointType,
730
+ body0: body0[0] ?? null,
731
+ body1: body1[0] ?? null,
732
+ ...axisAndLimits,
733
+ localPos0: toVector3(schemaAttributeValue(joint, "GetLocalPos0Attr", time, [
734
+ 0,
735
+ 0,
736
+ 0
737
+ ]), [
738
+ 0,
739
+ 0,
740
+ 0
741
+ ]),
742
+ localPos1: toVector3(schemaAttributeValue(joint, "GetLocalPos1Attr", time, [
743
+ 0,
744
+ 0,
745
+ 0
746
+ ]), [
747
+ 0,
748
+ 0,
749
+ 0
750
+ ]),
751
+ localRot0: toVector4(schemaAttributeValue(joint, "GetLocalRot0Attr", time)),
752
+ localRot1: toVector4(schemaAttributeValue(joint, "GetLocalRot1Attr", time)),
753
+ enabled: Boolean(schemaAttributeValue(joint, "GetJointEnabledAttr", time, true)),
754
+ drive,
755
+ drives
756
+ });
757
+ } finally {
758
+ dispose$1(joint);
759
+ }
1201
760
  }
1202
761
  return joints;
1203
762
  }
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
763
  function getStageInfo(pxr, stage, options) {
1219
764
  const defaultPrim = stage.GetDefaultPrim();
1220
- const rootLayer = stage.GetRootLayer();
1221
765
  try {
1222
- const timing = stageTimingFromLayer(rootLayer);
1223
766
  return {
1224
767
  sourcePath: options.sourcePath,
1225
768
  rootLayerIdentifier: options.rootLayerIdentifier,
1226
769
  defaultPrim: isValid$1(defaultPrim) ? String(defaultPrim.GetPath()) : null,
1227
770
  upAxis: String(pxr.UsdGeom.GetStageUpAxis(stage)),
1228
- ...timing
771
+ startTimeCode: stage.GetStartTimeCode(),
772
+ endTimeCode: stage.GetEndTimeCode(),
773
+ timeCodesPerSecond: stage.GetTimeCodesPerSecond()
1229
774
  };
1230
775
  } finally {
1231
- dispose$1(rootLayer);
1232
776
  dispose$1(defaultPrim);
1233
777
  }
1234
778
  }
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
779
  function sampleLocalMatrix(pxr, prim, frame) {
1253
780
  const time = new pxr.Usd.TimeCode(frame);
1254
781
  try {
1255
- return localMatrixFromXformOps(prim, time);
782
+ return localTransformForPrim(pxr, prim, time).localMatrix;
1256
783
  } finally {
1257
784
  dispose$1(time);
1258
785
  }
@@ -1269,10 +796,10 @@ function animationSampleFrames(start, end) {
1269
796
  });
1270
797
  }
1271
798
  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 [];
799
+ const start = stageInfo.startTimeCode;
800
+ const end = stageInfo.endTimeCode;
801
+ const timeCodesPerSecond = stageInfo.timeCodesPerSecond;
802
+ if (end <= start) return [];
1276
803
  const frames = animationSampleFrames(start, end);
1277
804
  if (frames.length < 2) return [];
1278
805
  const transforms = [];
@@ -1319,8 +846,8 @@ function extractUSDModelData(pxr, stage, options = {}) {
1319
846
  stage: stageInfo,
1320
847
  view: getModelView(pxr, prims, time),
1321
848
  elements: getModelElements(pxr, stage, prims, time),
1322
- materials: getModelMaterials(prims, time),
1323
- joints: getModelJoints(prims, time),
849
+ materials: getModelMaterials(pxr, prims, time),
850
+ joints: getModelJoints(pxr, prims, time),
1324
851
  animations: getModelAnimations(pxr, prims, stageInfo)
1325
852
  };
1326
853
  } finally {
@@ -1393,7 +920,7 @@ function shaderPathFromConnection(value) {
1393
920
  function findTexture(material, options, connectionSuffixes, fallbackToAnyTexture = false) {
1394
921
  if (!material) return null;
1395
922
  const connectedPath = shaderPathFromConnection(firstInput(material, connectionSuffixes));
1396
- const textureShader = material.shaders.find((shader) => shader.id === "UsdUVTexture" && (!connectedPath || shader.path === connectedPath)) ?? (fallbackToAnyTexture ? material.shaders.find((shader) => shader.id === "UsdUVTexture") : null);
923
+ const textureShader = connectedPath ? material.shaders.find((shader) => shader.id === "UsdUVTexture" && shader.path === connectedPath) : fallbackToAnyTexture ? material.shaders.find((shader) => shader.id === "UsdUVTexture") : null;
1397
924
  if (!textureShader) return null;
1398
925
  const url = publicUrlFromAsset(textureShader.inputs["inputs:file"], options, material, textureShader);
1399
926
  if (!url) return null;
@@ -1625,13 +1152,19 @@ function deleteFileIfExists(pxr, path) {
1625
1152
  if (pxr.FS.exists(path)) pxr.FS.deleteFile(path);
1626
1153
  } catch {}
1627
1154
  }
1628
- async function writeUSDInput(pxr, input, options) {
1629
- const directory = joinFsPath(options.workingDirectory, String(++nextLoadId));
1155
+ function createUSDInputDirectory(pxr, workingDirectory) {
1156
+ const directory = joinFsPath(workingDirectory, String(++nextLoadId));
1630
1157
  pxr.FS.createDir(directory);
1631
- for (const [relativePath, file] of Object.entries(options.files ?? {})) {
1158
+ return directory;
1159
+ }
1160
+ async function writeUSDFiles(pxr, directory, files) {
1161
+ for (const [relativePath, file] of Object.entries(files ?? {})) {
1632
1162
  const filePath = joinFsPath(directory, relativePath);
1633
1163
  pxr.FS.writeFile(filePath, await sourceToWritableData(file));
1634
1164
  }
1165
+ }
1166
+ async function writeUSDInput(pxr, directory, input, options) {
1167
+ await writeUSDFiles(pxr, directory, options.files);
1635
1168
  const path = joinFsPath(directory, sanitizeFileName(options.fileName));
1636
1169
  pxr.FS.writeFile(path, input);
1637
1170
  return path;
@@ -1714,15 +1247,19 @@ var USDLoader = class extends Loader {
1714
1247
  const extension = extensionForPath(mergedOptions.fileName);
1715
1248
  const writableData = await sourceToWritableData(input);
1716
1249
  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
- }
1250
+ const packageRootLayer = extension === "usdz" && writableData instanceof Uint8Array ? mergedOptions.usdzLayer ?? findPackageRootLayer(writableData) ?? void 0 : mergedOptions.usdzLayer;
1251
+ const rootFileName = sanitizeFileName(mergedOptions.fileName);
1252
+ const directory = createUSDInputDirectory(pxr, mergedOptions.workingDirectory);
1253
+ const filePath = await writeUSDInput(pxr, directory, writableData, {
1254
+ fileName: rootFileName,
1255
+ files: mergedOptions.files
1724
1256
  });
1725
- const { stage, identifier } = openStage(pxr, filePath, extension, mergedOptions.usdzLayer);
1257
+ let autoFiles = extension !== "usdz" ? await autoResolveAssetFiles(pxr, filePath, {
1258
+ ...mergedOptions,
1259
+ fileName: rootFileName
1260
+ }) : {};
1261
+ await writeUSDFiles(pxr, directory, autoFiles);
1262
+ const { stage, identifier } = openStage(pxr, filePath, extension, packageRootLayer);
1726
1263
  try {
1727
1264
  const metadata = extractUSDModelData(pxr, stage, {
1728
1265
  sourcePath: mergedOptions.sourcePath,
@@ -1778,7 +1315,7 @@ function limitsForJoint(joint) {
1778
1315
  }
1779
1316
  function initialValueForJoint(joint) {
1780
1317
  const { lower, upper } = limitsForJoint(joint);
1781
- return clamp(joint.drive?.targetPosition ?? lower, lower, upper);
1318
+ return clamp(joint.drive?.targetPosition ?? 0, lower, upper);
1782
1319
  }
1783
1320
  function isManipulableJoint(joint) {
1784
1321
  return joint.enabled && (joint.jointType === "revolute" || joint.jointType === "prismatic");
@@ -1844,7 +1381,12 @@ function jointValueKey(model, joint) {
1844
1381
  return `${model.root.uuid}:${joint.path}`;
1845
1382
  }
1846
1383
  function localAxisForJoint(joint) {
1847
- return axisVectors[joint.axis ?? ""]?.clone() ?? axisVectors.X.clone();
1384
+ const axis = axisVectors[joint.axis ?? ""]?.clone() ?? axisVectors.X.clone();
1385
+ const rotation = joint.localRot0;
1386
+ if (!rotation) return axis;
1387
+ const quaternion = new THREE.Quaternion(rotation[1] ?? 0, rotation[2] ?? 0, rotation[3] ?? 0, rotation[0] ?? 1);
1388
+ if (quaternion.lengthSq() < 1e-10) return axis;
1389
+ return axis.applyQuaternion(quaternion.normalize());
1848
1390
  }
1849
1391
  function localPivotForJoint(joint) {
1850
1392
  return new THREE.Vector3(joint.localPos0[0] ?? 0, joint.localPos0[1] ?? 0, joint.localPos0[2] ?? 0);
@@ -2204,4 +1746,4 @@ var USDManipulationControls = class {
2204
1746
  }
2205
1747
  };
2206
1748
  //#endregion
2207
- export { USDLoader, USDManipulationControls, autoResolveAssetFiles, autoResolveTextureFiles, buildUSDObject, createPackageTextureResolver, createTextureResolverFromEntries, createUSDLoadedModel, extractUSDModelData };
1749
+ export { USDLoader, USDManipulationControls, buildUSDObject, createUSDLoadedModel, extractUSDModelData };