@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.
- package/README.md +12 -0
- package/dist/openusd_three_loader.d.ts +7 -27
- package/dist/openusd_three_loader.js +389 -847
- package/package.json +6 -4
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
771
|
-
|
|
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
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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
|
|
781
|
-
const
|
|
782
|
-
if (!
|
|
783
|
-
|
|
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
|
-
|
|
799
|
-
let ownsCurrent = false;
|
|
205
|
+
function materialBinding(pxr, prim) {
|
|
206
|
+
const binding = new pxr.UsdShade.MaterialBindingAPI(prim);
|
|
800
207
|
try {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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
|
-
|
|
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
|
|
331
|
+
const input = shader.GetInput(inputName);
|
|
896
332
|
try {
|
|
897
|
-
if (!isValid$1(
|
|
898
|
-
return
|
|
333
|
+
if (!isValid$1(input)) return null;
|
|
334
|
+
return inputSourcePrimPath(input);
|
|
899
335
|
} finally {
|
|
900
|
-
dispose$1(
|
|
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, "
|
|
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
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
|
948
|
-
if (
|
|
949
|
-
const
|
|
950
|
-
|
|
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
|
|
968
|
-
if (
|
|
969
|
-
const
|
|
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(
|
|
972
|
-
const sourcePath =
|
|
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)
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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(
|
|
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
|
|
551
|
+
function collectShadeInputs(connectable, time) {
|
|
1092
552
|
const inputs = {};
|
|
1093
|
-
const
|
|
553
|
+
const shadeInputs = connectable.GetInputs();
|
|
1094
554
|
try {
|
|
1095
|
-
for (const
|
|
1096
|
-
const
|
|
1097
|
-
if (!
|
|
1098
|
-
const
|
|
1099
|
-
const
|
|
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)
|
|
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(
|
|
569
|
+
disposeAll(shadeInputs);
|
|
1105
570
|
}
|
|
1106
571
|
return inputs;
|
|
1107
572
|
}
|
|
1108
|
-
function
|
|
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
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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:
|
|
1141
|
-
targetVelocity:
|
|
1142
|
-
stiffness:
|
|
1143
|
-
damping:
|
|
1144
|
-
maxForce:
|
|
636
|
+
targetPosition: null,
|
|
637
|
+
targetVelocity: null,
|
|
638
|
+
stiffness: null,
|
|
639
|
+
damping: null,
|
|
640
|
+
maxForce: null
|
|
1145
641
|
};
|
|
1146
|
-
|
|
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
|
|
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
|
|
1160
|
-
|
|
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 =
|
|
1165
|
-
const body1 =
|
|
720
|
+
const body0 = schemaRelationshipTargets(joint, "GetBody0Rel");
|
|
721
|
+
const body1 = schemaRelationshipTargets(joint, "GetBody1Rel");
|
|
1166
722
|
const drive = jointType === "prismatic" ? linearDrive ?? angularDrive : angularDrive ?? linearDrive;
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
1273
|
-
const end = stageInfo.endTimeCode
|
|
1274
|
-
const timeCodesPerSecond = stageInfo.timeCodesPerSecond
|
|
1275
|
-
if (
|
|
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" &&
|
|
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
|
-
|
|
1629
|
-
const directory = joinFsPath(
|
|
1155
|
+
function createUSDInputDirectory(pxr, workingDirectory) {
|
|
1156
|
+
const directory = joinFsPath(workingDirectory, String(++nextLoadId));
|
|
1630
1157
|
pxr.FS.createDir(directory);
|
|
1631
|
-
|
|
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
|
-
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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
|
-
|
|
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 ??
|
|
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
|
-
|
|
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,
|
|
1749
|
+
export { USDLoader, USDManipulationControls, buildUSDObject, createUSDLoadedModel, extractUSDModelData };
|