@openusd-wasm/three-loader 0.0.1 → 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,447 +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
- ];
18
- const DEFAULT_ASSET_SEARCH_ROOTS = [
19
- "resource",
20
- "resources",
21
- "asset",
22
- "assets",
23
- "img",
24
- "image",
25
- "images",
26
- "texture",
27
- "textures",
28
- "material",
29
- "materials"
30
- ];
31
- const DEFAULT_MAX_ASSET_REFERENCES = 80;
32
- const DEFAULT_MAX_ASSET_REFERENCE_DEPTH = 4;
33
- function toFetchableUrl(value) {
34
- try {
35
- const url = new URL(value, globalThis.location?.href);
36
- return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "file:" ? url.href : null;
37
- } catch {
38
- return null;
39
- }
40
- }
41
- function normalizeFsRelativePath(path) {
42
- const parts = [];
43
- for (const part of path.replaceAll("\\", "/").split("/")) {
44
- if (!part || part === ".") continue;
45
- if (part === "..") {
46
- parts.pop();
47
- continue;
48
- }
49
- parts.push(part);
50
- }
51
- return parts.join("/");
52
- }
53
- function dirname(path) {
54
- const normalized = normalizeFsRelativePath(path);
55
- const index = normalized.lastIndexOf("/");
56
- return index === -1 ? "" : normalized.slice(0, index);
57
- }
58
- function joinRelativePath(base, path) {
59
- return normalizeFsRelativePath(base ? `${base}/${path}` : path);
60
- }
61
- function extensionOf(path) {
62
- const match = /\.([A-Za-z0-9]+)$/.exec(path.split(/[?#]/)[0] ?? path);
63
- return match ? `.${match[1]?.toLowerCase()}` : "";
64
- }
65
- function contentTypeForPath(path) {
66
- switch (extensionOf(path)) {
67
- case ".png": return "image/png";
68
- case ".jpg":
69
- case ".jpeg": return "image/jpeg";
70
- case ".webp": return "image/webp";
71
- default: return "application/octet-stream";
72
- }
73
- }
74
- function baseName(path) {
75
- return normalizeFsRelativePath(path).split("/").pop() ?? "";
76
- }
77
- function fileNameForSource$1(sourcePath) {
78
- const name = (sourcePath.split(/[?#]/)[0] ?? sourcePath).split("/").filter(Boolean).pop();
79
- return name && /\.[a-z0-9]+$/i.test(name) ? name : "scene.usda";
80
- }
81
- function toArrayBuffer(data) {
82
- const buffer = new ArrayBuffer(data.byteLength);
83
- new Uint8Array(buffer).set(data);
84
- return buffer;
85
- }
86
- function trimAssetReference(value) {
87
- let ref = value.trim();
88
- if (!ref || ref.length > 220) return null;
89
- ref = ref.replace(/^[@<"']+/, "").replace(/[@>"')\]},;]+$/g, "");
90
- if (!ref || ref.startsWith("#") || ref.startsWith("$")) return null;
91
- if (/^[A-Za-z]:[\\/]/.test(ref)) return null;
92
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(ref)) return null;
93
- if (ref.startsWith("/")) return null;
94
- if (ref.includes("\0") || /\s/.test(ref)) return null;
95
- if (!/^[A-Za-z0-9._~!$&'()+,;=@/-]+$/.test(ref)) return null;
96
- return ref;
97
- }
98
- function isUsdCrate(data) {
99
- 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;
100
- }
101
- function startsWithAssetSearchRoot(value) {
102
- return /^(resource|resources|asset|assets|img|image|images|texture|textures|material|materials)(\/|$)/i.test(value);
103
- }
104
- function startsWithAssetSearchRootPath(value) {
105
- const normalized = normalizeFsRelativePath(value);
106
- return normalized.includes("/") && startsWithAssetSearchRoot(normalized);
107
- }
108
- function looksLikeExtensionlessAssetPath(value) {
109
- const parts = normalizeFsRelativePath(value).split("/");
110
- if (parts.length < 2 || parts.length > 3) return false;
111
- if (parts.some((part) => part.length < 2 || part.length > 80)) return false;
112
- if (parts.some((part) => part.startsWith("."))) return false;
113
- if (parts.some((part) => !/^[A-Za-z0-9._-]+$/.test(part))) return false;
114
- if (!/_/.test(value) || !/\d/.test(value)) return false;
115
- return true;
116
- }
117
- function looksLikeKnownExtensionlessAsset(value) {
118
- const normalized = normalizeFsRelativePath(value);
119
- if (!normalized.includes("/")) return false;
120
- return [
121
- "material",
122
- "model",
123
- "scene"
124
- ].includes(baseName(normalized).toLowerCase());
125
- }
126
- function looksLikeAssetReference(value, searchExtensions, includeExtensionlessAssetPaths, wholeString) {
127
- const extension = extensionOf(value);
128
- if (extension) {
129
- if (!searchExtensions.includes(extension)) return false;
130
- const name = baseName(value);
131
- if (name === extension) return false;
132
- if (wholeString && !value.startsWith("./") && !value.startsWith("../") && !value.includes("/")) return name.length >= 8;
133
- return true;
134
- }
135
- if (value.startsWith("./") || value.startsWith("../")) {
136
- const relative = normalizeFsRelativePath(value);
137
- if (startsWithAssetSearchRootPath(relative)) return looksLikeExtensionlessAssetPath(relative) || looksLikeKnownExtensionlessAsset(relative);
138
- return looksLikeExtensionlessAssetPath(relative);
139
- }
140
- if (startsWithAssetSearchRootPath(value)) return looksLikeExtensionlessAssetPath(value) || looksLikeKnownExtensionlessAsset(value);
141
- return includeExtensionlessAssetPaths && looksLikeExtensionlessAssetPath(value);
142
- }
143
- function extractAsciiStrings(data) {
144
- const strings = [];
145
- let current = "";
146
- for (const byte of data) if (byte >= 32 && byte <= 126) current += String.fromCharCode(byte);
147
- else {
148
- if (current.length >= 4) strings.push(current);
149
- current = "";
150
- }
151
- if (current.length >= 4) strings.push(current);
152
- return strings;
153
- }
154
- function discoverAssetReferences(data, searchExtensions, includeExtensionlessAssetPaths = false) {
155
- const out = /* @__PURE__ */ new Set();
156
- for (const text of extractAsciiStrings(data)) {
157
- const whole = trimAssetReference(text);
158
- if (whole && looksLikeAssetReference(whole, searchExtensions, includeExtensionlessAssetPaths, true)) out.add(whole);
159
- for (const match of text.matchAll(/@([^@\n\r]+)@/g)) {
160
- const ref = trimAssetReference(match[1] ?? "");
161
- if (ref && looksLikeAssetReference(ref, searchExtensions, includeExtensionlessAssetPaths, false)) out.add(ref);
162
- }
163
- for (const match of text.matchAll(/["']([^"'\n\r]+\.(?:usd|usda|usdc|usdz|png|jpe?g|webp|ktx2|exr|hdr|mdl))["']/gi)) {
164
- const ref = trimAssetReference(match[1] ?? "");
165
- if (ref && looksLikeAssetReference(ref, searchExtensions, includeExtensionlessAssetPaths, false)) out.add(ref);
166
- }
167
- }
168
- return [...out];
169
- }
170
- function discoverAssetSearchRoots(data, searchRoots) {
171
- const out = /* @__PURE__ */ new Set();
172
- const rootSet = new Set(searchRoots);
173
- for (const text of extractAsciiStrings(data)) {
174
- const ref = trimAssetReference(text);
175
- if (!ref) continue;
176
- const segment = firstPathSegment(ref);
177
- if (rootSet.has(segment) && normalizeFsRelativePath(ref).includes("/")) out.add(segment);
178
- }
179
- return [...out];
180
- }
181
- function buildExtensionVariants(path, searchExtensions) {
182
- const normalized = normalizeFsRelativePath(path);
183
- if (!normalized) return [];
184
- if (extensionOf(normalized)) return [normalized];
185
- return [...searchExtensions.map((extension) => `${normalized}${extension}`), normalized];
186
- }
187
- function buildReferencePathVariants(relativePath) {
188
- const normalized = normalizeFsRelativePath(relativePath);
189
- if (!normalized) return [];
190
- return [{
191
- path: normalized,
192
- aliases: [normalized]
193
- }];
194
- }
195
- function unique(values) {
196
- return [...new Set(values)];
197
- }
198
- function uniqueFileCount(files) {
199
- return new Set(Object.values(files)).size;
200
- }
201
- function firstPathSegment(path) {
202
- return normalizeFsRelativePath(path).split("/")[0] ?? "";
203
- }
204
- function normalizeSearchRoots(roots) {
205
- return unique((roots ?? DEFAULT_ASSET_SEARCH_ROOTS).map((root) => normalizeFsRelativePath(root)).filter(Boolean));
206
- }
207
- function inferReferencedSearchRoots(refs, searchRoots) {
208
- const rootSet = new Set(searchRoots);
209
- return unique(refs.map((ref) => firstPathSegment(ref)).filter((segment) => rootSet.has(segment)));
210
- }
211
- function buildFetchAttempts(ref, sourceUrl, sourceRelativePath, searchExtensions, rootSourceUrl, searchRoots) {
212
- const sourceDir = dirname(sourceRelativePath);
213
- const attempts = [];
214
- const seen = /* @__PURE__ */ new Set();
215
- const normalizedRef = normalizeFsRelativePath(ref);
216
- const isDotRelative = /^\.{1,2}\//.test(ref);
217
- const candidates = [];
218
- function addCandidate(fsPath, fetchPath, baseUrl, aliases = []) {
219
- const normalizedFsPath = normalizeFsRelativePath(fsPath);
220
- if (!normalizedFsPath) return;
221
- candidates.push({
222
- fsPath: normalizedFsPath,
223
- fetchPath,
224
- baseUrl,
225
- aliases: unique([
226
- normalizedRef,
227
- normalizedFsPath,
228
- ...aliases
229
- ].filter(Boolean))
230
- });
231
- }
232
- if (!isDotRelative) {
233
- for (const root of searchRoots) if (normalizedRef !== root && !normalizedRef.startsWith(`${root}/`)) addCandidate(`${root}/${normalizedRef}`, `${root}/${normalizedRef}`, rootSourceUrl);
234
- }
235
- addCandidate(joinRelativePath(sourceDir, ref), ref, sourceUrl);
236
- if (!isDotRelative && sourceDir) addCandidate(normalizedRef, normalizedRef, rootSourceUrl);
237
- for (const candidate of candidates) for (const variant of buildReferencePathVariants(candidate.fsPath)) {
238
- const fsAliases = new Set([...candidate.aliases, ...variant.aliases]);
239
- for (const pathWithExtension of buildExtensionVariants(variant.path, searchExtensions)) {
240
- const extension = extensionOf(pathWithExtension);
241
- fsAliases.add(pathWithExtension);
242
- const fetchPath = extensionOf(candidate.fetchPath) ? candidate.fetchPath : `${candidate.fetchPath}${extension}`;
243
- try {
244
- const url = new URL(fetchPath, candidate.baseUrl).href;
245
- if (seen.has(url)) continue;
246
- seen.add(url);
247
- attempts.push({
248
- url,
249
- fsPaths: [...fsAliases]
250
- });
251
- } catch {}
252
- }
253
- }
254
- return attempts;
255
- }
256
- async function fetchBinary(url) {
257
- try {
258
- const response = await fetch(url);
259
- if (!response.ok) return null;
260
- return new Uint8Array(await response.arrayBuffer());
261
- } catch {
262
- return null;
263
- }
264
- }
265
- async function autoResolveAssetFiles(rootData, options) {
266
- const sourcePath = options.sourcePath ?? "";
267
- if (options.autoResolveAssets === false || !sourcePath) return {};
268
- const sourceUrl = toFetchableUrl(sourcePath);
269
- if (!sourceUrl) return {};
270
- const searchExtensions = options.assetSearchExtensions ?? DEFAULT_ASSET_SEARCH_EXTENSIONS;
271
- const configuredSearchRoots = normalizeSearchRoots(options.assetSearchRoots);
272
- const maxFiles = options.maxAssetReferences ?? DEFAULT_MAX_ASSET_REFERENCES;
273
- const maxDepth = options.maxAssetReferenceDepth ?? DEFAULT_MAX_ASSET_REFERENCE_DEPTH;
274
- const rootRelativePath = normalizeFsRelativePath(options.fileName ?? fileNameForSource$1(sourcePath));
275
- const files = {};
276
- const fetchedUrls = /* @__PURE__ */ new Set();
277
- const queued = /* @__PURE__ */ new Set();
278
- const rootRefs = discoverAssetReferences(rootData, searchExtensions, true);
279
- const inferredSearchRoots = unique([...inferReferencedSearchRoots(rootRefs, configuredSearchRoots), ...discoverAssetSearchRoots(rootData, configuredSearchRoots)]);
280
- const queue = rootRefs.map((ref) => ({
281
- ref,
282
- sourceUrl,
283
- rootSourceUrl: sourceUrl,
284
- sourceRelativePath: rootRelativePath,
285
- searchRoots: inferredSearchRoots,
286
- depth: 0
287
- }));
288
- for (const item of queue) queued.add(`${item.sourceUrl}\n${item.ref}`);
289
- while (queue.length > 0 && uniqueFileCount(files) < maxFiles) {
290
- const item = queue.shift();
291
- if (!item || item.depth > maxDepth) continue;
292
- for (const attempt of buildFetchAttempts(item.ref, item.sourceUrl, item.sourceRelativePath, searchExtensions, item.rootSourceUrl, item.searchRoots)) {
293
- if (fetchedUrls.has(attempt.url)) continue;
294
- fetchedUrls.add(attempt.url);
295
- const data = await fetchBinary(attempt.url);
296
- if (!data) continue;
297
- for (const fsPath of attempt.fsPaths) files[fsPath] = data;
298
- if (item.depth < maxDepth) {
299
- const sourceRelativePath = attempt.fsPaths.find((path) => extensionOf(path)) ?? attempt.fsPaths[0];
300
- if (sourceRelativePath) for (const ref of discoverAssetReferences(data, searchExtensions, !isUsdCrate(data))) {
301
- const queueKey = `${attempt.url}\n${ref}`;
302
- if (queued.has(queueKey)) continue;
303
- queued.add(queueKey);
304
- const nestedSearchRoots = unique([...item.searchRoots, ...inferReferencedSearchRoots([sourceRelativePath], configuredSearchRoots)]);
305
- queue.push({
306
- ref,
307
- sourceUrl: attempt.url,
308
- rootSourceUrl: item.rootSourceUrl,
309
- sourceRelativePath,
310
- searchRoots: nestedSearchRoots,
311
- depth: item.depth + 1
312
- });
313
- }
314
- }
315
- break;
316
- }
317
- }
318
- return files;
319
- }
320
- function isTextureAssetReference(value) {
321
- if (/^(blob|data):/i.test(value)) return false;
322
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(value)) return false;
323
- if (value.startsWith("/")) return false;
324
- return [
325
- ".png",
326
- ".jpg",
327
- ".jpeg",
328
- ".webp"
329
- ].includes(extensionOf(value));
330
- }
331
- function collectTextureAssetReferences(value, refs = /* @__PURE__ */ new Set()) {
332
- if (!value) return refs;
333
- if (Array.isArray(value)) {
334
- for (const item of value) collectTextureAssetReferences(item, refs);
335
- return refs;
336
- }
337
- if (typeof value !== "object") return refs;
338
- const asset = value;
339
- for (const item of [asset.path, asset.url]) if (typeof item === "string" && isTextureAssetReference(item)) refs.add(item);
340
- for (const item of Object.values(value)) collectTextureAssetReferences(item, refs);
341
- return refs;
342
- }
343
- async function autoResolveTextureFiles(metadata, existingFiles, options) {
344
- const sourcePath = options.sourcePath ?? "";
345
- if (options.autoResolveAssets === false || !sourcePath) return {};
346
- const sourceUrl = toFetchableUrl(sourcePath);
347
- if (!sourceUrl) return {};
348
- const searchExtensions = options.assetSearchExtensions ?? DEFAULT_ASSET_SEARCH_EXTENSIONS;
349
- const configuredSearchRoots = normalizeSearchRoots(options.assetSearchRoots);
350
- const textureRefs = [...collectTextureAssetReferences(metadata)];
351
- const searchRoots = unique([...inferReferencedSearchRoots(Object.keys(existingFiles), configuredSearchRoots), ...inferReferencedSearchRoots(textureRefs, configuredSearchRoots)]);
352
- const maxFiles = options.maxAssetReferences ?? DEFAULT_MAX_ASSET_REFERENCES;
353
- const remainingFiles = Math.max(maxFiles - uniqueFileCount(existingFiles), 0);
354
- if (remainingFiles === 0) return {};
355
- const rootRelativePath = normalizeFsRelativePath(options.fileName ?? fileNameForSource$1(sourcePath));
356
- const files = {};
357
- const fetchedUrls = /* @__PURE__ */ new Set();
358
- let resolvedFiles = 0;
359
- for (const ref of textureRefs) {
360
- if (resolvedFiles >= remainingFiles) break;
361
- for (const attempt of buildFetchAttempts(ref, sourceUrl, rootRelativePath, searchExtensions, sourceUrl, searchRoots)) {
362
- if (attempt.fsPaths.some((path) => existingFiles[path] || files[path])) break;
363
- if (fetchedUrls.has(attempt.url)) continue;
364
- fetchedUrls.add(attempt.url);
365
- const data = await fetchBinary(attempt.url);
366
- if (!data) continue;
367
- for (const fsPath of attempt.fsPaths) files[fsPath] = data;
368
- resolvedFiles += 1;
369
- break;
370
- }
371
- }
372
- return files;
373
- }
374
- function readZipEntryName(data, start, length) {
375
- return new TextDecoder().decode(data.subarray(start, start + length));
376
- }
377
- function extractStoredZipEntries(data) {
378
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
379
- const entries = /* @__PURE__ */ new Map();
380
- let offset = 0;
381
- while (offset + 30 <= data.byteLength) {
382
- if (view.getUint32(offset, true) !== 67324752) break;
383
- const method = view.getUint16(offset + 8, true);
384
- const compressedSize = view.getUint32(offset + 18, true);
385
- const fileNameLength = view.getUint16(offset + 26, true);
386
- const extraLength = view.getUint16(offset + 28, true);
387
- const nameStart = offset + 30;
388
- const dataStart = nameStart + fileNameLength + extraLength;
389
- const dataEnd = dataStart + compressedSize;
390
- if (dataEnd > data.byteLength) break;
391
- const name = normalizeFsRelativePath(readZipEntryName(data, nameStart, fileNameLength));
392
- if (name && method === 0) entries.set(name, data.slice(dataStart, dataEnd));
393
- offset = dataEnd;
394
- }
395
- return entries;
396
- }
397
- function createTextureResolverFromEntries(entries) {
398
- if (typeof Blob === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return null;
399
- const imageEntries = new Map([...entries].filter(([path]) => [
400
- ".png",
401
- ".jpg",
402
- ".jpeg",
403
- ".webp"
404
- ].includes(extensionOf(path))));
405
- if (imageEntries.size === 0) return null;
406
- const byBaseName = /* @__PURE__ */ new Map();
407
- for (const path of imageEntries.keys()) {
408
- const name = baseName(path);
409
- byBaseName.set(name, byBaseName.has(name) ? null : path);
410
- }
411
- const urls = /* @__PURE__ */ new Map();
412
- const urlForEntry = (path) => {
413
- const entry = imageEntries.get(path);
414
- if (!entry) return null;
415
- let url = urls.get(path);
416
- if (!url) {
417
- url = URL.createObjectURL(new Blob([toArrayBuffer(entry)], { type: contentTypeForPath(path) }));
418
- urls.set(path, url);
419
- }
420
- return url;
421
- };
422
- const resolvePath = (value) => {
423
- if (!value) return null;
424
- const normalized = normalizeFsRelativePath(value.split(/[?#]/)[0] ?? value);
425
- if (!normalized) return null;
426
- if (imageEntries.has(normalized)) return urlForEntry(normalized);
427
- const suffixMatches = [...imageEntries.keys()].filter((path) => path.endsWith(`/${normalized}`));
428
- if (suffixMatches.length === 1 && suffixMatches[0]) return urlForEntry(suffixMatches[0]);
429
- const virtualFsMatches = [...imageEntries.keys()].filter((path) => normalized.endsWith(`/${path}`));
430
- if (virtualFsMatches.length === 1 && virtualFsMatches[0]) return urlForEntry(virtualFsMatches[0]);
431
- const uniqueBaseName = byBaseName.get(baseName(normalized));
432
- return uniqueBaseName ? urlForEntry(uniqueBaseName) : null;
433
- };
434
- return {
435
- urls,
436
- resolve(asset) {
437
- return resolvePath(asset.resolvedPath) ?? resolvePath(asset.path) ?? resolvePath(asset.url) ?? null;
438
- }
439
- };
440
- }
441
- function createPackageTextureResolver(data) {
442
- return createTextureResolverFromEntries(extractStoredZipEntries(data));
443
- }
444
- //#endregion
4
+ import { autoResolveAssetFiles, autoResolveTextureFiles, createPackageTextureResolver, createTextureResolverFromEntries, findPackageRootLayer } from "@openusd-wasm/utils";
445
5
  //#region src/metadata.ts
446
6
  const MATERIAL_INPUT_SUFFIXES = [
447
7
  "diffuseColor",
@@ -458,6 +18,9 @@ const MATERIAL_INPUT_SUFFIXES = [
458
18
  "file",
459
19
  "wrapS",
460
20
  "wrapT",
21
+ "st",
22
+ "in",
23
+ "varname",
461
24
  "scale",
462
25
  "translation",
463
26
  "rotation"
@@ -476,6 +39,13 @@ function dispose$1(value) {
476
39
  function disposeAll(values) {
477
40
  for (let i = values.length - 1; i >= 0; --i) dispose$1(values[i]);
478
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
+ }
479
49
  function isValid$1(value) {
480
50
  if (!value) return false;
481
51
  if (typeof value.IsValid === "function") return Boolean(value.IsValid());
@@ -533,170 +103,24 @@ function toVector4(value) {
533
103
  }
534
104
  return null;
535
105
  }
536
- function identityMatrix() {
537
- return [
538
- 1,
539
- 0,
540
- 0,
541
- 0,
542
- 0,
543
- 1,
544
- 0,
545
- 0,
546
- 0,
547
- 0,
548
- 1,
549
- 0,
550
- 0,
551
- 0,
552
- 0,
553
- 1
554
- ];
555
- }
556
- function multiplyMatrices(a, b) {
557
- const out = new Array(16).fill(0);
558
- 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);
559
- return out;
560
- }
561
106
  function matricesNearlyEqual(a, b) {
562
107
  if (!a || !b || a.length < 16 || b.length < 16) return false;
563
108
  return a.every((value, index) => Math.abs(value - (b[index] ?? 0)) < 1e-8);
564
109
  }
565
- function matrixFromXformOp(opName, value) {
566
- const numbers = toNumberArray(value);
567
- if (numbers.length === 0) return null;
568
- if (opName.startsWith("xformOp:transform")) return numbers.length >= 16 ? numbers.slice(0, 16) : null;
569
- if (opName.startsWith("xformOp:translate")) return [
570
- 1,
571
- 0,
572
- 0,
573
- 0,
574
- 0,
575
- 1,
576
- 0,
577
- 0,
578
- 0,
579
- 0,
580
- 1,
581
- 0,
582
- numbers[0] ?? 0,
583
- numbers[1] ?? 0,
584
- numbers[2] ?? 0,
585
- 1
586
- ];
587
- if (opName.startsWith("xformOp:scale")) return [
588
- numbers[0] ?? 1,
589
- 0,
590
- 0,
591
- 0,
592
- 0,
593
- numbers[1] ?? numbers[0] ?? 1,
594
- 0,
595
- 0,
596
- 0,
597
- 0,
598
- numbers[2] ?? numbers[0] ?? 1,
599
- 0,
600
- 0,
601
- 0,
602
- 0,
603
- 1
604
- ];
605
- if (opName.startsWith("xformOp:rotate")) return rotationMatrixFromXformOp(opName, numbers);
606
- return null;
607
- }
608
- function rotationMatrixFromXformOp(opName, degrees) {
609
- const rotateX = (degree) => {
610
- const radians = degree * Math.PI / 180;
611
- const cos = Math.cos(radians);
612
- const sin = Math.sin(radians);
613
- return [
614
- 1,
615
- 0,
616
- 0,
617
- 0,
618
- 0,
619
- cos,
620
- sin,
621
- 0,
622
- 0,
623
- -sin,
624
- cos,
625
- 0,
626
- 0,
627
- 0,
628
- 0,
629
- 1
630
- ];
631
- };
632
- const rotateY = (degree) => {
633
- const radians = degree * Math.PI / 180;
634
- const cos = Math.cos(radians);
635
- const sin = Math.sin(radians);
636
- return [
637
- cos,
638
- 0,
639
- -sin,
640
- 0,
641
- 0,
642
- 1,
643
- 0,
644
- 0,
645
- sin,
646
- 0,
647
- cos,
648
- 0,
649
- 0,
650
- 0,
651
- 0,
652
- 1
653
- ];
654
- };
655
- const rotateZ = (degree) => {
656
- const radians = degree * Math.PI / 180;
657
- const cos = Math.cos(radians);
658
- const sin = Math.sin(radians);
659
- return [
660
- cos,
661
- sin,
662
- 0,
663
- 0,
664
- -sin,
665
- cos,
666
- 0,
667
- 0,
668
- 0,
669
- 0,
670
- 1,
671
- 0,
672
- 0,
673
- 0,
674
- 0,
675
- 1
676
- ];
677
- };
678
- const suffix = opName.split(":")[1] ?? "";
679
- if (suffix === "rotateX") return rotateX(degrees[0] ?? 0);
680
- if (suffix === "rotateY") return rotateY(degrees[0] ?? 0);
681
- if (suffix === "rotateZ") return rotateZ(degrees[0] ?? 0);
682
- const axes = suffix.replace(/^rotate/, "").split("");
683
- if (axes.length === 0) return null;
684
- return axes.reduce((matrix, axis, index) => {
685
- const degree = degrees[index] ?? 0;
686
- const next = axis === "X" ? rotateX(degree) : axis === "Y" ? rotateY(degree) : axis === "Z" ? rotateZ(degree) : null;
687
- return next ? multiplyMatrices(matrix, next) : matrix;
688
- }, identityMatrix());
689
- }
690
110
  function attr(prim, name, time, defaultValue = null) {
691
111
  const attribute = prim.GetAttribute(name);
692
112
  try {
693
- if (!isValid$1(attribute)) return defaultValue;
694
- const value = attribute.Get(time);
695
- return value === null || value === void 0 ? defaultValue : normalizeValue(value);
113
+ return attributeValue(attribute, time, defaultValue);
696
114
  } finally {
697
115
  dispose$1(attribute);
698
116
  }
699
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
+ }
700
124
  function attrMetadata(prim, name, key, defaultValue = null) {
701
125
  const attribute = prim.GetAttribute(name);
702
126
  try {
@@ -707,20 +131,64 @@ function attrMetadata(prim, name, key, defaultValue = null) {
707
131
  dispose$1(attribute);
708
132
  }
709
133
  }
710
- function relationshipTargets(prim, name) {
711
- 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]();
712
154
  try {
713
- if (!isValid$1(relationship)) return [];
714
- const targets = relationship.GetTargets();
715
- return Array.isArray(targets) ? targets.map(String) : [];
155
+ return relationshipTargetPaths(relationship);
716
156
  } finally {
717
157
  dispose$1(relationship);
718
158
  }
719
159
  }
720
- function connectionSourcePath(attribute) {
721
- const connections = attribute.GetConnections();
722
- if (!Array.isArray(connections) || connections.length === 0) return null;
723
- 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
+ }
724
192
  }
725
193
  function primChildren(prim) {
726
194
  const children = prim.GetChildren();
@@ -734,24 +202,37 @@ function primAtPath(pxr, stage, path) {
734
202
  dispose$1(sdfPath);
735
203
  }
736
204
  }
737
- function materialBinding(prim) {
738
- let current = prim;
739
- let ownsCurrent = false;
205
+ function materialBinding(pxr, prim) {
206
+ const binding = new pxr.UsdShade.MaterialBindingAPI(prim);
740
207
  try {
741
- while (current && !current.IsPseudoRoot()) {
742
- const targets = relationshipTargets(current, "material:binding");
743
- if (targets.length > 0) return targets[0] ?? null;
744
- const parent = current.GetParent();
745
- if (ownsCurrent) dispose$1(current);
746
- current = parent;
747
- 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);
748
230
  }
749
- return null;
750
231
  } finally {
751
- if (ownsCurrent) dispose$1(current);
232
+ dispose$1(binding);
752
233
  }
753
234
  }
754
- function localTransformForPrim(pxr, prim) {
235
+ function localTransformForPrim(pxr, prim, time) {
755
236
  if (!prim.IsA("Xformable")) return {
756
237
  localMatrix: null,
757
238
  resetsXformStack: false
@@ -762,7 +243,7 @@ function localTransformForPrim(pxr, prim) {
762
243
  localMatrix: null,
763
244
  resetsXformStack: false
764
245
  };
765
- const transform = xformable.GetLocalTransformation();
246
+ const transform = xformable.GetLocalTransformation(time);
766
247
  return {
767
248
  localMatrix: toNumberArray(transform?.matrix),
768
249
  resetsXformStack: Boolean(transform?.resetsXformStack)
@@ -782,7 +263,7 @@ function parentPathForPrim(prim) {
782
263
  }
783
264
  function getModelView(pxr, prims, time) {
784
265
  return { prims: prims.map((prim) => {
785
- const transform = localTransformForPrim(pxr, prim);
266
+ const transform = localTransformForPrim(pxr, prim, time);
786
267
  return {
787
268
  path: String(prim.GetPath()),
788
269
  parentPath: parentPathForPrim(prim),
@@ -831,13 +312,27 @@ function asFloat(value, fallback = 0) {
831
312
  const number = asFiniteNumber(value);
832
313
  return number === null ? fallback : number;
833
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
+ }
834
329
  function shaderInputConnectionPath(shader, inputName) {
835
- const attribute = shader.GetAttribute(inputName);
330
+ const input = shader.GetInput(inputName);
836
331
  try {
837
- if (!isValid$1(attribute)) return null;
838
- return connectionSourcePath(attribute);
332
+ if (!isValid$1(input)) return null;
333
+ return inputSourcePrimPath(input);
839
334
  } finally {
840
- dispose$1(attribute);
335
+ dispose$1(input);
841
336
  }
842
337
  }
843
338
  function shaderChildByPath(material, path) {
@@ -850,29 +345,41 @@ function shaderChildByPath(material, path) {
850
345
  }
851
346
  }
852
347
  function primvarNameForTextureShader(pxr, stage, material, textureShader, time) {
853
- const stSourcePath = shaderInputConnectionPath(textureShader, "inputs:st");
348
+ const stSourcePath = shaderInputConnectionPath(textureShader, "st");
854
349
  if (!stSourcePath) return null;
855
350
  const stSource = shaderChildByPath(material, stSourcePath) ?? primAtPath(pxr, stage, stSourcePath);
856
351
  try {
857
352
  if (!isValid$1(stSource)) return null;
858
- const id = attr(stSource, "info:id", time);
859
- if (id === "UsdPrimvarReader_float2") {
860
- const varname = attr(stSource, "inputs:varname", time);
861
- return typeof varname === "string" && varname ? varname : null;
862
- }
863
- if (id === "UsdTransform2d") {
864
- const nestedSourcePath = shaderInputConnectionPath(stSource, "inputs:in") ?? shaderInputConnectionPath(stSource, "inputs:st");
865
- if (!nestedSourcePath) return null;
866
- const nestedSource = shaderChildByPath(material, nestedSourcePath) ?? primAtPath(pxr, stage, nestedSourcePath);
867
- try {
868
- if (!isValid$1(nestedSource) || attr(nestedSource, "info:id", time) !== "UsdPrimvarReader_float2") return null;
869
- 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);
870
359
  return typeof varname === "string" && varname ? varname : null;
871
- } finally {
872
- dispose$1(nestedSource);
873
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);
874
382
  }
875
- return null;
876
383
  } finally {
877
384
  dispose$1(stSource);
878
385
  }
@@ -884,10 +391,16 @@ function texturePrimvarForMaterial(pxr, stage, materialPath, time) {
884
391
  if (!isValid$1(material)) return "primvars:st";
885
392
  const children = primChildren(material);
886
393
  try {
887
- for (const shader of children) {
888
- if (shader.GetTypeName() !== "Shader" || attr(shader, "info:id", time) !== "UsdUVTexture") continue;
889
- const varname = primvarNameForTextureShader(pxr, stage, material, shader, time);
890
- 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
+ }
891
404
  }
892
405
  } finally {
893
406
  disposeAll(children);
@@ -904,30 +417,36 @@ function textureTransformForMaterial(pxr, stage, materialPath, time) {
904
417
  if (!isValid$1(material)) return null;
905
418
  const children = primChildren(material);
906
419
  try {
907
- for (const shader of children) {
908
- if (shader.GetTypeName() !== "Shader" || attr(shader, "info:id", time) !== "UsdUVTexture") continue;
909
- 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);
910
423
  try {
911
- if (!isValid$1(stAttribute)) continue;
912
- const sourcePath = connectionSourcePath(stAttribute);
424
+ if (!isValid$1(shader) || shaderId(shader) !== "UsdUVTexture") continue;
425
+ const sourcePath = shaderInputConnectionPath(shader, "st");
913
426
  if (!sourcePath) return null;
914
427
  const transform = primAtPath(pxr, stage, sourcePath);
915
428
  try {
916
- if (!isValid$1(transform) || attr(transform, "info:id", time) !== "UsdTransform2d") return null;
917
- const scale = asVec2(attr(transform, "inputs:scale", time), [1, 1]);
918
- const translation = asVec2(attr(transform, "inputs:translation", time), [0, 0]);
919
- return {
920
- scaleX: scale[0],
921
- scaleY: scale[1],
922
- translateX: translation[0],
923
- translateY: translation[1],
924
- rotation: asFloat(attr(transform, "inputs:rotation", time))
925
- };
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
+ }
926
445
  } finally {
927
446
  dispose$1(transform);
928
447
  }
929
448
  } finally {
930
- dispose$1(stAttribute);
449
+ dispose$1(shader);
931
450
  }
932
451
  }
933
452
  } finally {
@@ -963,7 +482,7 @@ function triangulateMesh(pxr, stage, prim, time) {
963
482
  const faceIndices = toNumberArray(attr(prim, "faceVertexIndices", time, []));
964
483
  const normals = toArray(attr(prim, "normals", time, []));
965
484
  const normalInterpolation = String(attrMetadata(prim, "normals", "interpolation", attr(prim, "normals:interpolation", time, "")) || "") || null;
966
- const boundMaterial = materialBinding(prim);
485
+ const boundMaterial = materialBinding(pxr, prim);
967
486
  const uv = meshPrimvar(prim, texturePrimvarForMaterial(pxr, stage, boundMaterial, time), time);
968
487
  const colors = toArray(attr(prim, "primvars:displayColor", time, []));
969
488
  const opacities = toArray(attr(prim, "primvars:displayOpacity", time, []));
@@ -1022,178 +541,244 @@ function getModelElements(pxr, stage, prims, time) {
1022
541
  elements.push({
1023
542
  path: String(prim.GetPath()),
1024
543
  doubleSided: Boolean(attr(prim, "doubleSided", time, false)),
1025
- material: materialBinding(prim),
544
+ material: materialBinding(pxr, prim),
1026
545
  geometry: triangulateMesh(pxr, stage, prim, time)
1027
546
  });
1028
547
  }
1029
548
  return elements;
1030
549
  }
1031
- function collectShaderInputs(prim, time) {
550
+ function collectShadeInputs(connectable, time) {
1032
551
  const inputs = {};
1033
- const attributes = prim.GetAttributes();
552
+ const shadeInputs = connectable.GetInputs();
1034
553
  try {
1035
- for (const attribute of attributes) {
1036
- const name = String(attribute.GetName());
1037
- if (!name.includes("inputs:")) continue;
1038
- if (!MATERIAL_INPUT_SUFFIXES.some((suffix) => name.endsWith(suffix))) continue;
1039
- const value = attribute.Get(time);
1040
- 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();
1041
560
  if (value !== null && value !== void 0) inputs[name] = normalizeValue(value);
1042
- 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
+ }
1043
566
  }
1044
567
  } finally {
1045
- disposeAll(attributes);
568
+ disposeAll(shadeInputs);
569
+ }
570
+ return inputs;
571
+ }
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
+ }
1046
587
  }
1047
588
  return inputs;
1048
589
  }
1049
- function getModelMaterials(prims, time) {
590
+ function getModelMaterials(pxr, prims, time) {
1050
591
  const materials = [];
1051
592
  for (const prim of prims) {
1052
593
  if (prim.GetTypeName() !== "Material") continue;
594
+ const material = new pxr.UsdShade.Material(prim);
1053
595
  const children = primChildren(prim);
1054
596
  const shaders = [];
1055
597
  try {
598
+ if (!isValid$1(material)) continue;
1056
599
  for (const child of children) {
1057
600
  if (child.GetTypeName() !== "Shader") continue;
1058
- const idValue = attr(child, "info:id", time);
1059
- const id = typeof idValue === "string" ? idValue : null;
1060
- const inputs = collectShaderInputs(child, time);
1061
- if (Object.keys(inputs).length === 0 && id !== "UsdPreviewSurface" && id !== "UsdUVTexture") continue;
1062
- shaders.push({
1063
- path: String(child.GetPath()),
1064
- id,
1065
- inputs
1066
- });
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
+ }
1067
619
  }
620
+ materials.push({
621
+ path: String(prim.GetPath()),
622
+ inputs: collectShadeInputs(material, time),
623
+ shaders
624
+ });
1068
625
  } finally {
1069
626
  disposeAll(children);
627
+ dispose$1(material);
1070
628
  }
1071
- materials.push({
1072
- path: String(prim.GetPath()),
1073
- inputs: collectShaderInputs(prim, time),
1074
- shaders
1075
- });
1076
629
  }
1077
630
  return materials;
1078
631
  }
1079
- function driveForNamespace(prim, namespace, time) {
632
+ function driveForNamespace(pxr, prim, namespace, time) {
633
+ const driveAPI = new pxr.UsdPhysics.DriveAPI(prim, namespace);
1080
634
  const drive = {
1081
- targetPosition: asFiniteNumber(attr(prim, `drive:${namespace}:physics:targetPosition`, time)),
1082
- targetVelocity: asFiniteNumber(attr(prim, `drive:${namespace}:physics:targetVelocity`, time)),
1083
- stiffness: asFiniteNumber(attr(prim, `drive:${namespace}:physics:stiffness`, time)),
1084
- damping: asFiniteNumber(attr(prim, `drive:${namespace}:physics:damping`, time)),
1085
- maxForce: asFiniteNumber(attr(prim, `drive:${namespace}:physics:maxForce`, time))
635
+ targetPosition: null,
636
+ targetVelocity: null,
637
+ stiffness: null,
638
+ damping: null,
639
+ maxForce: null
1086
640
  };
1087
- 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
+ }
1088
652
  }
1089
653
  function jointTypeForPrim(typeName) {
1090
654
  if (JOINT_TYPES[typeName]) return JOINT_TYPES[typeName];
1091
655
  if (typeName.startsWith("Physics") && typeName.endsWith("Joint")) return typeName.slice(7, -5).toLowerCase() || "joint";
1092
656
  return null;
1093
657
  }
1094
- 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) {
1095
704
  const joints = [];
1096
705
  for (const prim of prims) {
1097
706
  const typeName = String(prim.GetTypeName());
1098
707
  const jointType = jointTypeForPrim(typeName);
1099
708
  if (!jointType) continue;
1100
- const angularDrive = driveForNamespace(prim, "angular", time);
1101
- 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);
1102
716
  const drives = {};
1103
717
  if (angularDrive) drives.angular = angularDrive;
1104
718
  if (linearDrive) drives.linear = linearDrive;
1105
- const body0 = relationshipTargets(prim, "physics:body0");
1106
- const body1 = relationshipTargets(prim, "physics:body1");
719
+ const body0 = schemaRelationshipTargets(joint, "GetBody0Rel");
720
+ const body1 = schemaRelationshipTargets(joint, "GetBody1Rel");
1107
721
  const drive = jointType === "prismatic" ? linearDrive ?? angularDrive : angularDrive ?? linearDrive;
1108
- joints.push({
1109
- path: String(prim.GetPath()),
1110
- name: String(prim.GetName()),
1111
- typeName,
1112
- jointType,
1113
- body0: body0[0] ?? null,
1114
- body1: body1[0] ?? null,
1115
- axis: typeof attr(prim, "physics:axis", time) === "string" ? String(attr(prim, "physics:axis", time)) : null,
1116
- localPos0: toVector3(attr(prim, "physics:localPos0", time, [
1117
- 0,
1118
- 0,
1119
- 0
1120
- ]), [
1121
- 0,
1122
- 0,
1123
- 0
1124
- ]),
1125
- localPos1: toVector3(attr(prim, "physics:localPos1", time, [
1126
- 0,
1127
- 0,
1128
- 0
1129
- ]), [
1130
- 0,
1131
- 0,
1132
- 0
1133
- ]),
1134
- localRot0: toVector4(attr(prim, "physics:localRot0", time)),
1135
- localRot1: toVector4(attr(prim, "physics:localRot1", time)),
1136
- lowerLimit: asFiniteNumber(attr(prim, "physics:lowerLimit", time)),
1137
- upperLimit: asFiniteNumber(attr(prim, "physics:upperLimit", time)),
1138
- enabled: Boolean(attr(prim, "physics:jointEnabled", time, true)),
1139
- drive,
1140
- drives
1141
- });
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
+ }
1142
759
  }
1143
760
  return joints;
1144
761
  }
1145
- function stageTimingFromLayer(rootLayer) {
1146
- const text = String(rootLayer.ExportToString?.() ?? "");
1147
- const numberFor = (name) => {
1148
- const match = new RegExp(`${name}\\s*=\\s*(-?\\d+(?:\\.\\d+)?)`).exec(text);
1149
- if (!match?.[1]) return null;
1150
- const value = Number(match[1]);
1151
- return Number.isFinite(value) ? value : null;
1152
- };
1153
- return {
1154
- startTimeCode: numberFor("startTimeCode"),
1155
- endTimeCode: numberFor("endTimeCode"),
1156
- timeCodesPerSecond: numberFor("timeCodesPerSecond") ?? 24
1157
- };
1158
- }
1159
762
  function getStageInfo(pxr, stage, options) {
1160
763
  const defaultPrim = stage.GetDefaultPrim();
1161
- const rootLayer = stage.GetRootLayer();
1162
764
  try {
1163
- const timing = stageTimingFromLayer(rootLayer);
1164
765
  return {
1165
766
  sourcePath: options.sourcePath,
1166
767
  rootLayerIdentifier: options.rootLayerIdentifier,
1167
768
  defaultPrim: isValid$1(defaultPrim) ? String(defaultPrim.GetPath()) : null,
1168
769
  upAxis: String(pxr.UsdGeom.GetStageUpAxis(stage)),
1169
- ...timing
770
+ startTimeCode: stage.GetStartTimeCode(),
771
+ endTimeCode: stage.GetEndTimeCode(),
772
+ timeCodesPerSecond: stage.GetTimeCodesPerSecond()
1170
773
  };
1171
774
  } finally {
1172
- dispose$1(rootLayer);
1173
775
  dispose$1(defaultPrim);
1174
776
  }
1175
777
  }
1176
- function localMatrixFromXformOps(prim, time) {
1177
- const opOrder = toArray(attr(prim, "xformOpOrder", time, [])).map(String);
1178
- if (opOrder.length === 0) return null;
1179
- let matrix = identityMatrix();
1180
- let foundOp = false;
1181
- for (const opName of opOrder) {
1182
- if (opName.startsWith("!")) {
1183
- matrix = identityMatrix();
1184
- continue;
1185
- }
1186
- const opMatrix = matrixFromXformOp(opName, attr(prim, opName, time));
1187
- if (!opMatrix) continue;
1188
- matrix = multiplyMatrices(matrix, opMatrix);
1189
- foundOp = true;
1190
- }
1191
- return foundOp ? matrix : null;
1192
- }
1193
778
  function sampleLocalMatrix(pxr, prim, frame) {
1194
779
  const time = new pxr.Usd.TimeCode(frame);
1195
780
  try {
1196
- return localMatrixFromXformOps(prim, time);
781
+ return localTransformForPrim(pxr, prim, time).localMatrix;
1197
782
  } finally {
1198
783
  dispose$1(time);
1199
784
  }
@@ -1210,10 +795,10 @@ function animationSampleFrames(start, end) {
1210
795
  });
1211
796
  }
1212
797
  function getModelAnimations(pxr, prims, stageInfo) {
1213
- const start = stageInfo.startTimeCode ?? null;
1214
- const end = stageInfo.endTimeCode ?? null;
1215
- const timeCodesPerSecond = stageInfo.timeCodesPerSecond ?? 24;
1216
- 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 [];
1217
802
  const frames = animationSampleFrames(start, end);
1218
803
  if (frames.length < 2) return [];
1219
804
  const transforms = [];
@@ -1260,8 +845,8 @@ function extractUSDModelData(pxr, stage, options = {}) {
1260
845
  stage: stageInfo,
1261
846
  view: getModelView(pxr, prims, time),
1262
847
  elements: getModelElements(pxr, stage, prims, time),
1263
- materials: getModelMaterials(prims, time),
1264
- joints: getModelJoints(prims, time),
848
+ materials: getModelMaterials(pxr, prims, time),
849
+ joints: getModelJoints(pxr, prims, time),
1265
850
  animations: getModelAnimations(pxr, prims, stageInfo)
1266
851
  };
1267
852
  } finally {
@@ -1566,13 +1151,19 @@ function deleteFileIfExists(pxr, path) {
1566
1151
  if (pxr.FS.exists(path)) pxr.FS.deleteFile(path);
1567
1152
  } catch {}
1568
1153
  }
1569
- async function writeUSDInput(pxr, input, options) {
1570
- const directory = joinFsPath(options.workingDirectory, String(++nextLoadId));
1154
+ function createUSDInputDirectory(pxr, workingDirectory) {
1155
+ const directory = joinFsPath(workingDirectory, String(++nextLoadId));
1571
1156
  pxr.FS.createDir(directory);
1572
- 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 ?? {})) {
1573
1161
  const filePath = joinFsPath(directory, relativePath);
1574
1162
  pxr.FS.writeFile(filePath, await sourceToWritableData(file));
1575
1163
  }
1164
+ }
1165
+ async function writeUSDInput(pxr, directory, input, options) {
1166
+ await writeUSDFiles(pxr, directory, options.files);
1576
1167
  const path = joinFsPath(directory, sanitizeFileName(options.fileName));
1577
1168
  pxr.FS.writeFile(path, input);
1578
1169
  return path;
@@ -1655,15 +1246,19 @@ var USDLoader = class extends Loader {
1655
1246
  const extension = extensionForPath(mergedOptions.fileName);
1656
1247
  const writableData = await sourceToWritableData(input);
1657
1248
  const packageTextureResolver = extension === "usdz" && writableData instanceof Uint8Array ? createPackageTextureResolver(writableData) : null;
1658
- let autoFiles = writableData instanceof Uint8Array && extension !== "usdz" ? await autoResolveAssetFiles(writableData, mergedOptions) : {};
1659
- const filePath = await writeUSDInput(pxr, writableData, {
1660
- ...mergedOptions,
1661
- files: {
1662
- ...autoFiles,
1663
- ...mergedOptions.files
1664
- }
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
1665
1255
  });
1666
- 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);
1667
1262
  try {
1668
1263
  const metadata = extractUSDModelData(pxr, stage, {
1669
1264
  sourcePath: mergedOptions.sourcePath,
@@ -1719,7 +1314,7 @@ function limitsForJoint(joint) {
1719
1314
  }
1720
1315
  function initialValueForJoint(joint) {
1721
1316
  const { lower, upper } = limitsForJoint(joint);
1722
- return clamp(joint.drive?.targetPosition ?? lower, lower, upper);
1317
+ return clamp(joint.drive?.targetPosition ?? 0, lower, upper);
1723
1318
  }
1724
1319
  function isManipulableJoint(joint) {
1725
1320
  return joint.enabled && (joint.jointType === "revolute" || joint.jointType === "prismatic");
@@ -1785,7 +1380,12 @@ function jointValueKey(model, joint) {
1785
1380
  return `${model.root.uuid}:${joint.path}`;
1786
1381
  }
1787
1382
  function localAxisForJoint(joint) {
1788
- 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());
1789
1389
  }
1790
1390
  function localPivotForJoint(joint) {
1791
1391
  return new THREE.Vector3(joint.localPos0[0] ?? 0, joint.localPos0[1] ?? 0, joint.localPos0[2] ?? 0);